{"id":29943386,"url":"https://github.com/sourcefuse/loopback4-authorization","last_synced_at":"2026-04-16T08:01:31.888Z","repository":{"id":34895452,"uuid":"181644741","full_name":"sourcefuse/loopback4-authorization","owner":"sourcefuse","description":"An Authorization Extension for LoopBack 4 Applications","archived":false,"fork":false,"pushed_at":"2025-05-15T15:19:06.000Z","size":3912,"stargazers_count":45,"open_issues_count":0,"forks_count":10,"subscribers_count":18,"default_branch":"master","last_synced_at":"2025-07-31T00:11:57.186Z","etag":null,"topics":["arcbysf","authorization","loopback-component","loopback-next","loopback4"],"latest_commit_sha":null,"homepage":"https://npmjs.com/package/loopback4-authorization","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:11:42.000Z","updated_at":"2024-12-18T11:04:35.000Z","dependencies_parsed_at":"2024-01-08T10:43:09.947Z","dependency_job_id":"e097b334-62de-4369-93c9-44e353f427a5","html_url":"https://github.com/sourcefuse/loopback4-authorization","commit_stats":{"total_commits":144,"total_committers":24,"mean_commits":6.0,"dds":0.7222222222222222,"last_synced_commit":"0987655bab93118ceed92dbc4640030a40693354"},"previous_names":[],"tags_count":39,"template":false,"template_full_name":null,"purl":"pkg:github/sourcefuse/loopback4-authorization","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sourcefuse%2Floopback4-authorization","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sourcefuse%2Floopback4-authorization/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sourcefuse%2Floopback4-authorization/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sourcefuse%2Floopback4-authorization/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sourcefuse","download_url":"https://codeload.github.com/sourcefuse/loopback4-authorization/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sourcefuse%2Floopback4-authorization/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","authorization","loopback-component","loopback-next","loopback4"],"created_at":"2025-08-03T02:15:14.099Z","updated_at":"2026-04-16T08:01:31.882Z","avatar_url":"https://github.com/sourcefuse.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"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-authorization](https://github.com/sourcefuse/loopback4-authorization)\n\n\u003cp align=\"left\"\u003e\n\u003ca href=\"https://www.npmjs.com/package/loopback4-authorization\"\u003e\n\u003cimg src=\"https://img.shields.io/npm/v/loopback4-authorization.svg?style=flat-square\" alt=\"npm version\" /\u003e\n\u003c/a\u003e\n\u003ca href=\"https://sonarcloud.io/summary/overall?id=sourcefuse_loopback4-authorization\" target=\"_blank\"\u003e\n\u003cimg alt=\"Sonar Quality Gate\" src=\"https://img.shields.io/sonar/quality_gate/sourcefuse_loopback4-authorization?server=https%3A%2F%2Fsonarcloud.io\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://github.com/sourcefuse/loopback4-authorization/graphs/contributors\" target=\"_blank\"\u003e\n\u003cimg alt=\"GitHub contributors\" src=\"https://img.shields.io/github/contributors/sourcefuse/loopback4-authorization\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/loopback4-authorization\" target=\"_blank\"\u003e\n\u003cimg alt=\"downloads\" src=\"https://img.shields.io/npm/dw/loopback4-authorization.svg\"\u003e\n\u003c/a\u003e\n\u003ca href=\"./LICENSE\"\u003e\n\u003cimg src=\"https://img.shields.io/github/license/sourcefuse/loopback4-authorization.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\nA LoopBack 4 extension for Authorization Capabilities. It's very simple to integration yet powerful and effective.\n\n## Install\n\n```sh\nnpm install loopback4-authorization\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 method #3 from the above in a simple multi-tenant application.\n\n## Usage\n\n### Ways of Integration:\n\nOn a higher level, it provides three ways of integration:\n\n#### 1. User Level Permissions Only\n\nWhere permissions are associated directly to user. In this case, each user entry in DB contains specific array of permission keys.\n\n#### 2. Role Based Permissions\n\nWhere permissions are associated to roles and users have a specific role attached. This actually reduces redundancy in DB a lot, as most of the time, users will have many common permissions. If that is not the case for you, then, use the first method.\n\n#### 3. Role Based Permissions with User Level Flexibility\n\nThis is the most flexible architecture. In this case, method #2 is implemented as is.\n\nOn top of it, we also add user-level permissions override, allow/deny permissions over role permissions. So, say there is user who can perform all admin role actions except he cannot remove users from the system. So, DeleteUser permission can be denied at user level and role can be set as Admin for the user.\n\n[Extension enhancement using CASBIN authorisation](#Extension-enhancement-using-CASBIN-authorisation)\n\nRefer to the usage section below for details on integration.\n\nIn order to use this component into your LoopBack application, please follow below steps.\n\n### Steps\n\n#### Bind Component\n\nAdd `AuthorizationComponent` to your application, Like below:\n\n```ts\nthis.bind(AuthorizationBindings.CONFIG).to({\n  allowAlwaysPaths: ['/explorer'],\n});\nthis.component(AuthorizationComponent);\n```\n\n#### Implement Permission Interface\n\nIf using method #1 from above, implement Permissions interface in User model and add permissions array.\n\n```ts\n@model({\n  name: 'users',\n})\nexport class User extends Entity implements Permissions\u003cstring\u003e {\n  // .....\n  // other attributes here\n  // .....\n\n  @property({\n    type: 'array',\n    itemType: 'string',\n  })\n  permissions: string[];\n\n  constructor(data?: Partial\u003cUser\u003e) {\n    super(data);\n  }\n}\n```\n\nIf using method #2 or #3 from above, implement Permissions interface in Role model and add permissions array.\n\n```ts\n@model({\n  name: 'roles',\n})\nexport class Role extends Entity implements Permissions\u003cstring\u003e {\n  // .....\n  // other attributes here\n  // .....\n\n  @property({\n    type: 'array',\n    itemType: 'string',\n  })\n  permissions: string[];\n\n  constructor(data?: Partial\u003cRole\u003e) {\n    super(data);\n  }\n}\n```\n\n#### Implement `UserPermissionsOverride` Interface\n\nIf using method #3 from above, implement UserPermissionsOverride interface in User model and add user level permissions array as below.\nDo this if there is a use-case of explicit allow/deny of permissions at user-level in the application.\nYou can skip otherwise.\n\n```ts\n@model({\n  name: 'users',\n})\nexport class User extends Entity implements UserPermissionsOverride\u003cstring\u003e {\n  // .....\n  // other attributes here\n  // .....\n\n  @property({\n    type: 'array',\n    itemType: 'object',\n  })\n  permissions: UserPermission\u003cstring\u003e[];\n\n  constructor(data?: Partial\u003cUser\u003e) {\n    super(data);\n  }\n}\n```\n\n#### User Permissions Provider\n\nFor method #3, This extension exposes a provider function [AuthorizationBindings.USER_PERMISSIONS](https://github.com/sourcefuse/loopback4-authorization/blob/master/src/providers/user-permissions.provider.ts) to evaluate the user permissions based on its role permissions and user-level overrides.\n\nJust inject it like below:\n\n```ts\n@inject(AuthorizationBindings.USER_PERMISSIONS)\nprivate readonly getUserPermissions: UserPermissionsFn\u003cstring\u003e,\n```\n\nand invoke it\n\n```ts\nconst permissions = this.getUserPermissions(user.permissions, role.permissions);\n```\n\nAdd a step in custom sequence to check for authorization whenever any endpoint is hit.\n\n```ts\nimport {inject} from '@loopback/context';\nimport {\n  FindRoute,\n  HttpErrors,\n  InvokeMethod,\n  ParseParams,\n  Reject,\n  RequestContext,\n  RestBindings,\n  Send,\n  SequenceHandler,\n} from '@loopback/rest';\nimport {AuthenticateFn, AuthenticationBindings} from 'loopback4-authentication';\nimport {\n  AuthorizationBindings,\n  AuthorizeErrorKeys,\n  AuthorizeFn,\n  UserPermissionsFn,\n} from 'loopback4-authorization';\n\nimport {AuthClient} from './models/auth-client.model';\nimport {User} from './models/user.model';\n\nconst SequenceActions = RestBindings.SequenceActions;\n\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    @inject(AuthenticationBindings.CLIENT_AUTH_ACTION)\n    protected authenticateRequestClient: AuthenticateFn\u003cAuthClient\u003e,\n    @inject(AuthorizationBindings.AUTHORIZE_ACTION)\n    protected checkAuthorisation: AuthorizeFn,\n    @inject(AuthorizationBindings.USER_PERMISSIONS)\n    private readonly getUserPermissions: UserPermissionsFn\u003cstring\u003e,\n  ) {}\n\n  async handle(context: RequestContext) {\n    const requestTime = Date.now();\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      await this.authenticateRequestClient(request);\n      const authUser: User = await this.authenticateRequest(request);\n\n      // Do ths if you are using method #3\n      const permissions = this.getUserPermissions(\n        authUser.permissions,\n        authUser.role.permissions,\n      );\n      // This is the important line added for authorization. Needed for all 3 methods\n      const isAccessAllowed: boolean = await this.checkAuthorisation(\n        permissions, // do authUser.permissions if using method #1\n        request,\n      );\n      // Checking access to route here\n      if (!isAccessAllowed) {\n        throw new HttpErrors.Forbidden(AuthorizeErrorKeys.NotAllowedAccess);\n      }\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\nThe above sequence also contains user authentication using [loopback4-authentication](https://github.com/sourcefuse/loopback4-authentication) package. You can refer to the documentation for the same for more details.\n\nNow we can add access permission keys to the controller methods using authorize decorator as below:\n\n```ts\n@authorize(['CreateRole'])\n@post(rolesPath, {\n  responses: {\n    [STATUS_CODE.OK]: {\n      description: 'Role model instance',\n      content: {\n        [CONTENT_TYPE.JSON]: {schema: {'x-ts-type': Role}},\n      },\n    },\n  },\n})\nasync create(@requestBody() role: Role): Promise\u003cRole\u003e {\n  return await this.roleRepository.create(role);\n}\n```\n\nThis endpoint will only be accessible if logged in user has permission\n`CreateRole`.\n\nA good practice is to keep all permission strings in a separate enum file like this.\n\n```ts\nexport const enum PermissionKey {\n  ViewOwnUser = 'ViewOwnUser',\n  ViewAnyUser = 'ViewAnyUser',\n  ViewTenantUser = 'ViewTenantUser',\n  CreateAnyUser = 'CreateAnyUser',\n  CreateTenantUser = 'CreateTenantUser',\n  UpdateOwnUser = 'UpdateOwnUser',\n  UpdateTenantUser = 'UpdateTenantUser',\n  UpdateAnyUser = 'UpdateAnyUser',\n  DeleteTenantUser = 'DeleteTenantUser',\n  DeleteAnyUser = 'DeleteAnyUser',\n\n  ViewTenant = 'ViewTenant',\n  CreateTenant = 'CreateTenant',\n  UpdateTenant = 'UpdateTenant',\n  DeleteTenant = 'DeleteTenant',\n\n  ViewRole = 'ViewRole',\n  CreateRole = 'CreateRole',\n  UpdateRole = 'UpdateRole',\n  DeleteRole = 'DeleteRole',\n\n  ViewAudit = 'ViewAudit',\n  CreateAudit = 'CreateAudit',\n  UpdateAudit = 'UpdateAudit',\n  DeleteAudit = 'DeleteAudit',\n}\n```\n\n### Overriding Permissions\n\nAPI endpoints provided by ARC API (aka Sourceloop) services have their permissions pre-defined in them bundled.\n\nIn order to override them you can bind your custom permissions in the `AuthorizationBindings.PERMISSION` binding key.\nThis accepts an object that should have Controller class name as the root level key and the value of which is another object of method to permissions array mapping.\n\nLike below:\n\n```ts\nthis.bind(AuthorizationBindings.PERMISSION).to({\n   MessageController: {\n      create: ['CreateMessage', 'ViewMessage'],\n      updateAll: ['UpdateMessage', 'ViewMessage', 'ViewMessageNum']\n   }\n   AttachmentFileController: {\n      create: ['CreateAttachmentFile', 'ViewAttachmentFile'],\n      updateAll: ['UpdateAttachmentFile', 'ViewAttachmentFileNum']\n   }\n});\n```\n\nYou can easily check the name of the controller and it's method name from the source code of the services or from the Swagger UI (clicking the endpoint in swagger append the controller and method name in the URL like `LoginController.login` where `login` is the method name).\n\n## Serving the static files:\n\nAuthorization configuration binding sets up paths that can be accessed without any authorization checks, allowing static files to be served directly from the root URL of the application.The allowAlwaysPaths property is used to define these paths for the files in public directory i.e for a test.html file in public directory ,one can provide its path as follows:\n\n```\nthis.bind(AuthorizationBindings.CONFIG).to({\n  allowAlwaysPaths: ['/explorer','/test.html'],\n});\n```\n\nTo set up the public directory as a static,one can add the following in application.ts file.\n\n```\nthis.static('/', path.join(__dirname, '../public'));\n\n```\n\nIf, in case the file is in some other folder then `app.static()` can be called multiple times to configure the app to serve static assets from different directories.\n\n```\nthis.static('/', path.join(__dirname, '../public'));\nthis.static('/downloads', path.join(__dirname, '../downloads'));\n\n```\n\nFor more details,refer [here](https://loopback.io/doc/en/lb4/Serving-static-files.html#:~:text=One%20of%20the%20basic%20requirements,the%20API%20are%20explained%20below.)\n\n## Extension enhancement using CASBIN authorisation\n\nAs a further enhancement to these methods, we are using [casbin library](https://casbin.org/docs/en/overview) to define permissions at level of entity or resource associated with an API call. Casbin authorisation implementation can be performed in two ways:\n\n1. **Using default casbin policy document** - Define policy document in default casbin format in the app, and configure authorise decorator to use those policies.\n2. **Defining custom logic to form dynamic policies** - Implement dynamic permissions based on app logic in casbin-enforcer-config provider. Authorisation extension will dynamically create casbin policy using this business logic to give the authorisation decisions.\n\n### Casbin Usage\n\nIn order to use this enhacement into your LoopBack application, please follow below steps.\n\n- Add providers to implement casbin authorisation along with authorisation component.\n\n```ts\nthis.bind(AuthorizationBindings.CONFIG).to({\n  allowAlwaysPaths: ['/explorer'],\n});\nthis.component(AuthorizationComponent);\n\nthis.bind(AuthorizationBindings.CASBIN_ENFORCER_CONFIG_GETTER).toProvider(\n  CasbinEnforcerConfigProvider,\n);\n\nthis.bind(AuthorizationBindings.CASBIN_RESOURCE_MODIFIER_FN).toProvider(\n  CasbinResValModifierProvider,\n);\n```\n\n- Implement the **Casbin Resource value modifier provider**. Customise the resource value based on business logic using route arguments parameter in the provider.\n\n```ts\nimport {Getter, inject, Provider} from '@loopback/context';\nimport {HttpErrors} from '@loopback/rest';\nimport {\n  AuthorizationBindings,\n  AuthorizationMetadata,\n  CasbinResourceModifierFn,\n} from 'loopback4-authorization';\n\nexport class CasbinResValModifierProvider\n  implements Provider\u003cCasbinResourceModifierFn\u003e\n{\n  constructor(\n    @inject.getter(AuthorizationBindings.METADATA)\n    private readonly getCasbinMetadata: Getter\u003cAuthorizationMetadata\u003e,\n    @inject(AuthorizationBindings.PATHS_TO_ALLOW_ALWAYS)\n    private readonly allowAlwaysPath: string[],\n  ) {}\n\n  value(): CasbinResourceModifierFn {\n    return (pathParams: string[], req: Request) =\u003e this.action(pathParams, req);\n  }\n\n  async action(pathParams: string[], req: Request): Promise\u003cstring\u003e {\n    const metadata: AuthorizationMetadata = await this.getCasbinMetadata();\n\n    if (\n      !metadata \u0026\u0026\n      !!this.allowAlwaysPath.find(path =\u003e req.path.indexOf(path) === 0)\n    ) {\n      return '';\n    }\n\n    if (!metadata) {\n      throw new HttpErrors.InternalServerError(`Metadata object not found`);\n    }\n    const res = metadata.resource;\n\n    // Now modify the resource parameter using on path params, as per business logic.\n    // Returning resource value as such for default case.\n\n    return `${res}`;\n  }\n}\n```\n\n- Implement the **casbin enforcer config provider** . Provide the casbin model path. Model definition can be initialized from [.CONF file, from code, or from a string](https://casbin.org/docs/en/model-storage).\n  In the case of policy creation being handled by extension (isCasbinPolicy parameter is false), provide the array of Resource-Permission objects for a given user, based on business logic.\n  In other case, provide the policy from file or as CSV string or from [casbin Adapters](https://casbin.org/docs/en/adapters).\n  **NOTE**: In the second case, if model is initialized from .CONF file, then any of the above formats can be used for policy. But if model is being initialised from code or string, then policy should be provided as [casbin adapter](https://casbin.org/docs/en/adapters) only.\n\n```ts\nimport {Provider} from '@loopback/context';\nimport {\n  CasbinConfig,\n  CasbinEnforcerConfigGetterFn,\n  IAuthUserWithPermissions,\n} from 'loopback4-authorization';\nimport * as path from 'path';\n\nexport class CasbinEnforcerConfigProvider\n  implements Provider\u003cCasbinEnforcerConfigGetterFn\u003e\n{\n  constructor() {}\n\n  value(): CasbinEnforcerConfigGetterFn {\n    return (\n      authUser: IAuthUserWithPermissions,\n      resource: string,\n      isCasbinPolicy?: boolean,\n    ) =\u003e this.action(authUser, resource, isCasbinPolicy);\n  }\n\n  async action(\n    authUser: IAuthUserWithPermissions,\n    resource: string,\n    isCasbinPolicy?: boolean,\n  ): Promise\u003cCasbinConfig\u003e {\n    const model = path.resolve(__dirname, './../../fixtures/casbin/model.conf'); // Model initialization from file path\n    /**\n     * import * as casbin from 'casbin';\n     *\n     * To initialize model from code, use\n     *      let m = new casbin.Model();\n     *      m.addDef('r', 'r', 'sub, obj, act'); and so on...\n     *\n     * To initialize model from string, use\n     *      const text = `\n     *      [request_definition]\n     *     r = sub, obj, act\n     *\n     *      [policy_definition]\n     *      p = sub, obj, act\n     *\n     *      [policy_effect]\n     *      e = some(where (p.eft == allow))\n     *\n     *      [matchers]\n     *      m = r.sub == p.sub \u0026\u0026 r.obj == p.obj \u0026\u0026 r.act == p.act\n     *       `;\n     *      const model = casbin.newModelFromString(text);\n     */\n\n    // Write business logic to find out the allowed resource-permission sets for this user. Below is a dummy value.\n    //const allowedRes = [{resource: 'session', permission: \"CreateMeetingSession\"}];\n\n    const policy = path.resolve(\n      __dirname,\n      './../../fixtures/casbin/policy.csv',\n    );\n\n    const result: CasbinConfig = {\n      model,\n      //allowedRes,\n      policy,\n    };\n    return result;\n  }\n}\n```\n\n- Add the dependency injections for resource value modifer provider, and casbin authorisation function in the sequence.ts\n\n```ts\n    @inject(AuthorizationBindings.CASBIN_AUTHORIZE_ACTION)\n    protected checkAuthorisation: CasbinAuthorizeFn,\n    @inject(AuthorizationBindings.CASBIN_RESOURCE_MODIFIER_FN)\n    protected casbinResModifierFn: CasbinResourceModifierFn,\n```\n\n- Add a step in custom sequence to check for authorization whenever any end\n  point is hit.\n\n```ts\nimport {inject} from '@loopback/context';\nimport {\n  FindRoute,\n  HttpErrors,\n  InvokeMethod,\n  ParseParams,\n  Reject,\n  RequestContext,\n  RestBindings,\n  Send,\n  SequenceHandler,\n} from '@loopback/rest';\nimport {AuthenticateFn, AuthenticationBindings} from 'loopback4-authentication';\nimport {\n  AuthorizationBindings,\n  AuthorizeErrorKeys,\n  AuthorizeFn,\n  UserPermissionsFn,\n} from 'loopback4-authorization';\n\nimport {AuthClient} from './models/auth-client.model';\nimport {User} from './models/user.model';\n\nconst SequenceActions = RestBindings.SequenceActions;\n\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    @inject(AuthenticationBindings.CLIENT_AUTH_ACTION)\n    protected authenticateRequestClient: AuthenticateFn\u003cAuthClient\u003e,\n    @inject(AuthorizationBindings.CASBIN_AUTHORIZE_ACTION)\n    protected checkAuthorisation: CasbinAuthorizeFn,\n    @inject(AuthorizationBindings.CASBIN_RESOURCE_MODIFIER_FN)\n    protected casbinResModifierFn: CasbinResourceModifierFn,\n  ) {}\n\n  async handle(context: RequestContext) {\n    const requestTime = Date.now();\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      await this.authenticateRequestClient(request);\n      const authUser: User = await this.authenticateRequest(request);\n\n      // Invoke Resource value modifier\n      const resVal = await this.casbinResModifierFn(args);\n\n      // Check authorisation\n      const isAccessAllowed: boolean = await this.checkAuthorisation(\n        authUser,\n        resVal,\n        request,\n      );\n      // Checking access to route here\n      if (!isAccessAllowed) {\n        throw new HttpErrors.Forbidden(AuthorizeErrorKeys.NotAllowedAccess);\n      }\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\n- Now we can add access permission keys to the controller methods using authorize\n  decorator as below. Set isCasbinPolicy parameter to use casbin default policy format. Default is false.\n\n```ts\n@authorize({permissions: ['CreateRole'], resource:'role', isCasbinPolicy: true})\n@post(rolesPath, {\n  responses: {\n    [STATUS_CODE.OK]: {\n      description: 'Role model instance',\n      content: {\n        [CONTENT_TYPE.JSON]: {schema: {'x-ts-type': Role}},\n      },\n    },\n  },\n})\nasync create(@requestBody() role: Role): Promise\u003cRole\u003e {\n  return await this.roleRepository.create(role);\n}\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-authorization/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-authorization/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-authorization/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-authorization/blob/master/.github/CODE_OF_CONDUCT.md).\n\n## License\n\n[MIT](https://github.com/sourcefuse/loopback4-authorization/blob/master/LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsourcefuse%2Floopback4-authorization","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsourcefuse%2Floopback4-authorization","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsourcefuse%2Floopback4-authorization/lists"}