Home > database >  How to correctly throw an UnauthenticatedException in Cakephp 4?
How to correctly throw an UnauthenticatedException in Cakephp 4?

Time:12-22

I'm using the plugin Authentication 2 in cakephp 4.

I would like to throw an UnauthenticatedException when a user is not logged in and in the case of ajax request. The goal is to catch the exception in JSON.

Here is my code from server :

// in src/Controller/Admin/AdminController.php
use Authentication\Authenticator\UnauthenticatedException;

class AdminController extends AppController {

    public function initialize(): void
    {
        parent::initialize();
        $this->loadComponent('Authentication.Authentication');
    }

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

        // The server receives an ajax request and the user is not logged in (any more), an UnauthenticatedException is thrown
        if ($this->request->is('ajax') && $this->request->getAttribute('identity') === null) {
            throw new UnauthenticatedException('Please log in');
        }
    }

}

Here is my code from client :

$.ajax({
    dataType: 'json';
    type: 'POST',
    data: $(form).serialize(),
    // [...]
})
// [...]
.fail(function (jqXHR, textStatus, errorThrown) {
    console.log(jqXHR.responseJSON); // There's no responseJSON in jqXHR...
    alert("("   errorThrown   ")"   jqXHR.responseJSON.message);
    if (errorThrown == 'Unauthenticated') {
        location.reload();
    }
});

The problem is that there's no responseJSON in jqXHR.

Why is any other Exception (e.g UnauthorizedException that I used before) generating responseJSON in the return and not UnauthenticatedException ?

How to do to make it work with UnauthenticatedException ?

CodePudding user response:

The authentication middleware by default re-throws unauthenticated exceptions, that is unless you configure the unauthenticatedRedirect option, in that case it will transform those exceptions into redirects accordingly.

If you need to support both HTML and JSON requests/responses, then you can for example dynamically configure, respectively not configure the unauthenticatedRedirect option, based on the current request, eg in your Application::getAuthenticationService() method do something along the lines of:

$service = new AuthenticationService();

$accepts = array_map('trim', explode(',', $request->getHeaderLine('Accept')));
$isJsonRequest = in_array('application/json', $accepts, true);

if (!$isJsonRequest) {
    // service config for non-JSON requests
    $service->setConfig([
        'unauthenticatedRedirect' => /* ...*/,
        'queryParam' => 'redirect',
    ]);
}

Alternatively to evaluating the header manually, require the request to be an instance of \Cake\Http\ServerRequest and use its is() method:

assert($request instanceof \Cake\Http\ServerRequest);
if (!$request->is('json')) {
    $service->setConfig([
        'unauthenticatedRedirect' => [/* ...*/],
        'queryParam' => 'redirect',
    ]);
}

Also note that the authentication component will by default require the identity to be present and throw an exception accordingly, you do not have to do that yourself.

  • Related