@loopback/docs
Version:
Documentation files rendered at [https://loopback.io](https://loopback.io)
122 lines (105 loc) • 3.99 kB
Markdown
---
lang: en
title: 'Authorization Component - Authorizer'
keywords: LoopBack 4.0, LoopBack 4, Node.js, TypeScript, OpenAPI, Authorization
sidebar: lb4_sidebar
permalink: /doc/en/lb4/Authorization-component-authorizer.html
---
Users are expected to program policies that enforce access control in two of the
following options:
- `Authorizer` functions
- The authorizer functions are applied globally, i.e, they are enforced on all
endpoints in the application
- `Voter` functions
- voters are specific for the endpoint that is decorated with it
- multiple voters can be configured for an endpoint
> Usually the `authorize` functions are bound through a provider as below
- The `AuthorizationContext` parameter of the `authorize` function contains the
current principal (in the example given above,that would be the current user
invoking `cancelOrder`) and details of the invoked endpoint.
- The `AuthorizationMetadata` parameter of the `authorize` function contains
all the details provided in the invoked method's decorator.
```ts
class MyAuthorizationProvider implements Provider<Authorizer> {
/**
* @returns an authorizer function
*
*/
value(): Authorizer {
return this.authorize.bind(this);
}
async authorize(
context: AuthorizationContext,
metadata: AuthorizationMetadata,
) {
events.push(context.resource);
if (
context.resource === 'OrderController.prototype.cancelOrder' &&
context.principals[0].name === 'user-01'
) {
return AuthorizationDecision.DENY;
}
return AuthorizationDecision.ALLOW;
}
}
```
> the `authorize` function is then tagged to an application as
> `AuthorizationTags.AUTHORIZER` as below.
{% include code-caption.html content="src/application.ts" %}
```ts
export class MyApplication extends BootMixin(
ServiceMixin(RepositoryMixin(RestApplication)),
) {
constructor(options: ApplicationConfig = {}) {
super(options);
// mount authorization component
this.component(AuthorizationComponent);
// bind the authorizer provider
this.bind('authorizationProviders.my-authorizer-provider')
.toProvider(MyAuthorizationProvider)
.tag(AuthorizationTags.AUTHORIZER);
}
}
```
- This creates a list of `authorize()` functions.
- The `authorize(AuthorizationContext, AuthorizationMetadata)` function in the
provider class is expected to be called by the `Authorization Interceptor`
which is called for every API endpoint decorated with `()`.
- The authorize interceptor gets the list of functions tagged with
`AuthorizationTags.AUTHORIZER` (and also the voters listed in the
`` decorator per endpoint) and calls the functions one after
another.
- The `authorize()` function is expected to return an object of type
`AuthorizationDecision`. If the type returned is
`AuthorizationDecision.ALLOW` the current `Principal` has passed the
executed `authorize()` function's criteria.
> Voter functions are directly provided in the decorator of the remote method
```ts
async function compareId(
authorizationCtx: AuthorizationContext,
metadata: MyAuthorizationMetadata,
) {
let currentUser: UserProfile;
if (authorizationCtx.principals.length > 0) {
const user = _.pick(authorizationCtx.principals[0], [
'id',
'name',
'email',
]);
return AuthorizationDecision.ALLOW;
} else {
return AuthorizationDecision.DENY;
}
}
('jwt')
({resource: 'order', scopes: ['patch'], voters: [compareId]})
async patchOrders(
.path.string('userId') userId: string,
() order: Partial<Order>,
.query.string('where') where?: Where<Order>,
): Promise<Count> {
return this.userRepo.orders(userId).patch(order, where);
}
```
In the above example `compareId()` is an authorizing function which is provided
as a voter in the decorator for the `patchOrders()` method.