Authentification JWT Symfony 6
La version 6 de Symfony vient avec un bon nombre de changements parmi lesquels la structure de l’entité User et l’exclusivité de la version 8 de Php.
Une grande démarcation avec la version 5 qui va impacter la configuration du JWT pour l’authentification.
Dans l’entité User, la fonction getUsername(), identifiant visuel qui représente cet utilisateur, jusqu’ici présente dans la version 5 de Symfony est remplacé par getUserIdentifier(). Changement qui va modifier les entrées ou en têtes du JWT dans le fichier security.yaml
Configuration
- Prérequis
Pour ce projet, voici les prérequis:
Composer, PHP 8, XAMPP et un éditeur de texte.
2. Installation et configuration
Nous allons commencer par installer un projet symfony de type api.
Dans la console : symfony new my-api
Rendez vous ensuite dans la racine du projet et ouvrez le dans un éditeur de texte.
Nous allons utiliser Visual Studio Code.
Nous allons installer un certain nombre de librairies(Bundles).
composer require symfony/orm-pack(pour la gestion de la base de données)
composer require — dev symfony/maker-bundle(permettre
de gérer nos entités, contrôleurs, autres commandes..)
Dans le fichier .env, choisir un type de base de données.
Nous travaillerons avec Mysql
DATABASE_URL=”mysql://root:@127.0.0.1:3306/api_db? serverVersion=8&charset=utf8mb4"
Installer ensuite les bundles :
composer require security:
pour configurer les accès à notre application dans le fichier config/packages/security.yaml)
composer require annotation:
pour pouvoir écrire nos routes dans le contrôleur)
composer require twig
composer require profiler(pour le debug de notre application)
composer require serializer(pour sérialisé nos données )
Créons l’entité User
Symfony console make:User
* A visual identifier that represents this user.
*
* @see UserInterface
*/
public function getUserIdentifier(): string
{
return (string) $this->email;
}
Nous pouvons voir la nouvelle configuration de l’entité User qui fera toute la différence dans la suite.
Nous pouvons maintenant installer notre bundle JWT
Principe
Dans la console: composer require lexik/jwt-authentication-bundle
Le fichier config/packages/lexik_jwt_authentification.yaml sera créé.
Nous avons configuré le temps de validité du token.
Création des publiques et privées
Avant de passer à cette étape, dans le fichier .env, nous modifions le champ JWT_PASSPHRASE du fait que nous allons créer nos clés manuellement.
Dans la console, nous exécutons les commandes suivantes. La console dans les deux cas nous demandera de saisir la phrase secrète.
$ mkdir config/jwt
$ openssl genrsa -out config/jwt/private.pem -aes256 4096
$ openssl rsa -pubout -in config/jwt/private.pem -out config/jwt/public.pem
Configuration du fichier security.yaml
On notera trois choses dans ce fichier:
Le hachage du mot pass: Nous laisserons plaintext pour ne pas avoir un hachage
le provider : ça sera l’entité user via l’email.
l’implémentation du jwt:
login:
pattern: ^/api/login
stateless: true
provider: app_user_provider
json_login:
check_path: /api/login_check
username_path: email
password_path: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
api:
pattern: ^/api
stateless: true
jwt: ~
Contrairement aux versions précédentes, deux champs seront ajoutés.
username_path: email
password_path: password
Ceci suite à la modification de l’entité User.
Ensuite ajouter la route dans le fichier routes.yaml
api_login_check:
path: /api/login_check
Avec ceci, notre configuration du bundle JWT se termine.
le fichier final security.yaml sera :
security:
enable_authenticator_manager: true
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
password_hashers:
App\Entity\User:
algorithm: plaintext
encode_as_base64: false
iterations: 1
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/api/login
stateless: true
provider: app_user_provider
json_login:
check_path: /api/login_check
username_path: email
password_path: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
api:
pattern: ^/api
stateless: true
jwt: ~
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#the-firewall
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/api/login, roles: PUBLIC_ACCESS }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
when@test:
security:
password_hashers:
# By default, password hashers are resource intensive and take time. This is
# important to generate secure password hashes. In tests however, secure hashes
# are not important, waste resources and increase test times. The following
# reduces the work factor to the lowest possible values.
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
algorithm: plaintext
cost: 4 # Lowest possible value for bcrypt
time_cost: 3 # Lowest possible value for argon
memory_cost: 10 # Lowest possible value for argon
Test de configuration
Pour ce faire nous allons créer un utilisateur.
Générons un contrôleur dans la console: symfony console make:controller UserController
Nous utiliserons le manager pour communiquer avec la base de données, l’injection de dépendances pour l’entité User.
Nous allons hacher le mot de passe pour le besoin du test.
Notre contrôleur sera comme suivant:
<?php
namespace App\Controller;
use App\Entity\User;
use App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class UserController extends AbstractController
{
public function __construct(EntityManagerInterface $manager, UserRepository $userRepository)
{
$this->em=$manager;
$this->user=$userRepository;
}
//Création d’un utilisateur
#[Route(‘/userCreate’, name: ‘user_create’, methods:’POST’)]
public function userCreate(Request $request): Response
{
$data=json_decode($request->getContent(),true);
$email=$data[“email”];
$password=$data[“password”];
//Vérification de l’email
$checkEmail=$this->user->findOneByEmail($email);
if($checkEmail)
{
return new JsonResponse([
“status”=>false,
“message”=>”Cet email existe déjà, vous pouvez choisir un autre !”
]);
}
else
//Création d’un nouvel utilisateur
$user= new User();
$user->setEmail($email)
->setPassword(sha1($password));
$this->em->persist($user);
$this->em->flush();
return new JsonResponse([
“status”=>true,
“message”=>”L’utilisateur a été créé avec succès !”
]);
}
}
Nous utliserons Talend API Tester pour tester notre api
Démarrons notre projet : symfony serve
Notre utilisateur a bien été créé.
Nous pouvons le voir dans la base de données.
Nous testons enfin notre authentification
Le token est bien généré et peut être utilisé par un client front pour accorder ou restreindre l’accès aux utilisateurs.
Conclusion
Nous avons voulu à travers cet article implémenter un mode d’authentification sous symfony entre autres le JWT(JSON WEB TOKEN).
Nous avons parcouru divers étapes en notant les changements dus à la version récente de Symfony.
Cet article peut davantage être enrichi avec plusieurs features, nous avons abordé la base de l’architecture.
Bonne lecture.