Search code examples
phpauthenticationcakephpcakephp-3.x

How do you return custom errors from CakePHP AuthComponent?


I am currently adding a CakePHP 3 Authentication component to and existing CakePHP application following the documentation listed here:

https://book.cakephp.org/3/en/controllers/components/authentication.html

I am currently handling the display of error messages, following this example:

https://book.cakephp.org/3/en/controllers/components/authentication.html#identifying-users-and-logging-them-in

public function login()
{
    if ($this->request->is('post')) {
        $user = $this->Auth->identify();
        if ($user) {
            $this->Auth->setUser($user);
            return $this->redirect($this->Auth->redirectUrl());
        } else {
            $this->Flash->error(__('Username or password is incorrect'));
        }
    }
}

I am integrating the auth components following the documentation here, where an array of user data is returned if a user is able to be authenticated and false if they are not (as specified in the docs):

https://book.cakephp.org/3/en/controllers/components/authentication.html#creating-custom-authentication-objects

namespace App\Auth;

use Cake\Auth\BaseAuthenticate;
use Cake\Http\ServerRequest;
use Cake\Http\Response;

class OpenidAuthenticate extends BaseAuthenticate
{
    public function authenticate(ServerRequest $request, Response $response)
    {
        // Do things for OpenID here.
        // Return an array of user if they could authenticate the user,
        // return false if not.

        if($failureCondition) {
            return false;
        }

        return $user;
    }
}

However I would like to dynamically determine the error in the auth component:

namespace App\Auth;

use Cake\Auth\BaseAuthenticate;
use Cake\Http\ServerRequest;
use Cake\Http\Response;

class OpenidAuthenticate extends BaseAuthenticate
{
    public function authenticate(ServerRequest $request, Response $response)
    {
        if($failureConditionA) {
            $this->error = 'Error A'; 
            return false;
        }

        if($failureConditionB) {
            $this->error = 'Error B'; 
            return false;
        }

        return $user;
    }
}

And print the dynamically produced error in the flash message like so:

public function login()
{
    if ($this->request->is('post')) {
        $user = $this->Auth->identify();
        if ($user) {
            $this->Auth->setUser($user);
            return $this->redirect($this->Auth->redirectUrl());
        } else {
            // 'Error A' or 'Error B' displayed on failure
            $this->Flash->error($this->Auth->error());
        }
    }
}

What is the correct code to use to do this?

If this violates the intention behind how the AuthComponent is supposed to function, I would be interested to have that explained and / or know any other correct ways to do this?

Thanks in advance!


Solution

  • There's no overly clean way of exposing error details, the whole auth component stuff was never specifically designed to return failure details.

    There's lots of ways to solve this, the most simple one would probably be to obtain a specific authentication object, and access the errors that the object stored via a public API that it would need to implement, ie your authenticator could for example expose a getError() method, then you could something like this in your controller:

    $openIdAuth = $this->Auth->getAuthenticate('Openid');
    $error = $openIdAuth->getError();
    

    For something a little more sophisticated you could implement a custom/extended authentication component, where you have access to the complete list of authentication objects and can easily access the last one in the chain and return possible error information that it holds.

    If you're implementing authentication into an application that doesn't yet use any authentication, then I'd very, very strongly recommend that you ditch the deprecated auth component, and use the new authentication plugin instead! It's way cleaner and much more versatile, and returning error details is supported out of the box.