{"_id":"@cool-ai/beach-auth-rbac","_rev":"3-b65633ab620eb4f3e91d3124f2d4669e","name":"@cool-ai/beach-auth-rbac","dist-tags":{"latest":"0.1.3"},"versions":{"0.1.1":{"name":"@cool-ai/beach-auth-rbac","version":"0.1.1","license":"Apache-2.0","_id":"@cool-ai/beach-auth-rbac@0.1.1","maintainers":[{"name":"johncandrew","email":"johncandrew@yahoo.co.uk"}],"homepage":"https://cool-ai.org","bugs":{"url":"https://gitlab.com/johncandrew/beach/-/issues"},"dist":{"shasum":"476b830a06d04e7689e9c60c988ebaaac175f0f6","tarball":"https://registry.npmjs.org/@cool-ai/beach-auth-rbac/-/beach-auth-rbac-0.1.1.tgz","fileCount":11,"integrity":"sha512-eZH6GOxnlj7JFizOXvAnH4QTjtvqADjCiM5bD3qdwoHtxTFcBIiFf1TLp16uECUUML3HW++a0JpElLJloeUbWw==","signatures":[{"sig":"MEYCIQD5gxM3YyRwSzNIP8Tcy0ctxDtAZo/VpZb3zSBE6Aj5XQIhALKIW2k60jbWf6wZzaJL7cMHAP5Np//f49LELcue82gJ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":29826},"type":"module","_from":"file:cool-ai-beach-auth-rbac-0.1.1.tgz","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"dev":"tsc --project tsconfig.json --watch","test":"vitest run","build":"tsc --project tsconfig.json","coverage":"vitest run --coverage","typecheck":"tsc --noEmit && tsc --noEmit --project tsconfig.test.json","test:watch":"vitest"},"_npmUser":{"name":"johncandrew","email":"johncandrew@yahoo.co.uk"},"_resolved":"/tmp/1ea5820391308345d62f6abe625171d5/cool-ai-beach-auth-rbac-0.1.1.tgz","_integrity":"sha512-eZH6GOxnlj7JFizOXvAnH4QTjtvqADjCiM5bD3qdwoHtxTFcBIiFf1TLp16uECUUML3HW++a0JpElLJloeUbWw==","repository":{"url":"git+https://gitlab.com/johncandrew/beach.git","type":"git","directory":"packages/beach-auth-rbac"},"_npmVersion":"10.9.8","description":"Beach-native RBAC reference Authoriser — reads CapabilityToken values from Principal.claims['capability-tokens'] and matches against the routing rule's requiresCapability. The default reference adapter for CAIB-138's authorisation primitive.","directories":{},"_nodeVersion":"22.22.3","dependencies":{"@cool-ai/beach-core":"^0.11.15"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.0.0","typescript":"^5.5.0"},"_npmOperationalInternal":{"tmp":"tmp/beach-auth-rbac_0.1.1_1780398551288_0.33581765594348956","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@cool-ai/beach-auth-rbac","version":"0.1.2","license":"Apache-2.0","_id":"@cool-ai/beach-auth-rbac@0.1.2","maintainers":[{"name":"johncandrew","email":"johncandrew@yahoo.co.uk"}],"homepage":"https://cool-ai.org","bugs":{"url":"https://gitlab.com/johncandrew/beach/-/issues"},"dist":{"shasum":"11e32eedd695c47dc18c6876adc80ebb3886ce5a","tarball":"https://registry.npmjs.org/@cool-ai/beach-auth-rbac/-/beach-auth-rbac-0.1.2.tgz","fileCount":11,"integrity":"sha512-PiNZbaSCO83msv+OaGaAUoQ5xvTsAa5CGR5OzIcF63wPNX+Uv5BYSIB/ZPqtrPAvsm4OOfpxhfrX7xOg0JtaVQ==","signatures":[{"sig":"MEYCIQDCEm8mG9hgZdaCyMt18hbX3ijRumGCM/D03KosegnPQwIhAJqt508McOkOhKUtvcwi/HUV5WoeLDRcn55oqaQcjjEI","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":29825},"type":"module","_from":"file:cool-ai-beach-auth-rbac-0.1.2.tgz","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"dev":"tsc --project tsconfig.json --watch","test":"vitest run","build":"tsc --project tsconfig.json","coverage":"vitest run --coverage","typecheck":"tsc --noEmit && tsc --noEmit --project tsconfig.test.json","test:watch":"vitest"},"_npmUser":{"name":"johncandrew","email":"johncandrew@yahoo.co.uk"},"_resolved":"/tmp/b1388b33e8bdb4e7b189f6d02869ad4a/cool-ai-beach-auth-rbac-0.1.2.tgz","_integrity":"sha512-PiNZbaSCO83msv+OaGaAUoQ5xvTsAa5CGR5OzIcF63wPNX+Uv5BYSIB/ZPqtrPAvsm4OOfpxhfrX7xOg0JtaVQ==","repository":{"url":"git+https://gitlab.com/johncandrew/beach.git","type":"git","directory":"packages/beach-auth-rbac"},"_npmVersion":"10.9.8","description":"Beach-native RBAC reference Authoriser — reads CapabilityToken values from Principal.claims['capability-tokens'] and matches against the routing rule's requiresCapability. The default reference adapter for CAIB-138's authorisation primitive.","directories":{},"_nodeVersion":"22.22.3","dependencies":{"@cool-ai/beach-core":"^0.12.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.0.0","typescript":"^5.5.0"},"_npmOperationalInternal":{"tmp":"tmp/beach-auth-rbac_0.1.2_1780572515150_0.4250488180400045","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@cool-ai/beach-auth-rbac","version":"0.1.3","description":"Beach-native RBAC reference Authoriser — reads CapabilityToken values from Principal.claims['capability-tokens'] and matches against the routing rule's requiresCapability. The default reference adapter for CAIB-138's authorisation primitive.","license":"Apache-2.0","repository":{"type":"git","url":"git+https://gitlab.com/johncandrew/beach.git","directory":"packages/beach-auth-rbac"},"homepage":"https://cool-ai.org","bugs":{"url":"https://gitlab.com/johncandrew/beach/-/issues"},"type":"module","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"}},"publishConfig":{"access":"public"},"dependencies":{"@cool-ai/beach-core":"^0.13.0"},"devDependencies":{"typescript":"^5.5.0","vitest":"^2.0.0"},"scripts":{"build":"tsc --project tsconfig.json","dev":"tsc --project tsconfig.json --watch","test":"vitest run","coverage":"vitest run --coverage","test:watch":"vitest","typecheck":"tsc --noEmit && tsc --noEmit --project tsconfig.test.json"},"_id":"@cool-ai/beach-auth-rbac@0.1.3","_integrity":"sha512-ggOS46JLqvfBOFiTweFl4TwrL5y4LVx/YoacuHBbU85+Dm/zub0fRB6oiYt9jAerBxCSMHy1ze+qHU0ORZ7Vfg==","_resolved":"/tmp/7ac21ba3c9a1b99883a1d16015f3a403/cool-ai-beach-auth-rbac-0.1.3.tgz","_from":"file:cool-ai-beach-auth-rbac-0.1.3.tgz","_nodeVersion":"22.22.3","_npmVersion":"10.9.8","dist":{"integrity":"sha512-ggOS46JLqvfBOFiTweFl4TwrL5y4LVx/YoacuHBbU85+Dm/zub0fRB6oiYt9jAerBxCSMHy1ze+qHU0ORZ7Vfg==","shasum":"d0281931fa3a33731a3ddb37e383fda318f1e39a","tarball":"https://registry.npmjs.org/@cool-ai/beach-auth-rbac/-/beach-auth-rbac-0.1.3.tgz","fileCount":11,"unpackedSize":29796,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIGLpulf6ZPY9sMKuJT0koebtuXur2/ezDs8/W2AVrnh6AiEA0jnDcTu2OydZ0vKkYAMj2NsCiDAihz5JsSXjI6q1ews="}]},"_npmUser":{"name":"johncandrew","email":"johncandrew@yahoo.co.uk"},"directories":{},"maintainers":[{"name":"johncandrew","email":"johncandrew@yahoo.co.uk"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/beach-auth-rbac_0.1.3_1780677375626_0.8997540690759456"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-02T11:09:11.069Z","modified":"2026-06-05T16:36:15.904Z","0.1.1":"2026-06-02T11:09:11.429Z","0.1.2":"2026-06-04T11:28:35.294Z","0.1.3":"2026-06-05T16:36:15.774Z"},"bugs":{"url":"https://gitlab.com/johncandrew/beach/-/issues"},"license":"Apache-2.0","homepage":"https://cool-ai.org","repository":{"type":"git","url":"git+https://gitlab.com/johncandrew/beach.git","directory":"packages/beach-auth-rbac"},"description":"Beach-native RBAC reference Authoriser — reads CapabilityToken values from Principal.claims['capability-tokens'] and matches against the routing rule's requiresCapability. The default reference adapter for CAIB-138's authorisation primitive.","maintainers":[{"name":"johncandrew","email":"johncandrew@yahoo.co.uk"}],"readme":"# @cool-ai/beach-auth-rbac\n\nBeach-native RBAC reference `Authoriser` — reads `CapabilityToken` values from `Principal.claims['capability-tokens']` and matches against the routing rule's `requiresCapability`.\n\nThe default reference adapter for [CAIB-138](https://cooltravel.atlassian.net/browse/CAIB-138)'s authorisation primitive. In-process, no external service, no extra deployment.\n\n## Install\n\n```sh\npnpm add @cool-ai/beach-auth-rbac\n```\n\n## Usage\n\n```typescript\nimport { EventRouter } from '@cool-ai/beach-core';\nimport { RBACAuthoriser } from '@cool-ai/beach-auth-rbac';\n\nconst router = new EventRouter({ authoriser: new RBACAuthoriser() });\n\nrouter.loadRoutingConfig({\n  rules: [\n    {\n      source: 'inbound:chat',\n      eventType: 'project_update',\n      to: [{ handler: 'project-updater' }],\n      requiresCapability: 'project:update',\n    },\n  ],\n});\n```\n\nThe router invokes `RBACAuthoriser.check(event, 'project:update')` after the rule matches and before the handler runs. The check reads `event.principal.claims['capability-tokens']` (an array of `CapabilityToken` values) and returns `true` if any token's `capability` field matches the requested capability.\n\nIf no matching token is found — or the principal carries no `'capability-tokens'` claim, or the principal is `undefined` — the check returns `false` and the router emits a `RouteAuthorisationDeniedEvent` via `routeEvent`.\n\n## Where tokens come from\n\nTokens attach to a `Principal` at the inbound boundary. The canonical attachment key is `'capability-tokens'` (exported as `CAPABILITY_TOKENS_CLAIM_KEY` from `@cool-ai/beach-core`):\n\n```typescript\nimport type { CapabilityToken, Principal } from '@cool-ai/beach-core';\nimport { CAPABILITY_TOKENS_CLAIM_KEY } from '@cool-ai/beach-core';\n\nfunction principalFromGatewayHeaders(headers: Headers): Principal {\n  const userId = headers.get('X-User-Id')!;\n  const capabilities = (headers.get('X-User-Capabilities') ?? '').split(',').filter(Boolean);\n  const tokens: CapabilityToken[] = capabilities.map((capability) => ({\n    principalId: userId,\n    capability,\n  }));\n  return {\n    principalId: userId,\n    claims: { [CAPABILITY_TOKENS_CLAIM_KEY]: tokens },\n  };\n}\n```\n\nThe pattern is documented in [Beach behind an external auth gateway](../../documentation/guides/beach-behind-an-external-auth-gateway.md) — Gate 2's worked example for the v1.0 readiness criteria.\n\n## Options\n\n```typescript\nnew RBACAuthoriser({\n  scope: 'session:abc123',           // require every token to match this scope\n  anonymousPrincipal: 'deny',        // default; 'allow-anonymous' is for tests only\n});\n```\n\n### `scope`\n\nWhen set, only tokens whose `scope` field matches the configured value satisfy a check. Consumers needing **per-event scope** (extracted from `event.data`) implement a custom `Authoriser` directly against the interface; the `findCapabilityTokens` helper from this package is reusable:\n\n```typescript\nimport type { Authoriser, RouteEvent } from '@cool-ai/beach-core';\nimport { findCapabilityTokens } from '@cool-ai/beach-auth-rbac';\n\nconst perEventScope: Authoriser = {\n  async check(event: RouteEvent, capability: string): Promise<boolean> {\n    if (!event.principal) return false;\n    const data = event.data as { domainId?: string } | null;\n    const scope = data?.domainId ? `domain:${data.domainId}` : undefined;\n    return findCapabilityTokens(event.principal, capability, scope).length > 0;\n  },\n};\n```\n\n### `anonymousPrincipal`\n\nBehaviour when `event.principal` is `undefined`. Default `'deny'`. `'allow-anonymous'` returns `true` regardless of capability — for tests where authorisation is not the unit under test. **Do not ship `'allow-anonymous'` to production.**\n\n## Exports\n\n| Export | Purpose |\n|---|---|\n| `RBACAuthoriser` | The class implementing `Authoriser` |\n| `RBACAuthoriserOptions` | Construction options type |\n| `findCapabilityTokens(principal, capability, scope?)` | Pure-function token lookup, reusable in custom `Authoriser` implementations |\n| `isCapabilityToken(value)` | Runtime type guard for the `CapabilityToken` shape |\n\n## When to use a different Authoriser\n\nThe `Authoriser` interface from `@cool-ai/beach-core` is small (one method, two parameters). Consumers needing more than capability-token matching write their own implementation directly against the interface:\n\n- **Policy-as-code** (Cerbos, OPA, AuthZed) — wrap the engine's check API. The `findCapabilityTokens` helper from this package is not used; the engine is the source of truth.\n- **Relationship-based access control** (OpenFGA) — pre-model the domain as a graph, translate capabilities to (relation, object) pairs inside the Authoriser implementation.\n- **Custom RBAC** — when the token shape differs from `CapabilityToken`, or when scope must be derived from event data, write a custom Authoriser. The `findCapabilityTokens` helper is reusable when the token storage convention is preserved.\n\nThe `Authoriser` interface ships in `@cool-ai/beach-core`; this package is one reference implementation among N.\n\n## Related\n\n- [`@cool-ai/beach-core` README § Identity and authorisation](../core/README.md#identity-and-authorisation) — the `Authoriser` interface, `CapabilityToken` shape, `requiresCapability` routing-rule field.\n- [`documentation/guides/identity-and-authorisation.md`](../../documentation/guides/identity-and-authorisation.md) — how the Principal flows through events and the Authoriser is consulted at dispatch.\n- [`documentation/guides/beach-behind-an-external-auth-gateway.md`](../../documentation/guides/beach-behind-an-external-auth-gateway.md) — Gate 2's worked example.\n- [`documentation/dependencies/diy-rbac/`](../../documentation/dependencies/diy-rbac/) — surface-read + compromise audit for this adapter.\n\n## Licence\n\nApache-2.0. Copyright 2026 John Andrew.\n","readmeFilename":"README.md"}