Overview

Security is of paramount importance when developing a web or mobile applicationand usually consists of two distinct pieces:

  • Authentication
  • AuthorizationAuthentication is a process ofverifying someone’s identity before a protected resource is accessed.

Authorization is a process ofdeciding if a user can perform an action on a protected resource.

Note:

For a description of an Authorization process, please see Authorization.

This document describes the details of the LoopBack 4 Authentication componentfrom the @loopback/authentication package.

Here is a high level overview of the authentication component.

authentication_overview_highlevel

  • A decorator to express an authentication requirement on controller methods
  • A provider to access method-level authentication metadata
  • An action in the REST sequence to enforce authentication
  • An extension point to discover all authentication strategies and handle thedelegationHere is a detailed overview of the authentication component.

authentication_overview_detailed

Basically, to secure your API endpoints, you need to:

  • decorate the endpoints of a controller with the@authenticate(strategyName, options?) decorator (app developer)
  • insert the authentication action in a custom sequence (app developer)
  • create a custom authentication strategy with a unique name (extensiondeveloper)
  • register the custom authentication strategy (app developer)The Authentication Component takes care of the rest.

Installation

  1. npm install --save @loopback/authentication

Authentication Component

To utilize authentication in an application application.ts, you must loadthe authentication component named AuthenticationComponent.

  1. export class MyApplication extends BootMixin(
  2. ServiceMixin(RepositoryMixin(RestApplication)),
  3. ) {
  4. constructor(options?: ApplicationConfig) {
  5. super(options);
  6. //...
  7. this.component(AuthenticationComponent);
  8. //...
  9. }
  10. }

The AuthenticationComponent is defined as follows:

  1. export class AuthenticationComponent implements Component {
  2. providers?: ProviderMap;
  3. constructor() {
  4. this.providers = {
  5. [AuthenticationBindings.AUTH_ACTION.key]: AuthenticateActionProvider,
  6. [AuthenticationBindings.STRATEGY.key]: AuthenticationStrategyProvider,
  7. [AuthenticationBindings.METADATA.key]: AuthMetadataProvider,
  8. };
  9. }
  10. }

As you can see, there are a few providerswhich make up the bulk of the authenticaton component.

Essentially

  • The binding key AuthenticationBindings.AUTH_ACTION.key is bound toAuthenticateActionProvider which returns an authenticating function of typeAuthenticateFn
  • The binding key AuthenticationBindings.STRATEGY.key is bound toAuthenticationStrategyProvider which resolves and returns an authenticationstrategy of type AuthenticationStrategy
  • The binding key AuthenticationBindings.METADATA.key is bound toAuthMetadataProvider which returns authentication decorator metadata of typeAuthenticationMetadataThe purpose of these providers and the values they return will be explained inthe sections below.

Using the Authentication Decorator

Securing your application’s API endpoints is done by decorating controllerfunctions with theAuthentication Decorator.

The decorator’s syntax is:

  1. @authenticate(strategyName: string, options?: object)

The strategyName is the unique name of the authentication strategy.

When the options object is specified, it must be relevant to that particularstrategy.

Here is an example of the decorator using a custom authentication strategy named‘basic’ without options, for the endpoint /whoami in a controller namedWhoAmIController. (We willcreate andregister the ‘basic’authentication strategy in later sections)

  1. import {inject} from '@loopback/context';
  2. import {
  3. AuthenticationBindings,
  4. UserProfile,
  5. authenticate,
  6. } from '@loopback/authentication';
  7. import {SecurityBindings} from '@loopback/security';
  8. import {get} from '@loopback/rest';
  9. export class WhoAmIController {
  10. constructor(
  11. // After extracting the CURRENT_USER key to module `@loopback/security`,
  12. // `AuthenticationBindings.CURRENT_USER` is turned to an alias of
  13. // `SecurityBindings.USER`
  14. @inject(SecurityBindings.USER)
  15. private userProfile: UserProfile,
  16. ) {}
  17. @authenticate('basic')
  18. @get('/whoami')
  19. whoAmI(): string {
  20. return this.userProfile.id;
  21. }
  22. }

