How do you implement authentication for separate prefixes?

Hi,

I have a project with a public site, the maintenance and API access are in separate prefixes in routes.
Oh… little detail, I’m upgrading this project from CakePHP 3.9.x to 4.3.x

In my attempt to find a solution I wanted to implement the middleware registration in the routes.php, but this fails :frowning: I’ve added this for the maintenance part of the site:

$routes->prefix('maintenance', function ($builder) {
    $authenticationService = new Authentication\AuthenticationService();
    $fields = [
        Authentication\Identifier\IdentifierInterface::CREDENTIAL_USERNAME => 'email',
        Authentication\Identifier\IdentifierInterface::CREDENTIAL_PASSWORD => 'password',
    ];
    $authenticationService->loadAuthenticator('Authentication.Session');
    $authenticationService->loadAuthenticator('Authentication.Form', [
        'fields' => $fields,
        'loginUrl' => '/maintenance/login',
    ]);

    $builder->registerMiddleware('authentication', new Authentication\Middleware\AuthenticationMiddleware($authenticationService));
    $builder->applyMiddleware('authentication');

    $builder->connect('/', ['controller' => 'Login', 'action' => 'index']);
    $builder->connect('/logout', ['controller' => 'Login', 'action' => 'logout']);
    
    $builder->fallbacks(DashedRoute::class);
});

Found this example, but the result is error:

Subject must be an instance of Authentication\AuthenticationServiceInterface or Authentication\AuthenticationServiceProviderInterface, App\Application given.

Also tried a setup like this:

$provider = new \App\Provider\AuthenticationProvider();
    $middleware = new \Authentication\Middleware\AuthenticationMiddleware($provider);
    $builder->registerMiddleware('auth', $middleware);
    $builder->applyMiddleware('auth');

Found here:

The error is the same :frowning:
I must admit that I liked the idea to split everything up, but that error doesn’t help :wink:

The only thing I can come up is to implement the activation of the authorization in the middleware (Application.php) by using something like:

if (strpos($_SERVER['REQUEST_URI'], '/api') !== 0) {
           $middlewareQueue->add(new AuthenticationMiddleware($this));
           $middlewareQueue->add(new AuthorizationMiddleware($this, [
             'requireAuthorizationCheck' => false,
             'unauthorizedHandler' => [
                 'className' => 'Authorization.CakeRedirect',
                 'url' => ['plugin' => false, 'controller' => 'Login', 'action' => 'index'],
                 'queryParam' => 'redirectUrl',
                 'exceptions' => $exceptions,
             ],
         ]));
}

So the authorization logic isn’t triggered when a normal user visits the portal…

When I go for the solution from the manual:

public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
{
    $path = $request->getPath();

    $service = new AuthenticationService();
    if (strpos($path, '/api') === 0) {
        // Accept API tokens only
        $service->loadAuthenticator('Authentication.Token');
        $service->loadIdentifier('Authentication.Token');

        return $service;
    }

    // Web authentication
    // Support sessions and form login.
    $service->loadAuthenticator('Authentication.Session');
    $service->loadAuthenticator('Authentication.Form');

    $service->loadIdentifier('Authentication.Password');

    return $service;
}

The result is that when I remove the “web authentication” part I get errors that no “service” is configured…

Is this really the only way? It looks more or less ugly in my eyes to solve the problem in this way…

Thank you for your input/reaction!

1 Like

My problem is solved.
I was doing to many things at the same time when adding the middleware to the routes.

The example does work, the error was caused by Authorization or RequestPolicy that there implemented in the Application.php…

Thanks for your input!

At the end I was messing up the code during testing :frowning: