lexik/jwt-authentication-bundle
JWT authentication bundle for Symfony APIs. Issues and validates JSON Web Tokens, supports PHP 8.2+ and Symfony 6.4–8, and offers extensive docs for setup, configuration, customization, testing, CORS, and programmatic token creation.
Installation
composer require lexik/jwt-authentication-bundle
Enable the bundle in config/bundles.php:
return [
// ...
Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true],
];
Configure config/packages/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
secret_key: '%env(JWT_SECRET_KEY)%' # Generate with `openssl rand -hex 32`
public_key: '%env(JWT_PUBLIC_KEY)%' # Optional for asymmetric encryption
pass_phrase: '%env(JWT_PASSPHRASE)%' # Optional
token_ttl: 3600 # Token lifetime in seconds
First Use Case: Secure an API Endpoint
Add the is_granted('IS_AUTHENTICATED_FULLY') voter to your controller:
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('IS_AUTHENTICATED_FULLY')]
public function secureEndpoint(): JsonResponse
{
return new JsonResponse(['message' => 'Access granted']);
}
Generate a Token
Use the /api/login_check endpoint (configured in security.yaml):
security:
firewalls:
api:
pattern: ^/api
stateless: true
jwt: ~
Send a POST request with credentials to /api/login_check to receive a JWT.
Token-Based Authentication Flow
/login_check with username/password.Authorization: Bearer <token> header.JWTInvalidationManager (requires token storage).Custom User Provider
Extend Lexik\Bundle\JWTAuthenticationBundle\Services\JWTUserProvider:
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTUserProviderInterface;
class CustomUserProvider implements JWTUserProviderInterface
{
public function loadUserByIdentifier(string $identifier): UserInterface
{
// Custom logic to fetch user (e.g., from API, DB, or cache)
return $this->userRepository->findOneBy(['email' => $identifier]);
}
}
Register as a service:
lexik_jwt_authentication.user_provider: App\Security\CustomUserProvider
Token Creation Programmatically
Use JWTManager to generate tokens:
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
public function __construct(private JWTTokenManagerInterface $jwtManager) {}
public function generateToken(UserInterface $user): string
{
return $this->jwtManager->create($user);
}
Event-Driven Customization
Listen to JWT events (e.g., JWTEncodedEvent, JWTAuthenticatedEvent):
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTEncodedEvent;
public function onJWTEncoded(JWTEncodedEvent $event): void
{
$data = $event->getData();
$data['custom_claim'] = 'value'; // Add custom claims
$event->setData($data);
}
Register the subscriber:
services:
App\EventListener\JWTSubscriber:
tags: ['kernel.event_subscriber']
Token Extraction from Cookies
Configure cookie-based auth in lexik_jwt_authentication.yaml:
lexik_jwt_authentication:
cookie:
enabled: true
name: jwt_token
path: /
domain: ~
secure: auto
http_only: true
same_site: lax
Symfony Security Integration
Use JWTTokenAuthenticator in your firewall:
security:
firewalls:
api:
pattern: ^/api
stateless: true
provider: app_user_provider
jwt: ~
API Platform Integration
Add #[IsGranted('ROLE_USER')] to API resources:
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_USER')]
#[ApiResource]
class User {}
Testing Tokens
Use JWTTokenManager in tests:
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
public function testAuthenticatedRequest(Client $client, JWTTokenManagerInterface $jwtManager)
{
$user = $this->createMock(UserInterface::class);
$token = $jwtManager->create($user);
$client->request('GET', '/api/endpoint', [], [], [
'HTTP_Authorization' => 'Bearer ' . $token,
]);
}
Token Invalidation
Store tokens in a database (e.g., jwt_tokens table) and invalidate on logout:
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
public function logout(JWTTokenManagerInterface $jwtManager, Request $request)
{
$token = $request->headers->get('Authorization', '');
if (preg_match('/Bearer\s(\S+)/', $token, $matches)) {
$jwtManager->invalidate($matches[1]);
}
}
Key Management
%env(JWT_SECRET_KEY)%) and rotate keys periodically.openssl genrsa -out private.pem 4096 and openssl rsa -pubout -in private.pem -out public.pem for asymmetric keys.Token Expiration
token_ttl or using 0 (infinite lifetime).3600 for 1 hour) and implement refresh tokens for long sessions.Stateless vs. Stateful
lexik_jwt_authentication.token_storage).JWTInvalidationManager for manual invalidation.CORS Headers
WWW-Authenticate header in 401 responses.lexik_jwt_authentication.yaml:
lexik_jwt_authentication:
cors_origin: ['http://localhost:3000']
WWW-Authenticate: Bearer for OAuth2 compliance.Event Propagation
kernel.event_subscriber and events are dispatched in the correct order.Cookie Security
Secure or HttpOnly.lexik_jwt_authentication:
cookie:
secure: true
http_only: true
same_site: strict
Token Decoding Errors
JWTException with "Invalid token signature".secret_key/public_key in config.pass_phrase matches if used.Authentication Failures
AuthenticationFailureHandler for custom responses.user_provider is correctly implemented.AuthenticationFailureHandler:
use Lexik\Bundle\JWTAuthenticationBundle\Exception\AuthenticationFailureHandler;
class CustomFailureHandler extends AuthenticationFailureHandler
{
public function createFailureResponse(Request $request): Response
{
return new JsonResponse(['error' => 'Invalid credentials'], 401);
}
}
Register in security.yaml:
security:
firewalls:
api:
jwt: ~
authentication_failure_handler: App\Security\CustomFailureHandler
Performance Issues
How can I help you explore Laravel packages today?