User Tools

Site Tools


backend:symfonyauth

Symfony : Auth

logo

Création

Pour créer le controller Auth, il faut taper :

symfony console make:auth

Il faut ensuite suivre la procédure :

Depuis Symfony 5.1, il existe un nouveau système d'authentication expérimental, voir le tuto Authentification et Autorisation avec Symfony 5

Controller

Méthodes

Deux méthodes sont crées :

login()

 * @Route("/login", name="app_login")
 */
public function login(AuthenticationUtils $authenticationUtils): Response
{
  // if ($this->getUser()) {
  //     return $this->redirectToRoute('target_path');
  // }
 
  // get the login error if there is one
  $error = $authenticationUtils->getLastAuthenticationError();
  // last username entered by the user
  $lastUsername = $authenticationUtils->getLastUsername();
 
  return $this->render('security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]);
}
  • $lastUsername correspond au dernier identifiant visuel (l'adresse email ici) entré par l'utilisateur. Il a été sauvegardé par la méthode getCredentials() dans constante LAST_USERNAME de la session.

  • Pour cela, on injecte le service AuthenticationUtils pour utiliser la méthode getLastUsername() pour la récupérer et la passer au template security/login.html.twig.

  • error correspond à la dernière erreur d'authentication défini dans la constante AUTHENTICATION_ERROR de la session grâce à la méthode getLastAuthenticationError() de la classe AuthenticationUtils.

logout()

  * @Route("/logout", name="app_logout")
  */
public function logout()
{
  throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
}

➤ Cette méthode ne fait que lever une exception.

  • Lorsqu'un utilisateur va sur le lien de déconnexion, Symfony gère cette déconnexion automatiquement.

  • La seule chose importante est que la route de déconnexion soit défini, ici @Route(“/logout”, name=“app_logout”)

  • C'est cette route qui doit être défini dans le fichier config\packages\security.yaml : logout: path: app_logout pour qu'elle soit interceptée au niveau du firewall.

Form

Méthodes

class LoginFormAuthenticator extends AbstractFormLoginAuthenticator implements PasswordAuthenticatedInterface

La classe LoginFormAuthenticator implémente AuthenticatorInterface (car elle hérite d'AbstractFormLoginAuthenticator qui hérite d'AbstractGuardAuthenticator qui l'implémente) et PasswordAuthenticatedInterface, elle doit donc implémenter toutes les méthodes de ces dernières. A la création, automatiquement, ces méthodes sont crées et ainsi que d'autres.


Pour PasswordAuthenticatedInterface :

getPassword()

public function getPassword($credentials): ?string
{
    return $credentials['password'];
}

➤ Cette méthode renvoie le mot de passe en clair, elle est utilisée lorsqu'il y a un changement d'algorithme de hachage.


Pour AuthenticatorInterface :

supports()

public function supports(Request $request)
{
    return self::LOGIN_ROUTE === $request->attributes->get('_route')
        && $request->isMethod('POST');
}

➤ Les méthodes supports des authentificateurs sont appelées à chaque page. Elle doivent retourner true ou false selon si on souhaite que l'authentificateur applique sa logique ou nom.

  • self::LOGIN_ROUTE === $request→attributes→get('_route') vérifie que la route courante est égale à la route indiquée dans la constante LOGIN_ROUTE

  • $request→isMethod('POST') vérifie que la méthode HTTP est POST

Si ces 2 conditions sont remplis, l'authentificateur doit appliquer sa logique et appeler la méthode getCredentials().


getCredentials()

public function getCredentials(Request $request)
{
    $credentials = [
        'email' => $request->request->get('email'),
        'password' => $request->request->get('password'),
        'csrf_token' => $request->request->get('_csrf_token'),
    ];
    $request->getSession()->set(
        Security::LAST_USERNAME,
        $credentials['email']
    );
 
    return $credentials;
}

➤ Cette méthode est appelée si la méthode supports() a retourné true.

  • Elle récupère les données entrées dans le formulaire passé avec la méthode HTTP POST dans $credentials

  • En suite via la requête, elle accède à la session et affecte à la constante LAST_USERNAME, de la session, le texte entré dans le champ email qui correspond à l'identifiant visuel choisi pour représenter User. Cela permet de préremplir le champs du formulaire avec ce texte (en cas d'erreur de connexion, le formulaire apparaît avec ce champs prérempli).

  • Cela correspond à une sauvegarde temporaire de l'email de l'utilisateur.

  • Les informations du formulaire sont ensuite passées à la méthode getUser()

A noter : On utilise $request→request pour les méthodes POST et $request→query pour les méthodes GET.


getUser()

public function getUser($credentials, UserProviderInterface $userProvider)
{
  $token = new CsrfToken('authenticate', $credentials['csrf_token']);
  if (!$this->csrfTokenManager->isTokenValid($token)) {
    throw new InvalidCsrfTokenException();
  }
 
  $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $credentials['email']]);
 
  if (!$user) {
    // fail authentication with a custom error
    throw new CustomUserMessageAuthenticationException('Email could not be found.');
  }
 
  return $user;
}

