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
  5. Data customization and validation
  • Documentation
  • Book
  • Reference
  • Bundles
  • Cloud

Table of Contents

  • Table of contents
  • Adding custom data or headers to the JWT
    • Using Events::JWT_CREATED
    • Using a custom payload at JWT creation
  • Events::JWT_DECODED - Validating data in the JWT payload
    • Example: Check client ip the decoded payload (from example 1)
    • Example: Add additional data to payload - to get it in your custom UserProvider
  • Events::JWT_AUTHENTICATED - Customizing your security token
    • Example: Keep a UUID that was set into the JWT in the authenticated token
  • Events::AUTHENTICATION_SUCCESS - Adding public data to the JWT response
    • Example: Add user roles to the response body
  • Events::JWT_ENCODED - Getting the JWT token string after encoding
    • Example: Obtain JWT string
  • Events::AUTHENTICATION_FAILURE - Customizing the failure response body
  • Events::JWT_INVALID - Customizing the invalid token response
    • Example: Set a custom response message and status code on invalid token
  • Events::JWT_NOT_FOUND - Customizing the response on token not found
    • Example: Set a custom response message on token not found
  • Events::JWT_EXPIRED - Customizing the response message on expired token
    • Example: Customize the response in case of expired token

Data customization and validation

Edit this page

Data customization and validation

