Cake 5.3 + Authentication plugin 4.x - exception Identifier configuration must specify a class name

I have recently upgraded an old project from CakePHP 4.3 to 5.3, according to the migration guides: 1) updated from 4.3 to 4.6, 2) applied the Upgrade/rector/migration tool to upgrade to 5.x, 3) updated to 5.3. I then installed the Authentication plugin 4.0.1.

This is my composer.json:

{
    "name": "cakephp/app",
    "description": "CakePHP skeleton app",
    "license": "MIT",
    "type": "project",
    "homepage": "https://cakephp.org",
    "require": {
        "php": ">=8.4",
        "cakephp/authentication": "^4.0",
        "cakephp/cakephp": "5.3.*",
        "cakephp/migrations": "^5.0",
        "cakephp/plugin-installer": "^2.0",
        "mobiledetect/mobiledetectlib": "^4.8.03"
    },
    "require-dev": {
        "cakephp/bake": "^3.6",
        "cakephp/cakephp-codesniffer": "^5.3",
        "cakephp/debug_kit": "^5.2",
        "josegonzalez/dotenv": "^4.0",
        "phpunit/phpunit": "^11.5.3 || ^12.1.3 || ^13.0"
    },
    "suggest": {
        "cakephp/repl": "Console tools for a REPL interface for CakePHP applications.",
        "dereuromark/cakephp-ide-helper": "After baking your code, this keeps your annotations in sync with the code evolving from there on for maximum IDE and PHPStan/Psalm compatibility.",
        "markstory/asset_compress": "An asset compression plugin which provides file concatenation and a flexible filter system for preprocessing and minification.",
        "phpstan/phpstan": "PHPStan focuses on finding errors in your code without actually running it. It catches whole classes of bugs even before you write tests for the code."
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "App\\Test\\": "tests/",
            "Cake\\Test\\": "vendor/cakephp/cakephp/tests/"
        }
    },
    "config": {
        "allow-plugins": {
            "cakephp/plugin-installer": true,
            "dealerdirect/phpcodesniffer-composer-installer": true
        },
        "platform-check": true,
        "sort-packages": true
    },
    "scripts": {
        "post-install-cmd": "App\\Console\\Installer::postInstall",
        "post-create-project-cmd": "App\\Console\\Installer::postInstall",
        "check": [
            "@test",
            "@cs-check"
        ],
        "cs-check": "phpcs --colors -p",
        "cs-fix": "phpcbf --colors -p",
        "test": "phpunit --colors=always"
    }
}

/config/plugins.php:

<?php

return [
    'Authentication' => [],
];

I made the changes to Application.php per https://book.cakephp.org/authentication/4/en/index.html:

<?php
declare(strict_types=1);

/**
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 * @link      https://cakephp.org CakePHP(tm) Project
 * @since     3.3.0
 * @license   https://opensource.org/licenses/mit-license.php MIT License
 */
namespace App;

use Authentication\AuthenticationService;
use Authentication\AuthenticationServiceInterface;
use Authentication\AuthenticationServiceProviderInterface;
use Authentication\Identifier\AbstractIdentifier;
use Authentication\Middleware\AuthenticationMiddleware;
use Cake\Core\Configure;
use Cake\Core\ContainerInterface;
use Cake\Datasource\FactoryLocator;
use Cake\Error\Middleware\ErrorHandlerMiddleware;
use Cake\Event\EventManagerInterface;
use Cake\Http\BaseApplication;
use Cake\Http\Middleware\BodyParserMiddleware;
use Cake\Http\Middleware\CsrfProtectionMiddleware;
use Cake\Http\MiddlewareQueue;
use Cake\ORM\Locator\TableLocator;
use Cake\Routing\Middleware\AssetMiddleware;
use Cake\Routing\Middleware\RoutingMiddleware;
use Cake\Routing\Router;
use Psr\Http\Message\ServerRequestInterface;

/**
 * Application setup class.
 *
 * This defines the bootstrapping logic and middleware layers you
 * want to use in your application.
 */
class Application extends BaseApplication implements AuthenticationServiceProviderInterface
{
    /**
     * Load all the application configuration and bootstrap logic.
     *
     * @return void
     */
    public function bootstrap(): void
    {
        // Call parent to load bootstrap from files.
        parent::bootstrap();
        
        // By default, does not allow fallback classes.
        FactoryLocator::add(
            'Table',
            new TableLocator()->allowFallbackClass(false), // @phpstan-ignore argument.type
        );
    }
    
