UNPKG

@loopback/docs

Version:

Documentation files rendered at [https://loopback.io](https://loopback.io)

122 lines (105 loc) 3.99 kB
--- 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 `@authorize()`. - The authorize interceptor gets the list of functions tagged with `AuthorizationTags.AUTHORIZER` (and also the voters listed in the `@authorize` 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; } } @authenticate('jwt') @authorize({resource: 'order', scopes: ['patch'], voters: [compareId]}) async patchOrders( @param.path.string('userId') userId: string, @requestBody() order: Partial<Order>, @param.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.