Note:If only some of the controller methods are decorated with the @authenticate decorator, then the injection decorator for CURRENT_USER in the controller’s constructor must be specified as @inject(SecurityBindings.USER, {optional:true}) to avoid a binding error when an unauthenticated endpoint is accessed. Alternatively, do not inject CURRENT_USER in the controller constructor, but in the controller methods which are actually decorated with the @authenticate decorator. See Method Injection, Constructor Injection and Optional Dependencies for details.

An example of the decorator when options are specified looks like this:

  1. @authenticate('basic', { /* some options for the strategy */})

Tip: To avoid repeating the same options in the @authenticate decorator for many endpoints in a controller, you can instead define global options which can be injected into an authentication strategy thereby allowing you to avoid specifying the options object in the decorator itself. For controller endpoints that need to override a global option, you can specify it in an options object passed into the decorator. Your authentication strategy would need to handle the option overrides. See Managing Custom Authentication Strategy Options for details.

After a request is successfully authenticated, the current user profile isavailable on the request context. You can obtain it via dependency injection byusing the SecurityBindings.USER binding key.

Parameters of the @authenticate decorator can be obtained via dependencyinjection using the AuthenticationBindings.METADATA binding key. It returnsdata of type AuthenticationMetadata provided by AuthMetadataProvider. TheAuthenticationStrategyProvider, discussed in a later section, makes use ofAuthenticationMetadata to figure out what name you specified as aparameter in the @authenticate decorator of a specific controller endpoint.

Adding an Authentication Action to a Custom Sequence

In a LoopBack 4 application with REST API endpoints, each request passes througha stateless grouping of actions called a Sequence.

Here is an example of the default sequence that is created in a LoopBack 4application.

  1. export class DefaultSequence implements SequenceHandler {
  2. /**
  3. * Constructor: Injects findRoute, invokeMethod & logError
  4. * methods as promises.
  5. *
  6. * @param {FindRoute} findRoute Finds the appropriate controller method,
  7. * spec and args for invocation (injected via SequenceActions.FIND_ROUTE).
  8. * @param {ParseParams} parseParams The parameter parsing function (injected
  9. * via SequenceActions.PARSE_PARAMS).
  10. * @param {InvokeMethod} invoke Invokes the method specified by the route
  11. * (injected via SequenceActions.INVOKE_METHOD).
  12. * @param {Send} send The action to merge the invoke result with the response
  13. * (injected via SequenceActions.SEND)
  14. * @param {Reject} reject The action to take if the invoke returns a rejected
  15. * promise result (injected via SequenceActions.REJECT).
  16. */
  17. constructor(
  18. @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,
  19. @inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams,
  20. @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,
  21. @inject(SequenceActions.SEND) public send: Send,
  22. @inject(SequenceActions.REJECT) public reject: Reject,
  23. ) {}
  24. /**
  25. * Runs the default sequence. Given a handler context (request and response),
  26. * running the sequence will produce a response or an error.
  27. *
  28. * Default sequence executes these steps
  29. * - Finds the appropriate controller method, swagger spec
  30. * and args for invocation
  31. * - Parses HTTP request to get API argument list
  32. * - Invokes the API which is defined in the Application Controller
  33. * - Writes the result from API into the HTTP response
  34. * - Error is caught and logged using 'logError' if any of the above steps
  35. * in the sequence fails with an error.
  36. *
  37. * @param context The request context: HTTP request and response objects,
  38. * per-request IoC container and more.
  39. */
  40. async handle(context: RequestContext): Promise<void> {
  41. try {
  42. const {request, response} = context;
  43. const route = this.findRoute(request);
  44. const args = await this.parseParams(request, route);
  45. const result = await this.invoke(route, args);
  46. debug('%s result -', route.describe(), result);
  47. this.send(response, result);
  48. } catch (error) {
  49. this.reject(context, error);
  50. }
  51. }
  52. }

