Symfony 6 and Lexic JWT Bundle 2: Authentication with JSON Web Token

summary

JWT, JSON Web Token, is one of the Open Internet Protocol standards, described in RFC 7519 as “a compact, URL-secure means of representing claims to be transferred between two parties”.
They are popular and widely used to validate where Web APIs work. They are also available on SSO aka Single Sign-on.

One of the Symfony great bundles called LexikJWTAuthenticationBundle gives us the power to add JWT access controls to apps

This post shows how to implement JWT for authentication. In addition, it can be used for authorization, by allowing it to cooperate with the access controls in Symfony’s SecurityBundle.
here is my.

environment

Reference


tutorial

Observation

The steps are as follows:

  1. Create Symfony Project
  2. bundle install
  3. configure
  4. test via command line

1. Preparation

1-1. create symfony project

This post may be useful:

2. Build JWT Authentication and Authorization

2-1. Install LexikJWTAuthenticationBundle

thanks for doing composer And chosit (lexic), the command line will take you through with just one step!

$ composer require "lexik/jwt-authentication-bundle"
enter fullscreen mode

exit fullscreen mode

Output was:

Info from https://repo.packagist.org: #StandWithUkraine
Using version ^2.16 for lexik/jwt-authentication-bundle
./composer.json has been updated
Running composer update lexik/jwt-authentication-bundle
Loading composer repositories with package information
Updating dependencies
Lock file operations: 6 installs, 0 updates, 0 removals
  - Locking lcobucci/clock (2.2.0)
  - Locking lcobucci/jwt (4.0.4)
  - Locking lexik/jwt-authentication-bundle (v2.16.0)
  - Locking namshi/jose (7.2.3)
  - Locking stella-maris/clock (0.1.6)
  - Locking symfony/polyfill-php56 (v1.20.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 6 installs, 0 updates, 0 removals
  - Downloading stella-maris/clock (0.1.6)
  - Downloading lcobucci/clock (2.2.0)
  - Downloading namshi/jose (7.2.3)
  - Downloading lcobucci/jwt (4.0.4)
  - Downloading lexik/jwt-authentication-bundle (v2.16.0)
  - Installing stella-maris/clock (0.1.6): Extracting archive
  - Installing lcobucci/clock (2.2.0): Extracting archive
  - Installing symfony/polyfill-php56 (v1.20.0)
  - Installing namshi/jose (7.2.3): Extracting archive
  - Installing lcobucci/jwt (4.0.4): Extracting archive
  - Installing lexik/jwt-authentication-bundle (v2.16.0): Extracting archive
Generating optimized autoload files
116 packages you are using are looking for funding.
Use the `composer fund` command to find out more!

Symfony operations: 1 recipe (9ad0fc3489604428ab4d55a826a98856)
  - Configuring lexik/jwt-authentication-bundle (>=2.5): From github.com/symfony/recipes:main
Executing script cache:clear [OK]
Executing script assets:install public [OK]

 What's next? 


Some files have been created and/or updated to configure your new packages.
Please review, edit and commit them: these files are yours.

No security vulnerability advisories found
enter fullscreen mode

exit fullscreen mode

Also, namshi/jose and lcobucci/jwt are the major packages.

config file is generated, kept as config/packages/lexik_jwt_authentication.yaml Which contains:

lexik_jwt_authentication:
    secret_key: '%env(resolve:JWT_SECRET_KEY)%'
    public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
    pass_phrase: '%env(JWT_PASSPHRASE)%'
enter fullscreen mode

exit fullscreen mode

Also, the lines below have been added .env,

###> lexik/jwt-authentication-bundle ###
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE=(your-secret)
###< lexik/jwt-authentication-bundle ###
enter fullscreen mode

exit fullscreen mode

2-2. generate a keypair

Well, in fact, the keys themselves have not yet been generated. However, this is not a problem, because the bundle also helps us to pair well 🙂

$ php bin/console lexik:jwt:generate-keypair
enter fullscreen mode

exit fullscreen mode

Then you will see:

$ ls config/jwt/
private.pem  public.pem
enter fullscreen mode

exit fullscreen mode

2-3. Configure routes and firewalls

There are a few steps left on the JSON login to implement the “endpoint that provides these tokens based on username (or email) and password”.

edit config/routes.yaml To add a route to authenticate or authorize:

  controllers:
      resource: ../src/Controller/
      type: attribute
+ jwt_auth:
+     path: /auth
enter fullscreen mode

exit fullscreen mode

then edit config/packages/security.yaml To use the route as an authentic gate:

  security:
      # ...
      firewalls:
          # ...
+         jwt_auth:
+             pattern: ^/auth
+             stateless: true
+             json_login:
+                 check_path: jwt_auth
+                 success_handler: lexik_jwt_authentication.handler.authentication_success
+                 failure_handler: lexik_jwt_authentication.handler.authentication_failure
          main:
              # ...
enter fullscreen mode

exit fullscreen mode

Also, as of 5.4 only, the below is required in addition:

  security:
+     enable_authenticator_manager: true
enter fullscreen mode

exit fullscreen mode

3. Let’s Play: API Access with JWT

3-1. draw a route

Let’s create the API route.

$ php bin/console make:controller api
enter fullscreen mode

exit fullscreen mode

Output was:

 created: src/Controller/ApiController.php
 created: templates/api/index.html.twig


  Success! 


 Next: Open your new controller class and add some pages!
enter fullscreen mode

exit fullscreen mode

3-2. Prepare routes and firewalls

Then, let the JWT be required in the route:

  security:
      # ...
      firewalls:
          # ...
          jwt_auth:
              # ...
          api:
+             pattern: ^/api
+             stateless: true
+             jwt: ~
          # ...
          main:
              # ...
      access_control:
          # ...
+         - { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
enter fullscreen mode

exit fullscreen mode

3-3. get the token by asking the server to generate it

We are ready now. join /auth with curl To get the token:

$ curl -X POST \
      -H "Content-Type: application/json" \
      -d '{"username":"your-username","password":"your-password"}' \
      https://your-domain/auth
enter fullscreen mode

exit fullscreen mode

In addition, the Appendix -k,--insecure If you have to suppress the tls error.

you will get:

{"token":"xxx.xxx.xxx"}
enter fullscreen mode

exit fullscreen mode

3-4. use token

$ curl \
      -o /dev/null -s -w "HTTP Response = %{http_code}\n" \
      https://your-domain/api
enter fullscreen mode

exit fullscreen mode

Without a valid token, you will see a 401 error due to Access Denied.

Next, try to include your token:

$ curl \
      -o /dev/null -s -w "HTTP Response = %{http_code}\n" \
      -H "Authorization: Bearer xxx.xxx.xxx" \
      https://your-domain/api
enter fullscreen mode

exit fullscreen mode

You’ll see a 200 (Well, you’ll get a 404 when your app can’t find the controller attached to the root. That’s fine, for JWT auth too.)
yes accepted

Leave a Comment