A database-less user provider

Edit this page

A database-less user provider

From jwt.io:

Self-contained: The payload contains all the required information about the user, avoiding the need to query the database more than once. https://jwt.io/introduction

A JWT is self-contained, meaning that we can trust into its payload for processing the authentication. In a nutshell, there should be no need for loading the user from the database when authenticating a JWT Token, the database should be hit only once for delivering the token.

That's why we decided to provide a user provider which is able to create User instances from the JWT payload.

Configuring the user provider

To work, the provider just needs a few lines of configuration:

1
2
3
4
5
# config/packages/security.yaml
security:
    providers:
        jwt:
            lexik_jwt: ~

Then, use it on your JWT protected firewall:

Symfony versions prior to 5.3

1
2
3
4
5
6
7
# config/packages/security.yaml
security:
    firewalls:
        api:
            provider: jwt
            guard:
                # ...

Symfony 5.3 and higher

1
2
3
4
5
6
# config/packages/security.yaml
security:
    firewalls:
        api:
            provider: jwt
            jwt: ~

What does it change?

Now that the provider is configured, it will automatically be used by the JWTAuthenticator when authenticating a token. Instead of loading the user from a "datastore" (i.e. memory or any database engine), a JWTUserInterface instance will be created from the JWT payload, will be cached for a request and be authenticated. We provide a simple JWTUser class implementing this interface, which is used by default when configuring the provider.

Can I use my own user class?

Of course, you can. You just need to make your user class implement the JWTUserInterface interface. This interface contains only a createFromPayload() named constructor which takes the user's username and the JWT token payload as arguments and returns an instance of the class.

Sample implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
namespace App\Security;

final class User implements JWTUserInterface
{
    // Your own logic

    public function __construct($username, array $roles, $email)
    {
        $this->username = $username;
        $this->roles = $roles;
        $this->email = $email;
    }

    public static function createFromPayload($username, array $payload)
    {
        return new self(
            $username,
            $payload['roles'], // Added by default
            $payload['email']  // Custom
        );
    }
}

Note

You can extend the default JWTUser class if that fits your needs.

Configuration

1
2
3
4
5
6
# config/packages/security.yaml
providers:
    # ...
    jwt:
        lexik_jwt:
            class: App\Security\User

And voilà!

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.