Le pouvoir de l’interface 💪 🤔

Ismaile ABDALLAH
3 min readJan 29, 2022

--

Photo by Sigmund on Unsplash

Je me suis lancé dans un projet Symfony 5.2, j’ai commencé par faire l’authentification 🚀.

Pour Hacher (hummm viande hachée…. burger 😂) le mot de passe, la documentation 5.2 conseille d’utiliser UserPasswordEncoderInterface situé dans le dossierSymfony\Component\Security\Core\Encode’.

namespace Symfony\Component\Security\Core\Encoder;

use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Security\Core\User\UserInterface;

/**
* UserPasswordEncoderInterface is the interface for the password encoder service.
*
*
@author Ariel Ferrandini <arielferrandini@gmail.com>
*/
interface UserPasswordEncoderInterface
{
/**
* Encodes the plain password.
*
*
@return string
*/
public function encodePassword(UserInterface $user, string $plainPassword);

/**
*
@return bool
*/
public function isPasswordValid(UserInterface $user, string $raw);

/**
* Checks if an encoded password would benefit from rehashing.
*/
public function needsRehash(UserInterface $user): bool;
}

Cette interface contient deux méthodes essentielles à l’authentification : encodePassword pour hacher le password et la méthode isPasswordValid pour valider le password.

“ Pourquoi tu me parles de hacher le mot de passe ? Il faut l’enregistrer en clair 😂 😜 😝”

Ainsi, je pouvais par exemple injecter cette interface directement dans mon projet et utiliser les deux méthodes. Mais, je me suis dit 🤔 “et si jamais Symfony décide de supprimer cette interface ?”

Alors, ce que j’ai fait, c’est de créer ma propre interface, exactement la même que celle de Symfony et dans mon implémentation, j’ai injecté l’interface de Symfony.

namespace App\Main\Authentification\Shared\Infrastructure\Service;

use App\Entity\User;
use App\Main\Authentification\Shared\Domain\Service\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface as SymfonyUserPasswordEncoderInterface;

class UserPasswordEncoderService implements UserPasswordEncoderInterface <-- ceci et mon interface a moi dans mon projet
{
public function __construct(
private SymfonyUserPasswordEncoderInterface $passwordEncoder
) {
}

public function encodePassword(User $user, string $plainPassword): string
{
return $this->passwordEncoder->encodePassword($user, $plainPassword);
}

public function isPasswordValid(User $user, string $raw): bool
{
return $this->passwordEncoder->isPasswordValid($user, $raw);
}

public function needsRehash(User $user): bool
{
return $this->passwordEncoder->needsRehash($user);
}
}

Voilà, maintenant j’ai juste à utiliser mon interface à moi lors du register du user et lors du login 🚀.

Photo by Tim Mossholder on Unsplash

Devinez quoi, Symfony a déprécié l’interface Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface depuis sa version 5.3+.

Bon ♻️, il faut maintenant migrer cette interface. Si j’avais utilisé directement l’interface de symfony dans mon projet, ça serait compliqué. Surtout que l’une des méthodes a changé de nom (hashPassword au lieu de encodePassoword). Ainsi, il faudrait modifier plusieurs fichiers pour pouvoir supprimer complètement cette interface deprecated 😢 🤢.

Mais vu qu’on a utilisé notre propre interface avec notre implémentation, tout ce qu’il faut faire maintenant pour migrer, c’est de changer l’implémentation en utilisant la nouvelle interface conseillée par Symfony.

Voilà à quoi va ressembler la migration, un seul fichier est modifié :

use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;

class UserPasswordEncoderService implements UserPasswordEncoderInterface
{
public function __construct(
private UserPasswordHasherInterface $passwordHasher
) {
}

public function encodePassword(User $user, string $plainPassword): string
{
return $this->passwordHasher->hashPassword($user, $plainPassword); <-- j'utlise la nouvelle méthode
}

public function isPasswordValid(User $user, string $raw): bool
{
return $this->passwordHasher->isPasswordValid($user, $raw);
}

public function needsRehash(User $user): bool
{
return $this->passwordHasher->needsRehash($user);
}
}

Voilà, il m’a suffi juste de changer l’implémentation de mon interface et c’est fini.

Conclusion

J’ai suivi les principes de la clean architecture, l’objectif est de faire tout son possible pour séparer son Domaine des différentes implémentations (framwork, doctrine, api externe…etc) sans être trop extrémiste car des fois on n’a pas le choix… ☮️

--

--