Bonjour,

J'ai deux rôles, ROLE_WORKER et ROLE_CUSTOMER je souhaite rediriger l'utilisateur après sa connexion selon son rôle.

J'ai déjà généré la base de code avec le make:auth voici mon code (voir ligne 53) :

<?php

namespace App\Security;

use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait;

class AppAuthenticator extends AbstractLoginFormAuthenticator
{
    use TargetPathTrait;

    public const LOGIN_ROUTE = 'app_login';

    private UrlGeneratorInterface $urlGenerator;

    public function __construct(UrlGeneratorInterface $urlGenerator)
    {
        $this->urlGenerator = $urlGenerator;
    }

    public function authenticate(Request $request): PassportInterface
    {
        $email = $request->request->get('email', '');

        $request->getSession()->set(Security::LAST_USERNAME, $email);

        return new Passport(
            new UserBadge($email),
            new PasswordCredentials($request->request->get('password', '')),
            [
                new CsrfTokenBadge('authenticate', $request->get('_csrf_token')),
            ]
        );
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
    {
        if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
            return new RedirectResponse($targetPath);
        }

        // Je pense que je dois écrire mon code ici avec un if selon le rôle mais je ne trouve aucune doc la dessus pour accéder au rôle du user
        return new RedirectResponse($this->urlGenerator->generate('home'));
    }

    protected function getLoginUrl(Request $request): string
    {
        return $this->urlGenerator->generate(self::LOGIN_ROUTE);
    }
}

Vous avez des idées ?

merci.

8 réponses


Gulivert
Réponse acceptée

Hello,

La où tu as mis ton commentaire dans la méthode onAuthenticationSuccess tu dois effectivement créer une condition en rapport avec tes roles.
La première chose que tu dois faire est de récupérer l'utilisateur, tu peux le récupérer via le token de l'instance TokenInterface qui est en pramêtre de la méthode. Ou tu récupères directement le tableau des rôles si tu le souhaites.

Donc avant de retourner la réponse RedirectResponse :

$user = $token->getUser();

Puis tu vérifies dans le tableau des rôles si un des rôles existe et tu rediriges en conséquence. Par exemple :

if (in_array("ROLE_WORKER", $user->getRoles())) {
return new RedirectResponse($this->urlGenerator->generate('worker_home'));
}

return new RedirectResponse($this->urlGenerator->generate('home'));

Dans ta fonction onAuthentificate tu peux t'imaginer injecter la classe authentification si tu en as une, et récupérer le user en faisant $user = $this->authentification->getUser(); (ceci est sensé prendre l'utilisateur connecté en session) ensuite vérifier si tu as un utilisateur en faisant un if($user && $user->role === "ROLE_WORKER"){
//dans ce cas tu redirige vers une page
}elseif($user){
//dans ce cas tu as juste un un utilisateur et vu que tu as deux rôles uniquement si c'est pas le premier rôle donc c'est le deuxième alors redirection vers une page x
}else{
//tu n'as pas d'utilisateur authentifié redirection vers le login
}

dd24
Auteur

Salut, je n'ai pas de fonction onAuthentificate, je pense que tu as mal lu mon code.
le code : $user->role === "ROLE_WORKER"
est une mauvaise pratique.

Je n'ai pas mon pc sous la main mais dans ta classe User tu as bien une methode getRoles? Il suffitait de l'appeler et de rediriger selon le role.

"onAuthentificationSuccess" je suppose que dans ton entité user tu as un getter getRoles() et tu peux l'appeler si l'utilisateur s'est authentifié ensuite vérifier son rôle et rediriger selon son rôle.

dd24
Auteur

super ! merci a Wilfried Musanzi, floriqn et surtout Gulivert qui apporte une réponse complete et detaillée !
Gulivert : Je regarde ton code car apparemment l'article https://numa-bord.com/miniblog/symfony-5-utiliser-la-fonction-isgranted-sur-nimporte-quel-objet-utilisateur/
indique que ce n'est pas une bonne pratique mais je pense qu'ils parlent selon les cas.

Tout dépend, si tu souhaites rediriger selon un certain rôle mais ne pas prendre en compte la hiérarchie l'example donner ici est à mon sens le plus simple et 100% fonctionel.

Par contre si tu veux prendre en compte la hiérarchie, je préconise d'utiliser le service de Symfony Security que tu peux injecter via le constructeur et à la place de vérifier si le groupe existe dans le tableau des rôles tu vérifies directement avec la méthode isGranted() du composant Security.

J'avais plutôt l'impression ici que tes redirections souhaitées étaient via un rôle spécifique à pas à l'ensemble des rôles ayant un accès à un sous rôle (j'espère être clair).

Dans le cas ou tu veux utiliser le service security tu peux faire ceci :

<?php

namespace App\Security;

use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait;

class AppAuthenticator extends AbstractLoginFormAuthenticator
{
    use TargetPathTrait;

    public const LOGIN_ROUTE = 'app_login';

    private UrlGeneratorInterface $urlGenerator;
    private Security $security;

    public function __construct(UrlGeneratorInterface $urlGenerator, Security $security)
    {
        $this->urlGenerator = $urlGenerator;
        $this->security = $security;
    }

    public function authenticate(Request $request): PassportInterface
    {
        $email = $request->request->get('email', '');

        $request->getSession()->set(Security::LAST_USERNAME, $email);

        return new Passport(
            new UserBadge($email),
            new PasswordCredentials($request->request->get('password', '')),
            [
                new CsrfTokenBadge('authenticate', $request->get('_csrf_token')),
            ]
        );
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
    {
        if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
            return new RedirectResponse($targetPath);
        }

        // ATTENTION AVEC CECI LE ROLE ADMIN ET SUPER_ADMIN SERONT AUSSI REDIRIGE
        if ($this->security->isGranted("ROLE_WORKER")) {
            return new RedirectResponse($this->urlGenerator->generate('home_worker'));
        }
        return new RedirectResponse($this->urlGenerator->generate('home'));
    }

    protected function getLoginUrl(Request $request): string
    {
        return $this->urlGenerator->generate(self::LOGIN_ROUTE);
    }
}

Attention avec cette deuxième solution tu vas rediriger tous les rôles au dessus du rôle ROLE_WORKER.

dd24
Auteur

oui j'ai bien compris, tu es très clair dans les explications et je te remercie. Je pense que je vais rester sur la première solution car oui en effet elle ne risque pas d'interférée avec les autres rôles et faire des redirection non voulue pour les admin etc.. Je te remercie pour ton code (je garde les deux solutions sous la main au cas ou)..