{"id":22802729,"url":"https://github.com/buffolander/express-authorizer","last_synced_at":"2025-03-30T20:17:04.882Z","repository":{"id":143745422,"uuid":"364437274","full_name":"buffolander/express-authorizer","owner":"buffolander","description":"Express middleware handles JWT authentication and role-based authorization","archived":false,"fork":false,"pushed_at":"2021-06-04T03:49:43.000Z","size":617,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-13T09:54:54.898Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/buffolander.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"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}},"created_at":"2021-05-05T02:05:29.000Z","updated_at":"2023-03-05T07:58:59.000Z","dependencies_parsed_at":"2023-03-29T00:48:40.947Z","dependency_job_id":null,"html_url":"https://github.com/buffolander/express-authorizer","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buffolander%2Fexpress-authorizer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buffolander%2Fexpress-authorizer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buffolander%2Fexpress-authorizer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buffolander%2Fexpress-authorizer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/buffolander","download_url":"https://codeload.github.com/buffolander/express-authorizer/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246372746,"owners_count":20766635,"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","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":[],"created_at":"2024-12-12T09:07:08.224Z","updated_at":"2025-03-30T20:17:04.854Z","avatar_url":"https://github.com/buffolander.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Introduction\n\nThis package handles authentication and role-based auhtorization for JSON Web Tokens (JWTs) as middlewares on Express Servers.\n\n# Usage\n\nGet it up and running on your server with a single configuration file, in three easy steps:\n\n1. Add your Authentication parameters\n2. Add your Authorization parameters\n3. Create your policies\n\n```javascript\n// file: gatekeeper.js, your configuration file\nconst { default: ExpressAuthorizer } = require('@brdu/express-authorizer')\n\nconst gatekeeper = new ExpressAuthorizer('EXPRESS')\n\ngatekeeper.set_authentication_params(/* Authentication parameters */)\n\ngatekeeper.set_authorization_params(/* Authorization parameters */)\n\ngatekeeper.add_policy(/* Policy 1 */)\ngatekeeper.add_policy(/* Policy 2 */)\n/* ... */\ngatekeeper.add_policy(/* Policy n */)\n\nmodule.exports = gatekeeper\n```\n\nImport the configuration on your server file and apply the middlewares.\n\n```javascript\n// file: server.js\nconst express = require('express')\nconst gatekeeper = require('./gatekeeper')\n\nconst app = express()\napp.use(gatekeeper.authenticate) // \u003c==\napp.use(gatekeeper.authorize) // \u003c==\n\n/* Declare your routes */\napp.use('*', (req, res) =\u003e res.sendStatus(404))\n\nconst port = process.env.PORT || 8080\napp.listen(port, () =\u003e console.info(`server listening on port ${port}`))\n```\n\nThe ExpressAuthorizer constructor takes a single parameter to initialize a new instance.\n\nIt accepts either one of `EXPRESS` or `API_GATEWAY`.\n\nWhen initialized with the value `EXPRESS`, your server you'll be responsible for authenticating each request.\n\nYou're required to set up athentication parameters on your configuration if you intend to use the `ExpressAuthorizer.authenticate` middleware on your server.\n\nInitializing the ExpressAuthorizer instance with the value `API_GATEWAY` means the request has been previously authenticated by an API Gateway or another service.\n\nAuthentication parameters will be ignored if declared, and using `ExpressAuthorizer.authenticate` middleware on your server will throw an exception.\n\nYou may choose to use only `ExpressAuthorizer.authenticate` if authorization isn't required, as well as only use `ExpressAuthorizer.authorize` if requests have been previously authenticated before received by your server.\n\n# Setting up Authentication Parameters\n\nThis method sets up all parameters required by the `ExpressAuthorizer.authenticate` middleware.\n\n```javascript\ngatekeeper.set_authentication_params({\n  secret_type: 'PEM',\n  secret: 'https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com',\n  secret_refresh_interval: 5,\n  audience: 'my-app',\n  issuer: 'https://securetoken.google.com/my-app',\n})\n```\n\n| AuthenticationParams properties |\n| :--- |\n| \u003cbr /\u003e**`secret_type`** (required, enum `PLAIN_TEXT`, `PEM`, `JWK`): The property specifies the type of secret available to verify the JWT sent with the request. |\n| \u003cbr /\u003e**`secret`** (required): The property accepts multiple formats. (next) |\n| \u003cbr /\u003e**`secret_refresh_interval`** (optional): Whenever your identity service uses key rotation for signing JWTs, you may specify the time interval (in minutes) those keys must be refreshed and ExpressAuthorizer will handle it in the background. |\n| \u003cbr /\u003e**`audience`** (optional): When not declared, ExpressAuthorizer won't verify the `aud` claim from the request JWT. |\n| \u003cbr /\u003e**`issuer`** (optional): When not declared, ExpressAuthorizer won't verify the `iss` claim from the request JWT. |\n\u003cbr /\u003e\n\n| `secret` accepted formats |\n| :--- |\n| \u003cbr /\u003e**String (format: url)**: When a url is passed as secret, ExpressAuthorizer will retrieve it when your server starts and pass the returned value to the JWT verifier. URLs are accepted for any `secret_type` value. |\n| \u003cbr /\u003e**String (format: any)**: This secret format is only accepted when `service_type=PLAIN_TEXT`. |\n| \u003cbr /\u003e**PEM Public Key(s)**: This secret format is only accepted when `service_type=PEM`. ExpressAuthorizer expects either a single PEM as a string or a JSON object with multiple PEMs where keys are each PEM **kid**. ([example](https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com)) |\n| \u003cbr /\u003e**JWK or JWKS**: This secret format is only accepted when `service_type=JWK`. ExpressAuthorizer will retrieve the **kid** from the JWT header and build a PEM from your JWK. ([example](https://dev-45670431.okta.com/oauth2/v1/keys)) |\n\u003cbr /\u003e\n\n# Setting up Authorization Parameters\n\nAt the moment it's required all of the custom claims assigned to users to be nested under a root property. In the following example `users_claims_root_key` is *organizations*.\n\nUsers custom claims might be represented as a JSON object or as a collection (an array of objects).\n\nAdditionally, all claims in the organization object (`organization_group`, `organization_id`, `roles`) are required in your JWT. *On the next minor version this requirement will be dropped.*\n\n```\n{\n  \"organizations\": [{\n    \"organization_group\": \"internal\",\n    \"organization_id\": \"marketing\",\n    \"roles\": [\"manager\", \"user\"],\n  }, {\n    \"organization_group\": \"internal\",\n    \"organization_id\": \"global\",\n    \"roles\": [\"user\"],\n  }],\n  \"iss\": \"https://securetoken.google.com/my-app\",\n  \"aud\": \"my-app\",\n  \"auth_time\": 1620618241,\n  \"user_id\": \"Z0KuS5Hjn0UfBzW86p5zqGqBTIP2\",\n  \"sub\": \"Z0KuS5Hjn0UfBzW86p5zqGqBTIP2\",\n  \"iat\": 1620618241,\n  \"exp\": 1620621841,\n  \"email\": \"johndoe@example.com\",\n  \"email_verified\": true,\n  \"firebase\": {\n    \"identities\": {\n      \"email\": [\"johndoe@example.com\"]\n    },\n    \"sign_in_provider\": \"password\"\n  }\n}\n```\n\nThis method sets up all parameters required to map users' custom claims on JWTs. `ExpressAuthorizer.authorize` will work with the payload from the decoded JWT and your policies to determine users authority over any operation.\n\n```javascript\ngatekeeper.set_authorization_params({\n  user_id_key: 'user_id',\n  user_claims_root_key: 'organizations',\n  organization_group_key: 'organization_group',\n  organization_id_key: 'organization_id',\n  user_roles_key: 'roles',\n  // The next properties apply only when auth_agent=API_GATEWAY\n  identity_context_header_key: 'X-Endpoint-API-UserInfo',\n  identity_context_transformation_function: (value) =\u003e {\n    let parsedContext\n    try {\n      const contextString = Buffer.from(value, 'base64').toString('utf-8')\n      parsedContext = JSON.parse(contextString)\n    } catch (err) {\n      parsedContext = {}\n    }\n    return parsedContext\n  },\n})\n```\n\n| AuthorizationParams properties |\n| :--- |\n| \u003cbr /\u003e**`user_id_key`** (optional, default 'user_id'): It maps the user id key in your JWT. |\n| \u003cbr /\u003e**`user_claims_root_key`** (optional, default 'organizations'): It maps the root key for your custom claims. Removing the requirement for a root to the custom claims object (or array) is in the project backlog. |\n| \u003cbr /\u003e**`organization_group_key`** (optional, default 'organization_group'): It maps the key for organization groups in your JWT. It's currently required both in your authorization parameters, as well as in your policies. |\n| \u003cbr /\u003e**`organization_id_key`** (optional, default 'organization_id'): It maps the organization id key in your JWT. |\n| \u003cbr /\u003e**`user_roles_key`** (optional, default 'user_id'): It maps the key to the user roles array in your JWT. |\n| \u003cbr /\u003e**`identity_context_header_key`** (optional, type String): In case your requests are authenticated by an API Gateway, when it's upstreamed to the internal service, the decoded JWT is usually passed on another header. |\n| \u003cbr /\u003e**`identity_context_transformation_function`** (optional, type Function): Since request headers accept only strings - JSON objects can't be passed in the headers -, the function specified here will be responsible for hydrating the identity context back into a JSON object.  |\n\u003cbr /\u003e\n# Setting up Policies\n\nPolicies determine which users are allowed to consume your service operations. ExpressAuthorizer will extract relevant data from the JWT token, build the user roles and compare them against your policies to determine whether to deny acess (and return status code 403), or allow the request to reach your controllers.\n\nExpressAuthorizer assumes that routes not present in any policy are open to any authenticated request.\n\n```javascript\ngatekeeper.add_policy({\n  operations: [{\n    path: '/users/:id',\n    methods: ['POST', 'PATCH'],\n  }],\n  authorized_roles: 'self',\n  user_id_alt_key: 'id',\n})\n\ngatekeeper.add_policy({\n  operations: [{\n    path: '/companies/:company_id/banking-info',\n    methods: ['GET', 'POST', 'PATCH'],\n  }],\n  authorized_roles: [{\n    organization_group: 'customers',\n    roles: ['admin', 'billing'],\n  }],\n  organization_id_alt_key: 'company_id',\n  organization_restricted: true,\n})\n```\n\n## Policy properties\n\n| Policy properties |\n| :--- |\n| \u003cbr /\u003e**`operations`** (required): An API operation is the combination of a path and a method. You may specify mutiple operations that must adhere to the same policy, and declare multiple methods for a single path - as seen on the previous example. |\n| \u003cbr /\u003e**`operations.path`** (required): The endpoint path in Express format. |\n| \u003cbr /\u003e**`operations.methods`** (required): The methods included in the policy for each one of the paths declared. |\n| \u003cbr /\u003e**`authorized_roles`** (required): This is a complex property that accepts as value either a string or an array of objects. The accepted string values are `'self'` and `'*'`; where `'self'` refers to only the authenticated user itself, and `'*'` refers to any authenticated user. |\n| \u003cbr /\u003e**`authorized_roles.organization_group`** (required): It refers to the organization groups allowed to access those operations; e.g. on an accounting system, organization groups could be represented by `'customers'`, `'suppliers'`, and `'internal_staff'`. It also accepts `'*'` as a wildcard representing any organization group. |\n| \u003cbr /\u003e**`authorized_roles.roles`** (required): It represents the authenticated user role(s) within an organization. This property accepts either an array of strings, as well as the wilcard `'*'` representing any role within an organization. |\n| \u003cbr /\u003e**`user_id_alt_key`** (optional): When the declared `authorized_roles` is `'self'`, ExpressAuthorizer will look for a user_id in the path, then the request query string, and lastly in the request body. `user_id_alt_key` allows you to specify a key different from the one declared on `AuthorizationParams.user_id_key` |\n| \u003cbr /\u003e**`organization_id_alt_key`** (optional): When the declared `authorized_roles` is an array of objects, ExpressAuthorizer will look for an organization_id in the path, then the request query string, and lastly in the request body. `organization_id_alt_key` allows you to specify a key different from the one declared on `AuthorizationParams.organization_id_key` |\n| \u003cbr /\u003e**`organization_restricted`** (optional): The property tells ExpressAuthorizer whether or not to restrict resquests based on the organization_ids found on the JWT and the one found on the http request - either on its path, query string or the request body. |\n\u003cbr /\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbuffolander%2Fexpress-authorizer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbuffolander%2Fexpress-authorizer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbuffolander%2Fexpress-authorizer/lists"}