28 Juil, 2020

L’authentification sous CakePHP 4

Mise en place d'un système d'authentification sous CakePHP4.

CakePHP4 propose un système d’authentification et d’autorisation plus élaboré que son prédecesseur. En effet le composant Auth habituellement utilisé est désormais remplacé par deux plugins :

Nous verrons dans un cet article l’implémentation et les spécificités du plugin Authentication.

Installation et configuration 

Exécutez la commande suivante à la racine du projet afin d’installer le plugin :

composer require cakephp/authentication:^2.0

Chargez ensuite le plugin dans la méthode bootstrap située dans le fichier src/Application.php :

public function bootstrap(): void
{
    parent::bootstrap();

    $this->addPlugin('Authentication');
}

Le plugin s’implémente dans votre projet en tant que middleware.
Toujours dans le fichier src/Application.php, importez les classes suivantes :

use Authentication\AuthenticationService;
use Authentication\AuthenticationServiceInterface;
use Authentication\AuthenticationServiceProviderInterface;
use Authentication\Middleware\AuthenticationMiddleware;
use Cake\Http\MiddlewareQueue;
use Psr\HttpMessage\ServerRequestInterface;

Ajoutez ensuite l’interface AuthenticationProviderInterface aux interfaces implémentées sur votre application :

class Application extends BaseApplication implements AuthenticationServiceProviderInterface

Puis ajoutez le middleware AuthenticationMiddleware à la file d’attente des middlewares de votre application :

        $middlewareQueue
            ...
            ->add(new AuthenticationMiddleware($this));

Ce middleware appelle la fonction intitulée getAuthenticationService à chaque requête. Cette méthode permet de configurer votre système d’authentification : méthodes d’authentifications, méthodes d’identifications, champs requis, URL de redirection, etc.

Voici un exemple simple :

/**
 * Returns a service provider instance.
 *
 * @param PsrHttpMessageServerRequestInterface $request Request
 * @return AuthenticationAuthenticationServiceInterface
 */
public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
{
    $service = new AuthenticationService();

    // Define where users should be redirected to when they are not authenticated
    $service->setConfig([
        'unauthenticatedRedirect' => '/users/login',
        'queryParam' => 'redirect',
    ]);

    $fields = [
        'username' => 'email',
        'password' => 'password'
    ];
    // Load the authenticators. Session should be first.
    $service->loadAuthenticator('Authentication.Session');
    $service->loadAuthenticator('Authentication.Form', [
        'fields' => $fields,
        'loginUrl' => '/users/login'
    ]);

    // Load identifiers
    $service->loadIdentifier('Authentication.Password', compact('fields'));

    return $service;
}

Les authenticators sont des méthodes permettant d’authentifier l’utilisateur. On charge ici l’authentification par session et par formulaire. Il est également possible de charger l’authenticator Cookie qui permet par exemple de mettre en place un cookie “Remember Me” ou encore l’authenticator JWT utile pour allier votre application a une application front-end.

Les identifiers sont des méthodes d’identification. Utilisées par les authenticators, elles permettent d’identifier l’utilisateur qui souhaite s’authentifier.

Chargez ensuite le composant Authentication dans votre AppController :

public function initialize(): void
{
    parent::initialize();

    $this->loadComponent('Authentication.Authentication');
}

Par défaut, toutes les actions de votre application qui hérite de cet AppController nécessite une authentification. Vous pouvez dispenser des actions de cette règle en utilisant la méthode allowUnauthenticated dans un beforeFilter ou un initialize de votre controller.

Maintenant que votre système d’authentification est en place, vous pouvez intégrer une logique de connexion.

    public function login()
    {
        $result = $this->Authentication->getResult();
        if ($result->isValid()) {
            $target = $this->Authentication->getLoginRedirect() ?? ['prefix' => false, 'controller' => 'Pages', 'action' => 'index'];
            return $this->redirect($target);
        }

        if ($this->request->is('post') && !$result->isValid()) {
            $this->Flash->error('Adresse email ou mot de passe incorrect.');
        }
    }

Afin de rendre cette action accessible, il est nécessaire de l’autoriser via la méthode allowUnauthenticated :

public function beforeFilter(CakeEventEventInterface $event)
{
    parent::beforeFilter($event);

    $this->Authentication->allowUnauthenticated(['login']);
}

Vous pouvez ensuite intégrer la logique de déconnexion :

    public function logout()
    {
        return $this->redirect($this->Authentication->logout());
    }

Après la déconnexion, votre application redirige l’utilisateur directement vers une URL qui est paramétrée lors du chargement de votre composant Authentication :

    public function initialize(): void
    {
        parent::initialize();

        $this->loadComponent('Authentication.Authentication', [
            'logoutRedirect' => '/'
        ]);
    }

Allez plus loin

Si vous souhaitez directement authentifier un utilisateur sans passer par un authenticator, par exemple lors d’une reconnexion depuis un back office, vous pouvez utiliser la méthode setIdentity :

$this->Authentication->setIdentity($user);

Pour récupérer les informations de l’utilisateur authentifié, vous pouvez utiliser la méthode suivante dans vos controllers :

$this->Authentication->getIdentity();

// Accès à un champ
$this->Authentication->getIdentity()->username;
// Accès à un champ virtuel
$this->Authentication->getIdentity()->full_name;
// Accès à une méthode de votre entité
$this->Authentication->getIdentity()->isAdmin();

Si vous souhaitez faire de même dans vos vues, utilisez la méthode suivante :

$this->request->getAttribute('identity')

// Accès à un champ
$this->request->getAttribute('identity')->username;
// Accès à un champ virtuel
$this->request->getAttribute('identity')->full_name;
// Accès à une méthode de votre entité
$this->request->getAttribute('identity')->isAdmin();

Lors de vos tests, vous souhaiterez probablement simuler un utilisateur authentifié. Pour cela utilisez la méthode suivante :

$this->session(['Auth' => $user]);

En conclusion

Avec ce nouveau plugin, CakePHP offre un meilleur kit de gestion des authentifications. Désormais instancié et configuré en tant que middleware, ce dernier facilite grandement l’implémentation de différentes méthodes de connexion à un outil ainsi que les paramétrages possibles. Par exemple, plus besoin de gérer manuellement le cookie “Remember Me”, une simple configuration dans le fichier src/Application.php suffit.