➤ Elle doit retourner un objet de type UserInterface,'$credentials' est récupéré de la méthode getCredentials().

  • Elle éffectue la vérification du token csrf, pour cela elle utilise un objet de type CsrfTokenManagerInterface ($this→csrfTokenManager) qui a été injecté dans la méthode __construct().

  • Puis elle recherche l'utilisateur dans la table de la base de données grace a un objet de type EntityManagerInterface ($this→entityManager) qui a été injecté dans la méthode __construct() (car on ne peut pas l'injecter directement dans la méthode, ce n'est possible que pour les controller). Si un utilisateur est trouvé, elle va le renvoyer, sinon elle va lever une exception.

checkCredentials()

public function checkCredentials($credentials, UserInterface $user)
{
  return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
}

➤ Elle retourne true si les identifiants de connexion sont valides.

  • $credentials' est récupéré de la méthode getCredentials() et $user est récupéré de la méthode getUser().

  • Pour cela elle utilise un objet de type UserPasswordEncoderInterface ($this→passwordEncoder) qui a été injecté dans la méthode __construct() et la méthode isPasswordValid()

createAuthenticatedToken()

public function createAuthenticatedToken(UserInterface $user, string $providerKey)
{
    return new PostAuthenticationGuardToken(
        $user,
        $providerKey,
        $user->getRoles()
    );
}

➤ Cette méthode est déjà implémentée.

  • LoginFormAuthenticator hérite d'AbstractFormLoginAuthenticator qui hérite d'AbstractGuardAuthenticator dans laquelle cette méthode est implémentée.

  • Symfony indique : “Si vous ne vous souciez pas de la classe de token utilisée ou si vous ne comprenez pas vraiment ce qu'est un «token», vous pouvez ignorer cette méthode en étendant la classe AbstractGuardAuthenticator à partir de votre authentificateur.

start()

public function start(Request $request, AuthenticationException $authException = null)
{
  $url = $this->getLoginUrl();
 
  return new RedirectResponse($url);
}

➤ Cette méthode contrôle ce qui se passe lorsque l'utilisateur accède à une page sécurisée mais n'est pas encore connecté.

  • Cette méthode est déjà implémentée.
  • LoginFormAuthenticator hérite d'AbstractFormLoginAuthenticator dans laquelle cette méthode est implémentée.

  • Elle récupère l'url de login.
  • Elle redirige l'utilisateur vers cette url.

onAuthenticationFailure()

public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
    if ($request->hasSession()) {
        $request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception);
    }
 
    $url = $this->getLoginUrl();
 
    return new RedirectResponse($url);
}

➤ Cette méthode est appelée lorsque l'authentification a échouée.

  • LoginFormAuthenticator hérite d'AbstractFormLoginAuthenticator dans laquelle cette méthode est implémentée.

  • Si on a une session, elle va mettre une exception dans la variable AUTHENTICATION_ERROR de la session.

  • Elle récupère l'url de la page de login, pour cela elle utilise la méthode getLoginUrl() qui a été implémentée à la création.

  • Elle redirige l'utilisateur vers cette url

onAuthenticationSuccess()

public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
  if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
    return new RedirectResponse($targetPath);
  }
 
  // For example : return new RedirectResponse($this->urlGenerator->generate('some_route'));
  throw new \Exception('TODO: provide a valid redirect inside ' . __FILE__);
}

➤ Cette méthode est appelée lorsque l'authentification est un success.

  • $providerKey correspond au nom du firewall défini dans le fichier config\packages\security.yaml.

  • ' if ($targetPath = $this→getTargetPath($request→getSession(), $providerKey)) {return new RedirectResponse($targetPath);}' permet de rediriger l'utilisateur vers l'url qu'il a demandé avant d'arriver sur la page de connexion (par exemple il demande à créer un pin sans être connecté, il va alors aller sur la page de connexion et après s'être connecté, il sera redirigé directement sur la page de création de pin).

  • En cas de succès, il faut rediriger l'utilisateur vers une url valide (à la place de 'throw new \Exception('TODO: provide a valid redirect inside ' . FILE);')

supportsRememberMe()

public function supportsRememberMe()
{
    return true;
}

➤ Cette méthode retourne un booléen indiquant si on veut garder la session active via les cookies.

  • LoginFormAuthenticator implémente AuthenticatorInterface (car elle hérite d'AbstractFormLoginAuthenticator qui hérite d'AbstractGuardAuthenticator qui l'implémente) dans laquelle cette méthode est implémentée. \\
  • La clé “Remember_me” doit être configurée sous le firewall dans le fichier config\packages\security.yaml \\
  • La méthode onAuthenticationSuccess doit renvoyer un objet de type Response.
  • A l'utilisation, il y a l'ajout d'un cookie qui permet la reconnexion automatique.

__construct()

Autres fonctions crées :

public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
{
  $this->entityManager = $entityManager;
  $this->urlGenerator = $urlGenerator;
  $this->csrfTokenManager = $csrfTokenManager;
  $this->passwordEncoder = $passwordEncoder;
}

getLoginUrl()

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

➤ Retourne l'url de la page de login

  • Cette url est contenu dans la constante LOGIN_ROUTE défini dans LoginFormAuthenticator grace a un objet de type UrlGeneratorInterface ($this→urlGenerator) qui a été injecté dans la méthode __construct().

  • Elle utilise la méthode generate qui génère une url à partir d'une route donnée.

Messages d'erreurs

Affichage

Lors d'une tentative d'authentification, Symfony va renvoyer le paramètre error à un template, on peut alors l'afficher (voir Twig : Affichage des messages )

{{ error.messageKey }}

Personnalisation des messages

Modification des méthodes

On peut modifier directement les méthodes pour avoir des messages personnalisés, par exemple la fonction checkCredentials() :

public function checkCredentials($credentials, UserInterface $user)
{
  return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
}

Peut s'écrire :

public function checkCredentials($credentials, UserInterface $user)
{
  if (!$this->passwordEncoder->isPasswordValid($user, $credentials['password'])) {
    throw new CustomUserMessageAuthenticationException('Identifiants invalides');
  }
 
  return true;
}
Utilisation de la traduction

Tutoriels

- Lior Chamla : SYMFONY 4/4 : 1H POUR COMPRENDRE L'AUTHENTIFICATION !, voir les notes prises sur cette vidéo.

- LES TEACHER DU NET : Créer un clone de Pinterest avec Symfony 5 (Épisode 6/10), voir les notes prises sur cette vidéo.

backend/symfonyauth.txt · Last modified: 2020/09/18 09:45 (external edit)