Skip to content
  • About
    • What is Symfony?
    • Community
    • News
    • Contributing
    • Support
  • Documentation
    • Symfony Docs
    • Symfony Book
    • Screencasts
    • Symfony Bundles
    • Symfony Cloud
    • Training
  • Services
    • SensioLabs Professional services to help you with Symfony
    • Platform.sh for Symfony Best platform to deploy Symfony apps
    • SymfonyInsight Automatic quality checks for your apps
    • Symfony Certification Prove your knowledge and boost your career
    • Blackfire Profile and monitor performance of your apps
  • Other
  • Blog
  • Download
sponsored by SensioLabs
  1. Home
  2. Documentation
  3. Bundles
  4. LexikJWTAuthenticationBundle
  • Documentation
  • Book
  • Reference
  • Bundles
  • Cloud

Table of Contents

  • Prerequisites
  • Installation
    • Register the bundle
    • Generate the SSL keys
  • Configuration
    • Configure application security
    • Configure application routing
    • API Platform compatibility
  • Usage
    • 1. Obtain the token
    • 2. Use the token
    • Examples
  • Notes
    • About token expiration
    • Working with CORS requests
    • Impersonation
    • Important note for Apache users
  • Further documentation

Getting started

Edit this page

Getting started

Prerequisites

This bundle requires Symfony 4.4+ and the openssl extension.

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

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-exists will silently do nothing if keys already exist.
  • --overwrite will 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.

Symfony versions prior to 5.3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# config/packages/security.yaml
security:
    # ...

    firewalls:
        login:
            pattern: ^/api/login
            stateless: true
            json_login:
                check_path: /api/login_check # or api_login_check as defined in config/routes.yaml
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: lexik_jwt_authentication.handler.authentication_failure

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

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

Symfony 5.3 and higher

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
    # ...

    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 Symfony2 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
This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version
    We stand with Ukraine.
    Version:
    Become certified from home

    Become certified from home

    Be trained by SensioLabs experts (2 to 6 day sessions -- French or English).

    Be trained by SensioLabs experts (2 to 6 day sessions -- French or English).

    Symfony footer

    ↓ Our footer now uses the colors of the Ukrainian flag because Symfony stands with the people of Ukraine.

    Avatar of Patrick Bußmann, a Symfony contributor

    Thanks Patrick Bußmann for being a Symfony contributor

    1 commit • 5 lines changed

    View all contributors that help us make Symfony

    Become a Symfony contributor

    Be an active part of the community and contribute ideas, code and bug fixes. Both experts and newcomers are welcome.

    Learn how to contribute

    Symfony™ is a trademark of Symfony SAS. All rights reserved.

    • What is Symfony?

      • Symfony at a Glance
      • Symfony Components
      • Case Studies
      • Symfony Releases
      • Security Policy
      • Logo & Screenshots
      • Trademark & Licenses
      • symfony1 Legacy
    • Learn Symfony

      • Symfony Docs
      • Symfony Book
      • Reference
      • Bundles
      • Best Practices
      • Training
      • eLearning Platform
      • Certification
    • Screencasts

      • Learn Symfony
      • Learn PHP
      • Learn JavaScript
      • Learn Drupal
      • Learn RESTful APIs
    • Community

      • SymfonyConnect
      • Support
      • How to be Involved
      • Code of Conduct
      • Events & Meetups
      • Projects using Symfony
      • Downloads Stats
      • Contributors
      • Backers
    • Blog

      • Events & Meetups
      • A week of symfony
      • Case studies
      • Cloud
      • Community
      • Conferences
      • Diversity
      • Documentation
      • Living on the edge
      • Releases
      • Security Advisories
      • SymfonyInsight
      • Twig
      • SensioLabs
    • Services

      • SensioLabs services
      • Train developers
      • Manage your project quality
      • Improve your project performance
      • Host Symfony projects

      Deployed on

    Follow Symfony

    Search by Algolia