A LoopBack 4 component for JWT based authorization support.
- Use your own User and Role model without a strict fields scheme.
- Apply access rules to controller and method levels.
- Role based authorization.
- JWT token authentication.
npm install --save @mikeevstropov/loopback4-acl
or via yarn
yarn add @mikeevstropov/loopback4-acl
The following example shows the basic use of @acl.rules
decorator in class and method level.
@acl.rules([{
principal: ACLPrincipal.EVERYONE,
permission: ACLPermission.DENY,
}])
export class UserController {
@acl.rules([{
principal: ACLPrincipal.AUTHENTICATED,
permission: ACLPermission.ALLOW,
}])
@get('/users/whoAmI')
async whoAmI(): Promise<User> {
// ...
}
@acl.rules([{
principal: ACLPrincipal.OWNER,
permission: ACLPermission.ALLOW,
}])
@del('/users/{id}')
async deleteById(
@param.path.string('id') id: string,
): Promise<void> {
// ...
}
@acl.rules([{
principal: 'admin', // user role
permission: ACLPermission.ALLOW,
}])
@get('/users/test')
async test() {
// ...
}
}
From above:
- The class level decorator
@acl.rules
denies access to all endpoints. - The method level allows
whoAmI
forAUTHENTICATED
. - The method
deleteById
allowed forOWNER
. - And
test
allowed foradmin
role.
Also, you can skip access checking in the method level by a sugar decorator @acl.skip()
.
- Create your own
User
andRole
(optional). - Implement
ACLUserService
to resolve the session user. - Create
login
method to expose JWT token. - Mount
ACLComponent
in your App.
The User Service is designed to let you resolve an instance of session user and its roles as you prefer.
export class UserService implements ACLUserService {
constructor(
@repository(UserRepository)
public userRepository: UserRepository,
) {}
/**
* Resolve the Session User instance.
*/
public async resolveUser(tokenPayload: TokenPayload) {
return this.userRepository.findById(
tokenPayload.uid,
{include: ['roles']},
);
}
/**
* Resolve role-like names of the Session User.
*
* Optional.
* Do return an empty array if you're not using roles.
*/
public async resolvePrincipals(user: UserWithRelations) {
return user.roles.map((role: Role) => role.name);
}
}
It doesn't matter how you get the User instance in login
method, but you need to generate JWT token from its id
.
export class UserController {
constructor(
@repository(UserRepository)
public userRepository: UserRepository,
@inject(ACLBindings.TOKEN_SERVICE)
private tokenService: ACLTokenService,
) {}
// ...
async login(
@requestBody(LoginRequestBody)
loginParameters: LoginParameters,
): Promise<LoginResponse> {
// ...
const user = await this.userRepository.findOne({
where: {username, password}
});
if (!user)
throw HttpErrors.Forbidden();
const token = await this.tokenService.encode({
uid: user.id,
});
return token;
}
}
Finally, bind your own ACLUserService
and mount
the ACLComponent
to your App in application.ts
export class App extends BootMixin() {
// ...
this.bind(ACLBindings.USER_SERVICE).toClass(UserService);
this.component(ACLComponent);
}
run npm test
from the root folder.
MIT