By default, authentication is not part of the sequence of actions, so youmust create a custom sequence and add the authentication action.

An authentication action AuthenticateFn is provided by theAuthenticateActionProvider class.

AuthenticateActionProvider is defined as follows:

  1. export class AuthenticateActionProvider implements Provider<AuthenticateFn> {
  2. constructor(
  3. // The provider is instantiated for Sequence constructor,
  4. // at which time we don't have information about the current
  5. // route yet. This information is needed to determine
  6. // what auth strategy should be used.
  7. // To solve this, we are injecting a getter function that will
  8. // defer resolution of the strategy until authenticate() action
  9. // is executed.
  10. @inject.getter(AuthenticationBindings.STRATEGY)
  11. readonly getStrategy: Getter<AuthenticationStrategy>,
  12. @inject.setter(SecurityBindings.USER)
  13. readonly setCurrentUser: Setter<UserProfile>,
  14. ) {}
  15. /**
  16. * @returns authenticateFn
  17. */
  18. value(): AuthenticateFn {
  19. return request => this.action(request);
  20. }
  21. /**
  22. * The implementation of authenticate() sequence action.
  23. * @param request The incoming request provided by the REST layer
  24. */
  25. async action(request: Request): Promise<UserProfile | undefined> {
  26. const strategy = await this.getStrategy();
  27. if (!strategy) {
  28. // The invoked operation does not require authentication.
  29. return undefined;
  30. }
  31. const userProfile = await strategy.authenticate(request);
  32. if (!userProfile) {
  33. // important to throw a non-protocol-specific error here
  34. let error = new Error(
  35. `User profile not returned from strategy's authenticate function`,
  36. );
  37. Object.assign(error, {
  38. code: USER_PROFILE_NOT_FOUND,
  39. });
  40. throw error;
  41. }
  42. this.setCurrentUser(userProfile);
  43. return userProfile;
  44. }
  45. }

AuthenticateActionProvider’s value() function returns a function of typeAuthenticateFn. This function attempts to obtain an authentication strategy(resolved by AuthenticationStrategyProvider via theAuthenticationBindings.STRATEGY binding). If no authentication strategywas specified for this endpoint, the action immediately returns. If anauthentication strategy was specified for this endpoint, itsauthenticate(request) function is called. If a user profile is returned, thismeans the user was authenticated successfully, and the user profile is added tothe request context (via the SecurityBindings.USER binding); otherwise anerror is thrown.

Here is an example of a custom sequence which utilizes the authenticationaction.

  1. export class MyAuthenticatingSequence implements SequenceHandler {
  2. constructor(
  3. @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,
  4. @inject(SequenceActions.PARSE_PARAMS)
  5. protected parseParams: ParseParams,
  6. @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,
  7. @inject(SequenceActions.SEND) protected send: Send,
  8. @inject(SequenceActions.REJECT) protected reject: Reject,
  9. @inject(AuthenticationBindings.AUTH_ACTION)
  10. protected authenticateRequest: AuthenticateFn,
  11. ) {}
  12. async handle(context: RequestContext) {
  13. try {
  14. const {request, response} = context;
  15. const route = this.findRoute(request);
  16. //call authentication action
  17. await this.authenticateRequest(request);
  18. // Authentication successful, proceed to invoke controller
  19. const args = await this.parseParams(request, route);
  20. const result = await this.invoke(route, args);
  21. this.send(response, result);
  22. } catch (error) {
  23. //
  24. // The authentication action utilizes a strategy resolver to find
  25. // an authentication strategy by name, and then it calls
  26. // strategy.authenticate(request).
  27. //
  28. // The strategy resolver throws a non-http error if it cannot
  29. // resolve the strategy. When the strategy resolver obtains
  30. // a strategy, it calls strategy.authenticate(request) which
  31. // is expected to return a user profile. If the user profile
  32. // is undefined, then it throws a non-http error.
  33. //
  34. // It is necessary to catch these errors and add HTTP-specific status
  35. // code property.
  36. //
  37. // Errors thrown by the strategy implementations already come
  38. // with statusCode set.
  39. //
  40. // In the future, we want to improve `@loopback/rest` to provide
  41. // an extension point allowing `@loopback/authentication` to contribute
  42. // mappings from error codes to HTTP status codes, so that application
  43. // don't have to map codes themselves.
  44. if (
  45. error.code === AUTHENTICATION_STRATEGY_NOT_FOUND ||
  46. error.code === USER_PROFILE_NOT_FOUND
  47. ) {
  48. Object.assign(error, {statusCode: 401 /* Unauthorized */});
  49. }
  50. this.reject(context, error);
  51. return;
  52. }
  53. }
  54. }

