How to setup JWT authentication with Symfony

Edouard Courty
3 min readAug 19, 2022

--

LexikJWTAuthenticationBundle is Symfony’s officially supported JSON Web Token authentication bundle.

It turns your basic form login into a JSON Web Token (JWT) authentication mechanism, without you having to change anything in your code.

This can be pretty handy if you want to transform your Symfony project into an API that will be used by a separate front-end.

LexikJWTAuthenticationBundle adds JWT authentication to your project, but it doesn’t replace any other authentication mechanisms, meaning you can still use a form login after the setup of JWT authentication.

This bundle requires Symfony 4.4 and the openssl PHP extension.

Before anything, make sure you have a User provider configured in your Symfony project.

Setup

To get started, install the dependency by running the following composer command:

composer require "lexik/jwt-authentication-bundle"

Now that the bundle is installed (Symfony Flex added it to your config/bundles.php file automatically), we need to generate a set of keypairs that will be used to encode the to-be generated JSON Web Tokens.
This is done by running a Symfony command provided by the bundle:

bin/console lexik:jwt:generate-keypair

The keypair is generated by default in the config/jwt folder.

Configuration

Let’s configure the SSL keys and JWT passphrase in our .env file

JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE=<your passphrase here, it can be anything>

A config/packages/lexik_jwt_authentication.yaml file should have been generated by Symfony and contains the following configuration by default:

lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(JWT_PASSPHRASE)%'
token_ttl: 3600

Once this is done, navigate to your config/packages/security.yaml file and modify your firewalls to add the following:

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

and add this entry to your access-control rules as the first one:

- { path: ^/api/login, roles: PUBLIC_ACCESS }

This makes sure that your JWT login route is available to anyone without authentication.

Lastly, add this to your config/routes.yaml file:

api_login_check:
path: /api/login_check

Everything is now configured, authenticating with JWT is now possible.

To use this authentication mechanism, request a token that you will include in your future requests.

To get an authentication token, simply send a POST request to the configured endpoint (in our case, /api/login_check), with a body formatted like the following:

{
"username": "your username",
"password": "your password"
}

The response will contain the following:

{
"token": "your token"
}

To authenticate your requests, simply include this token as a normal bearer token: include a header:

Authorization: Bearer <your token here>

That’s it! Every request containing a valid token will now handle authentication with the user provider you configured, meaning that $this->getUser() works the same way it does as with a basic form authentication.

When authenticating with an expired token, the server will send a 401 HTTP Unauthorized response.

Refresh tokens are not basically handled by LexikJWTAuthenticationBundle, but can be added using JWTRefreshTokenBundle.

If this article helped you or gathered your interest, you may be interested in my other articles! I mainly publish stuff about PHP and the Symfony ecosystem. Follow my account to read my future content!

--

--

Edouard Courty

Web Developer & IT Teacher based in Paris - Back-end guru - Co-founder of @IMXrarity