Getting started
Prerequisites
This bundle requires Symfony 6.4+ and ext-openssl.
Protip: Though the bundle doesn't enforce you to do so, it is highly recommended to use HTTPS.
Installation
Add
lexik/jwt-authentication-bundle
to your composer.json file:
1
$ php composer.phar require "lexik/jwt-authentication-bundle"
    Register the bundle
Register bundle into config/bundles.php (Flex did it automatically):
1 2 3 4
return [
    //...
    Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true],
];
    Generate the SSL keys
Note
For the version 2.xx of this bundle, you can use Web-Token and generate
JSON Web Keys (JWK) and JSON Web Keysets (JWKSet) instead of
PEM encoded keys.
Please refer to the dedicated page Web-Token feature for more information.
1
$ php bin/console lexik:jwt:generate-keypair
    Your keys will land in config/jwt/private.pem and
config/jwt/public.pem (unless you configured a different path).
Available options:
--skip-if-existswill silently do nothing if keys already exist.--overwritewill overwrite your keys if they already exist.
Otherwise, an error will be raised to prevent you from overwriting your keys accidentally.
Configuration
Configure the SSL keys path and passphrase in your .env:
1 2 3
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE=
    1 2 3 4 5 6
# config/packages/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
    secret_key: '%env(resolve:JWT_SECRET_KEY)%' # required for token creation
    public_key: '%env(resolve:JWT_PUBLIC_KEY)%' # required for token verification
    pass_phrase: '%env(JWT_PASSPHRASE)%' # required for token creation
    token_ttl: 3600 # in seconds, default is 3600
    Configure application security
Caution
Make sure the firewall login is place before api, and if
main exists, put it after api, otherwise you will encounter
/api/login_check route not found.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# config/packages/security.yaml
security:
    enable_authenticator_manager: true # Only for Symfony 5.4
    # ...
    firewalls:
        login:
            pattern: ^/api/login
            stateless: true
            json_login:
                check_path: /api/login_check
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: lexik_jwt_authentication.handler.authentication_failure
        api:
            pattern:   ^/api
            stateless: true
            jwt: ~
    access_control:
        - { path: ^/api/login, roles: PUBLIC_ACCESS }
        - { path: ^/api,       roles: IS_AUTHENTICATED_FULLY }
    Configure application routing
1 2 3
# config/routes.yaml
api_login_check:
    path: /api/login_check
    API Platform compatibility
If API Platform is detected, the integration will be done with your security configuration.
If you wish to change some parameters, you can do it with this configuration:
1 2 3 4 5 6 7
# config/packages/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
    # ...
    api_platform:
        check_path: /api/login_check
        username_path: email
        password_path: security.credentials.password
    Usage
1. Obtain the token
The first step is to authenticate the user using its credentials. You can test getting the token with a simple curl command like this (adapt host and port):
Linux or macOS:
1
$ curl -X POST -H "Content-Type: application/json" https://localhost/api/login_check -d '{"username":"johndoe","password":"test"}'
    Windows:
1
C:\> curl -X POST -H "Content-Type: application/json" https://localhost/api/login_check --data {\"username\":\"johndoe\",\"password\":\"test\"}
    If it works, you will receive something like this:
1 2 3
{
    "token" : "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXUyJ9.eyJleHAiOjE0MzQ3Mjc1MzYsInVzZXJuYW1lIjoia29ybGVvbiIsImlhdCI6IjE0MzQ2NDExMzYifQ.nh0L_wuJy6ZKIQWh6OrW5hdLkviTs1_bau2GqYdDCB0Yqy_RplkFghsuqMpsFls8zKEErdX5TYCOR7muX0aQvQxGQ4mpBkvMDhJ4-pE4ct2obeMTr_s4X8nC00rBYPofrOONUOR4utbzvbd4d2xT_tj4TdR_0tsr91Y7VskCRFnoXAnNT-qQb7ci7HIBTbutb9zVStOFejrb4aLbr7Fl4byeIEYgp2Gd7gY"
}
    Store it (client side), the JWT is reusable until its TTL has expired (3600 seconds by default).
2. Use the token
Simply pass the JWT on each request to the protected firewall, either as an authorization header or as a query parameter.
By default only the authorization header mode is enabled :
Authorization: Bearer {token}
See the configuration reference document to enable query string parameter mode or change the header value prefix.
Examples
See Functionally testing a JWT protected api document or the sandbox application Symfony4) for a fully working example.
Notes
About token expiration
Each request after token expiration will result in a 401 response. Redo the authentication process to obtain a new token.
Maybe you want to use a refresh token to renew your JWT. In this case you can check JWTRefreshTokenBundle.
Working with CORS requests
This is more of a Symfony related topic, but see Working with CORS requests document to get a quick explanation on handling CORS requests.
Impersonation
For impersonating users using JWT, see https://symfony.com/doc/current/security/impersonating_user.html
Important note for Apache users
As stated in this link
and this one,
Apache server will strip any Authorization header not in a valid
HTTP BASIC AUTH format.
If you intend to use the authorization header mode of this bundle (and you should), please add those rules to your VirtualHost configuration :
1
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
    Further documentation
The following documents are available:
- Configuration reference
 - Data customization and validation
 - Functionally testing a JWT protected api
 - Working with CORS requests
 - JWT encoder service customization
 - Extending Authenticator
 - Creating JWT tokens programmatically
 - A database-less user provider
 - Accessing the authenticated JWT token
 - Web-Token feature
 - Invalidate token on logout