Notice the new dependency injection in the sequence’s constructor.

  1. @inject(AuthenticationBindings.AUTH_ACTION)
  2. protected authenticateRequest: AuthenticateFn,

The binding key AuthenticationBindings.AUTH_ACTION gives us access to theauthentication function authenticateRequest of type AuthenticateFn providedby AuthenticateActionProvider.

Now the authentication function authenticateRequest can be called in ourcustom sequence anywhere before the invoke action in order secure theendpoint.

There are two particular protocol-agnostic errorsAUTHENTICATION_STRATEGY_NOT_FOUND and USER_PROFILE_NOT_FOUND which must beaddressed in the sequence, and given an HTTP status code of 401 (UnAuthorized).

It is up to the developer to throw the appropriate HTTP error code from within acustom authentications strategy or its custom services.

If any error is thrown during the authentication process, the controllerfunction of the endpoint is never executed.

Binding the Authenticating Sequence to the Application

Now that we’ve defined a custom sequence that performs an authentication actionon every request, we must bind it to the application application.ts

  1. export class MyApplication extends BootMixin(
  2. ServiceMixin(RepositoryMixin(RestApplication)),
  3. ) {
  4. constructor(options?: ApplicationConfig) {
  5. super(options);
  6. //...
  7. this.sequence(MyAuthenticatingSequence);
  8. //...
  9. }
  10. }

Creating a Custom Authentication Strategy

Support for multiple authentication strategies is possible with a commonauthentication strategy interface, and an extensionPoint/extensions patternused to register and discover authentication strategies.

The AuthenticationComponent declares a common authentication strategyinterface named AuthenticationStrategy.

  1. export interface AuthenticationStrategy {
  2. /**
  3. * The 'name' property is a unique identifier for the
  4. * authentication strategy (for example: 'basic', 'jwt', etc)
  5. */
  6. name: string;
  7. /**
  8. * The 'authenticate' method takes in a given request and returns a user profile
  9. * which is an instance of 'UserProfile'.
  10. * (A user profile is a minimal subset of a user object)
  11. * If the user credentials are valid, this method should return a 'UserProfile' instance.
  12. * If the user credentials are invalid, this method should throw an error
  13. * If the user credentials are missing, this method should throw an error, or return 'undefined'
  14. * and let the authentication action deal with it.
  15. *
  16. * @param request - Express request object
  17. */
  18. authenticate(request: Request): Promise<UserProfile | undefined>;
  19. }

Developers that wish to create a custom authentication strategy must implementthis interface. The custom authentication strategy must have a unique nameand have an authenticate function which takes in a request and returns theuser profile of an authenticated user.

