Installation
composer require duylecampos/jwt-oauth2-bundle
Add to config/bundles.php:
return [
// ...
DuyleCampos\JwtOauth2Bundle\DuyleCamposJwtOauth2Bundle::class => ['all' => true],
];
Publish Config
php artisan vendor:publish --provider="DuyleCampos\JwtOauth2Bundle\DuyleCamposJwtOauth2Bundle" --tag="config"
Edit config/jwt_oauth2.php for your OAuth2 provider (e.g., Auth0, Okta).
First Use Case: Authenticate via OAuth2
Add middleware to app/Http/Kernel.php:
'api' => [
\DuyleCampos\JwtOauth2Bundle\Http\Middleware\JwtOauth2::class,
],
Test with a protected route:
Route::middleware(['api'])->get('/user', function () {
return auth()->user();
});
Token Validation
Automatically validates JWT tokens from the Authorization: Bearer <token> header.
if (auth()->check()) {
$user = auth()->user(); // Returns decoded user data
}
Custom Claims Handling Extend the bundle to handle custom claims:
// In a service provider
$this->app->bind(\DuyleCampos\JwtOauth2Bundle\Services\JwtDecoder::class, function ($app) {
return new CustomJwtDecoder($app['config']['jwt_oauth2']);
});
OAuth2 Provider Switching
Update config/jwt_oauth2.php to switch providers (e.g., Auth0 → Okta):
providers:
auth0:
enabled: false
okta:
enabled: true
client_id: "your_okta_client_id"
client_secret: "your_okta_secret"
domain: "your-okta-domain.okta.com"
Integration with Laravel Auth
Use the bundle’s User model trait for seamless auth:
use DuyleCampos\JwtOauth2Bundle\Traits\HasJwtOauth2;
class User extends Authenticatable {
use HasJwtOauth2;
}
Token Refresh Implement a refresh token endpoint:
Route::post('/refresh-token', function () {
$token = request()->input('refresh_token');
return response()->json([
'access_token' => auth()->refresh($token),
]);
});
Token Expiry Handling
try-catch for TokenExpiredException:
try {
$user = auth()->user();
} catch (\DuyleCampos\JwtOauth2Bundle\Exceptions\TokenExpiredException $e) {
return response()->json(['error' => 'Token expired'], 401);
}
Config Overrides
config/jwt_oauth2.php is correctly published. Missing keys (e.g., client_secret) will throw InvalidArgumentException.Middleware Order
JwtOauth2 middleware before auth:api to avoid conflicts:
'api' => [
\DuyleCampos\JwtOauth2Bundle\Http\Middleware\JwtOauth2::class,
\App\Http\Middleware\CheckForApiToken::class, // Laravel's default
],
Debugging Token Decoding
config/jwt_oauth2.php:
debug: true
storage/logs/laravel.log.Custom User Mapping
Map OAuth2 user data to your Laravel User model:
// In config/jwt_oauth2.php
user_mapping: [
'email' => 'email',
'name' => 'name',
'custom_field' => 'oauth_id',
]
Rate Limiting Combine with Laravel’s rate limiting:
Route::middleware(['throttle:60,1', 'api'])->get('/user');
Testing
Use Http\Testing\Middleware\JwtOauth2 in PHPUnit:
$response = $this->withHeaders([
'Authorization' => 'Bearer valid.jwt.token',
])->get('/user');
Extension Points
JwtDecoder to add custom logic (e.g., role validation):
public function decode($token) {
$decoded = parent::decode($token);
if (!$decoded['roles'] || !in_array('admin', $decoded['roles'])) {
throw new \RuntimeException('Insufficient permissions');
}
return $decoded;
}
CORS Configuration
Ensure your CORS middleware allows the Authorization header:
$headers = [
'Access-Control-Allow-Headers' => 'Origin, Content-Type, Accept, Authorization',
];
How can I help you explore Laravel packages today?