Migration depuis AuthComponent

Différences

  • Volontairement, ce plugin ne gère pas les autorisations. La fonctionnalité a été découplée de l’autorisation dans le but de proposer une séparation des préoccupations claire. Cf. aussi Contrôle d’accès. Ce plugin gère seulement l”identification et l”authentification. On peut avoir un autre plugin pour l’autorisation.

  • Il n’y a pas de vérification automatique de la session. Pour aller chercher les informations utilisateur dans la session, vous devrez utiliser le SessionAuthenticator. Il va vérifier dans la session s’il y a des données sous la clé de session configurée, et les place ensuite dans l’objet Identité.

  • Les informations sur l’utilisateur ne sont plus disponibles en passant par l’ancien AuthComponent, mais sont accessibles via un attribut de la requête et encapsulées dans un objet Identité: $request->getAttribute('authentication')->getIdentity();. En complément, vous pouvez exploiter les méthodes getIdentity() ou getIdentityData() de AuthenticationComponent.

  • La logique du processus d’authentification a été scindée en authentificateurs et identificateurs. Un authentificateur va extraire les identifiants de l’utilisateur (credentials) dans la requête, tandis que les identificateurs les vérifieront et désigneront l’utilisateur correspondant.

  • DigestAuthenticate a été renommé en HttpDigestAuthenticator.

  • BasicAuthenticate a été renommé en HttpBasicAuthenticator.

Similitudes

  • Tous les adaptateurs d’authentification existants, Form, Basic, Digest sont toujours là mais ont été remodelés en authentificateurs.

Identificateurs et authentificateurs

Suivant en cela le principe de séparation des préoccupations, les anciens objets d’authentification ont été scindés en objets bien séparés, les identificateurs et les authentificateurs.

  • Les authentificateurs prennent la requête entrante et tentent d’en extraire les identifiants de l’utilisateur. S’ils les trouvent, ils les passent à une collection d’identificateurs qui recherchent où se trouve l’utilisateur. Pour cette raison, les authentificateurs prennent une IdentifierCollection en premier argument dans leur constructeur.

  • Les identificateurs confrontent les identifiants à un système de stockage (par exemple des tables ORM, LDAP, etc) et renvoient les informations de l’utilisateur identifié.

Cela facilite le changement de logique d’identification en tant que de besoin, ou l’utilisation de plusieurs sources d’informations sur les utilisateurs.

Si vous voulez implémenter vos propres identificateurs, votre identificateur doit implémenter l’interface IdentifierInterface.

Migrer votre système d’authentification

La première chose à faire pour migrer votre application est de charger le plugin authentication dans la méthode bootstrap de votre application:

public function bootstrap(): void
{
    parent::bootstrap();
    $this->addPlugin('Authentication');
}

Ensuite, modifiez votre application pour lui faire implémenter l’interface de fournisseur de service d’authentification. Cela permet à votre AuthenticationMiddleware de savoir comment obtenir un service d’authentification à partir de votre application:

// dans src/Application.php

// Ajoutez les instructions 'use' suivantes.
use Authentication\AuthenticationService;
use Authentication\AuthenticationServiceInterface;
use Authentication\AuthenticationServiceProviderInterface;
use Authentication\Middleware\AuthenticationMiddleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

// Ajoutez l'interface d'authentification.
class Application extends BaseApplication implements AuthenticationServiceProviderInterface
{
    /**
     * Renvoie une instance du service provider.
     *
     * @param \Psr\Http\Message\ServerRequestInterface $request Request
     * @param \Psr\Http\Message\ResponseInterface $response Response
     * @return \Authentication\AuthenticationServiceInterface
     */
    public function getAuthenticationService(ServerRequestInterface $request) : AuthenticationServiceInterface
    {
        $service = new AuthenticationService();
        // Configurez le service. (cf. ci-dessous pour les détails)
        return $service;
    }
}

Puis ajoutez l”AuthenticationMiddleware à votre application:

// dans src/Application.php
public function middleware($middlewareQueue)
{
    // Divers autres middlewares pour la gestion des erreurs, le routing, etc, sont ajoutés ici.

    // Ajoutez le middleware à la middleware queue
    $middlewareQueue->add(new AuthenticationMiddleware($this));

    return $middlewareQueue;
}

Migrer vos réglages de AuthComponent

Le tableau de configuration de AuthComponent a besoin d’être scindé en identificateurs et authentificateurs lors de la configuration du service. Ainsi, si votre AuthComponent était configuré de cette façon:

$this->loadComponent('Auth', [
    'authentication' => [
        'Form' => [
            'fields' => [
                'username' => 'email',
                'password' => 'password',
            ]
        ]
    ]
]);

Vous devrez maintenant le configurer de cette façon:

// Instancier le service
$service = new AuthenticationService();

// Charger les identificateurs
$service->loadIdentifier('Authentication.Password', [
    'fields' => [
        'username' => 'email',
        'password' => 'password',
    ]
]);

// Charger les authentificateurs
$service->loadAuthenticator('Authentication.Session');
$service->loadAuthenticator('Authentication.Form');

Si vous aviez personnalisé le userModel, vous pouvez utiliser la configuration suivante:

// Instancier le service
$service = new AuthenticationService();

