Skip to content
21 contributors

Users who have contributed to this file

@chalasr @slashfan @clementtalleu @alborq @Spomky @yosmanyga @willdurand @teohhanhui @soullivaneuh @ruudk @pietereggink @Oliboy50
182 lines (129 sloc) 6.34 KB

Getting started


This bundle requires Symfony 3.4+ and the openssl extension.

Protip: Though the bundle doesn't enforce you to do so, it is highly recommended to use HTTPS.


Add lexik/jwt-authentication-bundle to your composer.json file:

php composer.phar require "lexik/jwt-authentication-bundle"

Register the bundle:

Symfony 3 Version:
Register bundle into app/AppKernel.php:

public function registerBundles()
    return array(
        // ...
        new Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle(),

Symfony 4 Version :
Register bundle into config/bundles.php (Flex did it automatically):

return [
    Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true],

Generate the SSH keys:

$ mkdir -p config/jwt
$ openssl genpkey -out config/jwt/private.pem -aes256 -algorithm rsa -pkeyopt rsa_keygen_bits:4096
$ openssl pkey -in config/jwt/private.pem -out config/jwt/public.pem -pubout


Configure the SSH keys path in your config/packages/lexik_jwt_authentication.yaml :

    secret_key:       '%kernel.project_dir%/config/jwt/private.pem' # required for token creation
    public_key:       '%kernel.project_dir%/config/jwt/public.pem'  # required for token verification
    pass_phrase:      'your_secret_passphrase' # required for token creation, usage of an environment variable is recommended
    token_ttl:        3600

Configure your config/packages/security.yaml :

    # ...

            pattern:  ^/api/login
            stateless: true
            anonymous: true
                check_path:               /api/login_check
                success_handler:          lexik_jwt_authentication.handler.authentication_success
                failure_handler:          lexik_jwt_authentication.handler.authentication_failure

            pattern:   ^/api
            stateless: true
                    - lexik_jwt_authentication.jwt_token_authenticator

        - { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/api,       roles: IS_AUTHENTICATED_FULLY }

Configure your routing into config/routes.yaml :

    path: /api/login_check


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):

curl -X POST -H "Content-Type: application/json" http://localhost/api/login_check -d '{"username":"johndoe","password":"test"}'

If it works, you will receive something like this:

   "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 configuration reference document to enable query string parameter mode or change the header value prefix.


See Functionally testing a JWT protected api document or the sandbox application (Symfony2 or Symfony4) for a fully working example.


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 Symfony2 related topic, but see Working with CORS requests document to get a quick explanation on handling CORS requests.

A stateless form_login replacement

Using form_login security factory is very straightforward but it involves cookies exchange, even if the stateless parameter is set to true.

This may not be a problem depending on the system that makes calls to your API (like a typical SPA). But if it is, take a look at the GfreeauGetJWTBundle, which provides a stateless replacement for form_login.


For impersonating users using JWT, see

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 :

SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1

Further documentation

The following documents are available:

You can’t perform that action at this time.