1

I use authentication (2) plugin with cakephp4. I have set :

        'unauthenticatedRedirect' => '/users/login',

in order to redirect requests that need authentication. It works fine.

But I'd like to add a message, a flash message for example, that would say "You must be log in to acceess this page".

Is there an easy way to do that ?

Thanks

ndm
  • 59,784
  • 9
  • 71
  • 110
dype
  • 500
  • 1
  • 4
  • 15

1 Answers1

2

There's no specific functionality for that yet: https://github.com/cakephp/authentication/issues/316

It could be solved in many different ways, personally I have done this before by catching Authentication\Authenticator\UnauthenticatedException in an extended authentication component, by overwriting \Authentication\Controller\Component\AuthenticationComponent::doIdentityCheck():

<?php
// src/Controller/Component/AppAuthenticationComponent.php

/*
load in `AppController::initialize()` via:

$this->loadComponent('Authentication', [
    'className' => \App\Controller\Component\AppAuthenticationComponent::class,
]);
*/

namespace App\Controller\Component;

use Authentication\Authenticator\UnauthenticatedException;
use Authentication\Controller\Component\AuthenticationComponent;
use Cake\Controller\Component\FlashComponent;

/**
 * @property FlashComponent $Flash
 */
class AppAuthenticationComponent extends AuthenticationComponent
{
    public $components = [
        'Flash'
    ];

    protected function doIdentityCheck(): void
    {
        try {
            parent::doIdentityCheck();
        } catch (UnauthenticatedException $exception) {
            $this->Flash->error(__('You must be logged in to access this page.'));

            throw $exception;
        }
    }
}

You could also do it manually in your app controller, for that you'd have to disable the plugin component's automatic identity check, and do that check on your own:

// src/Controller/AppController.php

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

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

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

    $action = $this->request->getParam('action');
    if (
        !in_array($action, $this->Authentication->getUnauthenticatedActions(), true) &&
        !$this->Authentication->getIdentity()
    ) {
        $this->Flash->error(__('You must be logged in to access this page.'));

        throw new UnauthenticatedException('No identity found.');
    }
}
ndm
  • 59,784
  • 9
  • 71
  • 110
  • Thanks @ndm, I've tried the first solution but I have this exception : `No identity found. You can skip this check by configuring requireIdentity to be false` In AppController.php I have the 2 lines: `$this->loadComponent('Authentication.Authentication'); $this->loadComponent('AppAuthentication');` Is it correct ? I guess I have missed something – dype Apr 02 '20 at 18:21
  • @dype Not quite correct, you need to stop using the original plugins authentication component, and only load `AppAuthentication`! – ndm Apr 02 '20 at 21:00
  • I've tried that, but then I have some errors : `Call to a member function allowUnauthenticated() on null`. But I found why : I have to change everywhere in my code : `$this->Authentication-> by `$this->AppAuthentication->` correct ? – dype Apr 03 '20 at 06:41
  • 1
    @dype Correct, but you could also use aliasing to use the original name, then you don't need to change anything else, I've updated the code example to show that. – ndm Apr 03 '20 at 10:16
  • ok thanks @npm, thank you for the cake lesson ;-) A small mistake in the alias : className' => \App\Controller\Component\AppAuthentication**Component**::class, – dype Apr 03 '20 at 12:28
  • @AngelS.Moreno Not using plugin notation was intentional, as the goal of that `loadComponent()` call is to create an alias with a specific class defined via the `className` Option, the call is _not_ meant to load the plugin's component! Using plugin notation in such a case can lead to undesired internal behavior, as the dot notated name would be used for duplicate checks and the like, unlike when not not using the `className` option, resulting in possible clashes to not be detected. – ndm Oct 10 '22 at 14:09