The authorization component is a flexible and extensible RBAC(role base access control) typescript library. It is designed using the plugin-architecture to allow for developers to easily implement and extend this library. This authorization component currently functions at the route based level.
A role will have a set of Permission
. Each Permission
will have the following attributes: effect, action, and subject. It could optionally contain a fields attribute. The effect
will determine whether the role has ALLOW
or DENY
access for this permission. An action
attribute can only be assigned one of the words from CRUD (CREATE
, READ
, UPDATE
, DELETE
). The subject
represents the item in which the user wants to perform the action on. The optional fields
attribute allows for access restriction to a subject's field (ex: a description
would be a field
of a Blog
in which UPDATE
access should be only ALLOW
to certain roles).
A route that is being checked for authorization will have a set of Operation
. Each Operation will have the following attributes: action and subject. It could optionally contain a field attribute. The action
represents the required CRUD word for this route. The subject
is what the route performs the action on. If a field
is given, then it represents the field of a subject that the action will be performed on.
const guestPermissions: Permissions[] = [
{
effect: 'ALLOW',
action: 'CREATE',
subject: 'Blog'
},
{
effect: 'DENY'
action: 'UPDATE',
subject: 'Subscription'
}
];
const adminPermissions: Permissions[] = [
{
effect: 'ALLOW',
action: 'CREATE',
subject: 'Blog'
},
{
effect: 'ALLOW'
action: 'UPDATE',
subject: 'Subscription'
}
];
const permissionsMap: PermissionsMap = {
guest: guestPermissions,
admin: adminPermissions
// Include more roles if needed
};
This will map a route with an associated HTTPMethod
to a set of Operations
that need to be performed by that route.
const blogPostOperations: Operation[] = [
{
action: 'CREATE',
subject: 'Blog'
}
];
const subscriptionPutOperations: Operation[] = [
{
action: 'UPDATE',
subject: 'Subscription'
}
];
const routesMap: RoutesMap = {
'/blog': {
'POST': blogPostOperations
},
'/subscription' {
'PUT': subscriptionPutOperations
}
};
const routesIgnored: RoutesIgnored = {
'/blog': {
'GET': true
}
};
The StaticPermissionsPlugin is a PermissionsPlugin for the Authorization Service and helps manage the permissions for each role. Permissions defined with this plugin are meant to be static.
//StaticPermissionsPlugin requires a LoggingService to enable logging.
const logger:LoggingService = new LoggingService();
const staticPermissionsPlugin:StaticPermissionsPlugin = new StaticPermissionsPlugin(
mockPermissionsMap,
routesMap,
routesIgnored,
logger
);
While the StaticPermissionsPlugin is a reference implementation, you are welcome to implement your own PermissionsPlugin to create more dynamic permissions mapping.
The CASLAuthorizationPlugin is an AuthorizationPlugin for the AuthorizationService and helps determine if a AuthenticatedUser can do a set Operations with their set of Permissions. The CASLAuthorizationPlugin uses CASL, an open-source authorization javascript library.
CASL (pronounced /ˈkæsəl/, like castle) is an isomorphic authorization JavaScript library which restricts what resources a given user is allowed to access. It's designed to be incrementally adoptable and can easily scale between a simple claim based and fully featured subject and attribute based authorization. It makes it easy to manage and share permissions across UI components, API services, and database queries.
const caslAuthorizationPlugin: CASLAuthorizationPlugin = new CASLAuthorizationPlugin();
While the CASLAuthorizationPlugin is a reference implementation, you are welcome to implement your own AuthorizationPlugin.
The AuthorizationService is the core service of this library. It requires a PermissionsPlugin and AuthorizationPlugin in order to use it.
const authorizationService:AuthorizationService = new AuthorizationService(
caslAuthorizationPlugin,
staticPermissionsPlugin
);
isAuthorizedOnRoute
requires a AuthenticatedUser and the route and method they are trying to access. This request will throw an Error
if a user is not authorized.
const guestUser:AuthenticatedUser = {
id: 'sampleId',
roles: ['guest']
}
try {
// This states that a guest user is requesting to POST to /blog
authorizationService.isAuthorizedOnRoute(guestUser, '/blog', 'POST');
} catch(err) {
console.log(err);
}
Authorization implemented as a middleware is a common use case. This library contains an authorization middleware that integrates with ExpressJS. The middleware expects an AuthenticatedUser to be made availabe to it by using the local variables of ExpressJS.
const app = express();
// This example shows an authorization middleware with no mount path. Authorization will execute every time a request is received
app.use(WithAuth(authorizationService));
REQUIRED:The middleware function needs to be mounted at the app level with no path, as it should execute every time a request is received. Click here for more information about ExpressJS middleware.