Using a Symfony secret to encode your JWT Tokens
Tags: php, jwt, symfony, vault
Published: 2024-05-24 12:03:35

## Introduction
In this post, I am going to show you how to generate a secret by using [symfony vaults](https://symfony.com/doc/current/configuration/secrets.html) and then how to use that secret to encode and decode a JWT Token using the [firebase-jwt](https://github.com/firebase/php-jwt) php component.

## Generating the secret
To generate a secret we must first generate the encryption keys. This can be done using the following command:

```shell
 bin/console secrets:generate-keys
```

This will generate a key pair in the following files:

- config/secrets/{env}/{env}.encrypt.public.php
- config/secrets/{env}/{env}.decrypt.private.php

> The {env} value will depend on the enviroment you are working. If you are developing in local it should be "dev".

Now we have generated the key-pair, we can create a secret. To do it, let's execute the following command:

```shell
bin/console secrets:set JWT_SECRET
```
The command will ask you for the secret value. Write it and press enter to save it.

## Binding the JWT_SECRET to a variable

As we can get the JWT_SECRET value as we'd do with any other environment variable, let's bind a variable to it in our _services.yaml_ file

```yaml
services:
    _defaults:
        autowire: true
        autoconfigure: true 
        bind:
            # Other vars
            $jwtSecret: '%env(JWT_SECRET)%'
```

## Install the Firebase JWT PHP component

Let's use composer to install the component:

```shell
composer require firebase/php-jwt
```
If you do not have sodium installed, install it using composer too.

```shell
composer require paragonie/sodium_compat
```
## Use the secret to encode and decode a jwt token

Let's create a service to encapsulate the logic which will encode and decode the tokens.

```php
namespace App\Service\Token

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

class TokenEncoder
{
    public function __construct(
        private readonly string $jwtSecret,
    ){}

    public function encode(string $exp, string $identifier): string
    {
        return JWT::encode([
            'exp'  => (new \DateTime())->add(\DateInterval::createFromDateString($exp))->getTimestamp(),
            'nbf'  => (new \DateTime())->getTimestamp(),
            'id' => $identifier
        ], $this->jwtSecret, 'HS256');
    }

    public function decode(string $token): string
    {
        $credentials = (array)JWT::decode($token, new Key($this->jwtSecret, 'HS256'));
    }
}

```
Let's analyze the service step by step:

- The constructor injects the recently binded variable in the services.yaml file ("$jwtSecret") which holds the JWT_SECRET value.
- The **encode** method encodes the token. It receives:
   - The array to encode with the token. It contains the following keys:
      - **exp**: The token expiration time as a timestamp
      - **nbf**: Indicates when the token starts to be valid. We set the current timestamp, that is, when the token is created it starts to be valid until the expiration date.
      - **id**: An identifier (Could be a user or application identifier).
   - The secret to encode the token with (we use our $jwtSecret binded var).
   - The encryption algorithm. We choose the HS256.

> You can learn more about HS256 and RS256 [here](https://auth0.com/blog/rs256-vs-hs256-whats-the-difference/). We choose HS256 since it is a symmetric hashing and we only need one secret key.

## Conclusion
In this post we have learned how we can securely store a secret within symfony secrets and how to use it to generate a JWT token. This could be useful on API's which use JWT tokens to autenticate their users or applications.
If you enjoy my content and like the Symfony framework, consider reading my book: [Building an Operation-Oriented Api using PHP and the Symfony Framework: A step-by-step guide](https://amzn.eu/d/3eO1DDi)