    /**
     * Set up the middleware queue your application will use.
     *
     * @param MiddlewareQueue $middlewareQueue The middleware queue to set up.
     * @return MiddlewareQueue The updated middleware queue.
     */
    public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
    {
        $middlewareQueue
            // Catch any exceptions in the lower layers,
            // and make an error page/response
            ->add(new ErrorHandlerMiddleware(Configure::read('Error'), $this))
            
            // Handle plugin/theme assets like CakePHP normally does.
            ->add(new AssetMiddleware([
                'cacheTime' => Configure::read('Asset.cacheTime'),
            ]))
            
            // Add routing middleware.
            // If you have a large number of routes connected, turning on routes
            // caching in production could improve performance.
            // See https://github.com/CakeDC/cakephp-cached-routing
            ->add(new RoutingMiddleware($this))
            
            // Parse various types of encoded request bodies so that they are
            // available as array through $request->getData()
            // https://book.cakephp.org/5/en/controllers/middleware.html#body-parser-middleware
            ->add(new BodyParserMiddleware())
            
            ->add(new AuthenticationMiddleware($this))
            
            // Cross Site Request Forgery (CSRF) Protection Middleware
            // https://book.cakephp.org/5/en/security/csrf.html#cross-site-request-forgery-csrf-middleware
            ->add(new CsrfProtectionMiddleware([
                'httponly' => true,
            ]));
        
        return $middlewareQueue;
    }
    
    public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
    {
        $service = new AuthenticationService();
    
        $service->setConfig([
            'unauthenticatedRedirect' => Router::url([
                'prefix' => false,
                'plugin' => false,
                'controller' => 'MyCustomModel', // in lieu of Users
                'action' => 'login',
            ])
        ]);

        $fields = [
            AbstractIdentifier::CREDENTIAL_USERNAME => 'username',
            AbstractIdentifier::CREDENTIAL_PASSWORD => 'password'
        ];

        $service->loadAuthenticator('Authentication.Session');
        $service->loadAuthenticator('Authentication.Form', [
            'identifier' => [
                'Authentication.Password' => [
                    'fields' => $fields,
                    'resolver' => [
                        'className' => 'Authentication.Orm',
                        'userModel' => 'MyCustomModel', // in lieu of Users
                        'finder' => 'myCustomFinder'
                    ],
                ],
            ],
            'fields' => $fields,
            'loginUrl' => Router::url([
                    'prefix' => false,
                    'plugin' => null,
                    'controller' => 'MyCustomModel', // in lieu of Users
                    'action' => 'login'
                ])
        ]);
        
        return $service;
    }

    /**
     * Register application container services.
     *
     * @param ContainerInterface $container The Container to update.
     * @return void
     * @link https://book.cakephp.org/5/en/development/dependency-injection.html#dependency-injection
     */
    public function services(ContainerInterface $container): void
    {
        // Allow your Tables to be dependency injected
        //$container->delegate(new \Cake\ORM\Locator\TableContainer());
    }
    
    /**
     * Register custom event listeners here
     *
     * @param EventManagerInterface $eventManager
     * @return EventManagerInterface
     * @link https://book.cakephp.org/5/en/core-libraries/events.html#registering-listeners
     */
    public function events(EventManagerInterface $eventManager): EventManagerInterface
    {
        // $eventManager->on(new SomeCustomListenerClass());
        
        return $eventManager;
    }
}

Error:

Starting the application, I get the RuntimeException: Identifier configuration must specify a class name.

/logs/error.log:

2026-04-01 13:07:36 error: [RuntimeException] Identifier configuration must specify a class name. in /var/www/html/vendor/cakephp/authentication/src/Identifier/IdentifierFactory.php on line 53Stack Trace:

ROOT/vendor/cakephp/authentication/src/Authenticator/AuthenticatorCollection.php:43

CORE/src/Core/ObjectRegistry.php:112

ROOT/vendor/cakephp/authentication/src/AuthenticationService.php:149

APP/Application.php:131

ROOT/vendor/cakephp/authentication/src/Middleware/AuthenticationMiddleware.php:136

ROOT/vendor/cakephp/authentication/src/Middleware/AuthenticationMiddleware.php:77

CORE/src/Http/Runner.php:82

CORE/src/Http/Middleware/BodyParserMiddleware.php:157

CORE/src/Http/Runner.php:82

CORE/src/Http/Runner.php:86

