Pour l'instant, notre application n'a pas d'identification. On souhaite maintenant ajouter des utilisateurs, leur permettre de s'inscrire à travers l'API, puis de se connecter.
Certaines requêtes de l'API nécessiteront un compte par la suite.
Tout d'abord, ajoutez une entité utilisateur :
symfony console make:user
Mettez en suite à jour le schéma de la base de données :
symfony console doctrine:schema:update --force
Créez ensuite un formulaire d'inscription, il nous servira d'exemple pour créer l'équivalent en version "API" (Attention: répondez "no" à la question vous demandant d'envoyer un e-mail):
symfony console make:registration
Il est désormais possible de s'inscrire via la route /register à l'aide d'une adresse e-mail et d'un mot
de passe. Ajoutez un endpoint dans l'API :
/api/registerPOSTVous pourrez fonctionner de la même manière que POST /api/message. Inspirez vous du contenu du
RegistrationController, notamment pour hasher le mot de passe.
Les erreurs (par exemple, si l'e-mail n'est pas renseignée ou déjà utilisée) doivent remonter à travers l'API également.
Note: À l'aide des tags, vous pouvez commencer à regrouper les mots d'API par section comme cela:
Nous allons authentifier nos utilisateurs à l'aide d'un jeton. Pour cela, utilisez :
symfony console make:entity
Pour ajouter un champ token dans l'entité User de type string nullable.
N'oubliez pas de répercuter le changement dans la base à l'aide de :
symfony console doctrine:schema:update --force
Modifier votre inscription afin que le jeton soit généré aléatoirement et stocké dans la base de données.
À l'aide de la documentation officielle,
créez maintenant un endpoint /api/login, qui permettra de distribuer le jeton.
Voici à quoi pourrait ressembler le endpoint
#[View()]
#[Route('/login', methods: ['POST'], name: "api_login")]
#[OA\Post(summary: "Login",tags: ["User"])]
#[OA\RequestBody(content:
new OA\JsonContent(
example: '{"username": "user@user.com", "password": "password"}'
))]
public function login(#[CurrentUser()] User $user)
{
return $user->getToken();
}
Testez votre endpoint de connexion à l'aide d'un compte utilisateur inscrit.
Dans config/packages/security.yaml, créez un deuxième firewall nommé api. Pour différencier les deux
nous utiliserons des pattern:
security:
firewalls:
main:
pattern: ^(!/api)
...
api:
pattern: ^/api
...
Cela va nous permettre de configurer une authentification basée sur un jeton pour les requêtes commençant par
/api, tout en gardant une authentification classique pour les autres.
Modifiez alors votre firewall api de manière à ce qu'il utilise une authentification basée sur un jeton:
security:
# ...
firewalls:
# ...
api:
stateless: true
access_token:
token_handler: App\Security\AccessTokenHandler
# ...
Placez ensuite le contenu suivant dans src/Security/AccessTokenHandler.php
(source: documentation officielle):
<?php
// src/Security/AccessTokenHandler.php
namespace App\Security;
use App\Repository\UserRepository;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
class AccessTokenHandler implements AccessTokenHandlerInterface
{
public function __construct(
private UserRepository $repository
) {
}
public function getUserBadgeFrom(string $accessToken): UserBadge
{
$user = $this->repository->findOneBy(["token" => $accessToken]);
if (null === $user) {
throw new BadCredentialsException('Invalid credentials.');
}
return new UserBadge($user->getUserIdentifier());
}
}
Nous allons maintenant ajouter cette authentification dans la documentation. Ajoutez à la section components de nelmio_api_doc.yaml :
securitySchemes:
Bearer:
type: http
scheme: bearer
Vous pouvez désormais ajouter les attributs suivants à POST /api/message :
// Provient de Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted
#[IsGranted("ROLE_USER")]
// Provient de Nelmio\ApiDocBundle\Annotation\Security;
#[Security(name: "Bearer")]
Vous devriez maintenant voir apparaître un cadenas indiquant que la route nécessite l'identification, et un bouton en haut à droite permettant de s'identifier :
Utilisez à nouveau symfony console make:entity pour ajouter un champ user de type ManyToOne
de Message vers User.
POST /api/message de manière à ce que l'utilisateur courant soit associé au messagemessage_basic (à chaque fois qu'un message est récupéré, on
pourra voir son e-mail)Faites en sorte que PATCH et DELETE ne fonctionnent pas si l'utilisateur n'est pas identifié, et n'est
pas propriétaire du message
Ajoutez l'utilisateur au groupe message_basic, de manière à ce que l'utilisateur apparaisse dans la recherche
des messages.