Careful: Before you add your own custom data, know that the JWT payload is not encrypted, it is only base64 encoded. The token signature ensures its integrity (meaning it cannot be modified), but anyone can read its content (try it using a simple tool like https://jwt.io/).

Table of contents

  • Adding custom data or headers to the JWT payload
  • Validating data in the JWT payload
  • Customize your security token
  • Adding public data to the JWT response
  • Getting the JWT token string after encoding
  • Customizing the response on invalid credentials
  • Customizing the response on invalid token
  • Customizing the response on token not found
  • Customizing the response on expired token

Adding custom data or headers to the JWT

Using Events::JWT_CREATED

By default the JWT payload will contain the username, the user's roles, the token creation date and expiration date, but you can add your own data.

You can also modify the header to fit on your application context.

1
2
3
4
5
6
7
# config/services.yaml
services:
    acme_api.event.jwt_created_listener:
        class: App\EventListener\JWTCreatedListener
        arguments: [ '@request_stack' ]
        tags:
            - { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_created, method: onJWTCreated }

Example: Add client IP address to the encoded payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// src/App/EventListener/JWTCreatedListener.php

use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTCreatedEvent;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * @var RequestStack
 */
private $requestStack;

/**
 * @param RequestStack $requestStack
 */
public function __construct(RequestStack $requestStack)
{
    $this->requestStack = $requestStack;
}

/**
 * @param JWTCreatedEvent $event
 *
 * @return void
 */
public function onJWTCreated(JWTCreatedEvent $event)
{
    $request = $this->requestStack->getCurrentRequest();

    $payload       = $event->getData();
    $payload['ip'] = $request->getClientIp();

    $event->setData($payload);

    $header        = $event->getHeader();
    $header['cty'] = 'JWT';

    $event->setHeader($header);
}

Example: Override token expiration date calculation to be more flexible

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// src/App/EventListener/JWTCreatedListener.php

use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTCreatedEvent;

/**
 * @param JWTCreatedEvent $event
 *
 * @return void
 */
public function onJWTCreated(JWTCreatedEvent $event)
{
    $expiration = new \DateTime('+1 day');
    $expiration->setTime(2, 0, 0);

    $payload        = $event->getData();
    $payload['exp'] = $expiration->getTimestamp();

    $event->setData($payload);
}

Using a custom payload at JWT creation

If you create JWT tokens programmatically, you can add custom data to the JWT using the method createFromPayload(UserInterface $user, array $payload)

1
2
3
$payload = ['foo' => 'bar'];

$jwt = $this->container->get('lexik_jwt_authentication.jwt_manager')->createFromPayload($user, $payload);

Events::JWT_DECODED - Validating data in the JWT payload

You can access the jwt payload once it has been decoded to perform your own additional validation.

1
2
3
4
5
6
7
# config/services.yaml
services:
    acme_api.event.jwt_decoded_listener:
        class: App\EventListener\JWTDecodedListener
        arguments: [ '@request_stack' ]
        tags:
            - { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_decoded, method: onJWTDecoded }

Example: Check client ip the decoded payload (from example 1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// src/App/EventListener/JWTDecodedListener.php
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTDecodedEvent;

/**
 * @param JWTDecodedEvent $event
 *
 * @return void
 */
public function onJWTDecoded(JWTDecodedEvent $event)
{
    $request = $this->requestStack->getCurrentRequest();

    $payload = $event->getPayload();

    if (!isset($payload['ip']) || $payload['ip'] !== $request->getClientIp()) {
        $event->markAsInvalid();
    }
}

Example: Add additional data to payload - to get it in your custom UserProvider

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// src/App/EventListener/JWTDecodedListener.php

use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTDecodedEvent;

/**
 * @param JWTDecodedEvent $event
 *
 * @return void
 */
public function onJWTDecoded(JWTDecodedEvent $event)
{
    $payload = $event->getPayload();
    $user = $this->userRepository->findOneByUsername($payload['username']);

    $payload['custom_user_data'] = $user->getCustomUserInformations();

    $event->setPayload($payload); // Don't forget to regive the payload for next event / step
}

Events::JWT_AUTHENTICATED - Customizing your security token

You can add attributes to the token once it has been authenticated to allow JWT properties to be used by your application.

1
2
3
4
5
6
# config/services.yaml
services:
    acme_api.event.jwt_authenticated_listener:
        class: App\EventListener\JWTAuthenticatedListener
        tags:
            - { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_authenticated, method: onJWTAuthenticated }

Example: Keep a UUID that was set into the JWT in the authenticated token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// src/App/EventListener/JWTAuthenticatedListener.php
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTAuthenticatedEvent;

/**
 * @param JWTAuthenticatedEvent $event
 *
 * @return void
 */
public function onJWTAuthenticated(JWTAuthenticatedEvent $event)
{
    $token = $event->getToken();
    $payload = $event->getPayload();

    $token->setAttribute('uuid', $payload['uuid']);
}

Events::AUTHENTICATION_SUCCESS - Adding public data to the JWT response

By default, the authentication response is just a json containing the JWT but you can add your own public data to it.

1
2
3
4
5
6
# config/services.yaml
services:
    acme_api.event.authentication_success_listener:
        class: App\EventListener\AuthenticationSuccessListener
        tags:
            - { name: kernel.event_listener, event: lexik_jwt_authentication.on_authentication_success, method: onAuthenticationSuccessResponse }

Example: Add user roles to the response body

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// src/App/EventListener/AuthenticationSuccessListener.php
use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationSuccessEvent;

/**
 * @param AuthenticationSuccessEvent $event
 */
public function onAuthenticationSuccessResponse(AuthenticationSuccessEvent $event)
{
    $data = $event->getData();
    $user = $event->getUser();

    if (!$user instanceof UserInterface) {
        return;
    }

    $data['data'] = array(
        'roles' => $user->getRoles(),
    );

    $event->setData($data);
}

Events::JWT_ENCODED - Getting the JWT token string after encoding

You may need to get JWT after its creation.

Example: Obtain JWT string

1
2
3
4
5
6
7
8
9
10
// src/App/EventListener/JWTEncodedListener.php
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTEncodedEvent;

/**
 * @param JWTEncodedEvent $event
 */
public function onJwtEncoded(JWTEncodedEvent $event)
{
    $token = $event->getJWTString();
}

Events::AUTHENTICATION_FAILURE - Customizing the failure response body

By default, the response in case of failed authentication is just a json containing a failure message and a 401 status code, but you can set a custom response.

1
2
3
4
5
6
# config/services.yaml
services:
    acme_api.event.authentication_failure_listener:
        class: App\EventListener\AuthenticationFailureListener
        tags:
            - { name: kernel.event_listener, event: lexik_jwt_authentication.on_authentication_failure, method: onAuthenticationFailureResponse }

Example: Set a custom response on authentication failure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// src/App/EventListener/AuthenticationFailureListener.php
use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationFailureEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationFailureResponse;
use Symfony\Component\HttpFoundation\JsonResponse;

/**
 * @param AuthenticationFailureEvent $event
 */
public function onAuthenticationFailureResponse(AuthenticationFailureEvent $event)
{
    $data = [
        'name' => 'John Doe',
        'foo'  => 'bar',
    ];

    $response = new JWTAuthenticationFailureResponse('Bad credentials, please verify that your username/password are correctly set', JsonResponse::HTTP_UNAUTHORIZED);
    $response->setData($data);

    $event->setResponse($response);
}

Events::JWT_INVALID - Customizing the invalid token response

By default, if the token is invalid, the response is just a json containing the corresponding error message and a 401 status code, but you can set a custom response.

1
2
3
4
5
6
# config/services.yaml
services:
    acme_api.event.jwt_invalid_listener:
        class: App\EventListener\JWTInvalidListener
        tags:
            - { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_invalid, method: onJWTInvalid }

Example: Set a custom response message and status code on invalid token

1
2
3
4
5
6
7
8
9
10
11
12
13
// src/App/EventListener/JWTInvalidListener.php
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTInvalidEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationFailureResponse;

/**
 * @param JWTInvalidEvent $event
 */
public function onJWTInvalid(JWTInvalidEvent $event)
{
    $response = new JWTAuthenticationFailureResponse('Your token is invalid, please login again to get a new one', 403);

    $event->setResponse($response);
}

Events::JWT_NOT_FOUND - Customizing the response on token not found

By default, if no token is found in a request, the authentication listener will either call the entry point that returns a unauthorized (401) json response, or (if the firewall allows anonymous requests), just let the request continue.

Thanks to this event, you can set a custom response.

1
2
3
4
5
6
# config/services.yaml
services:
    acme_api.event.jwt_notfound_listener:
        class: App\EventListener\JWTNotFoundListener
        tags:
            - { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_not_found, method: onJWTNotFound }

Example: Set a custom response message on token not found

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// src/App/EventListener/JWTNotFoundListener.php

use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTNotFoundEvent;
use Symfony\Component\HttpFoundation\JsonResponse;

/**
 * @param JWTNotFoundEvent $event
 */
public function onJWTNotFound(JWTNotFoundEvent $event)
{
    $data = [
        'status'  => '403 Forbidden',
        'message' => 'Missing token',
    ];

    $response = new JsonResponse($data, 403);

    $event->setResponse($response);
}

Events::JWT_EXPIRED - Customizing the response message on expired token

By default, if the token provided in the request is expired, the authentication listener will call the entry point returning an unauthorized (401) json response. Thanks to this event, you can set a custom response or simply change the response message.

1
2
3
4
5
6
# config/services.yaml
services:
    acme_api.event.jwt_expired_listener:
        class: App\EventListener\JWTExpiredListener
        tags:
            - { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_expired, method: onJWTExpired }

Example: Customize the response in case of expired token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// src/App/EventListener/JWTExpiredListener.php

use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTExpiredEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationFailureResponse;

/**
 * @param JWTExpiredEvent $event
 */
public function onJWTExpired(JWTExpiredEvent $event)
{
    /** @var JWTAuthenticationFailureResponse */
    $response = $event->getResponse();

    $response->setMessage('Your token is expired, please renew it.');
}

Protip: You might want to use the same method for customizing the response on both JWT_INVALID, JWT_NOT_FOUND and/or JWT_EXPIRED events. For that, use the Lexik\Bundle\JWTAuthenticationBundle\Event\JWTFailureEventInterface interface to type-hint the event argument of your listener's method instead of the concrete class corresponding to one of these specific events.

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version
    We stand with Ukraine.
    Version:
    Online Symfony certification, take it now!

    Online Symfony certification, take it now!

    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 Denis Gorbachev, a Symfony contributor

    Thanks Denis Gorbachev (@starfall) for being a Symfony contributor

    8 commits • 34 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