Update: I created a diagram to show the usage of RFC authentication and RFC authorization.
Goal
The goal of this RFC is to propose an authorization middleware for Expressive and PSR-7 applications.
This authorization module will be used to authorize authenticated users based on URL route and HTTP methods.
Requirements
This authorization module will require an authentication module to get a user’s role to be passed as PSR-7 attribute. This user attribute will be the input of the authorization middleware.
The authentication module will create a PSR-7 attribute with name AuthorizationInterface::class
containing the user’s role. Below is reported an example in pseudo-code.
use Zend\Expressive\Authorization\AuthorizationInterface;
class AuthenticationMiddleware implements ServerMiddlewareInterface
{
public function process($request, $delegate)
{
if (/* not authenticated */)
return new RedirectResponse($this->config['redirect']);
}
$role = /* get the user's role */
return $delegate->process(
$request->withAttribute(AuthorizationInterface::class, $role)
);
}
}
The user’s role stored in the PSR-7 attribute will be used by the Authorization middleware for permission grants, as follows:
$role = $request->getAttribute(AuthorizationInterface::class, false);
if (false === $role) {
return new Zend\Diactoros\Response\EmptyResponse(401);
}
If the user will be not available, the authorization module will return a 401 Unauthorized response.
The Zend\Expressive\Authorization\AuthorizationInterface
interface is reported as follows:
namespace Zend\Expressive\Authorization;
use Psr\Http\Message\ServerRequestInterface;
interface AuthorizationInterface
{
/**
* Check if a role is granted for the request
*
* @param string $role
* @param ServerRequestInterface $request
* @return bool
*/
public function isGranted(string $role, ServerRequestInterface $request): bool;
}
Implementation
The idea is to build an Expressive module that offers authorization using specific adapters (e.g. using zend-permissions-acl
, zend-permissions-rbac
, etc).
For instance, the ZendRbac
adapter will use the zend-permissions-rbac library for implementing the RBAC system.
Using the RBAC terminology, a role is the user’s role and a resource is the route name. In this way we can authorize URLs and HTTP methods for a specific user’s role.
This approach is the same used in this blog post. The authorization can be configured using a PHP file like to the follows:
return [
'authorization' => [
'roles' => [
'administrator' => [],
'editor' => ['administrator'],
'contributor' => ['editor'],
],
'permissions' => [
'contributor' => [
'admin.dashboard',
'admin.posts',
],
'editor' => [
'admin.publish',
],
'administrator' => [
'admin.settings',
],
],
],
];
where the roles can be configured using a parent role (e.g. administrator
is parent of editor
). The permissions are configured using route names (e.g. contributor
is allowed to access the routes admin.dashboard
and admin.posts
).
The authorization middleware will be configured in the pipe of a route. Here an example:
$app->get('/admin/dashboard', [
AuthenticationMiddleware::class,
AuthorizationMiddleware::class,
Action\DashboardAction::class
], 'admin.dashboard');
Before the authorization, we assumed the use of an authentication module (AuthenticationMiddleware
). The AuthenticationMiddleware
will store a user’s role as PSR-7 attribute.
Prototype
A prototype of the implementation will be available at ezimuel/zend-expressive-authorization.