Here is an example of a basic authentication strategyBasicAuthenticationStrategy with the name 'basic' inbasic-strategy.ts:

  1. export interface Credentials {
  2. username: string;
  3. password: string;
  4. }
  5. export class BasicAuthenticationStrategy implements AuthenticationStrategy {
  6. name: string = 'basic';
  7. constructor(
  8. @inject(UserServiceBindings.USER_SERVICE)
  9. private userService: UserService,
  10. ) {}
  11. async authenticate(request: Request): Promise<UserProfile | undefined> {
  12. const credentials: Credentials = this.extractCredentials(request);
  13. const user = await this.userService.verifyCredentials(credentials);
  14. const userProfile = this.userService.convertToUserProfile(user);
  15. return userProfile;
  16. }
  17. extractCredentials(request: Request): Credentials {
  18. let creds: Credentials;
  19. /**
  20. * Code to extract the 'basic' user credentials from the Authorization header
  21. */
  22. return creds;
  23. }
  24. }

As you can see in the example, an authentication strategy can inject customservices to help it accomplish certain tasks. See the complete examples forbasic authentication strategyandjwt authentication strategy.

The AuthenticationComponent component also provides two optional serviceinterfaces which may be of use to your application:UserServiceandTokenService.

Registering a Custom Authentication Strategy

The registration and discovery of authentication strategies is possiblevia the Extension Point and Extensionspattern.

You don’t have to worry about the discovery of authentication strategies,this is taken care of by AuthenticationStrategyProvider which resolves andreturns an authentication strategy of type AuthenticationStrategy.

The AuthenticationStrategyProvider class (shown below) declares anextension point namedAuthenticationBindings.AUTHENTICATION_STRATEGY_EXTENSION_POINT_NAME via the@extensionPoint decorator. The binding scope is set to transient becausean authentication strategy may differ with each request.

With the aid of metadata of type AuthenticationMetadata (provided byAuthMetadataProvider and injected via the AuthenticationBindings.METADATAbinding key), the name of the authentication strategy, specified in the@authenticate decorator for this request, is obtained.

Then, with the aid of the @extensions() getter decorator,AuthenticationStrategyProvider is responsible for finding andreturning the authentication strategy which has that specific name andhas been registered as an extension of the aforementioned extensionpoint.

  1. @extensionPoint(
  2. AuthenticationBindings.AUTHENTICATION_STRATEGY_EXTENSION_POINT_NAME,
  3. {scope: BindingScope.TRANSIENT},
  4. ) //this needs to be transient, e.g. for request level context.
  5. export class AuthenticationStrategyProvider
  6. implements Provider<AuthenticationStrategy | undefined> {
  7. constructor(
  8. @extensions()
  9. private authenticationStrategies: Getter<AuthenticationStrategy[]>,
  10. @inject(AuthenticationBindings.METADATA)
  11. private metadata?: AuthenticationMetadata,
  12. ) {}
  13. async value(): Promise<AuthenticationStrategy | undefined> {
  14. if (!this.metadata) {
  15. return undefined;
  16. }
  17. const name = this.metadata.strategy;
  18. const strategy = await this.findAuthenticationStrategy(name);
  19. if (!strategy) {
  20. // important to throw a non-protocol-specific error here
  21. let error = new Error(`The strategy '${name}' is not available.`);
  22. Object.assign(error, {
  23. code: AUTHENTICATION_STRATEGY_NOT_FOUND,
  24. });
  25. throw error;
  26. }
  27. return strategy;
  28. }
  29. async findAuthenticationStrategy(name: string) {
  30. const strategies = await this.authenticationStrategies();
  31. const matchingAuthStrategy = strategies.find(a => a.name === name);
  32. return matchingAuthStrategy;
  33. }
  34. }

In order for your custom authentication strategy to be found, it needs to beregistered.

Registering a custom authentication strategy BasicAuthenticationStrategyas an extension of theAuthenticationBindings.AUTHENTICATION_STRATEGY_EXTENSION_POINT_NAME extensionpoint in an application application.ts is as simple as:

  1. import {registerAuthenticationStrategy} from '@loopback/authentication';
  2. export class MyApplication extends BootMixin(
  3. ServiceMixin(RepositoryMixin(RestApplication)),
  4. ) {
  5. constructor(options?: ApplicationConfig) {
  6. super(options);
  7. //...
  8. registerAuthenticationStrategy(this, BasicAuthenticationStrategy);
  9. //...
  10. }
  11. }