// Charger les identificateurs
$service->loadIdentifier('Authentication.Password', [
    'resolver' => [
        'className' => 'Authentication.Orm',
        'userModel' => 'Employes',
    ],
    'fields' => [
        'username' => 'email',
        'password' => 'password',
    ]
]);

Bien qu’il y ait un petit peu plus de code qu’avant, vous avez plus de souplesse dans la gestion des authentifications.

Action Login

L”AuthenticationMiddleware va se charger de la vérification et de la définition de l’identité de l’utilisateur en s’appuyant sur les authentificateurs. D’habitude, après la connexion, AuthComponent redirigeait vers une URL définie dans la configuration. Pour rediriger après une connexion réussie, changez votre action login pour vérifier le résultat de la nouvelle identité:

public function login()
{
    $result = $this->Authentication->getResult();

    // Que l'on soit en POST ou GET, rediriger l'utilisateur s'il est connecté
    if ($result->isValid()) {
        $target = $this->Authentication->getLoginRedirect();
        return $this->redirect($target);
    }

    // Afficher une erreur si l'utilisateur a validé le formulaire et que
    // l'authentification a échoué
    if ($this->request->is(['post'])) {
        $this->Flash->error('Identifiant ou mot de passe invalide');
    }
}

Vérifier les identités

Après avoir appliqué le middleware vous pouvez utiliser les données d’identité en consultant l’attribut identity de la requête. Cela remplace les appels à $this->Auth->user() que vous utilisiez jusqu’à présent. Si l’utilisateur en cours n’est pas authentifié ou si les identifiants fournis étaient invalides, l’attribut identity sera null:

$user = $request->getAttribute('identity');

Pour plus de détails sur le résultat du processus d’authentification, vous pouvez accéder à l’objet Résultat qui est aussi fourni dans la requête et est accessible sous l’attribut authentication:

$result = $request->getAttribute('authentication')->getResult();
// Booléen si le résultat est valide
$isValid = $result->isValid();
// Un code de statut
$statusCode = $result->getStatus();
// Un tableau de messages d'erreur, ou des données si l'identificateur en a fournies
$errors = $result->getErrors();

À chaque endroit où vous appeliez AuthComponent::setUser(), vous devriez à présent utiliser setIdentity():

// Supposons que vous ayez besoin de rechercher un utilisateur à partir d'un jeton d'accès
$user = $this->Users->find('byToken', ['token' => $token])->first();

// Rendre l'utilisateur persistant dans les authentificateurs configurés.
$this->Authentication->setIdentity($user);

Migrer la logique allow/deny

Comme AuthComponent, l”AuthenticationComponent rend aisé le marquage d’actions spécifiques comme étant “publiques” et ne nécessitant pas la présence d’une identité valide:

// Dans la méthode beforeFilter de votre contrôleur.
$this->Authentication->allowUnauthenticated(['view']);

Chaque appel à allowUnauthenticated() écrasera la liste d’actions en cours.

Migrer les Redirections en cas de Non Authentification

Par défaut, AuthComponent renvoie les utilisateurs vers la page de connexion lorsqu’une authentification est exigée. Au contraire, dans ce scénario, l”AuthenticationComponent de ce plugin soulèvera une exception. Vous pouvez convertir cette exception en redirection en utilisant unauthenticatedRedirect dans la configuration de l”AuthenticationService.

Vous pouvez aussi passer l’URI ciblée par la requête en cours en tant que paramètre dans la query string de la redirection avec l’option queryParam:

// Dans la méthode getAuthenticationService() de votre src/Application.php

$service = new AuthenticationService();

// Configurer la redirection en cas de non authentification
$service->setConfig([
    'unauthenticatedRedirect' => '/users/login',
    'queryParam' => 'redirect',
]);

Puis, dans la méthode login de votre contrôleur, vous pouvez utiliser en toute sécurité getLoginRedirect() pour obtenir la cible redirigée, à partir du paramètre de la query string:

public function login()
{
    $result = $this->Authentication->getResult();

    // Que l'on soit en POST ou GET, rediriger l'utilisateur s'il est connecté
    if ($result->isValid()) {
        // Utiliser le paramètre de redirection s'il est présent.
        $target = $this->Authentication->getLoginRedirect();
        if (!$target) {
            $target = ['controller' => 'Pages', 'action' => 'display', 'home'];
        }
        return $this->redirect($target);
    }
}

Migrer la Mise à Niveau de la Logique de Hachage

Si votre application utilise la fonctionnalité de AuthComponent de mise à niveau du hachage. Vous pouvez répliquer cette logique dans ce plugin en tirant parti de l”AuthenticationService:

public function login()
{
    $result = $this->Authentication->getResult();

    // Que l'on soit en POST ou GET, rediriger l'utilisateur s'il est connecté
    if ($result->isValid()) {
        $authService = $this->Authentication->getAuthenticationService();

        // En supposant que vous utilisez l'identificateur `Password`.
        if ($authService->identifiers()->get('Password')->needsPasswordRehash()) {
            // Le re-hachage se produit lors de la sauvegarde.
            $user = $this->Users->get($this->Authentication->getIdentityData('id'));
            $user->password = $this->request->getData('password');
            $this->Users->save($user);
        }

        // Rediriger vers une page connectée
        return $this->redirect([
            'controller' => 'Pages',
            'action' => 'display',
            'home'
        ]);
    }
}