{"id":22362778,"url":"https://github.com/danmasta/rbac","last_synced_at":"2025-03-26T15:17:25.837Z","repository":{"id":57105755,"uuid":"355025905","full_name":"danmasta/rbac","owner":"danmasta","description":"Authorization helper for node apps","archived":false,"fork":false,"pushed_at":"2024-04-06T19:41:17.000Z","size":203,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-31T16:19:27.087Z","etag":null,"topics":["abac","authorization","oauth","oidc","permissions","rbac","saml"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/danmasta.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-04-06T01:56:23.000Z","updated_at":"2024-04-06T17:05:57.000Z","dependencies_parsed_at":"2024-12-04T17:11:50.213Z","dependency_job_id":"4f45bb93-1998-4c0e-915e-45c5325da269","html_url":"https://github.com/danmasta/rbac","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danmasta%2Frbac","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danmasta%2Frbac/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danmasta%2Frbac/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danmasta%2Frbac/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danmasta","download_url":"https://codeload.github.com/danmasta/rbac/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245678900,"owners_count":20654738,"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":["abac","authorization","oauth","oidc","permissions","rbac","saml"],"created_at":"2024-12-04T17:11:42.584Z","updated_at":"2025-03-26T15:17:25.805Z","avatar_url":"https://github.com/danmasta.png","language":"JavaScript","readme":"# RBAC\nRole based access control helper for node apps\n\nFeatures:\n* Easy to use\n* Define roles, permissions, and claims in a declarative way\n* Inherit permissions across roles\n* Map roles and permission sets to user claims\n* Authorize roles and permission sets against user claims\n* Includes helper middlewares for express apps\n* Supports [RBAC](https://developer.okta.com/books/api-security/authz/role-based/) and [ABAC](https://developer.okta.com/books/api-security/authz/attribute-based/) type flows\n* [Wildcard](#wildcards) support for permission definitions\n\n## About\nI wanted a better way to handle [authorization](https://www.okta.com/identity-101/authentication-vs-authorization/) in node apps. There are plenty of tools to help with authentication from different identity providers such as passport, oauth, saml, oidc, etc. But after you authenticate how do you handle authorization of specific resources and routes based on those [identity claims](https://developer.okta.com/blog/2017/07/25/oidc-primer-part-1#whats-a-claim)? I've seen a lot of applications do some sort of manual checking of session state and user claims on a per-route basis and in non-uniform ways. That pattern tends to be pretty error-prone and not very scalable or maintainable as the application grows. It can also be difficult to audit, change, or verify over time.\n\nThis package lets you declaratively define your permission sets with a very simple syntax. It supports heirarchical rules via permission inheritance and allows you to map those permissions to identity claims. It includes helper functions for express apps for authorizing against permissions and roles. It also supports wildcard matching for permission definitions.\n\n## Usage\nAdd rbac as a dependency for your app and install via npm\n```\nnpm install @danmasta/rbac --save\n```\nRequire the package in your app\n```js\nconst RBAC = require('@danmasta/rbac');\n\nlet rbac = RBAC(roles);\n```\nAdd authorization to a route\n```js\napp.use('/admin', rbac.isRole('admin'), router);\n```\n\n### Options\nname | type | description\n-----|------|------------\n`claims` | *`string`* | What property on the `req` object to find claims. Default is `'user'`\n`roles` | *`array`* | Role definitions to use for authorizing against. Default is `undefined`\n`authorizeAgainst` | *`string\\|array`* | Which claims to use to authorize permissions against. If not set it will attempt to authorize against all claims. Default is `undefined`\n`strict` | *`boolean`* | Whether or not to allow multiple role mappings per claim. If `true` it will throw an error if there is more than one role defined for a claim set. Default is `false`\n\n### Methods\nName | Description\n-----|------------\n`authorizeByPermissions(claims, permissions, authorizeAgainst?)` | Verify permissions against a set of claims. Accepts a set of claims, list of permissions, and optional claim key filter to authorize against. If more than one permission is provided it will succeed only if all permissions are verified. Returns a `promise` that resolves with claims or rejects with an `AuthorizationError`\n`authorizeByRoles(claims, roles, authorizeAgainst?)` | Verify roles against a set of claims. Accepts a set of claims, list of roles, and optional claim key filter to authorize against. If more than one role is provided it will succeed if any role is verified. Returns a `promise` that resolves with claims or rejects with an `AuthorizationError`\n`isAuthorized(permissions, opts?)` | Helper middleware for verifying permissions on routes. Accepts an optional options object of `{ permissions, authorizeAgainst, redirect }`. Returns a middleware `function`\n`isRole(roles, opts?)` | Helper middleware for verifying roles on routes. Accepts an optional options object of `{ roles, authorizeAgainst, redirect }`. Returns a middleware `function`\n`isAuthenticated(opts?)` | Helper middleware for checking authentication state. Expects a `req.isAuthenticated()` function to be set. Accepts an optional options object of `{ redirect }`. Returns a middleware `function`\n`express()` | Helper middleware for setting functions on the `req` object and `res.locals` object for routes and view templates. Returns a middleware `function`\n\n*Note: The `redirect` option for middleware functions is used to set `req.session.redirect` on failure, if desired*\n\n### Role Objects\nEach role object has the following signature:\n#### Properties\nname | type | description\n-----|------|------------\n`id` | *`string`* | ID for the role. Required or an error is thrown. Used to look up other roles by ID for permission inheritance. Default is `undefined`\n`description` | *`string`* | Description for the role. Default is `undefined`\n`permissions` | *`string\\|array`* | Permissions for the role. Should be an array of strings: `['posts.edit', 'posts.view']`. Default is `undefined`\n`inherit` | *`string\\|array`* | Which role ids to inherit permissions from. Default is `undefined`\n`claims` | *`object`* | Key/value pairs of claim names and values: `{ groups: ['editor', 'viewer'] }`. Default is `undefined`\n\n#### Methods\nName | Description\n-----|------------\n`generatePermissionTree()` | Generate the permission tree for the role. Recursively walks and merges inherited permissions. Returns a permissions `object`\n`isAuthorized(permission)` | Verifies whether or not the role has a permission: `role.isAuthorized('posts.edit')`. Returns a `boolean`\n\n### Wildcards\nWhen describing permissions you can use wildcards `*` to authorize patterns of resources. The syntax looks like:\n* `*` At the end of a permission matches anything after and including. If you want to set a superadmin type role you can just use `*` as the only permission\n* `**` Explicitly matches all from this point, alias for using `*` at the end\n* `*.` Match anything one level deep\n\n#### Wildcard Examples\nString | Description\n-------|------------\n`*` | Matches anything at any depth\n`api.*` | Matches any permission at any depth that begins with `api`\n`api.*.` | Matches any permission at 1 depth that begins with `api`. Matches `api.status`, but not `api.status.db`\n`api.*.view` | Matches any permission at 1 depth that begins with `api` and ends with `view`. Matches `api.users.view`, but not `api.users` or `api.users.edit`\n\n## Examples\nDefine roles, find claims on the `req.user` object, and authorize against the `groups` claim\n```js\nconst RBAC = require('@danmasta/rbac');\n\nconst roles = [\n    {\n        id: 'admin',\n        inherit: undefined,\n        permissions: [\n            '*'\n        ],\n        claims: {\n            groups: [\n                'admin'\n            ]\n        }\n    },\n    {\n        id: 'editor',\n        inherit: [\n            'viewer',\n        ],\n        permissions: [\n            'api.*.edit',\n            'posts.edit'\n        ],\n        claims: {\n            groups: [\n                'editor'\n            ]\n        }\n    },\n    {\n        id: 'viewer',\n        inherit: undefined,\n        permissions: [\n            'api.*.view',\n            'api.*.list',\n            'posts.view'\n        ],\n        claims: {\n            groups: [\n                'viewer'\n            ]\n        }\n    }\n];\n\nconst rbac = RBAC(roles, { claims: 'user', authorizeAgainst: 'groups' });\n```\n\nSecure resources and routes with middleware by permissions\n```js\nlet app = express();\n\napp.get('/post/:id', rbac.isAuthorized('posts.view'), (req, res, next) =\u003e {\n    res.render('post/index', req.params.id);\n});\n\napp.get('/post/:id/edit', rbac.isAuthorized('posts.edit'), (req, res, next) =\u003e {\n    res.render('post/edit', req.params.id);\n});\n\napp.post('/api/post/:id/edit', rbac.isAuthorized('api.post.edit'), (req, res, next) =\u003e {\n    res.json({ message: 'post udpated', id: req.params.id });\n});\n```\n\nSecure all admin routes by role\n```js\nlet app = express();\n\napp.use('/admin', rbac.isRole('admin'), require('./routes/admin'));\n```\n\nAdd express helpers to `req` and `res.locals` objects\n```js\nlet app = express();\n\napp.use(rbac.express());\n```\n\nVerify permissions inside a route handler function\n```js\nlet app = express();\n\napp.use(rbac.express());\n\napp.get('/post/:id', (req, res, next) =\u003e {\n\n    req.isAuthorized('posts.view').then(() =\u003e {\n        res.render('post/index', req.params.id);\n    }).catch(err =\u003e {\n        next(err);\n    });\n\n});\n```\n\n## Testing\nTests are currently run using mocha and chai. To execute tests run `npm run test`. To generate unit test coverage reports run `npm run coverage`\n\n## Contact\nIf you have any questions feel free to get in touch\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanmasta%2Frbac","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanmasta%2Frbac","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanmasta%2Frbac/lists"}