Using Passport-based Strategies

The earlier version of @loopback/authentication is based on an expressmiddleware called passport, which supports 500+ passport strategies forverifying an express app’s requests. In @loopback/authentication@2.0, wedefined our own interface AuthenticationStrategy that describes a strategywith different contracts than the passport strategy, but we still want to keepthe ability to support those existing 500+ community passport strategies.Therefore, we rewrote the adapter class. It now converts a passport strategy tothe one that LoopBack 4 authentication system expects and it was released in anew package @loopback/authentication-passport.

Creating and registering a passport strategy is explained inthe README.md file

The usage of authentication decorator and the change in sequence stay the same.

Managing Custom Authentication Strategy Options

This is an optional step.

If your custom authentication strategy doesn’t require special options, you canskip this section.

As previously mentioned in theUsing the Authentication Decoratorsection, a custom authentication strategy should avoid repeatedly specifying itsdefault options in the @authenticate decorator. Instead, it shoulddefine its default options in one place, and only specify overridingoptions in the @authenticate decorator when necessary.

Here are the steps for accomplishing this.

Define the Options Interface and Binding Key

Define an options interface and a binding key for the default options of thatspecific authentication strategy.

  1. export interface AuthenticationStrategyOptions {
  2. [property: string]: any;
  3. }
  4. export namespace BasicAuthenticationStrategyBindings {
  5. export const DEFAULT_OPTIONS = BindingKey.create<
  6. AuthenticationStrategyOptions
  7. >('authentication.strategies.basic.defaultoptions');
  8. }

Bind the Default Options

Bind the default options of the custom authentication strategy to theapplication application.ts via theBasicAuthenticationStrategyBindings.DEFAULT_OPTIONS binding key.

In this hypothetical example, our custom authentication strategy has adefault option of gatherStatistics with a value of true. (In a realcustom authentication strategy, the number of options could be more numerous)

  1. export class MyApplication extends BootMixin(
  2. ServiceMixin(RepositoryMixin(RestApplication)),
  3. ) {
  4. constructor(options?: ApplicationConfig) {
  5. super(options);
  6. //...
  7. this.bind(BasicAuthenticationStrategyBindings.DEFAULT_OPTIONS).to({
  8. gatherStatistics: true,
  9. });
  10. //...
  11. }
  12. }

Override Default Options In Authentication Decorator

Specify overriding options in the @authenticate decorator only when necessary.

In this example, we only specify an overriding option gatherStatisticswith a value of false for the /scareme endpoint. We use the defaultoption value for the /whoami endpoint.

  1. import {inject} from '@loopback/context';
  2. import {
  3. AuthenticationBindings,
  4. UserProfile,
  5. authenticate,
  6. } from '@loopback/authentication';
  7. import {get} from '@loopback/rest';
  8. export class WhoAmIController {
  9. constructor(
  10. @inject(SecurityBindings.USER)
  11. private userProfile: UserProfile,
  12. ) {}
  13. @authenticate('basic')
  14. @get('/whoami')
  15. whoAmI(): string {
  16. return this.userProfile.id;
  17. }
  18. @authenticate('basic', {gatherStatistics: false})
  19. @get('/scareme')
  20. scareMe(): string {
  21. return 'boo!';
  22. }
  23. }

Update Custom Authentication Strategy to Handle Options

The custom authentication strategy must be updated to handle the loading ofdefault options, and overriding them if they have been specified in the@authenticate decorator.

