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.