Authorization Middleware
Authorization is applied as middleware. AuthorizationMiddleware handles two main responsibilities:
- Decorating the request
identitywithcan,canResult, andapplyScopemethods when needed. - Ensuring each request has authorization checked or explicitly bypassed.
To use the middleware, implement AuthorizationServiceProviderInterface in your application class, then add the middleware to the queue.
namespace App;
use Authorization\AuthorizationService;
use Authorization\AuthorizationServiceInterface;
use Authorization\AuthorizationServiceProviderInterface;
use Authorization\Middleware\AuthorizationMiddleware;
use Authorization\Policy\OrmResolver;
use Cake\Http\BaseApplication;
use Psr\Http\Message\ServerRequestInterface;
class Application extends BaseApplication implements AuthorizationServiceProviderInterface
{
public function getAuthorizationService(ServerRequestInterface $request): AuthorizationServiceInterface
{
$resolver = new OrmResolver();
return new AuthorizationService($resolver);
}
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
{
$middlewareQueue->add(new AuthorizationMiddleware($this));
return $middlewareQueue;
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
The authorization service requires a policy resolver. See Policies and Policy Resolvers for the available options.
Identity Decorator
By default, the request identity is wrapped in Authorization\IdentityDecorator. The decorator proxies method calls, array access, and property access to the underlying identity. Use getOriginalData() to retrieve the original value:
$originalUser = $user->getOriginalData();If you use the cakephp/authentication plugin, Authorization\Identity is used instead. It implements both Authentication\IdentityInterface and Authorization\IdentityInterface, so the decorated identity works with Authentication helpers and components.
Using Your User Class as the Identity
If your existing user class already represents identity data, implement Authorization\IdentityInterface and use the identityDecorator option to skip the wrapper:
namespace App\Model\Entity;
use Authorization\AuthorizationServiceInterface;
use Authorization\IdentityInterface;
use Authorization\Policy\ResultInterface;
use Cake\ORM\Entity;
class User extends Entity implements IdentityInterface
{
public function can(string $action, mixed $resource): bool
{
return $this->authorization->can($this, $action, $resource);
}
public function canResult(string $action, mixed $resource): ResultInterface
{
return $this->authorization->canResult($this, $action, $resource);
}
public function applyScope(string $action, mixed $resource, mixed ...$optionalArgs): mixed
{
return $this->authorization->applyScope($this, $action, $resource, ...$optionalArgs);
}
public function getOriginalData(): \ArrayAccess|array
{
return $this;
}
public function setAuthorization(AuthorizationServiceInterface $service): static
{
$this->authorization = $service;
return $this;
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Then configure the middleware:
$middlewareQueue->add(new AuthorizationMiddleware($this, [
'identityDecorator' => function ($auth, $user) {
return $user->setAuthorization($auth);
},
]));2
3
4
5
If you also use Authentication, implement both interfaces:
use Authentication\IdentityInterface as AuthenticationIdentity;
use Authorization\IdentityInterface as AuthorizationIdentity;
class User extends Entity implements AuthorizationIdentity, AuthenticationIdentity
{
public function getIdentifier(): int|string|null
{
return $this->id;
}
}2
3
4
5
6
7
8
9
10
Ensuring Authorization Is Applied
By default, AuthorizationMiddleware verifies that each request with an identity also performed or bypassed authorization checks. If not, AuthorizationRequiredException is raised after controller and middleware execution completes.
This does not prevent unauthorized access by itself, but it is valuable during development and testing. You can disable the requirement:
$middlewareQueue->add(new AuthorizationMiddleware($this, [
'requireAuthorizationCheck' => false,
]));2
3
Handling Unauthorized Requests
By default, authorization exceptions are rethrown. You can configure handlers for unauthorized requests, such as redirecting a user to a login page.
Built-in handlers:
Exception, which rethrows the exception.Redirect, which redirects to a plain URL.CakeRedirect, which redirects using CakePHP router syntax.
Shared redirect options:
url, the redirect target.exceptions, the exception classes that trigger redirect handling.queryParam, the query string key used for the original URL. Defaults toredirect.statusCode, the redirect status code. Defaults to302.allowedRedirectExtensions, a whitelist of file extensions that may still redirect. Set this carefully to avoid redirecting API-style requests.
Example:
use Authorization\Exception\MissingIdentityException;
$middlewareQueue->add(new AuthorizationMiddleware($this, [
'unauthorizedHandler' => [
'className' => 'Authorization.Redirect',
'url' => '/pages/unauthorized',
'queryParam' => 'redirectUrl',
'exceptions' => [
MissingIdentityException::class,
OtherException::class,
],
'allowedRedirectExtensions' => ['csv', 'pdf'],
],
]));2
3
4
5
6
7
8
9
10
11
12
13
14
All handlers receive the thrown exception, which is always an instance of Authorization\Exception\Exception.
To handle more exception types gracefully, add them to exceptions:
'exceptions' => [
MissingIdentityException::class,
ForbiddenException::class,
],2
3
4
Adding a Flash Message After Redirect
If you need to add a flash message or custom side effects on redirect, create your own unauthorized handler based on the built-in redirect handler.
Create src/Middleware/UnauthorizedHandler/CustomRedirectHandler.php:
<?php
declare(strict_types=1);
namespace App\Middleware\UnauthorizedHandler;
use Authorization\Exception\Exception;
use Authorization\Middleware\UnauthorizedHandler\RedirectHandler;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
class CustomRedirectHandler extends RedirectHandler
{
public function handle(Exception $exception, ServerRequestInterface $request, array $options = []): ResponseInterface
{
$response = parent::handle($exception, $request, $options);
$request->getFlash()->error('You are not authorized to access that location');
return $response;
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Then reference it from your middleware configuration:
use Authorization\Exception\ForbiddenException;
use Authorization\Exception\MissingIdentityException;
$middlewareQueue->add(new AuthorizationMiddleware($this, [
'unauthorizedHandler' => [
'className' => 'CustomRedirect',
'url' => '/users/login',
'queryParam' => 'redirectUrl',
'exceptions' => [
MissingIdentityException::class,
ForbiddenException::class,
],
'custom_param' => true,
],
]));2
3
4
5
6
7
8
9
10
11
12
13
14
15
Any additional handler configuration is available in the $options array received by handle().