Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
2 contributors

Users who have contributed to this file

@raymondfeng @jannyHou
167 lines (117 sloc) 5.05 KB

@loopback/authorization

A LoopBack 4 component for authorization support.

Overview

Authorization decides if a subject can perform specific action on an object.

Authorization

Role based

Permission based

Vote based

Key building blocks

  1. Decorate a method to describe:
  • Permission (maps the method to an action on the protected resource)

    • Type of the protected resource (such as customer or order)
    • What action does the method represent (such as changeEmail, createOrder, or cancelOrder)
  • ACL (provides role based rules)

    • allowedRoles
    • deniedRoles
  • Voters (supplies a list of function to vote on the decision)

  1. Intercept a method invocation
  • Build authorization context

    • Subject (who) - from authentication
    • Map principals to roles
    • Inspect the target method for metadata - Permission, ACL, voters
  • Run through voters/enforcers to make decisions

Decision matrix

The final decision is controlled by voting results from authorizers and options for the authorization component.

The following table illustrates the decision matrix with 3 voters and corresponding options.

Vote #1 Vote # 2 Vote #3 Options Final Decision
Deny Deny Deny any Deny
Allow Allow Allow any Allow
Abstain Allow Abstain any Allow
Abstain Deny Abstain any Deny
Deny Allow Abstain {precedence: Deny} Deny
Deny Allow Abstain {precedence: Allow} Allow
Allow Abstain Deny {precedence: Deny} Deny
Allow Abstain Deny {precedence: Allow} Allow
Abstain Abstain Abstain {defaultDecision: Deny} Deny
Abstain Abstain Abstain {defaultDecision: Allow} Allow

The options is described as follows:

export interface AuthorizationOptions {
  /**
   * Default decision if all authorizers vote for ABSTAIN
   */
  defaultDecision?: AuthorizationDecision.DENY | AuthorizationDecision.ALLOW;
  /**
   * Controls if Allow/Deny vote takes precedence and override other votes
   */
  precedence?: AuthorizationDecision.DENY | AuthorizationDecision.ALLOW;
}

The authorization component can be configured with options:

const options: AuthorizationOptions = {
  precedence: AuthorizationDecisions.DENY;
  defaultDecision: AuthorizationDecisions.DENY;
}

const binding = app.component(AuthorizationComponent);
app.configure(binding.key).to(options);

Installation

npm install --save @loopback/authorization

Basic use

Start by decorating your controller methods with @authorize to require the request to be authorized.

In this example, we make the user profile available via dependency injection using a key available from @loopback/authorization package.

import {inject} from '@loopback/context';
import {authorize} from '@loopback/authorization';
import {get} from '@loopback/rest';

export class MyController {
  @authorize({allow: ['ADMIN']})
  @get('/number-of-views')
  numOfViews(): number {
    return 100;
  }
}

Extract common layer(TBD)

@loopback/authentication and @loopback/authorization shares the client information from the request. Therefore we need another module with types/interfaces that describe the client, like principles, userProfile, etc... A draft PR is created for this module: see branch https://github.com/strongloop/loopback-next/tree/security/packages/security

Since the common module is still in progress, as the first release of @loopback/authorization, we have two choices to inject a user in the interceptor:

  • @loopback/authorization requires @loopback/authentication as a dependency. The interceptor injects the current user using AuthenticationBindings.CURRENT_USER. Then we remove this dependency in the common layer PR, two auth modules will depend on @loopback/security.

    • This is what's been done in my refactor PR, Principle and UserProfile are still decoupled, I added a convertor function to turn a user profile into a principle.
  • The interceptor injects the user using another key not related to @loopback/authentication.(Which means the code that injects the user stays as it is in https://github.com/strongloop/loopback-next/pull/1205). Then we unify the user set and injection in the common layer PR: same as the 1st choice, two auth modules will depend on @loopback/security.

Related resources

Contributions

Tests

run npm test from the root folder.

Contributors

See all contributors.

License

MIT

You can’t perform that action at this time.