CakePHP 5 upgrade and Authentication

I’ve started going through the process to migrate a fairly large app from v4.6 to v5.3.5.

So far, I’ve gone fairly well and I’m presented with a login screen - not too bad for an afternoon’s work :stuck_out_tongue:

Now that I’m tying to log in, I’ve struck a problem with a supposedly invalid password

In v4 I had the following

            $authenticationService = new AuthenticationService([
                'unauthenticatedRedirect' => Router::url('/users/login'),
                'queryParam' => 'redirect',
            ]);

            // Load identifiers, ensure we check email and password fields
            $authenticationService->loadIdentifier('Authentication.Password', [
                'fields' => [
                    'username' => 'email_address',
                    'password' => 'password',
                ],
                'resolver' => [
                    'className' => 'Authentication.Orm',
                    'userModel' => 'Users',
                    'finder' => 'userDetails' // Customer Finder that returns associated Member record
                ]
            ]);

            // Load the Session impersonate authenticator first, lets us act as proxy on a users account
            $authenticationService->loadAuthenticator(Authenticator\SessionImpersonateAuthenticator::class);
            // Load the authenticators, you want session first
            $authenticationService->loadAuthenticator('Authentication.Session');
            // Configure form data check to pick email and password
            $authenticationService->loadAuthenticator('Authentication.Form', [
                'fields' => [
                    'username' => 'email_address',
                    'password' => 'password',
                ],
                'loginUrl' => Router::url('/users/login'),
            ]);

In V5 I have the following

            $authenticationService = new AuthenticationService();

            // Define where users should be redirected to when they are not authenticated
            $authenticationService->setConfig([
                'unauthenticatedRedirect' => [
                    'prefix' => false,
                    'plugin' => false,
                    'controller' => 'Users',
                    'action' => 'login',
                ],
                'queryParam' => 'redirect',
            ]);

            $fields = [
                'username' => 'email_address',
                'password' => 'password',
            ];

            // Load the authenticators. Session should be first.
            // Session just uses session data directly as identity, no identifier needed.
            $authenticationService->loadAuthenticator('Authentication.Session');
            $authenticationService->loadAuthenticator('Authentication.Form', [
                'identifier' => [
                    'className' => 'Authentication.Password',
                    'fields' =>   $fields,
                ],
                'fields' => $fields,
                'loginUrl' => Router::url([
                    'prefix' => false,
                    'plugin' => null,
                    'controller' => 'Users',
                    'action' => 'login',
                ]),
            ]);

            // Load the Session impersonate authenticator first, lets us act as proxy on a users account
            $authenticationService->loadAuthenticator(Authenticator\SessionImpersonateAuthenticator::class);

I’ve tried following the documentation as best I can but I’ve run out of ideas.

Any assistance/greatly appreciated

Regards,
Brian

Managed to get help on slack

Will post outcomes here if I get a chance

What was the problem? Could something be improved in the docs?

thread here on slack

ok Claude suggested changing

$authenticationService->setConfig([
    'unauthenticatedRedirect' => [
        'prefix' => false,
        'plugin' => false,
        'controller' => 'Users',
        'action' => 'login',
    ],
    'queryParam' => 'redirect',
]);

to

$authenticationService->setConfig([
    'unauthenticatedRedirect' => '/users/login',
    'queryParam' => 'redirect',
]);

and

'loginUrl' => Router::url([
    'prefix' => false,
    'plugin' => false,
    'controller' => 'Users',
    'action' => 'login',
]),

to

'loginUrl' => '/users/login',

which seems to have done the trick

ah right, this was more a router config problem than a authentication problem as the “URL-Array” didn’t resolve to the same URL you were expecting.

We will adjust the docs to mention, that either the URL-Array or a simple string can be used in this case.

1 Like

The Array should usually work just fine and is the clean primary version in v4, so I wonder what made it not work in your case. You are using v4.1+ of the plugin, right?

yes, 4.1 - bit strange but work now

Ok hopefully the last issue with authentication - I have an API and under CakePHP4 if would give a nice JSON or XML error


{

    "message": "Authentication is required to continue",

"url": "/api/members.json?in_unit=350",

"code": 401

}

but after upgrading to CakePHP5 and latest authentication plugin I’m getting HTML authentication errors.

Ok, after it hallucinated a few times for me, Claude helped with the explanation that the error/exception handling stack had changed a bit between 4/5. In 4 the component handled it all for me, but for 5 I had to implement and APiExceptionRenderer - got it working now

Good to hear that you found a solution.

CakePHP 4.4 introduced a new error and exception handling system which you can find here.

This means you were able to use the old error system till the next major version.

1 Like

Claude - sometimes good, sometimes shit :stuck_out_tongue:

“That didn’t work”
“Good catch. CakePHP 5 renamed and restructured the error handling classes. Here’s what changed:”

tell me that the first time :laughing:

that’s how this happens

this indicated to me, that you have already seen the deprecation for the old error handler :stuck_out_tongue:

No - must have missed it, although there were a fair few others