Here is the updated BasicAuthenticationStrategy:

  1. import {
  2. AuthenticationStrategy,
  3. UserProfile,
  4. TokenService,
  5. AuthenticationMetadata,
  6. AuthenticationBindings,
  7. } from '@loopback/authentication';
  8. import {Getter} from '@loopback/core';
  9. export interface Credentials {
  10. username: string;
  11. password: string;
  12. }
  13. export class BasicAuthenticationStrategy implements AuthenticationStrategy {
  14. name: string = 'basic';
  15. @inject(BasicAuthenticationStrategyBindings.DEFAULT_OPTIONS)
  16. options: AuthenticationStrategyOptions;
  17. constructor(
  18. @inject(UserServiceBindings.USER_SERVICE)
  19. private userService: UserService,
  20. @inject.getter(AuthenticationBindings.METADATA)
  21. readonly getMetaData: Getter<AuthenticationMetadata>,
  22. ) {}
  23. async authenticate(request: Request): Promise<UserProfile | undefined> {
  24. const credentials: Credentials = this.extractCredentials(request);
  25. await this.processOptions();
  26. if (this.options.gatherStatistics === true) {
  27. console.log(`\nGathering statistics...\n`);
  28. } else {
  29. console.log(`\nNot gathering statistics...\n`);
  30. }
  31. const user = await this.userService.verifyCredentials(credentials);
  32. const userProfile = this.userService.convertToUserProfile(user);
  33. return userProfile;
  34. }
  35. extractCredentials(request: Request): Credentials {
  36. let creds: Credentials;
  37. /**
  38. * Code to extract the 'basic' user credentials from the Authorization header
  39. */
  40. return creds;
  41. }
  42. async processOptions() {
  43. /**
  44. Obtain the options object specified in the @authenticate decorator
  45. of a controller method associated with the current request.
  46. The AuthenticationMetadata interface contains : strategy:string, options?:object
  47. We want the options property.
  48. */
  49. const controllerMethodAuthenticationMetadata = await this.getMetaData();
  50. if (!this.options) this.options = {}; //if no default options were bound, assign empty options object
  51. //override default options with request-level options
  52. this.options = Object.assign(
  53. {},
  54. this.options,
  55. controllerMethodAuthenticationMetadata.options,
  56. );
  57. }
  58. }

Inject default options into a property options using theBasicAuthenticationStrategyBindings.DEFAULT_OPTIONS binding key.

Inject a getter named getMetaData that returns AuthenticationMetadatausing the AuthenticationBindings.METADATA binding key. This metadata containsthe parameters passed into the @authenticate decorator.

Create a function named processOptions() that obtains the default options, andoverrides them with any request-level overriding options specified in the@authenticate decorator.

Then, in the authenticate() function of the custom authentication strategy,call the processOptions() function, and have the custom authenticationstrategy react to the updated options.

Summary

We’ve gone through the main steps for adding authentication to your LoopBack 4application.

Your application.ts should look similar to this:

  1. import {
  2. AuthenticationComponent,
  3. registerAuthenticationStrategy,
  4. } from '@loopback/authentication';
  5. export class MyApplication extends BootMixin(
  6. ServiceMixin(RepositoryMixin(RestApplication)),
  7. ) {
  8. constructor(options?: ApplicationConfig) {
  9. super(options);
  10. /* set up miscellaneous bindings */
  11. //...
  12. // load the authentication component
  13. this.component(AuthenticationComponent);
  14. // register your custom authentication strategy
  15. registerAuthenticationStrategy(this, BasicAuthenticationStrategy);
  16. // use your custom authenticating sequence
  17. this.sequence(MyAuthenticatingSequence);
  18. this.static('/', path.join(__dirname, '../public'));
  19. this.projectRoot = __dirname;
  20. this.bootOptions = {
  21. controllers: {
  22. dirs: ['controllers'],
  23. extensions: ['.controller.js'],
  24. nested: true,
  25. },
  26. };
  27. }

You can find a completed example and tutorial of a LoopBack 4 shoppingcart application with JWT authenticationhere.