CORE/src/Http/Middleware/CsrfProtectionMiddleware.php:159

CORE/src/Http/Runner.php:82

CORE/src/Http/Runner.php:60

CORE/src/Routing/Middleware/RoutingMiddleware.php:127

CORE/src/Http/Runner.php:82

CORE/src/Routing/Middleware/AssetMiddleware.php:71

CORE/src/Http/Runner.php:82

CORE/src/Error/Middleware/ErrorHandlerMiddleware.php:115

CORE/src/Http/Runner.php:82

CORE/src/Http/Runner.php:60

CORE/src/Http/Server.php:98

ROOT/webroot/index.php:40

[main]:

Request URL: /Client IP: x.x.x.x

The documentation doesn’t require a class name in the identifier config array, but the code for 4.0.1 seems to require it in IdentifierFactory.php.

After some trials, I passed the seemingly expected className value Authentication\\Authenticator\\FormAuthenticator to the identifier config, which was accepted, but produced a further error.

Application.php:

...
        $service->loadAuthenticator('Authentication.Form', [
            'identifier' => [
                'Authentication.Password' => [
                    'fields' => $fields,
                    'resolver' => [
                        'className' => 'Authentication.Orm',
                        'userModel' => 'Companies',
                        'finder' => 'authUser'
                    ],
                ],
                'className' => 'Authentication\\Authenticator\\FormAuthenticator',
            ],
            'fields' => $fields,
            'loginUrl' => Router::url([
                    'prefix' => false,
                    'plugin' => null,
                    'controller' => 'Companies',
                    'action' => 'login'
                ])
        ]);
...

/logs/error.log:

2026-04-01 13:09:02 error: [TypeError] Authentication\Authenticator\AbstractAuthenticator::__construct(): Argument #1 ($identifier) must be of type ?Authentication\Identifier\IdentifierInterface, array given, called in /var/www/html/vendor/cakephp/authentication/src/Identifier/IdentifierFactory.php on line 64 in /var/www/html/vendor/cakephp/authentication/src/Authenticator/AbstractAuthenticator.php on line 55Stack Trace:

ROOT/vendor/cakephp/authentication/src/Identifier/IdentifierFactory.php:64

ROOT/vendor/cakephp/authentication/src/Authenticator/AuthenticatorCollection.php:43

CORE/src/Core/ObjectRegistry.php:112

ROOT/vendor/cakephp/authentication/src/AuthenticationService.php:149

APP/Application.php:131

ROOT/vendor/cakephp/authentication/src/Middleware/AuthenticationMiddleware.php:136

ROOT/vendor/cakephp/authentication/src/Middleware/AuthenticationMiddleware.php:77

CORE/src/Http/Runner.php:82

CORE/src/Http/Middleware/BodyParserMiddleware.php:157

CORE/src/Http/Runner.php:82

CORE/src/Http/Runner.php:86

CORE/src/Http/Middleware/CsrfProtectionMiddleware.php:159

CORE/src/Http/Runner.php:82

CORE/src/Http/Runner.php:60

CORE/src/Routing/Middleware/RoutingMiddleware.php:127

CORE/src/Http/Runner.php:82

CORE/src/Routing/Middleware/AssetMiddleware.php:71

CORE/src/Http/Runner.php:82

CORE/src/Error/Middleware/ErrorHandlerMiddleware.php:115

CORE/src/Http/Runner.php:82

CORE/src/Http/Runner.php:60

CORE/src/Http/Server.php:98

ROOT/webroot/index.php:40

[main]:

Request URL: /Client IP: x.x.x.x

CakePHP 5.3 + Authentication 3.x

Last month, I successfully updated another older Cakephp application by the same process, but the Authentication plugin that was installed then was still version 3.x, which still uses Identifier/IdentifierCollection.php, while 4.x uses Identifier/IdentifierFactory.php instead. However the user documentation is the same for both versions.

Questions

Should the documentation for 4.x be different? Have I misconfigured the Authentication plugin? Is there a bug in what the new identifier code expects?

Thank you!

I see the following closed issue has updated the Authentication 4.x docs, thank you!

Instead of 'Authentication.Password' being a key in the 'identifier' array, it should now be a value for the key 'className'.

Hey @batsssss,

sorry this took so long till someone answered your question. This issue was reported by someone else in slack/discord support channel as well, so it got “a different kind” of attention :sweat_smile:

With this merged/released on the docs page everything is fine now, right?

1 Like

Yes, the updated docs are great! Thank you!