This package provides many different ways to implement authorization in loopback-next applications. https://www.npmjs.com/package/loopback4-authorization
A very basic usage is mentioned below. But many more mechanisms are also available.
npm install loopback4-authorization
- Add component to application.
this.bind(AuthorizationBindings.CONFIG).to({
allowAlwaysPaths: ['/explorer'],
});
this.component(AuthorizationComponent);
- Implement Permissions interface in User model and add permissions array.
@model({
name: 'users',
})
export class User extends Entity implements Permissions<string> {
// .....
// other attributes here
// .....
@property({
type: 'array',
itemType: 'string',
})
permissions: string[];
constructor(data?: Partial<User>) {
super(data);
}
}
- Add a step in custom sequence to check for authorization whenever any end
point is hit.
import {inject} from '@loopback/context';
import {
FindRoute,
HttpErrors,
InvokeMethod,
ParseParams,
Reject,
RequestContext,
RestBindings,
Send,
SequenceHandler,
} from '@loopback/rest';
import {AuthenticateFn, AuthenticationBindings} from 'loopback4-authentication';
import {
AuthorizationBindings,
AuthorizeErrorKeys,
AuthorizeFn,
} from 'loopback4-authorization';
import {AuthClient} from './models/auth-client.model';
import {User} from './models/user.model';
const SequenceActions = RestBindings.SequenceActions;
export class MySequence implements SequenceHandler {
constructor(
@inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,
@inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams,
@inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,
@inject(SequenceActions.SEND) public send: Send,
@inject(SequenceActions.REJECT) public reject: Reject,
@inject(AuthenticationBindings.USER_AUTH_ACTION)
protected authenticateRequest: AuthenticateFn<AuthUser>,
@inject(AuthorizationBindings.AUTHORIZE_ACTION)
protected checkAuthorisation: AuthorizeFn,
) {}
async handle(context: RequestContext) {
const requestTime = Date.now();
try {
const {request, response} = context;
const finished = await this.invokeMiddleware(context);
if (finished) return;
const route = this.findRoute(request);
const args = await this.parseParams(request, route);
const authUser: User = await this.authenticateRequest(
request,
response,
);
// This is the important line added for authorization.
const isAccessAllowed: boolean = await this.checkAuthorisation(
authUser?.permissions,
request,
);
// Checking access to route here
if (!isAccessAllowed) {
throw new HttpErrors.Forbidden(AuthorizeErrorKeys.NotAllowedAccess);
}
const result = await this.invoke(route, args);
this.send(response, result);
} catch (err) {
this.reject(context, err);
}
}
}
The above sequence also contains user authentication using loopback4-authentication package. You can refer to the documentation for the same for more details.
- Now we can add access permission keys to the controller methods using authorize
decorator as below.
@authorize(['CreateRole'])
@post(rolesPath, {
responses: {
[STATUS_CODE.OK]: {
description: 'Role model instance',
content: {
[CONTENT_TYPE.JSON]: {schema: {'x-ts-type': Role}},
},
},
},
})
async create(@requestBody() role: Role): Promise<Role> {
return await this.roleRepository.create(role);
}
This endpoint will only be accessible if logged in user has permission
'CreateRole'.
For a quick starter guide, you can refer to the loopback 4 starter application which contains authorization in a multi-tenant system.