{"id":29943381,"url":"https://github.com/sourcefuse/loopback4-authentication","last_synced_at":"2025-08-03T02:16:59.485Z","repository":{"id":34871335,"uuid":"181644151","full_name":"sourcefuse/loopback4-authentication","owner":"sourcefuse","description":"A loopback-next extension for authentication feature. Oauth strategies supported.","archived":false,"fork":false,"pushed_at":"2025-05-15T15:25:43.000Z","size":3018,"stargazers_count":73,"open_issues_count":0,"forks_count":33,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-07-28T08:29:15.584Z","etag":null,"topics":["arcbysf","authentication","facebook-authentication","google-authentication","instagram-authentication","keycloak","loopback4","loopback4-extension","oauth2","sourceloop"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/loopback4-authentication","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sourcefuse.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-04-16T08:08:36.000Z","updated_at":"2025-01-09T09:20:24.000Z","dependencies_parsed_at":"2023-11-20T19:45:33.920Z","dependency_job_id":"e35b992f-8941-4687-992a-bb39971637aa","html_url":"https://github.com/sourcefuse/loopback4-authentication","commit_stats":{"total_commits":160,"total_committers":24,"mean_commits":6.666666666666667,"dds":0.63125,"last_synced_commit":"13b67aca7f96317f43cdb1f590eb5f4c89e5c736"},"previous_names":[],"tags_count":68,"template":false,"template_full_name":null,"purl":"pkg:github/sourcefuse/loopback4-authentication","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sourcefuse%2Floopback4-authentication","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sourcefuse%2Floopback4-authentication/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sourcefuse%2Floopback4-authentication/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sourcefuse%2Floopback4-authentication/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sourcefuse","download_url":"https://codeload.github.com/sourcefuse/loopback4-authentication/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sourcefuse%2Floopback4-authentication/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268484362,"owners_count":24257660,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-03T02:00:12.545Z","response_time":2577,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arcbysf","authentication","facebook-authentication","google-authentication","instagram-authentication","keycloak","loopback4","loopback4-extension","oauth2","sourceloop"],"created_at":"2025-08-03T02:15:12.224Z","updated_at":"2025-08-03T02:16:59.455Z","avatar_url":"https://github.com/sourcefuse.png","language":"TypeScript","readme":"\u003ca href=\"https://sourcefuse.github.io/arc-docs/arc-api-docs\" target=\"_blank\"\u003e\u003cimg src=\"https://github.com/sourcefuse/loopback4-microservice-catalog/blob/master/docs/assets/logo-dark-bg.png?raw=true\" alt=\"ARC By SourceFuse logo\" title=\"ARC By SourceFuse\" align=\"right\" width=\"150\" /\u003e\u003c/a\u003e\n\n# [loopback4-authentication](https://github.com/sourcefuse/loopback4-authentication)\n\n\u003cp align=\"left\"\u003e\n\u003ca href=\"https://www.npmjs.com/package/loopback4-authentication\"\u003e\n\u003cimg src=\"https://img.shields.io/npm/v/loopback4-authentication.svg\" alt=\"npm version\" /\u003e\n\u003c/a\u003e\n\u003ca href=\"https://sonarcloud.io/summary/new_code?id=sourcefuse_loopback4-authentication\" target=\"_blank\"\u003e\n\u003cimg alt=\"Sonar Quality Gate\" src=\"https://img.shields.io/sonar/quality_gate/sourcefuse_loopback4-authentication?server=https%3A%2F%2Fsonarcloud.io\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://app.snyk.io/org/ashishkaushik/reporting?context[page]=issues-detail\u0026project_target=%255B%2522sourcefuse%252Floopback4-authentication%2522%255D\u0026project_origin=%255B%2522github%2522%255D\u0026issue_status=%255B%2522Open%2522%255D\u0026issue_by=Severity\u0026table_issues_detail_cols=SCORE%257CCVE%257CCWE%257CPROJECT%257CEXPLOIT%2520MATURITY%257CAUTO%2520FIXABLE%257CINTRODUCED%257CSNYK%2520PRODUCT\u0026v=1\"\u003e\n\u003cimg alt=\"Synk Status\" src=\"https://img.shields.io/badge/SYNK_SECURITY-MONITORED-GREEN\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://github.com/sourcefuse/loopback4-authentication/graphs/contributors\" target=\"_blank\"\u003e\n\u003cimg alt=\"GitHub contributors\" src=\"https://img.shields.io/github/contributors/sourcefuse/loopback4-authentication?\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/loopback4-authentication\" target=\"_blank\"\u003e\n\u003cimg alt=\"downloads\" src=\"https://img.shields.io/npm/dw/loopback4-authentication.svg\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://github.com/sourcefuse/loopback4-authentication/blob/master/LICENSE\"\u003e\n\u003cimg src=\"https://img.shields.io/github/license/sourcefuse/loopback4-authentication.svg\" alt=\"License\" /\u003e\n\u003c/a\u003e\n\u003ca href=\"https://loopback.io/\" target=\"_blank\"\u003e\n\u003cimg alt=\"Powered By LoopBack 4\" src=\"https://img.shields.io/badge/Powered%20by-LoopBack 4-brightgreen\" /\u003e\n\u003c/a\u003e\n\u003c/p\u003e\n\n## Overview\n\nThis is a loopback-next extension for adding authentication layer to a REST application in loopback 4.\nThis extension is based on the implementation guidelines provided on official [@loopback/authentication](https://github.com/strongloop/loopback-next/blob/master/packages/authentication/README.md) page.\nIt provides support for seven passport based strategies.\n\n1. [passport-oauth2-client-password](https://github.com/jaredhanson/passport-oauth2-client-password) - OAuth 2.0 client password authentication strategy for Passport. This module lets you authenticate requests containing client credentials in the request body, as [defined](http://tools.ietf.org/html/draft-ietf-oauth-v2-27#section-2.3.1) by the OAuth 2.0 specification.\n2. [passport-http-bearer](https://github.com/jaredhanson/passport-http-bearer) - HTTP Bearer authentication strategy for Passport. This module lets you authenticate HTTP requests using bearer tokens, as specified by [RFC 6750](https://tools.ietf.org/html/rfc6750), in your Node.js applications.\n3. [passport-local](https://github.com/jaredhanson/passport-local) - Passport strategy for authenticating with a username and password. This module lets you authenticate using a username and password in your Node.js applications.\n4. [passport-oauth2-resource-owner-password](https://www.npmjs.com/package/passport-oauth2-resource-owner-password) - OAuth 2.0 resource owner password authentication strategy for Passport. This module lets you authenticate requests containing resource owner credentials in the request body, as [defined](http://tools.ietf.org/html/draft-ietf-oauth-v2-27#section-1.3.3) by the OAuth 2.0 specification.\n5. [passport-google-oauth2](https://github.com/jaredhanson/passport-google-oauth2) - Passport strategy for authenticating with Google using the Google OAuth 2.0 API. This module lets you authenticate using Google in your Node.js applications.\n6. [keycloak-passport](https://github.com/exlinc/keycloak-passport) - Passport strategy for authenticating with Keycloak. This library offers a production-ready and maintained Keycloak Passport connector.\n7. [passport-instagram](https://github.com/jaredhanson/passport-instagram) - Passport strategy for authenticating with Instagram using the Instagram OAuth 2.0 API. This module lets you authenticate using Instagram in your Node.js applications.\n8. [passport-apple](https://github.com/ananay/passport-apple) - Passport strategy for authenticating with Apple using the Apple OAuth 2.0 API. This module lets you authenticate using Apple in your Node.js applications.\n9. [passport-facebook](https://github.com/jaredhanson/passport-facebook) - Passport strategy for authenticating with Facebook using the Facebook OAuth 2.0 API. This module lets you authenticate using Facebook in your Node.js applications.\n10. [passport-cognito-oauth2](https://github.com/ebuychance/passport-cognito-oauth2) - Passport strategy for authenticating with Cognito using the Cognito OAuth 2.0 API. This module lets you authenticate using Cognito in your Node.js applications.\n11. [passport-SAML](https://github.com/node-saml/passport-saml) - Passport strategy for authenticating with SAML using the SAML 2.0 API. This module lets you authenticate using SAML in your Node.js applications\n12. custom-passport-otp - Created a Custom Passport strategy for 2-Factor-Authentication using OTP (One Time Password).\n13. [passport-auth0](https://github.com/auth0/passport-auth0) - Passport strategy for authenticating with auth0. This module lets you authenticate using [Auth0](https://auth0.com/) in your Node.js applications.\n\nYou can use one or more strategies of the above in your application. For each of the strategy (only which you use), you just need to provide your own verifier function, making it easily configurable. Rest of the strategy implementation intricacies is handled by extension.\n\n## Install\n\n```sh\nnpm install loopback4-authentication\n```\n\n## Quick Starter\n\nFor a quick starter guide, you can refer to our [loopback 4 starter](https://github.com/sourcefuse/loopback4-starter) application which utilizes all of the above auth strategies from the extension in a simple multi-tenant application. Refer to the auth module [there](https://github.com/sourcefuse/loopback4-starter/tree/master/src/modules/auth) for specific details on authentication.\n\n## Detailed Usage\n\nThe first and common step for all of the startegies is to add the component to the application. See below\n\n```ts\n// application.ts\nexport class ToDoApplication extends BootMixin(\n  ServiceMixin(RepositoryMixin(RestApplication)),\n) {\n  constructor(options: ApplicationConfig = {}) {\n    super(options);\n\n    // Set up the custom sequence\n    this.sequence(MySequence);\n\n    // Set up default home page\n    this.static('/', path.join(__dirname, '../public'));\n\n    // Add authentication component\n    this.component(AuthenticationComponent);\n\n    // .... Rest of the code below\n  }\n}\n```\n\nOnce this is done, you are ready to configure any of the available strategy in the application.\n\n### Oauth2-client-password\n\nFirst, create an AuthClient model implementing the IAuthClient interface. The purpose of this model is to store oauth registered clients for the app in the DB. See sample below.\n\n```ts\n@model({\n  name: 'auth_clients',\n})\nexport class AuthClient extends Entity implements IAuthClient {\n  @property({\n    type: 'number',\n    id: true,\n  })\n  id?: number;\n\n  @property({\n    type: 'string',\n    required: true,\n    name: 'client_id',\n  })\n  clientId: string;\n\n  @property({\n    type: 'string',\n    required: true,\n    name: 'client_secret',\n  })\n  clientSecret: string;\n\n  @property({\n    type: 'array',\n    itemType: 'number',\n    name: 'user_ids',\n  })\n  userIds: number[];\n\n  constructor(data?: Partial\u003cAuthClient\u003e) {\n    super(data);\n  }\n}\n```\n\nCreate CRUD repository for the above model. Use loopback CLI.\n\n```sh\nlb4 repository\n```\n\nAdd the verifier function for the strategy. You need to create a provider for the same strategy. You can add your application specific business logic for client auth here. Here is simple example.\n\n```ts\nimport {Provider} from '@loopback/context';\nimport {repository} from '@loopback/repository';\nimport {VerifyFunction} from 'loopback4-authentication';\n\nimport {AuthClientRepository} from '../../../repositories';\n\nexport class ClientPasswordVerifyProvider\n  implements Provider\u003cVerifyFunction.OauthClientPasswordFn\u003e\n{\n  constructor(\n    @repository(AuthClientRepository)\n    public authClientRepository: AuthClientRepository,\n  ) {}\n\n  value(): VerifyFunction.OauthClientPasswordFn {\n    return async (clientId, clientSecret, req) =\u003e {\n      return this.authClientRepository.findOne({\n        where: {\n          clientId,\n          clientSecret,\n        },\n      });\n    };\n  }\n}\n```\n\nPlease note the Verify function type _VerifyFunction.OauthClientPasswordFn_.\n\nNow bind this provider to the application in application.ts.\n\n```ts\nimport {AuthenticationComponent, Strategies} from 'loopback4-authentication';\n```\n\n```ts\n// Add authentication component\nthis.component(AuthenticationComponent);\n// Customize authentication verify handlers\nthis.bind(Strategies.Passport.OAUTH2_CLIENT_PASSWORD_VERIFIER).toProvider(\n  ClientPasswordVerifyProvider,\n);\n```\n\nFinally, add the authenticate function as a sequence action to sequence.ts.\n\n```ts\nexport class MySequence implements SequenceHandler {\n  constructor(\n    @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,\n    @inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams,\n    @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,\n    @inject(SequenceActions.SEND) public send: Send,\n    @inject(SequenceActions.REJECT) public reject: Reject,\n    // Inject CLIENT_AUTH_ACTION sequence action provider\n    @inject(AuthenticationBindings.CLIENT_AUTH_ACTION)\n    protected authenticateRequestClient: AuthenticateFn\u003cAuthClient\u003e,\n  ) {}\n\n  async handle(context: RequestContext) {\n    try {\n      const {request, response} = context;\n      const route = this.findRoute(request);\n      const args = await this.parseParams(request, route);\n      request.body = args[args.length - 1];\n\n      // Perform client authentication here\n      await this.authenticateRequestClient(request);\n\n      const result = await this.invoke(route, args);\n      this.send(response, result);\n    } catch (err) {\n      this.reject(context, err);\n    }\n  }\n}\n```\n\nAfter this, you can use decorator to apply auth to controller functions wherever needed. See below.\n\n```ts\n@authenticateClient(STRATEGY.CLIENT_PASSWORD, {\n  passReqToCallback: true\n})\n@post('/auth/login', {\n  responses: {\n    [STATUS_CODE.OK]: {\n      description: 'Auth Code',\n      content: {\n        [CONTENT_TYPE.JSON]: Object,\n      },\n    },\n  },\n})\nasync login(\n  @requestBody()\n  req: LoginRequest,\n): Promise\u003c{\n  code: string;\n}\u003e {\n  ....\n}\n```\n\nFor accessing the authenticated AuthClient model reference, you can inject the CURRENT_CLIENT provider, provided by the extension, which is populated by the auth action sequence above.\n\n```ts\n  @inject.getter(AuthenticationBindings.CURRENT_CLIENT)\n  private readonly getCurrentClient: Getter\u003cAuthClient\u003e,\n```\n\n### Http-bearer\n\nFirst, create a AuthUser model implementing the IAuthUser interface. You can implement the interface in the user model itself. See sample below.\n\n```ts\n@model({\n  name: 'users',\n})\nexport class User extends Entity implements IAuthUser {\n  @property({\n    type: 'number',\n    id: true,\n  })\n  id?: number;\n\n  @property({\n    type: 'string',\n    required: true,\n    name: 'first_name',\n  })\n  firstName: string;\n\n  @property({\n    type: 'string',\n    name: 'last_name',\n  })\n  lastName: string;\n\n  @property({\n    type: 'string',\n    name: 'middle_name',\n  })\n  middleName?: string;\n\n  @property({\n    type: 'string',\n    required: true,\n  })\n  username: string;\n\n  @property({\n    type: 'string',\n  })\n  email?: string;\n\n  @property({\n    type: 'string',\n  })\n  password?: string;\n\n  constructor(data?: Partial\u003cUser\u003e) {\n    super(data);\n  }\n}\n```\n\nNow bind this model to USER_MODEL key in application.ts\n\n```ts\nthis.bind(AuthenticationBindings.USER_MODEL).to(User);\n```\n\nCreate CRUD repository for the above model. Use loopback CLI.\n\n```sh\nlb4 repository\n```\n\nAdd the verifier function for the strategy. You need to create a provider for the same. You can add your application specific business logic for client auth here. Here is simple example for JWT tokens.\n\n```ts\nimport {Provider} from '@loopback/context';\nimport {repository} from '@loopback/repository';\nimport {verify} from 'jsonwebtoken';\nimport {VerifyFunction} from 'loopback4-authentication';\n\nimport {User} from '../models/user.model';\n\nexport class BearerTokenVerifyProvider\n  implements Provider\u003cVerifyFunction.BearerFn\u003e\n{\n  constructor(\n    @repository(RevokedTokenRepository)\n    public revokedTokenRepository: RevokedTokenRepository,\n  ) {}\n\n  value(): VerifyFunction.BearerFn {\n    return async token =\u003e {\n      if (token \u0026\u0026 (await this.revokedTokenRepository.get(token))) {\n        throw new HttpErrors.Unauthorized('Token Revoked');\n      }\n      const user = verify(token, process.env.JWT_SECRET as string, {\n        issuer: process.env.JWT_ISSUER,\n      }) as User;\n      return user;\n    };\n  }\n}\n```\n\nThe above example has an import and injection of a RevokedTokenRepository, which could be used to keep track of revoked tokens in a datasource like Redis. You can find an implementation of this repository [here](https://github.com/sourcefuse/loopback4-starter/blob/master/src/repositories/revoked-token.repository.ts) and the Redis datasource [here](https://github.com/sourcefuse/loopback4-starter/blob/master/src/datasources/redis.datasource.ts).\n\nPlease note the Verify function type _VerifyFunction.BearerFn_\n\nNow bind this provider to the application in application.ts.\n\n```ts\nimport {AuthenticationComponent, Strategies} from 'loopback4-authentication';\n```\n\n```ts\n// Add authentication component\nthis.component(AuthenticationComponent);\n// Customize authentication verify handlers\nthis.bind(Strategies.Passport.BEARER_TOKEN_VERIFIER).toProvider(\n  BearerTokenVerifyProvider,\n);\n```\n\nFinally, add the authenticate function as a sequence action to sequence.ts.\n\n```ts\nexport class MySequence implements SequenceHandler {\n  constructor(\n    @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,\n    @inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams,\n    @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,\n    @inject(SequenceActions.SEND) public send: Send,\n    @inject(SequenceActions.REJECT) public reject: Reject,\n    @inject(AuthenticationBindings.USER_AUTH_ACTION)\n    protected authenticateRequest: AuthenticateFn\u003cAuthUser\u003e,\n  ) {}\n\n  async handle(context: RequestContext) {\n    try {\n      const {request, response} = context;\n\n      const route = this.findRoute(request);\n      const args = await this.parseParams(request, route);\n      request.body = args[args.length - 1];\n      const authUser: AuthUser = await this.authenticateRequest(request);\n      const result = await this.invoke(route, args);\n      this.send(response, result);\n    } catch (err) {\n      this.reject(context, err);\n    }\n  }\n}\n```\n\nAfter this, you can use decorator to apply auth to controller functions wherever needed. See below.\n\n```ts\n@authenticate(STRATEGY.BEARER)\n@get('/users', {\n  responses: {\n    '200': {\n      description: 'Array of User model instances',\n      content: {\n        'application/json': {\n          schema: {type: 'array', items: {'x-ts-type': User}},\n        },\n      },\n    },\n  },\n})\nasync find(\n  @param.query.object('filter', getFilterSchemaFor(User)) filter?: Filter,\n): Promise\u003cUser[]\u003e {\n  return await this.userRepository.find(filter);\n}\n```\n\nFor accessing the authenticated AuthUser model reference, you can inject the CURRENT_USER provider, provided by the extension, which is populated by the auth action sequence above.\n\n```ts\n  @inject.getter(AuthenticationBindings.CURRENT_USER)\n  private readonly getCurrentUser: Getter\u003cUser\u003e,\n```\n\n### local\n\nIn order to use it, run `npm install passport-local`.\nFirst, create a AuthUser model implementing the IAuthUser interface. You can implement the interface in the user model itself. See sample below.\n\n```ts\n@model({\n  name: 'users',\n})\nexport class User extends Entity implements IAuthUser {\n  @property({\n    type: 'number',\n    id: true,\n  })\n  id?: number;\n\n  @property({\n    type: 'string',\n    required: true,\n    name: 'first_name',\n  })\n  firstName: string;\n\n  @property({\n    type: 'string',\n    name: 'last_name',\n  })\n  lastName: string;\n\n  @property({\n    type: 'string',\n    name: 'middle_name',\n  })\n  middleName?: string;\n\n  @property({\n    type: 'string',\n    required: true,\n  })\n  username: string;\n\n  @property({\n    type: 'string',\n  })\n  email?: string;\n\n  @property({\n    type: 'string',\n  })\n  password?: string;\n\n  constructor(data?: Partial\u003cUser\u003e) {\n    super(data);\n  }\n}\n```\n\nNow bind this model to USER_MODEL key in application.ts\n\n```ts\nthis.bind(AuthenticationBindings.USER_MODEL).to(User);\n```\n\nCreate CRUD repository for the above model. Use loopback CLI.\n\n```sh\nlb4 repository\n```\n\nAdd the verifier function for the strategy. You need to create a provider for the same. You can add your application specific business logic for client auth here. Here is a simple example.\n\n```ts\nexport class LocalPasswordVerifyProvider\n  implements Provider\u003cVerifyFunction.LocalPasswordFn\u003e\n{\n  constructor(\n    @repository(UserRepository)\n    public userRepository: UserRepository,\n  ) {}\n\n  value(): VerifyFunction.LocalPasswordFn {\n    return async (username: any, password: any) =\u003e {\n      try {\n        const user: AuthUser = new AuthUser(\n          await this.userRepository.verifyPassword(username, password),\n        );\n        return user;\n      } catch (error) {\n        throw new HttpErrors.Unauthorized(AuthErrorKeys.InvalidCredentials)\n          .message;\n      }\n    };\n  }\n}\n```\n\nPlease note the Verify function type _VerifyFunction.LocalPasswordFn_\n\nNow bind this provider to the application in application.ts.\n\n```ts\nimport {AuthenticationComponent, Strategies} from 'loopback4-authentication';\n```\n\n```ts\n// Add authentication component\nthis.component(AuthenticationComponent);\n// Customize authentication verify handlers\nthis.bind(Strategies.Passport.LOCAL_PASSWORD_VERIFIER).toProvider(\n  LocalPasswordVerifyProvider,\n);\n```\n\nFinally, add the authenticate function as a sequence action to sequence.ts.\n\n```ts\nexport class MySequence implements SequenceHandler {\n  constructor(\n    @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,\n    @inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams,\n    @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,\n    @inject(SequenceActions.SEND) public send: Send,\n    @inject(SequenceActions.REJECT) public reject: Reject,\n    @inject(AuthenticationBindings.USER_AUTH_ACTION)\n    protected authenticateRequest: AuthenticateFn\u003cAuthUser\u003e,\n  ) {}\n\n  async handle(context: RequestContext) {\n    try {\n      const {request, response} = context;\n\n      const route = this.findRoute(request);\n      const args = await this.parseParams(request, route);\n      request.body = args[args.length - 1];\n      const authUser: AuthUser = await this.authenticateRequest(request);\n      const result = await this.invoke(route, args);\n      this.send(response, result);\n    } catch (err) {\n      this.reject(context, err);\n    }\n  }\n}\n```\n\nAfter this, you can use decorator to apply auth to controller functions wherever needed. See below.\n\n```ts\n  @authenticate(STRATEGY.LOCAL)\n  @post('/auth/login', {\n    responses: {\n      [STATUS_CODE.OK]: {\n        description: 'Auth Code',\n        content: {\n          [CONTENT_TYPE.JSON]: Object,\n        },\n      },\n    },\n  })\n  async login(\n    @requestBody()\n    req: LoginRequest,\n  ): Promise\u003c{\n    code: string;\n  }\u003e {\n    ......\n  }\n```\n\nFor accessing the authenticated AuthUser model reference, you can inject the CURRENT_USER provider, provided by the extension, which is populated by the auth action sequence above.\n\n```ts\n  @inject.getter(AuthenticationBindings.CURRENT_USER)\n  private readonly getCurrentUser: Getter\u003cUser\u003e,\n```\n\n### Oauth2-resource-owner-password\n\nIn order to use it, run `npm install passport-oauth2-resource-owner-password`.\nFirst, create an AuthClient model implementing the IAuthClient interface. The purpose of this model is to store oauth registered clients for the app in the DB. See sample below.\n\n```ts\n@model({\n  name: 'auth_clients',\n})\nexport class AuthClient extends Entity implements IAuthClient {\n  @property({\n    type: 'number',\n    id: true,\n  })\n  id?: number;\n\n  @property({\n    type: 'string',\n    required: true,\n    name: 'client_id',\n  })\n  clientId: string;\n\n  @property({\n    type: 'string',\n    required: true,\n    name: 'client_secret',\n  })\n  clientSecret: string;\n\n  @property({\n    type: 'array',\n    itemType: 'number',\n    name: 'user_ids',\n  })\n  userIds: number[];\n\n  constructor(data?: Partial\u003cAuthClient\u003e) {\n    super(data);\n  }\n}\n```\n\nNext, create a AuthUser model implementing the IAuthUser interface. You can implement the interface in the user model itself. See sample below.\n\n```ts\n@model({\n  name: 'users',\n})\nexport class User extends Entity implements IAuthUser {\n  @property({\n    type: 'number',\n    id: true,\n  })\n  id?: number;\n\n  @property({\n    type: 'string',\n    required: true,\n    name: 'first_name',\n  })\n  firstName: string;\n\n  @property({\n    type: 'string',\n    name: 'last_name',\n  })\n  lastName: string;\n\n  @property({\n    type: 'string',\n    name: 'middle_name',\n  })\n  middleName?: string;\n\n  @property({\n    type: 'string',\n    required: true,\n  })\n  username: string;\n\n  @property({\n    type: 'string',\n  })\n  email?: string;\n\n  @property({\n    type: 'string',\n  })\n  password?: string;\n\n  constructor(data?: Partial\u003cUser\u003e) {\n    super(data);\n  }\n}\n```\n\nNow bind this model to USER_MODEL key in application.ts\n\n```ts\nthis.bind(AuthenticationBindings.USER_MODEL).to(User);\n```\n\nCreate CRUD repository for both of the above models. Use loopback CLI.\n\n```sh\nlb4 repository\n```\n\nAdd the verifier function for the strategy. You need to create a provider for the same. You can add your application specific business logic for client auth here. Here is a simple example.\n\n```ts\nexport class ResourceOwnerVerifyProvider\n  implements Provider\u003cVerifyFunction.ResourceOwnerPasswordFn\u003e\n{\n  constructor(\n    @repository(UserRepository)\n    public userRepository: UserRepository,\n    @repository(AuthClientRepository)\n    public authClientRepository: AuthClientRepository,\n  ) {}\n\n  value(): VerifyFunction.ResourceOwnerPasswordFn {\n    return async (clientId, clientSecret, username, password) =\u003e {\n      const user = await this.userRepository.verifyPassword(username, password);\n      if (!user) {\n        throw new HttpErrors.Unauthorized(AuthErrorKeys.InvalidCredentials);\n      }\n      const client = await this.authClientRepository.findOne({\n        where: {\n          clientId,\n        },\n      });\n      if (!client || client.userIds.indexOf(user.id || 0) \u003c 0) {\n        throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);\n      } else if (!client.clientSecret || client.clientSecret !== clientSecret) {\n        throw new HttpErrors.Unauthorized(\n          AuthErrorKeys.ClientVerificationFailed,\n        );\n      }\n      return {\n        client,\n        user,\n      };\n    };\n  }\n}\n```\n\nPlease note the Verify function type _VerifyFunction.LocalPasswordFn_.\nAlso, in this case, verifier will return AuthClient as well as User model.\n\nNow bind this provider to the application in application.ts.\n\n```ts\nimport {AuthenticationComponent, Strategies} from 'loopback4-authentication';\n```\n\n```ts\n// Add authentication component\nthis.component(AuthenticationComponent);\n// Customize authentication verify handlers\nthis.bind(Strategies.Passport.RESOURCE_OWNER_PASSWORD_VERIFIER).toProvider(\n  ResourceOwnerVerifyProvider,\n);\n```\n\nFinally, add the authenticate function as a sequence action to sequence.ts.\n\n```ts\nexport class MySequence implements SequenceHandler {\n  constructor(\n    @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,\n    @inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams,\n    @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,\n    @inject(SequenceActions.SEND) public send: Send,\n    @inject(SequenceActions.REJECT) public reject: Reject,\n    @inject(AuthenticationBindings.USER_AUTH_ACTION)\n    protected authenticateRequest: AuthenticateFn\u003cAuthUser\u003e,\n  ) {}\n\n  async handle(context: RequestContext) {\n    try {\n      const {request, response} = context;\n\n      const route = this.findRoute(request);\n      const args = await this.parseParams(request, route);\n      request.body = args[args.length - 1];\n      const authUser: AuthUser = await this.authenticateRequest(request);\n      const result = await this.invoke(route, args);\n      this.send(response, result);\n    } catch (err) {\n      this.reject(context, err);\n    }\n  }\n}\n```\n\nAfter this, you can use decorator to apply auth to controller functions wherever needed. See below.\n\n```ts\n  @authenticate(STRATEGY.OAUTH2_RESOURCE_OWNER_GRANT)\n  @post('/auth/login-token', {\n    responses: {\n      [STATUS_CODE.OK]: {\n        description: 'Token Response Model',\n        content: {\n          [CONTENT_TYPE.JSON]: {\n            schema: {'x-ts-type': TokenResponse},\n          },\n        },\n      },\n    },\n  })\n  async loginWithClientUser(\n    @requestBody() req: LoginRequest,\n  ): Promise\u003cTokenResponse\u003e {\n    ......\n  }\n```\n\nFor accessing the authenticated AuthUser and AuthClient model reference, you can inject the CURRENT_USER and CURRENT_CLIENT provider, provided by the extension, which is populated by the auth action sequence above.\n\n```ts\n  @inject.getter(AuthenticationBindings.CURRENT_USER)\n  private readonly getCurrentUser: Getter\u003cUser\u003e,\n  @inject.getter(AuthenticationBindings.CURRENT_CLIENT)\n  private readonly getCurrentClient: Getter\u003cAuthClient\u003e,\n```\n\n### OTP\n\nFirst, create a OtpCache model. This model should have OTP and few details of user and client (which will be used to retrieve them from database), it will be used to verify otp and get user, client. See sample below.\n\n```ts\n@model()\nexport class OtpCache extends Entity {\n  @property({\n    type: 'string',\n  })\n  otp: string;\n\n  @property({\n    type: 'string',\n  })\n  userId: string;\n\n  @property({\n    type: 'string',\n  })\n  clientId: string;\n\n  @property({\n    type: 'string',\n  })\n  clientSecret: string;\n\n  constructor(data?: Partial\u003cOtpCache\u003e) {\n    super(data);\n  }\n}\n```\n\nCreate [redis-repository](https://loopback.io/doc/en/lb4/Repository.html#define-a-keyvaluerepository) for the above model. Use loopback CLI.\n\n```sh\nlb4 repository\n```\n\nHere is a simple example.\n\n```ts\nimport {OtpCache} from '../models';\nimport {AuthCacheSourceName} from 'loopback4-authentication';\n\nexport class OtpCacheRepository extends DefaultKeyValueRepository\u003cOtpCache\u003e {\n  constructor(\n    @inject(`datasources.${AuthCacheSourceName}`)\n    dataSource: juggler.DataSource,\n  ) {\n    super(OtpCache, dataSource);\n  }\n}\n```\n\nAdd the verifier function for the strategy. You need to create a provider for the same. You can add your application specific business logic for auth here. Here is a simple example.\n\n```ts\nexport class OtpVerifyProvider implements Provider\u003cVerifyFunction.OtpAuthFn\u003e {\n  constructor(\n    @repository(UserRepository)\n    public userRepository: UserRepository,\n    @repository(OtpCacheRepository)\n    public otpCacheRepo: OtpCacheRepository,\n  ) {}\n\n  value(): VerifyFunction.OtpAuthFn {\n    return async (key: string, otp: string) =\u003e {\n      const otpCache = await this.otpCacheRepo.get(key);\n      if (!otpCache) {\n        throw new HttpErrors.Unauthorized(AuthErrorKeys.InvalidCredentials);\n      }\n      if (otpCache.otp.toString() !== otp) {\n        throw new HttpErrors.Unauthorized('Invalid OTP');\n      }\n      return this.userRepository.findById(otpCache.userId);\n    };\n  }\n}\n```\n\nPlease note the Verify function type _VerifyFunction.OtpAuthFn_\n\nNow bind this provider to the application in application.ts.\n\n```ts\nimport {AuthenticationComponent, Strategies} from 'loopback4-authentication';\n```\n\n```ts\n// Add authentication component\nthis.component(AuthenticationComponent);\n// Customize authentication verify handlers\nthis.bind(Strategies.Passport.OTP_VERIFIER).toProvider(OtpVerifyProvider);\n```\n\nFinally, add the authenticate function as a sequence action to sequence.ts.\n\n```ts\nexport class MySequence implements SequenceHandler {\n  constructor(\n    @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,\n    @inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams,\n    @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,\n    @inject(SequenceActions.SEND) public send: Send,\n    @inject(SequenceActions.REJECT) public reject: Reject,\n    @inject(AuthenticationBindings.USER_AUTH_ACTION)\n    protected authenticateRequest: AuthenticateFn\u003cAuthUser\u003e,\n  ) {}\n\n  async handle(context: RequestContext) {\n    try {\n      const {request, response} = context;\n\n      const route = this.findRoute(request);\n      const args = await this.parseParams(request, route);\n      request.body = args[args.length - 1];\n      const authUser: AuthUser = await this.authenticateRequest(request);\n      const result = await this.invoke(route, args);\n      this.send(response, result);\n    } catch (err) {\n      this.reject(context, err);\n    }\n  }\n}\n```\n\nThen, you need to create APIs, where you will first authenticate the user, and then send the OTP to user's email/phone. See below.\n\n```ts\n  //You can use your other strategies also\n  @authenticate(STRATEGY.LOCAL)\n  @post('/auth/send-otp', {\n    responses: {\n      [STATUS_CODE.OK]: {\n        description: 'Send Otp',\n        content: {\n          [CONTENT_TYPE.JSON]: Object,\n        },\n      },\n    },\n  })\n  async login(\n    @requestBody()\n    req: LoginRequest,\n  ): Promise\u003c{\n    key: string;\n  }\u003e {\n\n    // User is authenticated before this step.\n    // Now follow these steps:\n    // 1. Create a unique key.\n    // 2. Generate and send OTP to user's email/phone.\n    // 3. Store the details in redis-cache using key created in step-1. (Refer OtpCache model mentioned above)\n    // 4. Response will be the key created in step-1\n  }\n```\n\nAfter this, create an API with @@authenticate(STRATEGY.OTP) decorator. See below.\n\n```ts\n  @authenticate(STRATEGY.OTP)\n  @post('/auth/login-otp', {\n    responses: {\n      [STATUS_CODE.OK]: {\n        description: 'Auth Code',\n        content: {\n          [CONTENT_TYPE.JSON]: Object,\n        },\n      },\n    },\n  })\n  async login(\n    @requestBody()\n    req: {\n      key: 'string';\n      otp: 'string';\n    },\n  ): Promise\u003c{\n    code: string;\n  }\u003e {\n    ......\n  }\n```\n\nFor accessing the authenticated AuthUser model reference, you can inject the CURRENT_USER provider, provided by the extension, which is populated by the auth action sequence above.\n\n```ts\n  @inject.getter(AuthenticationBindings.CURRENT_USER)\n  private readonly getCurrentUser: Getter\u003cUser\u003e,\n```\n\n### Google Oauth 2\n\nIn order to use it, run `npm install passport-google-oauth20` and `npm install @types/passport-google-oauth20`.\nFirst, create a AuthUser model implementing the IAuthUser interface. You can implement the interface in the user model itself. See sample below.\n\n```ts\n@model({\n  name: 'users',\n})\nexport class User extends Entity implements IAuthUser {\n  @property({\n    type: 'number',\n    id: true,\n  })\n  id?: number;\n\n  @property({\n    type: 'string',\n    required: true,\n    name: 'first_name',\n  })\n  firstName: string;\n\n  @property({\n    type: 'string',\n    name: 'last_name',\n  })\n  lastName: string;\n\n  @property({\n    type: 'string',\n    name: 'middle_name',\n  })\n  middleName?: string;\n\n  @property({\n    type: 'string',\n    required: true,\n  })\n  username: string;\n\n  @property({\n    type: 'string',\n  })\n  email?: string;\n\n  // Auth provider - 'google'\n  @property({\n    type: 'string',\n    required: true,\n    name: 'auth_provider',\n  })\n  authProvider: string;\n\n  // Id from external provider\n  @property({\n    type: 'string',\n    name: 'auth_id',\n  })\n  authId?: string;\n\n  @property({\n    type: 'string',\n    name: 'auth_token',\n  })\n  authToken?: string;\n\n  @property({\n    type: 'string',\n  })\n  password?: string;\n\n  constructor(data?: Partial\u003cUser\u003e) {\n    super(data);\n  }\n}\n```\n\nNow bind this model to USER_MODEL key in application.ts\n\n```ts\nthis.bind(AuthenticationBindings.USER_MODEL).to(User);\n```\n\nCreate CRUD repository for the above model. Use loopback CLI.\n\n```sh\nlb4 repository\n```\n\nAdd the verifier function for the strategy. You need to create a provider for the same. You can add your application specific business logic for client auth here. Here is a simple example.\n\n```ts\nimport {Provider} from '@loopback/context';\nimport {repository} from '@loopback/repository';\nimport {HttpErrors} from '@loopback/rest';\nimport {AuthErrorKeys, VerifyFunction} from 'loopback4-authentication';\n\nimport {Tenant} from '../../../models';\nimport {UserCredentialsRepository, UserRepository} from '../../../repositories';\nimport {AuthUser} from '../models/auth-user.model';\n\nexport class GoogleOauth2VerifyProvider\n  implements Provider\u003cVerifyFunction.GoogleAuthFn\u003e\n{\n  constructor(\n    @repository(UserRepository)\n    public userRepository: UserRepository,\n    @repository(UserCredentialsRepository)\n    public userCredsRepository: UserCredentialsRepository,\n  ) {}\n\n  value(): VerifyFunction.GoogleAuthFn {\n    return async (accessToken, refreshToken, profile) =\u003e {\n      const user = await this.userRepository.findOne({\n        where: {\n          /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n          email: (profile as any)._json.email,\n        },\n      });\n      if (!user) {\n        throw new HttpErrors.Unauthorized(AuthErrorKeys.InvalidCredentials);\n      }\n      if (\n        !user ||\n        user.authProvider !== 'google' ||\n        user.authId !== profile.id\n      ) {\n        throw new HttpErrors.Unauthorized(AuthErrorKeys.InvalidCredentials);\n      }\n\n      const authUser: AuthUser = new AuthUser(user);\n      authUser.permissions = [];\n      authUser.externalAuthToken = accessToken;\n      authUser.externalRefreshToken = refreshToken;\n      authUser.tenant = new Tenant({id: user.defaultTenant});\n      return authUser;\n    };\n  }\n}\n```\n\nPlease note the Verify function type _VerifyFunction.LocalPasswordFn_\n\nNow bind this provider to the application in application.ts.\n\n```ts\nimport {AuthenticationComponent, Strategies} from 'loopback4-authentication';\n```\n\n```ts\n// Add authentication component\nthis.component(AuthenticationComponent);\n// Customize authentication verify handlers\nthis.bind(Strategies.Passport.GOOGLE_OAUTH2_VERIFIER).toProvider(\n  GoogleOauth2VerifyProvider,\n);\n```\n\nNow, bind this provider to the application in application.ts.\n\n```ts\nimport {GoogleAuthStrategyFactoryProvider} from 'loopback4-authentication/passport-google-oauth2';\n```\n\n```ts\nthis.bind(Strategies.Passport.GOOGLE_OAUTH2_STRATEGY_FACTORY.key).toProvider(\n  GoogleAuthStrategyFactoryProvider,\n);\n```\n\nFinally, add the authenticate function as a sequence action to sequence.ts.\n\n```ts\nexport class MySequence implements SequenceHandler {\n  constructor(\n    @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,\n    @inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams,\n    @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,\n    @inject(SequenceActions.SEND) public send: Send,\n    @inject(SequenceActions.REJECT) public reject: Reject,\n    @inject(AuthenticationBindings.USER_AUTH_ACTION)\n    protected authenticateRequest: AuthenticateFn\u003cAuthUser\u003e,\n  ) {}\n\n  async handle(context: RequestContext) {\n    try {\n      const {request, response} = context;\n\n      const route = this.findRoute(request);\n      const args = await this.parseParams(request, route);\n      request.body = args[args.length - 1];\n      const authUser: AuthUser = await this.authenticateRequest(\n        request,\n        response,\n      );\n      const result = await this.invoke(route, args);\n      this.send(response, result);\n    } catch (err) {\n      this.reject(context, err);\n    }\n  }\n}\n```\n\nAfter this, you can use decorator to apply auth to controller functions wherever needed. See below.\n\n```ts\n@authenticateClient(STRATEGY.CLIENT_PASSWORD)\n  @authenticate(\n    STRATEGY.GOOGLE_OAUTH2,\n    {\n      accessType: 'offline',\n      scope: ['profile', 'email'],\n      authorizationURL: process.env.GOOGLE_AUTH_URL,\n      callbackURL: process.env.GOOGLE_AUTH_CALLBACK_URL,\n      clientID: process.env.GOOGLE_AUTH_CLIENT_ID,\n      clientSecret: process.env.GOOGLE_AUTH_CLIENT_SECRET,\n      tokenURL: process.env.GOOGLE_AUTH_TOKEN_URL,\n    },\n    (req: Request) =\u003e {\n      return {\n        accessType: 'offline',\n        state: Object.keys(req.query)\n          .map(key =\u003e key + '=' + req.query[key])\n          .join('\u0026'),\n      };\n    },\n  )\n  @authorize(['*'])\n  @get('/auth/google', {\n    responses: {\n      [STATUS_CODE.OK]: {\n        description: 'Token Response',\n        content: {\n          [CONTENT_TYPE.JSON]: {\n            schema: {'x-ts-type': TokenResponse},\n          },\n        },\n      },\n    },\n  })\n  async loginViaGoogle(\n    @param.query.string('client_id')\n    clientId?: string,\n    @param.query.string('client_secret')\n    clientSecret?: string,\n  ): Promise\u003cvoid\u003e {}\n\n  @authenticate(\n    STRATEGY.GOOGLE_OAUTH2,\n    {\n      accessType: 'offline',\n      scope: ['profile', 'email'],\n      authorizationURL: process.env.GOOGLE_AUTH_URL,\n      callbackURL: process.env.GOOGLE_AUTH_CALLBACK_URL,\n      clientID: process.env.GOOGLE_AUTH_CLIENT_ID,\n      clientSecret: process.env.GOOGLE_AUTH_CLIENT_SECRET,\n      tokenURL: process.env.GOOGLE_AUTH_TOKEN_URL,\n    },\n    (req: Request) =\u003e {\n      return {\n        accessType: 'offline',\n        state: Object.keys(req.query)\n          .map(key =\u003e `${key}=${req.query[key]}`)\n          .join('\u0026'),\n      };\n    },\n  )\n  @authorize(['*'])\n  @get('/auth/google-auth-redirect', {\n    responses: {\n      [STATUS_CODE.OK]: {\n        description: 'Token Response',\n        content: {\n          [CONTENT_TYPE.JSON]: {\n            schema: {'x-ts-type': TokenResponse},\n          },\n        },\n      },\n    },\n  })\n  async googleCallback(\n    @param.query.string('code') code: string,\n    @param.query.string('state') state: string,\n    @inject(RestBindings.Http.RESPONSE) response: Response,\n  ): Promise\u003cvoid\u003e {\n    const clientId = new URLSearchParams(state).get('client_id');\n    if (!clientId || !this.user) {\n      throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);\n    }\n    const client = await this.authClientRepository.findOne({\n      where: {\n        clientId: clientId,\n      },\n    });\n    if (!client || !client.redirectUrl) {\n      throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);\n    }\n    try {\n      const codePayload: ClientAuthCode\u003cUser\u003e = {\n        clientId,\n        user: this.user,\n      };\n      const token = jwt.sign(codePayload, client.secret, {\n        expiresIn: client.authCodeExpiration,\n        audience: clientId,\n        subject: this.user.username,\n        issuer: process.env.JWT_ISSUER,\n      });\n      response.redirect(`${client.redirectUrl}?code=${token}`);\n    } catch (error) {\n      throw new HttpErrors.InternalServerError(AuthErrorKeys.UnknownError);\n    }\n  }\n```\n\nPlease note above that we are creating two new APIs for google auth. The first one is for UI clients to hit. We are authenticating client as well, then passing the details to the google auth. Then, the actual authentication is done by google authorization url, which redirects to the second API we created after success. The first API method body is empty as we do not need to handle its response. The google auth provider in this package will do the redirection for you automatically.\n\nFor accessing the authenticated AuthUser model reference, you can inject the CURRENT_USER provider, provided by the extension, which is populated by the auth action sequence above.\n\n```ts\n  @inject.getter(AuthenticationBindings.CURRENT_USER)\n  private readonly getCurrentUser: Getter\u003cUser\u003e,\n```\n\n### Instagram Oauth 2\n\nIn order to use it, run `npm install passport-instagram`.\nFirst, create a AuthUser model implementing the IAuthUser interface. You can implement the interface in the user model itself. See sample below.\n\n```ts\n@model({\n  name: 'users',\n})\nexport class User extends Entity implements IAuthUser {\n  @property({\n    type: 'number',\n    id: true,\n  })\n  id?: number;\n\n  @property({\n    type: 'string',\n    required: true,\n    name: 'first_name',\n  })\n  firstName: string;\n\n  @property({\n    type: 'string',\n    name: 'last_name',\n  })\n  lastName: string;\n\n  @property({\n    type: 'string',\n    name: 'middle_name',\n  })\n  middleName?: string;\n\n  @property({\n    type: 'string',\n    required: true,\n  })\n  username: string;\n\n  @property({\n    type: 'string',\n  })\n  email?: string;\n\n  // Auth provider - 'instagram'\n  @property({\n    type: 'string',\n    required: true,\n    name: 'auth_provider',\n  })\n  authProvider: string;\n\n  // Id from external provider\n  @property({\n    type: 'string',\n    name: 'auth_id',\n  })\n  authId?: string;\n\n  @property({\n    type: 'string',\n    name: 'auth_token',\n  })\n  authToken?: string;\n\n  @property({\n    type: 'string',\n  })\n  password?: string;\n\n  constructor(data?: Partial\u003cUser\u003e) {\n    super(data);\n  }\n}\n```\n\nNow bind this model to USER_MODEL key in application.ts\n\n```ts\nthis.bind(AuthenticationBindings.USER_MODEL).to(User);\n```\n\nCreate CRUD repository for the above model. Use loopback CLI.\n\n```sh\nlb4 repository\n```\n\nAdd the verifier function for the strategy. You need to create a provider for the same. You can add your application specific business logic for client auth here. Here is a simple example.\n\n```ts\nimport {Provider} from '@loopback/context';\nimport {repository} from '@loopback/repository';\nimport {HttpErrors} from '@loopback/rest';\nimport {AuthErrorKeys, VerifyFunction} from 'loopback4-authentication';\n\nimport {Tenant} from '../../../models';\nimport {UserCredentialsRepository, UserRepository} from '../../../repositories';\nimport {AuthUser} from '../models/auth-user.model';\n\nexport class InstagramOauth2VerifyProvider\n  implements Provider\u003cVerifyFunction.InstagramAuthFn\u003e\n{\n  constructor(\n    @repository(UserRepository)\n    public userRepository: UserRepository,\n    @repository(UserCredentialsRepository)\n    public userCredsRepository: UserCredentialsRepository,\n  ) {}\n\n  value(): VerifyFunction.InstagramAuthFn {\n    return async (accessToken, refreshToken, profile) =\u003e {\n      const user = await this.userRepository.findOne({\n        where: {\n          /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n          email: (profile as any)._json.email,\n        },\n      });\n      if (!user) {\n        throw new HttpErrors.Unauthorized(AuthErrorKeys.InvalidCredentials);\n      }\n      if (\n        !user ||\n        user.authProvider !== 'instagram' ||\n        user.authId !== profile.id\n      ) {\n        throw new HttpErrors.Unauthorized(AuthErrorKeys.InvalidCredentials);\n      }\n\n      const authUser: AuthUser = new AuthUser(user);\n      authUser.permissions = [];\n      authUser.externalAuthToken = accessToken;\n      authUser.externalRefreshToken = refreshToken;\n      authUser.tenant = new Tenant({id: user.defaultTenant});\n      return authUser;\n    };\n  }\n}\n```\n\nPlease note the Verify function type _VerifyFunction.LocalPasswordFn_\n\nNow bind this provider to the application in application.ts.\n\n```ts\nimport {AuthenticationComponent, Strategies} from 'loopback4-authentication';\n```\n\n```ts\n// Add authentication component\nthis.component(AuthenticationComponent);\n// Customize authentication verify handlers\nthis.bind(Strategies.Passport.INSTAGRAM_OAUTH2_VERIFIER).toProvider(\n  InstagramOauth2VerifyProvider,\n);\n```\n\nFinally, add the authenticate function as a sequence action to sequence.ts.\n\n```ts\nexport class MySequence implements SequenceHandler {\n  constructor(\n    @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,\n    @inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams,\n    @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,\n    @inject(SequenceActions.SEND) public send: Send,\n    @inject(SequenceActions.REJECT) public reject: Reject,\n    @inject(AuthenticationBindings.USER_AUTH_ACTION)\n    protected authenticateRequest: AuthenticateFn\u003cAuthUser\u003e,\n  ) {}\n\n  async handle(context: RequestContext) {\n    try {\n      const {request, response} = context;\n\n      const route = this.findRoute(request);\n      const args = await this.parseParams(request, route);\n      request.body = args[args.length - 1];\n      const authUser: AuthUser = await this.authenticateRequest(\n        request,\n        response,\n      );\n      const result = await this.invoke(route, args);\n      this.send(response, result);\n    } catch (err) {\n      this.reject(context, err);\n    }\n  }\n}\n```\n\nAfter this, you can use decorator to apply auth to controller functions wherever needed. See below.\n\n```ts\n@authenticateClient(STRATEGY.CLIENT_PASSWORD)\n  @authenticate(\n    STRATEGY.INSTAGRAM_OAUTH2,\n    {\n      accessType: 'offline',\n      authorizationURL: process.env.INSTAGRAM_AUTH_URL,\n      callbackURL: process.env.INSTAGRAM_AUTH_CALLBACK_URL,\n      clientID: process.env.INSTAGRAM_AUTH_CLIENT_ID,\n      clientSecret: process.env.INSTAGRAM_AUTH_CLIENT_SECRET,\n      tokenURL: process.env.INSTAGRAM_AUTH_TOKEN_URL,\n    },\n    (req: Request) =\u003e {\n      return {\n        accessType: 'offline',\n        state: Object.keys(req.query)\n          .map(key =\u003e key + '=' + req.query[key])\n          .join('\u0026'),\n      };\n    },\n  )\n  @authorize(['*'])\n  @get('/auth/instagram', {\n    responses: {\n      [STATUS_CODE.OK]: {\n        description: 'Token Response',\n        content: {\n          [CONTENT_TYPE.JSON]: {\n            schema: {'x-ts-type': TokenResponse},\n          },\n        },\n      },\n    },\n  })\n  async loginViaInstagram(\n    @param.query.string('client_id')\n    clientId?: string,\n    @param.query.string('client_secret')\n    clientSecret?: string,\n  ): Promise\u003cvoid\u003e {}\n\n  @authenticate(\n    STRATEGY.INSTAGRAM_OAUTH2,\n    {\n      accessType: 'offline',\n      authorizationURL: process.env.INSTAGRAM_AUTH_URL,\n      callbackURL: process.env.INSTAGRAM_AUTH_CALLBACK_URL,\n      clientID: process.env.INSTAGRAM_AUTH_CLIENT_ID,\n      clientSecret: process.env.INSTAGRAM_AUTH_CLIENT_SECRET,\n      tokenURL: process.env.INSTAGRAM_AUTH_TOKEN_URL,\n    },\n    (req: Request) =\u003e {\n      return {\n        accessType: 'offline',\n        state: Object.keys(req.query)\n          .map(key =\u003e `${key}=${req.query[key]}`)\n          .join('\u0026'),\n      };\n    },\n  )\n  @authorize(['*'])\n  @get('/auth/instagram-auth-redirect', {\n    responses: {\n      [STATUS_CODE.OK]: {\n        description: 'Token Response',\n        content: {\n          [CONTENT_TYPE.JSON]: {\n            schema: {'x-ts-type': TokenResponse},\n          },\n        },\n      },\n    },\n  })\n  async instagramCallback(\n    @param.query.string('code') code: string,\n    @param.query.string('state') state: string,\n    @inject(RestBindings.Http.RESPONSE) response: Response,\n  ): Promise\u003cvoid\u003e {\n    const clientId = new URLSearchParams(state).get('client_id');\n    if (!clientId || !this.user) {\n      throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);\n    }\n    const client = await this.authClientRepository.findOne({\n      where: {\n        clientId: clientId,\n      },\n    });\n    if (!client || !client.redirectUrl) {\n      throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);\n    }\n    try {\n      const codePayload: ClientAuthCode\u003cUser\u003e = {\n        clientId,\n        user: this.user,\n      };\n      const token = jwt.sign(codePayload, client.secret, {\n        expiresIn: client.authCodeExpiration,\n        audience: clientId,\n        subject: this.user.username,\n        issuer: process.env.JWT_ISSUER,\n      });\n      response.redirect(`${client.redirectUrl}?code=${token}`);\n    } catch (error) {\n      throw new HttpErrors.InternalServerError(AuthErrorKeys.UnknownError);\n    }\n  }\n```\n\nPlease note above that we are creating two new APIs for instagram auth. The first one is for UI clients to hit. We are authenticating client as well, then passing the details to the instagram auth. Then, the actual authentication is done by instagram authorization url, which redirects to the second API we created after success. The first API method body is empty as we do not need to handle its response. The instagram auth provider in this package will do the redirection for you automatically.\n\nFor accessing the authenticated AuthUser model reference, you can inject the CURRENT_USER provider, provided by the extension, which is populated by the auth action sequence above.\n\n```ts\n  @inject.getter(AuthenticationBindings.CURRENT_USER)\n  private readonly getCurrentUser: Getter\u003cUser\u003e,\n```\n\n### Apple Oauth 2\n\nIn order to use it, run `npm install --save passport-apple`.\nFirst, create a AuthUser model implementing the IAuthUser interface. You can implement the interface in the user model itself. See sample below.\n\n```ts\n@model({\n  name: 'users',\n})\nexport class User extends Entity implements IAuthUser {\n  @property({\n    type: 'number',\n    id: true,\n  })\n  id?: number;\n\n  @property({\n    type: 'string',\n    required: true,\n    name: 'first_name',\n  })\n  firstName: string;\n\n  @property({\n    type: 'string',\n    name: 'last_name',\n  })\n  lastName: string;\n\n  @property({\n    type: 'string',\n    name: 'middle_name',\n  })\n  middleName?: string;\n\n  @property({\n    type: 'string',\n    required: true,\n  })\n  username: string;\n\n  @property({\n    type: 'string',\n  })\n  email?: string;\n\n  // Auth provider - 'apple'\n  @property({\n    type: 'string',\n    required: true,\n    name: 'auth_provider',\n  })\n  authProvider: string;\n\n  // Id from external provider\n  @property({\n    type: 'string',\n    name: 'auth_id',\n  })\n  authId?: string;\n\n  @property({\n    type: 'string',\n    name: 'auth_token',\n  })\n  authToken?: string;\n\n  @property({\n    type: 'string',\n  })\n  password?: string;\n\n  constructor(data?: Partial\u003cUser\u003e) {\n    super(data);\n  }\n}\n```\n\nNow bind this model to USER_MODEL key in application.ts\n\n```ts\nthis.bind(AuthenticationBindings.USER_MODEL).to(User);\n```\n\nCreate CRUD repository for the above model. Use loopback CLI.\n\n```sh\nlb4 repository\n```\n\nAdd the verifier function for the strategy. You need to create a provider for the same. You can add your application specific business logic for client auth here. Here is a simple example.\n\n```ts\nimport {Provider} from '@loopback/context';\nimport {repository} from '@loopback/repository';\nimport {HttpErrors} from '@loopback/rest';\nimport {AuthErrorKeys, VerifyFunction} from 'loopback4-authentication';\n\nimport {Tenant} from '../../../models';\nimport {UserCredentialsRepository, UserRepository} from '../../../repositories';\nimport {AuthUser} from '../models/auth-user.model';\n\nexport class AppleOauth2VerifyProvider\n  implements Provider\u003cVerifyFunction.AppleAuthFn\u003e\n{\n  constructor(\n    @repository(UserRepository)\n    public userRepository: UserRepository,\n    @repository(UserCredentialsRepository)\n    public userCredsRepository: UserCredentialsRepository,\n  ) {}\n\n  value(): VerifyFunction.AppleAuthFn {\n    return async (accessToken, refreshToken, profile) =\u003e {\n      const user = await this.userRepository.findOne({\n        where: {\n          /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n          email: (profile as any)._json.email,\n        },\n      });\n      if (!user) {\n        throw new HttpErrors.Unauthorized(AuthErrorKeys.InvalidCredentials);\n      }\n      if (\n        !user ||\n        user.authProvider !== 'apple' ||\n        user.authId !== profile.id\n      ) {\n        throw new HttpErrors.Unauthorized(AuthErrorKeys.InvalidCredentials);\n      }\n\n      const authUser: AuthUser = new AuthUser(user);\n      authUser.permissions = [];\n      authUser.externalAuthToken = accessToken;\n      authUser.externalRefreshToken = refreshToken;\n      authUser.tenant = new Tenant({id: user.defaultTenant});\n      return authUser;\n    };\n  }\n}\n```\n\nPlease note the Verify function type _VerifyFunction.LocalPasswordFn_\n\nNow bind this provider to the application in application.ts.\n\n```ts\nimport {AuthenticationComponent, Strategies} from 'loopback4-authentication';\n```\n\n```ts\n// Add authentication component\nthis.component(AuthenticationComponent);\n// Customize authentication verify handlers\nthis.bind(Strategies.Passport.APPLE_OAUTH2_VERIFIER).toProvider(\n  AppleOauth2VerifyProvider,\n);\n```\n\nFinally, add the authenticate function as a sequence action to sequence.ts.\n\n```ts\nexport class MySequence implements SequenceHandler {\n  constructor(\n    @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,\n    @inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams,\n    @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,\n    @inject(SequenceActions.SEND) public send: Send,\n    @inject(SequenceActions.REJECT) public reject: Reject,\n    @inject(AuthenticationBindings.USER_AUTH_ACTION)\n    protected authenticateRequest: AuthenticateFn\u003cAuthUser\u003e,\n  ) {}\n\n  async handle(context: RequestContext) {\n    try {\n      const {request, response} = context;\n\n      const route = this.findRoute(request);\n      const args = await this.parseParams(request, route);\n      request.body = args[args.length - 1];\n      const authUser: AuthUser = await this.authenticateRequest(\n        request,\n        response,\n      );\n      const result = await this.invoke(route, args);\n      this.send(response, result);\n    } catch (err) {\n      this.reject(context, err);\n    }\n  }\n}\n```\n\nAfter this, you can use decorator to apply auth to controller functions wherever needed. See below.\n\n```ts\n@authenticateClient(STRATEGY.CLIENT_PASSWORD)\n  @authenticate(\n    STRATEGY.APPLE_OAUTH2,\n    {\n      accessType: 'offline',\n      scope: ['name', 'email'],\n      callbackURL: process.env.APPLE_AUTH_CALLBACK_URL,\n      clientID: process.env.APPLE_AUTH_CLIENT_ID,\n      teamID: process.env.APPLE_AUTH_TEAM_ID,\n      keyID: process.env.APPLE_AUTH_KEY_ID,\n      privateKeyLocation: process.env.APPLE_AUTH_PRIVATE_KEY_LOCATION,\n    },\n    (req: Request) =\u003e {\n      return {\n        accessType: 'offline',\n        state: Object.keys(req.query)\n          .map(key =\u003e key + '=' + req.query[key])\n          .join('\u0026'),\n      };\n    },\n  )\n  @authorize(['*'])\n  @get('/auth/oauth-apple', {\n    responses: {\n      [STATUS_CODE.OK]: {\n        description: 'Token Response',\n        content: {\n          [CONTENT_TYPE.JSON]: {\n            schema: {'x-ts-type': TokenResponse},\n          },\n        },\n      },\n    },\n  })\n  async loginViaApple(\n    @param.query.string('client_id')\n    clientId?: string,\n    @param.query.string('client_secret')\n    clientSecret?: string,\n  ): Promise\u003cvoid\u003e {}\n\n  @authenticate(\n    STRATEGY.APPLE_OAUTH2,\n    {\n      accessType: 'offline',\n      scope: ['name', 'email'],\n      callbackURL: process.env.APPLE_AUTH_CALLBACK_URL,\n      clientID: process.env.APPLE_AUTH_CLIENT_ID,\n      teamID: process.env.APPLE_AUTH_TEAM_ID,\n      keyID: process.env.APPLE_AUTH_KEY_ID,\n      privateKeyLocation: process.env.APPLE_AUTH_PRIVATE_KEY_LOCATION,\n    },\n    (req: Request) =\u003e {\n      return {\n        accessType: 'offline',\n        state: Object.keys(req.query)\n          .map(key =\u003e `${key}=${req.query[key]}`)\n          .join('\u0026'),\n      };\n    },\n  )\n  @authorize(['*'])\n  @get('/auth/apple-oauth-redirect', {\n    responses: {\n      [STATUS_CODE.OK]: {\n        description: 'Token Response',\n        content: {\n          [CONTENT_TYPE.JSON]: {\n            schema: {'x-ts-type': TokenResponse},\n          },\n        },\n      },\n    },\n  })\n  async appleCallback(\n    @param.query.string('code') code: string,\n    @param.query.string('state') state: string,\n    @inject(RestBindings.Http.RESPONSE) response: Response,\n  ): Promise\u003cvoid\u003e {\n    const clientId = new URLSearchParams(state).get('client_id');\n    if (!clientId || !this.user) {\n      throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);\n    }\n    const client = await this.authClientRepository.findOne({\n      where: {\n        clientId: clientId,\n      },\n    });\n    if (!client || !client.redirectUrl) {\n      throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);\n    }\n    try {\n      const codePayload: ClientAuthCode\u003cUser\u003e = {\n        clientId,\n        user: this.user,\n      };\n      const token = jwt.sign(codePayload, client.secret, {\n        expiresIn: client.authCodeExpiration,\n        audience: clientId,\n        subject: this.user.username,\n        issuer: process.env.JWT_ISSUER,\n      });\n      response.redirect(`${client.redirectUrl}?code=${token}`);\n    } catch (error) {\n      throw new HttpErrors.InternalServerError(AuthErrorKeys.UnknownError);\n    }\n  }\n```\n\nPlease note above that we are creating two new APIs for apple auth. The first one is for UI clients to hit. We are authenticating client as well, then passing the details to the apple auth. Then, the actual authentication is done by apple authorization url, which redirects to the second API we created after success. The first API method body is empty as we do not need to handle its response. The apple auth provider in this package will do the redirection for you automatically.\n\nFor accessing the authenticated AuthUser model reference, you can inject the CURRENT_USER provider, provided by the extension, which is populated by the auth action sequence above.\n\n```ts\n  @inject.getter(AuthenticationBindings.CURRENT_USER)\n  private readonly getCurrentUser: Getter\u003cUser\u003e,\n```\n\n### Facebook Oauth 2\n\nIn order to use it, run `npm install passport-facebook`.\nFirst, create a AuthUser model implementing the IAuthUser interface. You can implement the interface in the user model itself. See sample below.\n\n```ts\n@model({\n  name: 'users',\n})\nexport class User extends Entity implements IAuthUser {\n  @property({\n    type: 'number',\n    id: true,\n  })\n  id?: number;\n\n  @property({\n    type: 'string',\n    required: true,\n    name: 'first_name',\n  })\n  firstName: string;\n\n  @property({\n    type: 'string',\n    name: 'last_name',\n  })\n  lastName: string;\n\n  @property({\n    type: 'string',\n    name: 'middle_name',\n  })\n  middleName?: string;\n\n  @property({\n    type: 'string',\n    required: true,\n  })\n  username: string;\n\n  @property({\n    type: 'string',\n  })\n  email?: string;\n\n  // Auth provider - 'facebook'\n  @property({\n    type: 'string',\n    required: true,\n    name: 'auth_provider',\n  })\n  authProvider: string;\n\n  // Id from external provider\n  @property({\n    type: 'string',\n    name: 'auth_id',\n  })\n  authId?: string;\n\n  @property({\n    type: 'string',\n    name: 'auth_token',\n  })\n  authToken?: string;\n\n  @property({\n    type: 'string',\n  })\n  password?: string;\n\n  constructor(data?: Partial\u003cUser\u003e) {\n    super(data);\n  }\n}\n```\n\nNow bind this model to USER_MODEL key in application.ts\n\n```ts\nthis.bind(AuthenticationBindings.USER_MODEL).to(User);\n```\n\nCreate CRUD repository for the above model. Use loopback CLI.\n\n```sh\nlb4 repository\n```\n\nAdd the verifier function for the strategy. You need to create a provider for the same. You can add your application specific business logic for client auth here. Here is a simple example.\n\n```ts\nimport {Provider} from '@loopback/context';\nimport {repository} from '@loopback/repository';\nimport {HttpErrors} from '@loopback/rest';\nimport {AuthErrorKeys, VerifyFunction} from 'loopback4-authentication';\n\nimport {Tenant} from '../../../models';\nimport {UserCredentialsRepository, UserRepository} from '../../../repositories';\nimport {AuthUser} from '../models/auth-user.model';\n\nexport class FacebookOauth2VerifyProvider\n  implements Provider\u003cVerifyFunction.FacebookAuthFn\u003e\n{\n  constructor(\n    @repository(UserRepository)\n    public userRepository: UserRepository,\n    @repository(UserCredentialsRepository)\n    public userCredsRepository: UserCredentialsRepository,\n  ) {}\n\n  value(): VerifyFunction.FacebookAuthFn {\n    return async (accessToken, refreshToken, profile) =\u003e {\n      const user = await this.userRepository.findOne({\n        where: {\n          /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n          email: (profile as any)._json.email,\n        },\n      });\n      if (!user) {\n        throw new HttpErrors.Unauthorized(AuthErrorKeys.InvalidCredentials);\n      }\n      if (\n        !user ||\n        user.authProvider !== 'facebook' ||\n        user.authId !== profile.id\n      ) {\n        throw new HttpErrors.Unauthorized(AuthErrorKeys.InvalidCredentials);\n      }\n\n      const authUser: AuthUser = new AuthUser(user);\n      authUser.permissions = [];\n      authUser.externalAuthToken = accessToken;\n      authUser.externalRefreshToken = refreshToken;\n      authUser.tenant = new Tenant({id: user.defaultTenant});\n      return authUser;\n    };\n  }\n}\n```\n\nPlease note the Verify function type _VerifyFunction.LocalPasswordFn_\n\nNow bind this provider to the application in application.ts.\n\n```ts\nimport {AuthenticationComponent, Strategies} from 'loopback4-authentication';\n```\n\n```ts\n// Add authentication component\nthis.component(AuthenticationComponent);\n// Customize authentication verify handlers\nthis.bind(Strategies.Passport.FACEBOOK_OAUTH2_VERIFIER).toProvider(\n  FacebookOauth2VerifyProvider,\n);\n```\n\nFinally, add the authenticate function as a sequence action to sequence.ts.\n\n```ts\nexport class MySequence implements SequenceHandler {\n  constructor(\n    @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,\n    @inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams,\n    @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,\n    @inject(SequenceActions.SEND) public send: Send,\n    @inject(SequenceActions.REJECT) public reject: Reject,\n    @inject(AuthenticationBindings.USER_AUTH_ACTION)\n    protected authenticateRequest: AuthenticateFn\u003cAuthUser\u003e,\n  ) {}\n\n  async handle(context: RequestContext) {\n    try {\n      const {request, response} = context;\n\n      const route = this.findRoute(request);\n      const args = await this.parseParams(request, route);\n      request.body = args[args.length - 1];\n      const authUser: AuthUser = await this.authenticateRequest(\n        request,\n        response,\n      );\n      const result = await this.invoke(route, args);\n      this.send(response, result);\n    } catch (err) {\n      this.reject(context, err);\n    }\n  }\n}\n```\n\nAfter this, you can use decorator to apply auth to controller functions wherever needed. See below.\n\n```ts\n@authenticateClient(STRATEGY.CLIENT_PASSWORD)\n  @authenticate(\n    STRATEGY.FACEBOOK_OAUTH2,\n    {\n      accessType: 'offline',\n      authorizationURL: process.env.FACEBOOK_AUTH_URL,\n      callbackURL: process.env.FACEBOOK_AUTH_CALLBACK_URL,\n      clientID: process.env.FACEBOOK_AUTH_CLIENT_ID,\n      clientSecret: process.env.FACEBOOK_AUTH_CLIENT_SECRET,\n      tokenURL: process.env.FACEBOOK_AUTH_TOKEN_URL,\n    },\n    (req: Request) =\u003e {\n      return {\n        accessType: 'offline',\n        state: Object.keys(req.query)\n          .map(key =\u003e key + '=' + req.query[key])\n          .join('\u0026'),\n      };\n    },\n  )\n  @authorize(['*'])\n  @get('/auth/facebook', {\n    responses: {\n      [STATUS_CODE.OK]: {\n        description: 'Token Response',\n        content: {\n          [CONTENT_TYPE.JSON]: {\n            schema: {'x-ts-type': TokenResponse},\n          },\n        },\n      },\n    },\n  })\n  async loginViaFacebook(\n    @param.query.string('client_id')\n    clientId?: string,\n    @param.query.string('client_secret')\n    clientSecret?: string,\n  ): Promise\u003cvoid\u003e {}\n\n  @authenticate(\n    STRATEGY.FACEBOOK_OAUTH2,\n    {\n      accessType: 'offline',\n      authorizationURL: process.env.FACEBOOK_AUTH_URL,\n      callbackURL: process.env.FACEBOOK_AUTH_CALLBACK_URL,\n      clientID: process.env.FACEBOOK_AUTH_CLIENT_ID,\n      clientSecret: process.env.FACEBOOK_AUTH_CLIENT_SECRET,\n      tokenURL: process.env.FACEBOOK_AUTH_TOKEN_URL,\n    },\n    (req: Request) =\u003e {\n      return {\n        accessType: 'offline',\n        state: Object.keys(req.query)\n          .map(key =\u003e `${key}=${req.query[key]}`)\n          .join('\u0026'),\n      };\n    },\n  )\n  @authorize(['*'])\n  @get('/auth/facebook-auth-redirect', {\n    responses: {\n      [STATUS_CODE.OK]: {\n        description: 'Token Response',\n        content: {\n          [CONTENT_TYPE.JSON]: {\n            schema: {'x-ts-type': TokenResponse},\n          },\n        },\n      },\n    },\n  })\n  async facebookCallback(\n    @param.query.string('code') code: string,\n    @param.query.string('state') state: string,\n    @inject(RestBindings.Http.RESPONSE) response: Response,\n  ): Promise\u003cvoid\u003e {\n    const clientId = new URLSearchParams(state).get('client_id');\n    if (!clientId || !this.user) {\n      throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);\n    }\n    const client = await this.authClientRepository.findOne({\n      where: {\n        clientId: clientId,\n      },\n    });\n    if (!client || !client.redirectUrl) {\n      throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);\n    }\n    try {\n      const codePayload: ClientAuthCode\u003cUser\u003e = {\n        clientId,\n        user: this.user,\n      };\n      const token = jwt.sign(codePayload, client.secret, {\n        expiresIn: client.authCodeExpiration,\n        audience: clientId,\n        subject: this.user.username,\n        issuer: process.env.JWT_ISSUER,\n      });\n      response.redirect(`${client.redirectUrl}?code=${token}`);\n    } catch (error) {\n      throw new HttpErrors.InternalServerError(AuthErrorKeys.UnknownError);\n    }\n  }\n```\n\nPlease note above that we are creating two new APIs for facebook auth. The first one is for UI clients to hit. We are authenticating client as well, then passing the details to the facebook auth. Then, the actual authentication is done by facebook authorization url, which redirects to the second API we created after success. The first API method body is empty as we do not need to handle its response. The facebook auth provider in this package will do the redirection for you automatically.\n\nFor accessing the authenticated AuthUser model reference, you can inject the CURRENT_USER provider, provided by the extension, which is populated by the auth action sequence above.\n\n```ts\n  @inject.getter(AuthenticationBindings.CURRENT_USER)\n  private readonly getCurrentUser: Getter\u003cUser\u003e,\n```\n\n### Keycloak\n\nIn order to use it, run `npm install @exlinc/keycloak-passport`.\nFirst, create a AuthUser model implementing the IAuthUser interface. You can implement the interface in the user model itself. See sample below.\n\n```ts\n@model({\n  name: 'users',\n})\nexport class User extends Entity implements IAuthUser {\n  @property({\n    type: 'number',\n    id: true,\n  })\n  id?: number;\n\n  @property({\n    type: 'string',\n    required: true,\n    name: 'first_name',\n  })\n  firstName: string;\n\n  @property({\n    type: 'string',\n    name: 'last_name',\n  })\n  lastName: string;\n\n  @property({\n    type: 'string',\n    name: 'middle_name',\n  })\n  middleName?: string;\n\n  @property({\n    type: 'string',\n    required: true,\n  })\n  username: string;\n\n  @property({\n    type: 'string',\n  })\n  email?: string;\n\n  // Auth provider - 'keycloak'\n  @property({\n    type: 'string',\n    required: true,\n    name: 'auth_provider',\n  })\n  authProvider: string;\n\n  // Id from external provider\n  @property({\n    type: 'string',\n    name: 'auth_id',\n  })\n  authId?: string;\n\n  @property({\n    type: 'string',\n    name: 'auth_token',\n  })\n  authToken?: string;\n\n  @property({\n    type: 'string',\n  })\n  password?: string;\n\n  constructor(data?: Partial\u003cUser\u003e) {\n    super(data);\n  }\n}\n```\n\nNow bind this model to USER_MODEL key in application.ts\n\n```ts\nthis.bind(AuthenticationBindings.USER_MODEL).to(User);\n```\n\nCreate CRUD repository for the above model. Use loopback CLI.\n\n```sh\nlb4 repository\n```\n\nAdd the verifier function for the strategy. You need to create a provider for the same. You can add your application specific business logic for client auth here. Here is a simple example.\n\n```ts\nimport {Provider, inject} from '@loopback/context';\nimport {repository} from '@loopback/repository';\nimport {HttpErrors} from '@loopback/rest';\nimport {\n  AuthErrorKeys,\n  IAuthUser,\n  VerifyFunction,\n} from 'loopback4-authentication';\n\nimport {UserCredentialsRepository, UserRepository} from '../../../repositories';\nimport {AuthUser} from '../models/auth-user.model';\n\nexport class KeycloakVerifyProvider\n  implements Provider\u003cVerifyFunction.KeycloakAuthFn\u003e\n{\n  constructor(\n    @repository(UserRepository)\n    public userRepository: UserRepository,\n    @repository(UserCredentialsRepository)\n    public userCredsRepository: UserCredentialsRepository,\n  ) {}\n\n  value(): VerifyFunction.KeycloakAuthFn {\n    return async (accessToken, refreshToken, profile) =\u003e {\n      let user: IAuthUser | null = await this.userRepository.findOne({\n        where: {\n          email: profile.email,\n        },\n      });\n      if (!user) {\n        throw new HttpErrors.Unauthorized(AuthErrorKeys.InvalidCredentials);\n      }\n      const creds = await this.userCredsRepository.findOne({\n        where: {\n          userId: user.id as string,\n        },\n      });\n      if (\n        !creds ||\n        creds.authProvider !== 'keycloak' ||\n        creds.authId !== profile.keycloakId\n      ) {\n        throw new HttpErrors.Unauthorized(AuthErrorKeys.InvalidCredentials);\n      }\n\n      const authUser: AuthUser = new AuthUser({\n        ...user,\n        id: user.id as string,\n      });\n      authUser.permissions = [];\n      authUser.externalAuthToken = accessToken;\n      authUser.externalRefreshToken = refreshToken;\n      return authUser;\n    };\n  }\n}\n```\n\nPlease note the Verify function type _VerifyFunction.KeycloakAuthFn_\n\nNow bind this provider to the application in application.ts.\n\n```ts\nimport {AuthenticationComponent, Strategies} from 'loopback4-authentication';\n```\n\n```ts\n// Add authentication component\nthis.component(AuthenticationComponent);\n// Customize authentication verify handlers\nthis.bind(Strategies.Passport.KEYCLOAK_VERIFIER).toProvider(\n  KeycloakVerifyProvider,\n);\n```\n\nFinally, add the authenticate function as a sequence action to sequence.ts.\n\n```ts\nexport class MySequence implements SequenceHandler {\n  /**\n   * Optional invoker for registered middleware in a chain.\n   * To be injected via SequenceActions.INVOKE_MIDDLEWARE.\n   */\n  @inject(SequenceActions.INVOKE_MIDDLEWARE, {optional: true})\n  protected invokeMiddleware: InvokeMiddleware = () =\u003e false;\n\n  constructor(\n    @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,\n    @inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams,\n    @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,\n    @inject(SequenceActions.SEND) public send: Send,\n    @inject(SequenceActions.REJECT) public reject: Reject,\n    @inject(AuthenticationBindings.USER_AUTH_ACTION)\n    protected authenticateRequest: AuthenticateFn\u003cAuthUser\u003e,\n  ) {}\n\n  async handle(context: RequestContext) {\n    try {\n      const {request, response} = context;\n\n      const route = this.findRoute(request);\n      const args = await this.parseParams(request, route);\n      request.body = args[args.length - 1];\n      const authUser: AuthUser = await this.authenticateRequest(\n        request,\n        response,\n      );\n      const result = await this.invoke(route, args);\n      this.send(response, result);\n    } catch (err) {\n      this.reject(context, err);\n    }\n  }\n}\n```\n\nAfter this, you can use decorator to apply auth to controller functions wherever needed. See below.\n\n```ts\n@authenticateClient(STRATEGY.CLIENT_PASSWORD)\n  @authenticate(\n    STRATEGY.KEYCLOAK,\n    {\n      host: process.env.KEYCLOAK_HOST,\n      realm: process.env.KEYCLOAK_REALM, //'Tenant1',\n      clientID: process.env.KEYCLOAK_CLIENT_ID, //'onboarding',\n      clientSecret: process.env.KEYCLOAK_CLIENT_SECRET, //'e607fd75-adc8-4af7-9f03-c9e79a4b8b72',\n      callbackURL: process.env.KEYCLOAK_CALLBACK_URL, //'http://localhost:3001/auth/keycloak-auth-redirect',\n      authorizationURL: `${process.env.KEYCLOAK_HOST}/auth/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/auth`,\n      tokenURL: `${process.env.KEYCLOAK_HOST}/auth/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/token`,\n      userInfoURL: `${process.env.KEYCLOAK_HOST}/auth/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/userinfo`,\n    },\n    keycloakQueryGen,\n  )\n  @authorize({permissions: ['*']})\n  @get('/auth/keycloak', {\n    responses: {\n      [STATUS_CODE.OK]: {\n        description: 'Keycloak Token Response',\n        content: {\n          [CONTENT_TYPE.JSON]: {\n            schema: {'x-ts-type': TokenResponse},\n          },\n        },\n      },\n    },\n  })\n  async loginViaKeycloak(\n    @param.query.string('client_id')\n    clientId?: string,\n    @param.query.string('client_secret')\n    clientSecret?: string,\n  ): Promise\u003cvoid\u003e {}\n\n  @authenticate(\n    STRATEGY.KEYCLOAK,\n    {\n      host: process.env.KEYCLOAK_HOST,\n      realm: process.env.KEYCLOAK_REALM,\n      clientID: process.env.KEYCLOAK_CLIENT_ID,\n      clientSecret: process.env.KEYCLOAK_CLIENT_SECRET,\n      callbackURL: process.env.KEYCLOAK_CALLBACK_URL,\n      authorizationURL: `${process.env.KEYCLOAK_HOST}/auth/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/auth`,\n      tokenURL: `${process.env.KEYCLOAK_HOST}/auth/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/token`,\n      userInfoURL: `${process.env.KEYCLOAK_HOST}/auth/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/userinfo`,\n    },\n    keycloakQueryGen,\n  )\n  @authorize({permissions: ['*']})\n  @get('/auth/keycloak-auth-redirect', {\n    responses: {\n      [STATUS_CODE.OK]: {\n        description: 'Keycloak Redirect Token Response',\n        content: {\n          [CONTENT_TYPE.JSON]: {\n            schema: {'x-ts-type': TokenResponse},\n          },\n        },\n      },\n    },\n  })\n  async keycloakCallback(\n    @param.query.string('code') code: string,\n    @param.query.string('state') state: string,\n    @inject(RestBindings.Http.RESPONSE) response: Response,\n  ): Promise\u003cvoid\u003e {\n    const clientId = new URLSearchParams(state).get('client_id');\n    if (!clientId || !this.user) {\n      throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);\n    }\n    const client = await this.authClientRepository.findOne({\n      where: {\n        clientId,\n      },\n    });\n    if (!client || !client.redirectUrl) {\n      throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);\n    }\n    try {\n      const codePayload: ClientAuthCode\u003cUser, typeof User.prototype.id\u003e = {\n        clientId,\n        user: this.user,\n      };\n      const token = jwt.sign(codePayload, client.secret, {\n        expiresIn: client.authCodeExpiration,\n        audience: clientId,\n        subject: this.user.username,\n        issuer: process.env.JWT_ISSUER,\n      });\n      response.redirect(\n        `${client.redirectUrl}?code=${token}\u0026user=${this.user.username}`,\n      );\n    } catch (error) {\n      this.logger.error(error);\n      throw new HttpErrors.Unauthorized(AuthErrorKeys.InvalidCredentials);\n    }\n  }\n```\n\nPlease note above that we are creating two new APIs for keycloak auth. The first one is for UI clients to hit. We are authenticating client as well, then passing the details to the keycloak auth. Then, the actual authentication is done by keycloak authorization url, which redirects to the second API we created after success. The first API method body is empty as we do not need to handle its response. The keycloak auth provider in this package will do the redirection for you automatically.\n\nFor accessing the authenticated AuthUser model reference, you can inject the CURRENT_USER provider, provided by the extension, which is populated by the auth action sequence above.\n\n```ts\n  @inject.getter(AuthenticationBindings.CURRENT_USER)\n  private readonly getCurrentUser: Getter\u003cUser\u003e,\n```\n\n### Custom Verifier for Individual Routes\n\nFor providing a custom verifier for a particular route, you can pass a binding key for a verifier provider as the fourth parameter of the authenticate decorator.\nNote - The key VerifyBindings.BEARER_SIGNUP_VERIFY_PROVIDER can be any custom key, it just be bound to a verify function provider.\n\n```ts\n  @authenticate(\n    STRATEGY.BEARER,\n    undefined, //options\n    undefined, //authOptions\n    VerifyBindings.BEARER_SIGNUP_VERIFY_PROVIDER,\n  )\n```\n\nAnd binding this key to a verifier in the application.ts\n\n```ts\nthis.bind(VerifyBindings.BEARER_SIGNUP_VERIFY_PROVIDER).toProvider(\n  LocalPreSignupProvider as Constructor\u003cProvider\u003cPreSignupFn\u003e\u003e,\n);\n```\n\n### SAML\n\nIn order to use it, run `npm install @node-saml/passport-saml`.\nSAML (Security Assertion Markup Language) is an XML-based standard for exchanging authentication and authorization data between parties, in particular, between an identity provider (IdP) and a service provider (SP).\n\nFirst, create a AuthUser model implementing the IAuthUser interface. You can implement the interface in the user model itself. See sample below.\n\n```ts\n@model({\n  name: 'users',\n})\nexport class User extends Entity implements IAuthUser {\n  @property({\n    type: 'number',\n    id: true,\n  })\n  id?: number;\n  @property({\n    type: 'string',\n    required: true,\n    name: 'first_name',\n  })\n  firstName: string;\n  @property({\n    type: 'string',\n    name: 'last_name',\n  })\n  lastName: string;\n  @property({\n    type: 'string',\n    name: 'middle_name',\n  })\n  middleName?: string;\n  @property({\n    type: 'string',\n    required: true,\n  })\n  username: string;\n  @property({\n    type: 'string',\n  })\n  email?: string;\n  // Auth provider - 'SAML'\n  @property({\n    type: 'string',\n    required: true,\n    name: 'auth_provider',\n  })\n  authProvider: string;\n  // Id from external provider\n  @property({\n    type: 'string',\n    name: 'auth_id',\n  })\n  authId?: string;\n  @property({\n    type: 'string',\n    name: 'auth_token',\n  })\n  authToken?: string;\n  @property({\n    type: 'string',\n  })\n  password?: string;\n  constructor(data?: Partial\u003cUser\u003e) {\n    super(data);\n  }\n}\n```\n\nNow bind this model to USER_MODEL key in application.ts\n\n```ts\nthis.bind(AuthenticationBindings.USER_MODEL).to(User);\n```\n\nCreate CRUD repository for the above model. Use loopback CLI.\n\n```sh\nlb4 repository\n```\n\nAdd the verifier function for the strategy. You need to create a provider for the same. You can add your application specific business logic for client auth here. Here is a simple example.\n\n```ts\nimport {Provider} from '@loopback/context';\nimport {repository} from '@loopback/repository';\nimport {HttpErrors} from '@loopback/rest';\nimport {AuthErrorKeys, VerifyFunction} from 'loopback4-authentication';\nimport {Tenant} from '../../../models';\nimport {UserCredentialsRepository, UserRepository} from '../../../repositories';\nimport {AuthUser} from '../models/auth-user.model';\nexport class SamlVerifyProvider implements Provider\u003cVerifyFunction.SamlFn\u003e {\n  constructor(\n    @repository(UserRepository)\n    public userRepository: UserRepository,\n    @repository(UserCredentialsRepository)\n    public userCredsRepository: UserCredentialsRepository,\n  ) {}\n  value(): VerifyFunction.SamlFn {\n    return async profile =\u003e {\n      const user = await this.userRepository.findOne({\n        where: {\n          /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n          email: (profile as any)._json.email,\n        },\n      });\n      if (!user) {\n        throw new HttpErrors.Unauthorized(AuthErrorKeys.InvalidCredentials);\n      }\n      if (!user || user.authProvider !== 'saml' || user.authId !== profile.id) {\n        throw new HttpErrors.Unauthorized(AuthErrorKeys.InvalidCredentials);\n      }\n      const authUser: AuthUser = new AuthUser({\n        ...user,\n        id: user.id as string,\n      });\n      authUser.permissions = [];\n      return this.postVerifyProvider(profile, authUser);\n    };\n  }\n}\n```\n\nPlease note the Verify function type _VerifyFunction.LocalPasswordFn_\n\nNow bind this provider to the application in application.ts.\n\n```ts\nimport {AuthenticationComponent, Strategies} from 'loopback4-authentication';\n```\n\n```ts\n// Add authentication component\nthis.component(AuthenticationComponent);\n// Customize authentication verify handlers\nthis.bind(Strategies.Passport.SAML_VERIFIER).toProvider(SamlVerifyProvider);\n```\n\nFinally, add the authenticate function as a sequence action to sequence.ts.\n\n```ts\nexport class MySequence implements SequenceHandler {\n  constructor(\n    @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,\n    @inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams,\n    @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,\n    @inject(SequenceActions.SEND) public send: Send,\n    @inject(SequenceActions.REJECT) public reject: Reject,\n    @inject(AuthenticationBindings.USER_AUTH_ACTION)\n    protected authenticateRequest: AuthenticateFn\u003cAuthUser\u003e,\n  ) {}\n  async handle(context: RequestContext) {\n    try {\n      const {request, response} = context;\n      const route = this.findRoute(request);\n      const args = await this.parseParams(request, route);\n      request.body = args[args.length - 1];\n      const authUser: AuthUser = await this.authenticateRequest(\n        request,\n        response,\n      );\n      const result = await this.invoke(route, args);\n      this.send(response, result);\n    } catch (err) {\n      this.reject(context, err);\n    }\n  }\n}\n```\n\nAfter this, you can use decorator to apply auth to controller functions wherever needed. See below.\n\n```ts\n  @authenticateClient(STRATEGY.CLIENT_PASSWORD)\n  @authenticate(\n    STRATEGY.SAML,\n    {\n    accessType: 'offline',\n    scope: ['profile', 'email'],\n    callbackURL: process.env.SAML_CALLBACK_URL,\n    issuer: process.env.SAML_ISSUER,\n    cert: process.env.SAML_CERT,\n    entryPoint: process.env.SAML_ENTRY_POINT,\n    audience: process.env.SAML_AUDIENCE,\n    logoutUrl: process.env.SAML_LOGOUT_URL,\n    passReqToCallback: !!+(process.env.SAML_AUTH_PASS_REQ_CALLBACK ?? 0),\n    validateInResponseTo: !!+(process.env.VALIDATE_RESPONSE ?? 1),\n    idpIssuer: process.env.IDP_ISSUER,\n    logoutCallbackUrl: process.env.SAML_LOGOUT_CALLBACK_URL,\n    }\n  )\n  @authorize({permissions: ['*']})\n  @post('/auth/saml', {\n    description: 'POST Call for saml based login',\n    responses: {\n      [STATUS_CODE.OK]: {\n        description: 'Saml Token Response',\n        content: {\n          [CONTENT_TYPE.JSON]: {\n            schema: {[X_TS_TYPE]: TokenResponse},\n          },\n        },\n      },\n    },\n  })\n  async postLoginViaSaml(\n    @requestBody({\n      content: {\n        [CONTENT_TYPE.FORM_URLENCODED]: {\n          schema: getModelSchemaRef(ClientAuthRequest),\n        },\n      },\n    })\n    clientCreds?: ClientAuthRequest, //NOSONAR\n  ): Promise\u003cvoid\u003e {\n    //do nothing\n  }\n  @authenticate(\n    STRATEGY.SAML,\n    {\n    accessType: 'offline',\n    scope: ['profile', 'email'],\n    callbackURL: process.env.SAML_CALLBACK_URL,\n    issuer: process.env.SAML_ISSUER,\n    cert: process.env.SAML_CERT,\n    entryPoint: process.env.SAML_ENTRY_POINT,\n    audience: process.env.SAML_AUDIENCE,\n    logoutUrl: process.env.SAML_LOGOUT_URL,\n    passReqToCallback: !!+(process.env.SAML_AUTH_PASS_REQ_CALLBACK ?? 0),\n    validateInResponseTo: !!+(process.env.VALIDATE_RESPONSE ?? 1),\n    idpIssuer: process.env.IDP_ISSUER,\n    logoutCallbackUrl: process.env.SAML_LOGOUT_CALLBACK_URL,\n    }\n  )\n  @authorize({permissions: ['*']})\n  @post(`/auth/saml-redirect`, {\n    responses: {\n      [STATUS_CODE.OK]: {\n        description: 'Okta SAML callback',\n        content: {\n          [CONTENT_TYPE.JSON]: {\n            schema: {[X_TS_TYPE]: TokenResponse},\n          },\n        },\n      },\n    },\n  })\n  async oktaSamlCallback(\n    @inject(AuthenticationBindings.CURRENT_USER)\n    user: AuthUser | undefined,\n    @inject(RestBindings.Http.REQUEST) request: Request,\n    @param.query.string('client') clientId: string,\n    @inject(RestBindings.Http.RESPONSE) response: Response,\n    @requestBody({\n      content: {\n        [CONTENT_TYPE.FORM_URLENCODED]: {},\n      },\n    })\n    oktaData: AnyObject,\n  ): Promise\u003cvoid\u003e {\n    if (!clientId || !user) {\n      throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);\n    }\n    const client = await this.authClientRepository.findOne({\n      where: {\n        clientId,\n      },\n    });\n    if (!client?.redirectUrl) {\n      throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);\n    }\n    try {\n      const token = await this.getAuthCode(client, user);\n      response.redirect(`${client.redirectUrl}?code=${token}`);\n    } catch (error) {\n      this.logger.error(error);\n      throw new HttpErrors.Unauthorized(AuthErrorKeys.InvalidCredentials);\n    }\n  }\n```\n\nPlease note above that we are creating two new APIs for SAML. The first one is for UI clients to hit. We are authenticating client as well, then passing the details to the SAML. Then, the actual authentication is done by SAML authorization url, which redirects to the second API we created after success. The first API method body is empty as we do not need to handle its response. The SAML provider in this package will do the redirection for you automatically.\n\nNote: For `auth/saml-redirect` one needs to configure the SSO path by incorporating the client ID as a query parameter in existing application set up within your Okta environment for which you intend to enable SSO as follows:\n\n```\nhttp://localhost:3000/auth/saml-redirect?client=YOUR_CLIENT_ID\n\n```\n\nFor accessing the authenticated AuthUser model reference, you can inject the CURRENT_USER provider, provided by the extension, which is populated by the auth action sequence above.\n\n```ts\n  @inject.getter(AuthenticationBindings.CURRENT_USER)\n  private readonly getCurrentUser: Getter\u003cUser\u003e,\n```\n\nThe `logoutVerify` function is used in the node-saml library as a part of the Passport SAML authentication process. This function is used to verify the authenticity of a SAML logout request.\nThe logout process in SAML is used to end the user's session on the service provider, and the logoutVerify function is used to verify that the logout request is coming from a trusted IdP.\nThe implementation of the logoutVerify function may vary depending on the specific requirements and the security constraints of the application. It is typically used to verify the signature on the logout request, to ensure that the request has not been tampered with, and to extract the user's identity information from the request.\n\n```ts\nfunction logoutVerify(\n  req: Request\u003cAnyObject, AnyObject, AnyObject\u003e,\n  profile: Profile | null,\n  done: VerifiedCallback,\n): void {\n  // Check if a user is currently authenticated\n  if (req.isAuthenticated()) {\n    // Log the user out by removing their session data\n    req.logout(done);\n    // Call the \"done\" callback to indicate success\n    done(null, {message: 'User successfully logged out'});\n  } else {\n    // Call the \"done\" callback with an error to indicate that the user is not logged in\n    done(new Error('User is not currently logged in'));\n  }\n}\n```\n\nThis function is called when a user logs out of the application.Once this function is implemented,it will be called when the user logs out of the application,allowing the application to perform any necessary tasks before ending the user's session.\n@param req - The request object.\n@param {Profile | null} profile - The user's profile, as returned by the provider.\n@param {VerifiedCallback} done - A callback to be called when the verificationis complete.\n\n### Https proxy support for keycloak and google auth\n\nIf a https proxy agent is needed for keycloak and google auth, just add an environment variable named `HTTPS_PROXY` or `https_proxy` with proxy url as value. It will add that proxy agent to the request.\n\n## Middleware Sequence Support\n\nAs action based sequence will be deprecated soon, we have provided support for middleware based sequences. If you are using middleware sequence you can add authentication to your application by enabling client or user authentication middleware. This can be done by binding the AuthenticationBindings.CONFIG :\n\n```ts\nthis.bind(AuthenticationBindings.CONFIG).to({\n  useClientAuthenticationMiddleware: true,\n  useUserAuthenticationMiddleware: true,\n});\n\nthis.component(AuthenticationComponent);\n```\n\nThis binding needs to be done before adding the Authentication component to your application.\nApart from this all other steps for authentication for all strategies remain the same.\n\n### Custom Sequence Support\n\nYou can also configure `ClientAuthenticationMiddlewareProvider` and `UserAuthenticationMiddlewareProvider` options, which can be invoked using a custom sequence. See the sample below.\n\n`custom-sequence.ts`\n\n```ts title=\"custom-sequence.ts\"\nexport class CustomSequence implements SequenceHandler {\n  @inject(SequenceActions.INVOKE_MIDDLEWARE, {optional: true})\n  protected invokeMiddleware: InvokeMiddleware = () =\u003e false;\n  ...\n\n  async handle(context: RequestContext) {\n    ...\n    ...\n    // call custom registered middlewares in the pre-invoke chain\n    let finished = await this.invokeMiddleware(context, {\n      chain: CustomMiddlewareChain.PRE_INVOKE,\n    });\n      if (finished) return;\n      const result = await this.invoke(route, args);\n      this.send(response, result);\n   ...\n  }\n}\n```\n\n`application.ts`\n\n```ts title=\"application.ts\"\nimport {ClientAuthenticationMiddlewareProvider} from 'loopback4-authentication';\n...\n...\n\n// bind middleware with custom options\nthis.middleware(ClientAuthenticationMiddlewareProvider, {\n  chain: CustomMiddlwareChain.PRE_INVOKE\n});\n\n```\n\n### Passport Auth0\n\nIn order to use it, run `npm install passport-auth0` and `npm install @types/passport-auth0`.\nFirst, create a AuthUser model implementing the IAuthUser interface. You can implement the interface in the user model itself. See sample below.\n\n```ts\n@model({\n  name: 'users',\n})\nexport class User extends Entity implements IAuthUser {\n  @property({\n    type: 'number',\n    id: true,\n  })\n  id?: number;\n\n  @property({\n    type: 'string',\n    required: true,\n    name: 'first_name',\n  })\n  firstName: string;\n\n  @property({\n    type: 'string',\n    name: 'last_name',\n  })\n  lastName: string;\n\n  @property({\n    type: 'string',\n    name: 'middle_name',\n  })\n  middleName?: string;\n\n  @property({\n    type: 'string',\n    required: true,\n  })\n  username: string;\n\n  @property({\n    type: 'string',\n  })\n  email?: string;\n\n  // Auth provider - 'auth0'\n  @property({\n    type: 'string',\n    required: true,\n    name: 'auth_provider',\n  })\n  authProvider: string;\n\n  // Id from external provider\n  @property({\n    type: 'string',\n    name: 'auth_id',\n  })\n  authId?: string;\n\n  @property({\n    type: 'string',\n    name: 'auth_token',\n  })\n  authToken?: string;\n\n  @property({\n    type: 'string',\n  })\n  password?: string;\n\n  constructor(data?: Partial\u003cUser\u003e) {\n    super(data);\n  }\n}\n```\n\nNow bind this model to USER_MODEL key in application.ts\n\n```ts\nthis.bind(AuthenticationBindings.USER_MODEL).to(User);\n```\n\nCreate CRUD repository for the above model. Use loopback CLI.\n\n```sh\nlb4 repository\n```\n\nAdd the verifier function for the strategy. You need to create a provider for the same. You can add your application specific business logic for client auth here. Here is a simple example.\n\n```ts\nimport {Provider, inject} from '@loopback/context';\nimport {repository} from '@loopback/repository';\nimport {HttpErrors} from '@loopback/rest';\nimport {\n  AuthErrorKeys,\n  IAuthUser,\n  VerifyFunction,\n} from 'loopback4-authentication';\nimport * as Auth0Strategy from 'passport-auth0';\nimport {\n  Auth0PostVerifyFn,\n  Auth0PreVerifyFn,\n  Auth0SignUpFn,\n} from '../../../providers';\nimport {SignUpBindings, VerifyBindings} from '../../../providers/keys';\nimport {UserCredentialsRepository, UserRepository} from '../../../repositories';\nimport {AuthUser} from '../models';\n\nexport class Auth0VerifyProvider implements Provider\u003cVerifyFunction.Auth0Fn\u003e {\n  constructor(\n    @repository(UserRepository)\n    public userRepository: UserRepository,\n    @repository(UserCredentialsRepository)\n    public userCredsRepository: UserCredentialsRepository,\n  ) {}\n\n  value(): VerifyFunction.Auth0Fn {\n    return async (\n      accessToken: string,\n      refreshToken: string,\n      profile: Auth0Strategy.Profile,\n    ) =\u003e {\n      let user: IAuthUser | null = await this.userRepository.findOne({\n        where: {\n          email: profile._json.email,\n        },\n      });\n\n      if (!user) {\n          throw new HttpErrors.Unauthorized(AuthErrorKeys.InvalidCredentials);\n        }\n      }\n      const creds = await this.userCredsRepository.findOne({\n        where: {\n          userId: user.id as string,\n        },\n      });\n      if (\n        !user ||\n        user.authProvider !== 'auth0' ||\n        user.authId !== profile.id\n      ) {\n        throw new HttpErrors.Unauthorized(AuthErrorKeys.InvalidCredentials);\n      }\n\n      const authUser: AuthUser = new AuthUser(user);\n      authUser.permissions = [];\n      authUser.externalAuthToken = accessToken;\n      authUser.externalRefreshToken = refreshToken;\n      return this.postVerifyProvider(profile, authUser);\n    };\n  }\n}\n```\n\nPlease note the Verify function type _VerifyFunction.LocalPasswordFn_\n\nNow bind this provider to the application in application.ts.\n\n```ts\nimport {AuthenticationComponent, Strategies} from 'loopback4-authentication';\n```\n\n```ts\n// Add authentication component\nthis.component(AuthenticationComponent);\n// Customize authentication verify handlers\nthis.bind(Strategies.Passport.AUTH0_VERIFIER).toProvider(Auth0VerifyProvider);\n```\n\nNow, bind this provider to the application in application.ts.\n\n```ts\nimport {Auth0StrategyFactoryProvider} from 'loopback4-authentication/passport-auth0';\n```\n\n```ts\nthis.bind(Strategies.Passport.AUTH0_STRATEGY_FACTORY).toProvider(\n  Auth0StrategyFactoryProvider,\n);\n```\n\nFinally, add the authenticate function as a sequence action to sequence.ts.\n\n```ts\nexport class MySequence implements SequenceHandler {\n  constructor(\n    @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,\n    @inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams,\n    @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,\n    @inject(SequenceActions.SEND) public send: Send,\n    @inject(SequenceActions.REJECT) public reject: Reject,\n    @inject(AuthenticationBindings.USER_AUTH_ACTION)\n    protected authenticateRequest: AuthenticateFn\u003cAuthUser\u003e,\n  ) {}\n\n  async handle(context: RequestContext) {\n    try {\n      const {request, response} = context;\n\n      const route = this.findRoute(request);\n      const args = await this.parseParams(request, route);\n      request.body = args[args.length - 1];\n      const authUser: AuthUser = await this.authenticateRequest(\n        request,\n        response,\n      );\n      const result = await this.invoke(route, args);\n      this.send(response, result);\n    } catch (err) {\n      this.reject(context, err);\n    }\n  }\n}\n```\n\nAfter this, you can use decorator to apply auth to controller functions wherever needed. See below.\n\n```ts\n@authenticateClient(STRATEGY.CLIENT_PASSWORD)\n  @authenticate(\n   STRATEGY.AUTH0,\n    {\n      domain: process.env.AUTH0_DOMAIN,\n      clientID: process.env.AUTH0_CLIENT_ID,\n      clientSecret: process.env.AUTH0_CLIENT_SECRET,\n      callbackURL: process.env.AUTH0_CALLBACK_URL,\n      state: false,\n      profileFields: ['email', 'name'],\n      scope: 'openid email profile',\n    },\n    (req: Request) =\u003e {\n      return {\n        accessType: 'offline',\n        state: Object.keys(req.query)\n          .map(key =\u003e key + '=' + req.query[key])\n          .join('\u0026'),\n      };\n    },\n  )\n  @authorize(['*'])\n  @get('/auth/auth0', {\n    responses: {\n      [STATUS_CODE.OK]: {\n        description: 'Token Response',\n        content: {\n          [CONTENT_TYPE.JSON]: {\n            schema: {'x-ts-type': TokenResponse},\n          },\n        },\n      },\n    },\n  })\n  async loginViaAuth0(\n    @param.query.string('client_id')\n    clientId?: string,\n    @param.query.string('client_secret')\n    clientSecret?: string,\n  ): Promise\u003cvoid\u003e {}\n\n  @authenticate(\n    STRATEGY.AUTH0,\n    {\n      domain: process.env.AUTH0_DOMAIN,\n      clientID: process.env.AUTH0_CLIENT_ID,\n      clientSecret: process.env.AUTH0_CLIENT_SECRET,\n      callbackURL: process.env.AUTH0_CALLBACK_URL,\n      state: false,\n      profileFields: ['email', 'name'],\n      scope: 'openid email profile',\n    }\n    (req: Request) =\u003e {\n      return {\n        accessType: 'offline',\n        state: Object.keys(req.query)\n          .map(key =\u003e `${key}=${req.query[key]}`)\n          .join('\u0026'),\n      };\n    },\n  )\n  @authorize(['*'])\n  @get('/auth/auth0-auth-redirect', {\n    responses: {\n      [STATUS_CODE.OK]: {\n        description: 'Token Response',\n        content: {\n          [CONTENT_TYPE.JSON]: {\n            schema: {'x-ts-type': TokenResponse},\n          },\n        },\n      },\n    },\n  })\n  async callback(\n    @param.query.string('code') code: string,\n    @param.query.string('state') state: string,\n    @inject(RestBindings.Http.RESPONSE) response: Response,\n  ): Promise\u003cvoid\u003e {\n    const clientId = new URLSearchParams(state).get('client_id');\n    if (!clientId || !this.user) {\n      throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);\n    }\n    const client = await this.authClientRepository.findOne({\n      where: {\n        clientId: clientId,\n      },\n    });\n    if (!client || !client.redirectUrl) {\n      throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);\n    }\n    try {\n      const codePayload: ClientAuthCode\u003cUser\u003e = {\n        clientId,\n        user: this.user,\n      };\n      const token = jwt.sign(codePayload, client.secret, {\n        expiresIn: client.authCodeExpiration,\n        audience: clientId,\n        subject: this.user.username,\n        issuer: process.env.JWT_ISSUER,\n      });\n      response.redirect(`${client.redirectUrl}?code=${token}`);\n    } catch (error) {\n      throw new HttpErrors.InternalServerError(AuthErrorKeys.UnknownError);\n    }\n  }\n```\n\nPlease note above that we are creating two new APIs for auth0 authentication. The first one is for UI clients to hit. We are authenticating client as well, then passing the details to the auth0. Then, the actual authentication is done by auth0 authorization url, which redirects to the second API we created after success. The first API method body is empty as we do not need to handle its response. The provider in this package will do the redirection for you automatically.\n\nFor accessing the authenticated AuthUser model reference, you can inject the CURRENT_USER provider, provided by the extension, which is populated by the auth action sequence above.\n\n```ts\n  @inject.getter(AuthenticationBindings.CURRENT_USER)\n  private readonly getCurrentUser: Getter\u003cUser\u003e,\n```\n\n## Feedback\n\nIf you've noticed a bug or have a question or have a feature request, [search the issue tracker](https://github.com/sourcefuse/loopback4-authentication/issues) to see if someone else in the community has already created a ticket.\nIf not, go ahead and [make one](https://github.com/sourcefuse/loopback4-authentication/issues/new/choose)!\nAll feature requests are welcome. Implementation time may vary. Feel free to contribute the same, if you can.\nIf you think this extension is useful, please [star](https://help.github.com/en/articles/about-stars) it. Appreciation really helps in keeping this project alive.\n\n## Contributing\n\nPlease read [CONTRIBUTING.md](https://github.com/sourcefuse/loopback4-authentication/blob/master/.github/CONTRIBUTING.md) for details on the process for submitting pull requests to us.\n\n## Code of conduct\n\nCode of conduct guidelines [here](https://github.com/sourcefuse/loopback4-authentication/blob/master/.github/CODE_OF_CONDUCT.md).\n\n## License\n\n[MIT](https://github.com/sourcefuse/loopback4-authentication/blob/master/LICENSE)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsourcefuse%2Floopback4-authentication","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsourcefuse%2Floopback4-authentication","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsourcefuse%2Floopback4-authentication/lists"}