@owlmeans/server-iam 0.1.18-rc.51 → 0.1.18-rc.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,12 +7,14 @@ One-call OIDC RP wiring and IAM gate for OwlMeans servers — `appendIam()` and
7
7
  - `appendIam(context)` — registers the OIDC client, wrapping service, and IAM gate in a single call, replacing verbose manual wiring
8
8
  - `makeIamGate()` — produces a guard that asserts unscoped or resource-scoped permissions against the IAM backend (claims-first, UMA2 fallback)
9
9
  - Re-exports `hasPermission` from `@owlmeans/iam` for inline permission checks
10
+ - `organizationOf` / `organizationsOf` — the organizations of a tenanted session, from its session record
11
+ - `iamRuntime(context, request)` — a typed client of the provider's runtime IAM API, acting as the request's subject
10
12
  - Designed for IAM consumers such as the viable target template backend
11
13
 
12
14
  ## Installation
13
15
 
14
16
  ```bash
15
- bun add @owlmeans/server-iam@^0.1.18-rc.51
17
+ bun add @owlmeans/server-iam@^0.1.18-rc.53
16
18
  ```
17
19
 
18
20
  ## Usage
@@ -45,7 +47,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
45
47
  your project's skill store (`.agents/skills/`):
46
48
 
47
49
  ```sh
48
- npx @owlmeans/agent-skills@^0.1.18-rc.42
50
+ npx @owlmeans/agent-skills@^0.1.18-rc.45
49
51
  ```
50
52
 
51
53
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/server-iam",
4
- "version": "0.1.18-rc.51",
5
- "generatedAt": "2026-09-29T08:01:58.120Z",
4
+ "version": "0.1.18-rc.53",
5
+ "generatedAt": "2026-10-01T23:27:48.727Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: server-iam
3
- description: "How to use @owlmeans/server-iam — one-call OIDC RP wiring (appendIam) and the IAM gate that asserts unscoped and resource-scoped permissions (claims-first, UMA2 fallback), plus the gate-param grammar it re-exports. Use when gating server endpoints, declaring gate params, or diagnosing a permission refusal. Applies to files matching **/owlmeans.ts, **/gate*.ts, **/auth-guard*.ts."
3
+ description: "How to use @owlmeans/server-iam — one-call OIDC RP wiring (appendIam), the IAM gate that asserts unbound, organization-bound and resource-scoped permissions (claims-first, UMA2 fallback), the session's organizations (organizationOf/organizationsOf), the request-bound runtime IAM client (iamRuntime), plus the gate-param grammar it re-exports. Use when gating server endpoints, declaring gate params, acting in a tenanted client's organizations, managing members or grants from a target's server, or diagnosing a permission refusal. Applies to files matching **/owlmeans.ts, **/gate*.ts, **/auth-guard*.ts."
4
4
  metadata:
5
5
  applyTo: "**/owlmeans.ts, **/gate*.ts, **/auth-guard*.ts"
6
6
  ---
@@ -8,10 +8,12 @@ metadata:
8
8
 
9
9
  # Using `@owlmeans/server-iam`
10
10
 
11
- **Install:** `"@owlmeans/server-iam": "^0.1.18-rc.51"` in `dependencies`
11
+ **Install:** `"@owlmeans/server-iam": "^0.1.18-rc.53"` in `dependencies`
12
12
 
13
13
  Boilerplate-less server-side consumer of the OwlMeans IAM. One call wires the OIDC RP stack and the
14
- IAM gate; the consumer never knows which IAM backend (Keycloak or integrated) is active.
14
+ IAM gate; the consumer never knows which IAM backend (Keycloak or integrated) is active. For a
15
+ tenanted client it also reads the session's organizations and calls the provider's runtime IAM API
16
+ as the signed-in subject.
15
17
 
16
18
  ## Public API surface
17
19
 
@@ -24,6 +26,11 @@ IAM gate; the consumer never knows which IAM backend (Keycloak or integrated) is
24
26
  | `RESOURCE_PARAM_SEPARATOR` / `RESOURCE_SOURCE_SEPARATOR` / `RESOURCE_PATH_SEPARATOR` | const | `'@'`, `':'` and `'.'`, re-exported |
25
27
  | `GateParamSource` / `GateParamErrorCode` / `GateResolutionFailure` | enum | Re-exported; the sources a selector may name, and why one failed |
26
28
  | `hasPermission` | fn | Re-export from `@owlmeans/iam` |
29
+ | `organizationOf(context, request, entitySlug)` | fn | One organization of the request's subject as a `ResolvedEntity` (`{ id: entityKey, slug, iamKey: entityKey }`); one the subject is not in is `AuthForbidden(ORGANIZATION_REFUSAL)` |
30
+ | `organizationsOf(context, request)` | fn | Every organization of the request's subject, as `ResolvedEntity[]` |
31
+ | `iamRuntime(context, request)` | fn | The request-bound `IamRuntimeClient` of the provider's runtime IAM API — see "The runtime IAM client" |
32
+ | `IamRuntimeClient` | type | `organizations.{list,create,update}`, `members.{list,add,update,remove}`, `permissions.list`, `grants.{list,assign,revoke}` |
33
+ | `ORGANIZATION_REFUSAL` / `ORGANIZATION_OWNER_REFUSAL` | const | Re-exported from `@owlmeans/oidc`: the `AuthForbidden` reasons of a non-member and of a non-owner |
27
34
  | `SERVER_IAM_SERVICE` | const | `'server-iam-service'` — exported and referenced nowhere; the gate registers under `OIDC_GATE`, not under this |
28
35
  | Every `@owlmeans/iam` type | type | Re-exported wholesale, so a server needs one IAM import |
29
36
 
@@ -96,11 +103,19 @@ await ctx.service<GateService>(OIDC_GATE).assert(req, res, ['article--modify'])
96
103
  1. `req.auth == null` → `AuthForbidden('auth')`.
97
104
  2. **Claims mode** — when `req.auth.permissions` is a valid `PermissionSet[]` (minted into the id_token
98
105
  by the integrated IAM provider and mapped into `Auth` by `@owlmeans/server-oidc-rp`), params are
99
- asserted locally via `hasPermission`.
106
+ asserted locally via `hasPermission`, with `entitySlug: req.entity?.slug ?? entitySlugOf(auth)` —
107
+ the organization the request acts in.
100
108
  3. **Fallback** — otherwise `@…` suffixes are stripped and the check delegates to the UMA2 gate model
101
109
  from `@owlmeans/server-oidc-rp` (`createGateModel().loadPermissions`) — exactly the check
102
110
  `makeOidcGate` performs, which is what a Keycloak-backed deployment gets.
103
111
 
112
+ In claims mode the four permission kinds (`iam` skill, "Permission kinds") are all decided locally:
113
+ an unbound set applies everywhere, a set bound to an organization (`entitySlug`) only in that one, a
114
+ set with `resources` only for those ids. The relying party already strips the binding of the acting
115
+ organization's sets and drops every other bound set, so a bound set reaching the gate is a leak —
116
+ and `hasPermission` refuses it unless the request acts in exactly its organization, including when
117
+ nothing names an organization at all.
118
+
104
119
  Which branch runs is decided per request by the token alone: a conforming `permissions` claim selects
105
120
  claims mode, its absence selects the fallback. Nothing configures it, and there is no environment
106
121
  variable behind it.
@@ -128,13 +143,50 @@ Grants are stored against the id wherever one is resolvable. The slug fallback e
128
143
  with no organization store of their own, where the slug *is* the identifier; a deployment that has a
129
144
  store must not grant against slugs, because a rename would then orphan every grant.
130
145
 
146
+ ## Organizations of the session
147
+
148
+ A relying party of a tenanted client has no organization registry of its own: the provider's
149
+ `organizations` claim, kept in the session record (`@owlmeans/server-oidc-rp`), is the only source,
150
+ re-read on every validation. `organizationsOf` answers all of it and `organizationOf` one entry by
151
+ slug, as `ResolvedEntity` values keyed by the frozen `entityKey` — use them for a handler that acts in
152
+ an organization its URL names rather than the session's acting one (which the guard already attaches
153
+ as `req.entity`). A session of a client without the scope acts in none and has none to offer; an
154
+ unauthenticated request or a session that is gone is `AuthorizationError` (401).
155
+
156
+ ## The runtime IAM client
157
+
158
+ `iamRuntime(context, request)` calls the provider's runtime IAM API (`makeIamRuntimeProtocols` in
159
+ `@owlmeans/iam`) as that request's subject:
160
+
161
+ - **Base** — the provider's discovery field `IAM_API_METADATA` (`owlmeans_iam_api`), read through
162
+ the OIDC client service's adapter `getMetadata()` for the session's client, and cached per context
163
+ and client; a failed lookup is forgotten. A provider that advertises none (Keycloak) is
164
+ `IamClientError('runtime-api:<client>')`.
165
+ - **Bearer** — the provider access token of the request's session record, read afresh on every call
166
+ so a token the guard just refreshed is the one sent. A record without one is
167
+ `AuthForbidden('record')`, and nothing is sent.
168
+ - **Transport** — plain `fetch` over `IAM_RUNTIME_ROUTES`: the API is another service's tree, and a
169
+ relying party binds none of it.
170
+ - **Refusals** — a development server's marshalled error is rebuilt into its class. A production body
171
+ is only an incident id, so the status and the route decide: a 403 from an owner route is
172
+ `AuthForbidden(ORGANIZATION_OWNER_REFUSAL)`, from any other route `AuthForbidden(ORGANIZATION_REFUSAL)`;
173
+ a 401 is `AuthorizationError('iam-runtime')`; anything else `IamError('runtime:status:<n>')`. The
174
+ incident id travels on the error.
175
+
176
+ `members.add` is find-or-create by e-mail and idempotent — that is the provider's job, so a caller
177
+ never checks for an existing member first. Owner and member rights are decided by the provider from
178
+ the token; nothing sent here can widen them.
179
+
131
180
  ## Rules
132
181
 
133
182
  - Register the gate via `appendIam()`; do not also register `makeOidcGate()` — both use the
134
183
  `OIDC_GATE` alias and the IAM gate already covers the UMA2 path.
135
184
  - Never branch on the IAM backend in consumer code — the gate's claims/fallback split is the only seam.
136
185
  - The OIDC provider entry must request the `permissions` scope (`extraScopes`) for claims mode to
137
- engage; without it the gate transparently uses the fallback.
186
+ engage; without it the gate transparently uses the fallback. A tenanted client also requests
187
+ `organizations`.
188
+ - Reach the runtime IAM API only through `iamRuntime`; never forward the browser's wrapped token or
189
+ build a bearer from anything but the session record.
138
190
 
139
191
  ## Related instructions
140
192
 
package/build/gate.d.ts CHANGED
@@ -23,7 +23,8 @@ export interface IamGateOptions {
23
23
  * appendIam() so target code never knows which IAM backend is active.
24
24
  *
25
25
  * 1. When the Auth carries a valid PermissionSet[] claim (integrated IAM mode),
26
- * params are asserted locally against it — both unscoped and resource-scoped forms.
26
+ * params are asserted locally against it — both unscoped and resource-scoped forms, and a set
27
+ * bound to an organization only for the organization the request acts in.
27
28
  * 2. Otherwise (Keycloak mode — its tokens never produce a conforming claim) the
28
29
  * @-suffixes are stripped and the check delegates to the UMA2 gate model from
29
30
  * @owlmeans/server-oidc-rp, byte-equivalent to makeOidcGate.
@@ -1 +1 @@
1
- {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAqC,MAAM,sBAAsB,CAAA;AAY1F;;;;GAIG;AACH,OAAO,EACL,wBAAwB,EAAE,yBAAyB,EAAE,uBAAuB,EAC5E,eAAe,EAAE,kBAAkB,EAAE,qBAAqB,EAC1D,cAAc,EAAE,iBAAiB,EAAE,eAAe,EAAE,mBAAmB,EAAE,kBAAkB,EAC5F,MAAM,eAAe,CAAA;AACtB,YAAY,EAAE,eAAe,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAE1F,MAAM,WAAW,cAAc;IAC7B;;;;;;;;OAQG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAC9B;AAiED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,WAAW,WAAW,MAAM,SAAqB,cAAc,KAAG,WA4F9E,CAAA"}
1
+ {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAqC,MAAM,sBAAsB,CAAA;AAY1F;;;;GAIG;AACH,OAAO,EACL,wBAAwB,EAAE,yBAAyB,EAAE,uBAAuB,EAC5E,eAAe,EAAE,kBAAkB,EAAE,qBAAqB,EAC1D,cAAc,EAAE,iBAAiB,EAAE,eAAe,EAAE,mBAAmB,EAAE,kBAAkB,EAC5F,MAAM,eAAe,CAAA;AACtB,YAAY,EAAE,eAAe,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAE1F,MAAM,WAAW,cAAc;IAC7B;;;;;;;;OAQG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAC9B;AAiED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,WAAW,WAAW,MAAM,SAAqB,cAAc,KAAG,WAiG9E,CAAA"}
package/build/gate.js CHANGED
@@ -70,7 +70,8 @@ const auditEntrypoint = (ctx, req, params) => {
70
70
  * appendIam() so target code never knows which IAM backend is active.
71
71
  *
72
72
  * 1. When the Auth carries a valid PermissionSet[] claim (integrated IAM mode),
73
- * params are asserted locally against it — both unscoped and resource-scoped forms.
73
+ * params are asserted locally against it — both unscoped and resource-scoped forms, and a set
74
+ * bound to an organization only for the organization the request acts in.
74
75
  * 2. Otherwise (Keycloak mode — its tokens never produce a conforming claim) the
75
76
  * @-suffixes are stripped and the check delegates to the UMA2 gate model from
76
77
  * @owlmeans/server-oidc-rp, byte-equivalent to makeOidcGate.
@@ -95,6 +96,10 @@ export const makeIamGate = (alias = OIDC_GATE, opts) => {
95
96
  // the subject simply lacks the grant. The two look identical from the outside and are
96
97
  // repaired in completely different places.
97
98
  const structural = [];
99
+ // The organization the request acts in. A relying party of a tenanted client attaches it
100
+ // through the guard; otherwise it is the token's. A set bound to any other organization
101
+ // never satisfies the check.
102
+ const entitySlug = req.entity?.slug ?? entitySlugOf(auth);
98
103
  const granted = params.some(param => {
99
104
  const { permission, resource, error } = parseGateParam(param);
100
105
  // Grants are stored against the entity's stable id wherever one is resolvable; the
@@ -114,9 +119,9 @@ export const makeIamGate = (alias = OIDC_GATE, opts) => {
114
119
  }
115
120
  return false;
116
121
  }
117
- return hasPermission(auth, fixed, { resourceId: resolution.id });
122
+ return hasPermission(auth, fixed, { resourceId: resolution.id, entitySlug });
118
123
  }
119
- return hasPermission(auth, fixed);
124
+ return hasPermission(auth, fixed, { entitySlug });
120
125
  });
121
126
  if (!granted) {
122
127
  structural.forEach(detail => reportOnce(`${req.alias}\0${detail}`, `Gate on entrypoint "${req.alias}" refused a request it could not evaluate: ${detail}`));
package/build/gate.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"gate.js","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gate.js","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"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"}
package/build/index.d.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  export type * from '@owlmeans/iam';
2
2
  export { hasPermission } from '@owlmeans/iam';
3
+ export { ORGANIZATION_REFUSAL, ORGANIZATION_OWNER_REFUSAL } from '@owlmeans/oidc';
3
4
  export * from './consts.js';
4
5
  export * from './append.js';
5
6
  export * from './gate.js';
7
+ export { organizationOf, organizationsOf } from './organization.js';
8
+ export { iamRuntime } from './runtime.js';
9
+ export type { IamRuntimeClient } from './runtime.js';
6
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,eAAe,CAAA;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,eAAe,CAAA;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAA;AACjF,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA"}
package/build/index.js CHANGED
@@ -1,5 +1,8 @@
1
1
  export { hasPermission } from '@owlmeans/iam';
2
+ export { ORGANIZATION_REFUSAL, ORGANIZATION_OWNER_REFUSAL } from '@owlmeans/oidc';
2
3
  export * from './consts.js';
3
4
  export * from './append.js';
4
5
  export * from './gate.js';
6
+ export { organizationOf, organizationsOf } from './organization.js';
7
+ export { iamRuntime } from './runtime.js';
5
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAA;AACjF,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA"}
@@ -0,0 +1,18 @@
1
+ import type { AbstractRequest, ResolvedEntity } from '@owlmeans/entrypoint';
2
+ import type { Config, Context, OIDCAuthCache } from '@owlmeans/server-oidc-rp';
3
+ /** The session record behind the request's wrapped token — the guard has already refreshed it. */
4
+ export declare const sessionOf: <C extends Config, T extends Context<C>>(context: T, request: AbstractRequest) => Promise<OIDCAuthCache>;
5
+ /**
6
+ * Every organization of the request's subject, as request entities keyed by their frozen IAM key.
7
+ *
8
+ * The session record is the authority — the provider's last claim, re-read on every validation —
9
+ * never the browser: a relying party of a tenanted client has no organization registry of its own.
10
+ */
11
+ export declare const organizationsOf: <C extends Config, T extends Context<C>>(context: T, request: AbstractRequest) => Promise<ResolvedEntity[]>;
12
+ /**
13
+ * One organization of the request's subject by its slug — for a handler that acts in an
14
+ * organization the URL names rather than in the session's acting one. One the subject is not in is
15
+ * refused as `AuthForbidden(ORGANIZATION_REFUSAL)`, exactly as the switch refuses it.
16
+ */
17
+ export declare const organizationOf: <C extends Config, T extends Context<C>>(context: T, request: AbstractRequest, entitySlug: string) => Promise<ResolvedEntity>;
18
+ //# sourceMappingURL=organization.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"organization.d.ts","sourceRoot":"","sources":["../src/organization.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAI3E,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAE9E,kGAAkG;AAClG,eAAO,MAAM,SAAS,GAAU,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,WAC3D,CAAC,WAAW,eAAe,KACnC,OAAO,CAAC,aAAa,CAMvB,CAAA;AASD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAU,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,WACjE,CAAC,WAAW,eAAe,KACnC,OAAO,CAAC,cAAc,EAAE,CAA6E,CAAA;AAExG;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAU,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,WAChE,CAAC,WAAW,eAAe,cAAc,MAAM,KACvD,OAAO,CAAC,cAAc,CAOxB,CAAA"}
@@ -0,0 +1,35 @@
1
+ import { AuthForbidden, AuthorizationError } from '@owlmeans/auth';
2
+ import { ORGANIZATION_REFUSAL } from '@owlmeans/oidc';
3
+ import { resolvedEntityOf, sessionRecord } from '@owlmeans/server-oidc-rp';
4
+ /** The session record behind the request's wrapped token — the guard has already refreshed it. */
5
+ export const sessionOf = async (context, request) => {
6
+ if (request.auth == null) {
7
+ throw new AuthorizationError('auth');
8
+ }
9
+ return sessionRecord(context, request.auth.token);
10
+ };
11
+ /**
12
+ * The organizations the provider last claimed for the session's subject. A session that acts in
13
+ * none — a client without the organizations scope — has none to offer, whatever its record holds.
14
+ */
15
+ const claimedOf = (record) => record.acting == null ? [] : record.organizations ?? [];
16
+ /**
17
+ * Every organization of the request's subject, as request entities keyed by their frozen IAM key.
18
+ *
19
+ * The session record is the authority — the provider's last claim, re-read on every validation —
20
+ * never the browser: a relying party of a tenanted client has no organization registry of its own.
21
+ */
22
+ export const organizationsOf = async (context, request) => claimedOf(await sessionOf(context, request)).map(resolvedEntityOf);
23
+ /**
24
+ * One organization of the request's subject by its slug — for a handler that acts in an
25
+ * organization the URL names rather than in the session's acting one. One the subject is not in is
26
+ * refused as `AuthForbidden(ORGANIZATION_REFUSAL)`, exactly as the switch refuses it.
27
+ */
28
+ export const organizationOf = async (context, request, entitySlug) => {
29
+ const org = claimedOf(await sessionOf(context, request)).find(org => org.entitySlug === entitySlug);
30
+ if (org == null) {
31
+ throw new AuthForbidden(ORGANIZATION_REFUSAL);
32
+ }
33
+ return resolvedEntityOf(org);
34
+ };
35
+ //# sourceMappingURL=organization.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"organization.js","sourceRoot":"","sources":["../src/organization.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAElE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAErD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAG1E,kGAAkG;AAClG,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,EAC5B,OAAU,EAAE,OAAwB,EACZ,EAAE;IAC1B,IAAI,OAAO,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;QACzB,MAAM,IAAI,kBAAkB,CAAC,MAAM,CAAC,CAAA;IACtC,CAAC;IAED,OAAO,aAAa,CAAO,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;AACzD,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,SAAS,GAAG,CAAC,MAAqB,EAA2B,EAAE,CACnE,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,IAAI,EAAE,CAAA;AAEzD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAClC,OAAU,EAAE,OAAwB,EACT,EAAE,CAAC,SAAS,CAAC,MAAM,SAAS,CAAO,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAA;AAExG;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EACjC,OAAU,EAAE,OAAwB,EAAE,UAAkB,EAC/B,EAAE;IAC3B,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,SAAS,CAAO,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,KAAK,UAAU,CAAC,CAAA;IACzG,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;QAChB,MAAM,IAAI,aAAa,CAAC,oBAAoB,CAAC,CAAA;IAC/C,CAAC;IAED,OAAO,gBAAgB,CAAC,GAAG,CAAC,CAAA;AAC9B,CAAC,CAAA"}
@@ -0,0 +1,41 @@
1
+ import type { AbstractRequest } from '@owlmeans/entrypoint';
2
+ import type { IamRuntimeGrant, IamRuntimeGrantQuery, IamRuntimeGrantRequest, IamRuntimeMember, IamRuntimeMemberInvite, IamRuntimeMemberUpdate, IamRuntimeOrganization, IamRuntimeOrganizationCreate, IamRuntimeOrganizationUpdate, IamRuntimePermission } from '@owlmeans/iam';
3
+ import type { Config, Context } from '@owlmeans/server-oidc-rp';
4
+ /**
5
+ * The runtime IAM API as the signed-in subject of one request: its organizations, their members,
6
+ * the grantable definitions and the grants. Every call acts as that subject — the provider decides
7
+ * owner and member rights from the access token, never from anything sent here.
8
+ */
9
+ export interface IamRuntimeClient {
10
+ organizations: {
11
+ list: () => Promise<IamRuntimeOrganization[]>;
12
+ /** The subject becomes the new organization's owner. */
13
+ create: (body?: IamRuntimeOrganizationCreate) => Promise<IamRuntimeOrganization>;
14
+ update: (entitySlug: string, body: IamRuntimeOrganizationUpdate) => Promise<IamRuntimeOrganization>;
15
+ };
16
+ members: {
17
+ list: (entitySlug: string) => Promise<IamRuntimeMember[]>;
18
+ /** Find-or-create by e-mail: adding an address twice answers the same member. */
19
+ add: (entitySlug: string, invite: IamRuntimeMemberInvite) => Promise<IamRuntimeMember>;
20
+ update: (entitySlug: string, profileId: string, update: IamRuntimeMemberUpdate) => Promise<IamRuntimeMember>;
21
+ remove: (entitySlug: string, profileId: string) => Promise<void>;
22
+ };
23
+ permissions: {
24
+ list: (entitySlug: string) => Promise<IamRuntimePermission[]>;
25
+ };
26
+ grants: {
27
+ list: (entitySlug: string, query?: IamRuntimeGrantQuery) => Promise<IamRuntimeGrant[]>;
28
+ assign: (entitySlug: string, grant: IamRuntimeGrantRequest) => Promise<IamRuntimeGrant>;
29
+ revoke: (entitySlug: string, grant: IamRuntimeGrantRequest) => Promise<void>;
30
+ };
31
+ }
32
+ /**
33
+ * A typed client of the provider's runtime IAM API, bound to one request.
34
+ *
35
+ * It calls as the request's subject: the bearer is the provider access token of that request's
36
+ * session record, read afresh on every call, so a token the guard has just refreshed is the one
37
+ * sent. The base URL is the provider's discovery field `IAM_API_METADATA`. Calls travel over plain
38
+ * `fetch`, because the API is another service's tree and a relying party binds none of it.
39
+ */
40
+ export declare const iamRuntime: <C extends Config, T extends Context<C>>(context: T, request: AbstractRequest) => IamRuntimeClient;
41
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAG3D,OAAO,KAAK,EACK,eAAe,EAAuB,oBAAoB,EAAE,sBAAsB,EACjG,gBAAgB,EAAE,sBAAsB,EAAwB,sBAAsB,EAAE,sBAAsB,EAC9G,4BAA4B,EAA8B,4BAA4B,EAAE,oBAAoB,EAE7G,MAAM,eAAe,CAAA;AAGtB,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAqB,MAAM,0BAA0B,CAAA;AAGlF;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE;QACb,IAAI,EAAE,MAAM,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAAA;QAC7C,wDAAwD;QACxD,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,4BAA4B,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAA;QAChF,MAAM,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,4BAA4B,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAA;KACpG,CAAA;IACD,OAAO,EAAE;QACP,IAAI,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAA;QACzD,iFAAiF;QACjF,GAAG,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAA;QACtF,MAAM,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAA;QAC5G,MAAM,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;KACjE,CAAA;IACD,WAAW,EAAE;QACX,IAAI,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAA;KAC9D,CAAA;IACD,MAAM,EAAE;QACN,IAAI,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,oBAAoB,KAAK,OAAO,CAAC,eAAe,EAAE,CAAC,CAAA;QACtF,MAAM,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB,KAAK,OAAO,CAAC,eAAe,CAAC,CAAA;QACvF,MAAM,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;KAC7E,CAAA;CACF;AA+FD;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,WACtD,CAAC,WAAW,eAAe,KACnC,gBAsFF,CAAA"}
@@ -0,0 +1,165 @@
1
+ import { AuthForbidden, AuthorizationError, AuthUnavailable } from '@owlmeans/auth';
2
+ import { ResilientError } from '@owlmeans/error';
3
+ import { IAM_RUNTIME_ROUTES, IamClientError, IamError } from '@owlmeans/iam';
4
+ import { IAM_API_METADATA, ORGANIZATION_OWNER_REFUSAL, ORGANIZATION_REFUSAL } from '@owlmeans/oidc';
5
+ import { DEFAULT_ALIAS as OIDC_CLIENT_SERVICE } from '@owlmeans/server-oidc-rp';
6
+ import { sessionOf } from './organization.js';
7
+ /** `@owlmeans/server-api`'s incident header; a production error body is nothing but this id. */
8
+ const INCIDENT_ID_HEADER = 'x-incident-id';
9
+ /**
10
+ * Discovered runtime bases, per context and per client. Discovery is a network round trip the OIDC
11
+ * client service does not memoise, and the base never changes while a provider runs. A failed
12
+ * lookup is forgotten, so the next request asks again rather than inheriting the error.
13
+ */
14
+ const bases = new WeakMap();
15
+ const runtimeBase = (context, clientId) => {
16
+ const known = bases.get(context) ?? new Map();
17
+ bases.set(context, known);
18
+ const cached = known.get(clientId);
19
+ if (cached != null) {
20
+ return cached;
21
+ }
22
+ const attempt = (async () => {
23
+ const client = await context.service(OIDC_CLIENT_SERVICE).getClient(clientId);
24
+ const base = client.getMetadata()[IAM_API_METADATA];
25
+ if (typeof base !== 'string' || base === '') {
26
+ // A provider that advertises no runtime API — Keycloak, or an integrated provider that does not
27
+ // serve it — has nothing to call.
28
+ throw new IamClientError(`runtime-api:${clientId}`);
29
+ }
30
+ return base.replace(/\/+$/, '');
31
+ })();
32
+ known.set(clientId, attempt);
33
+ attempt.catch(() => {
34
+ if (known.get(clientId) === attempt) {
35
+ known.delete(clientId);
36
+ }
37
+ });
38
+ return attempt;
39
+ };
40
+ const pathOf = (path, params = {}) => path.replace(/:(\w+)/g, (_, key) => {
41
+ const value = params[key];
42
+ if (value == null || value === '') {
43
+ throw new IamError(`runtime:param:${key}`);
44
+ }
45
+ return encodeURIComponent(value);
46
+ });
47
+ /**
48
+ * The error a refused call rejects with.
49
+ *
50
+ * A development server sends the marshalled error, which is rebuilt into its own class. A production
51
+ * one sends only an incident id, so the status and the route's access level are all there is to go
52
+ * on: a 403 from an owner-level route is the subject not owning the organization, from any other
53
+ * route the subject not being in it.
54
+ */
55
+ const failureOf = async (response, access) => {
56
+ const text = await response.text().catch(() => '');
57
+ const incidentId = response.headers.get(INCIDENT_ID_HEADER) ?? undefined;
58
+ if (text.includes(ResilientError.separator)) {
59
+ try {
60
+ const error = ResilientError.ensure(text, true);
61
+ error.incidentId ??= incidentId;
62
+ return error;
63
+ }
64
+ catch {
65
+ // Not a marshalled error after all — fall through to the status.
66
+ }
67
+ }
68
+ const error = response.status === 401 ? new AuthorizationError('iam-runtime')
69
+ : response.status === 403 ? new AuthForbidden(access === 'owner' ? ORGANIZATION_OWNER_REFUSAL : ORGANIZATION_REFUSAL)
70
+ : new IamError(`runtime:status:${response.status}`);
71
+ if (incidentId != null) {
72
+ error.incidentId = incidentId;
73
+ }
74
+ return error;
75
+ };
76
+ /**
77
+ * A typed client of the provider's runtime IAM API, bound to one request.
78
+ *
79
+ * It calls as the request's subject: the bearer is the provider access token of that request's
80
+ * session record, read afresh on every call, so a token the guard has just refreshed is the one
81
+ * sent. The base URL is the provider's discovery field `IAM_API_METADATA`. Calls travel over plain
82
+ * `fetch`, because the API is another service's tree and a relying party binds none of it.
83
+ */
84
+ export const iamRuntime = (context, request) => {
85
+ const call = async ({ method, path, access, params, query, body }) => {
86
+ const record = await sessionOf(context, request);
87
+ const clientId = record.client ?? context.service(OIDC_CLIENT_SERVICE).getDefault();
88
+ if (clientId == null) {
89
+ throw new AuthUnavailable('oidc-client');
90
+ }
91
+ const token = record.payload?.access_token;
92
+ if (token == null || token === '') {
93
+ throw new AuthForbidden('record');
94
+ }
95
+ const url = new URL(`${await runtimeBase(context, clientId)}${pathOf(path, params)}`);
96
+ Object.entries(query ?? {}).forEach(([key, value]) => {
97
+ if (value != null) {
98
+ url.searchParams.set(key, String(value));
99
+ }
100
+ });
101
+ const response = await fetch(url, {
102
+ method,
103
+ headers: {
104
+ authorization: `Bearer ${token}`,
105
+ accept: 'application/json',
106
+ ...(method === 'POST' ? { 'content-type': 'application/json' } : {}),
107
+ },
108
+ // A JSON content type with an empty body is refused by the server, so a POST always sends one.
109
+ ...(method === 'POST' ? { body: JSON.stringify(body ?? {}) } : {}),
110
+ });
111
+ if (!response.ok) {
112
+ throw await failureOf(response, access);
113
+ }
114
+ return await response.json();
115
+ };
116
+ const R = IAM_RUNTIME_ROUTES;
117
+ return {
118
+ organizations: {
119
+ list: async () => (await call({
120
+ method: 'GET', path: R.organizations, access: 'subject',
121
+ })).items,
122
+ create: async (body) => call({
123
+ method: 'POST', path: R.organizations, access: 'subject', body: body ?? {},
124
+ }),
125
+ update: async (entitySlug, body) => call({
126
+ method: 'POST', path: R.organization, access: 'owner', params: { entitySlug }, body,
127
+ }),
128
+ },
129
+ members: {
130
+ list: async (entitySlug) => (await call({
131
+ method: 'GET', path: R.members, access: 'member', params: { entitySlug },
132
+ })).items,
133
+ add: async (entitySlug, invite) => call({
134
+ method: 'POST', path: R.members, access: 'owner', params: { entitySlug }, body: invite,
135
+ }),
136
+ update: async (entitySlug, profileId, update) => call({
137
+ method: 'POST', path: R.member, access: 'owner', params: { entitySlug, profileId }, body: update,
138
+ }),
139
+ remove: async (entitySlug, profileId) => {
140
+ await call({
141
+ method: 'POST', path: R.memberRemove, access: 'owner', params: { entitySlug, profileId },
142
+ });
143
+ },
144
+ },
145
+ permissions: {
146
+ list: async (entitySlug) => (await call({
147
+ method: 'GET', path: R.permissions, access: 'member', params: { entitySlug },
148
+ })).items,
149
+ },
150
+ grants: {
151
+ list: async (entitySlug, query) => (await call({
152
+ method: 'GET', path: R.grants, access: 'owner', params: { entitySlug }, query,
153
+ })).items,
154
+ assign: async (entitySlug, grant) => call({
155
+ method: 'POST', path: R.grants, access: 'owner', params: { entitySlug }, body: grant,
156
+ }),
157
+ revoke: async (entitySlug, grant) => {
158
+ await call({
159
+ method: 'POST', path: R.grantsRevoke, access: 'owner', params: { entitySlug }, body: grant,
160
+ });
161
+ },
162
+ },
163
+ };
164
+ };
165
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/server-iam",
3
- "version": "0.1.18-rc.51",
3
+ "version": "0.1.18-rc.53",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -22,13 +22,14 @@
22
22
  }
23
23
  },
24
24
  "dependencies": {
25
- "@owlmeans/auth": "^0.1.18-rc.36",
25
+ "@owlmeans/auth": "^0.1.18-rc.37",
26
26
  "@owlmeans/context": "^0.1.18-rc.32",
27
- "@owlmeans/entrypoint": "^0.1.18-rc.37",
28
- "@owlmeans/iam": "^0.1.18-rc.44",
29
- "@owlmeans/oidc": "^0.1.18-rc.49",
30
- "@owlmeans/server-context": "^0.1.18-rc.38",
31
- "@owlmeans/server-oidc-rp": "^0.1.18-rc.49"
27
+ "@owlmeans/entrypoint": "^0.1.18-rc.38",
28
+ "@owlmeans/error": "^0.1.18-rc.36",
29
+ "@owlmeans/iam": "^0.1.18-rc.46",
30
+ "@owlmeans/oidc": "^0.1.18-rc.51",
31
+ "@owlmeans/server-context": "^0.1.18-rc.39",
32
+ "@owlmeans/server-oidc-rp": "^0.1.18-rc.51"
32
33
  },
33
34
  "devDependencies": {
34
35
  "@owlmeans/dep-config": "workspace:*",
package/src/gate.ts CHANGED
@@ -104,7 +104,8 @@ const auditEntrypoint = (ctx: Context, req: AbstractRequest, params: string[]):
104
104
  * appendIam() so target code never knows which IAM backend is active.
105
105
  *
106
106
  * 1. When the Auth carries a valid PermissionSet[] claim (integrated IAM mode),
107
- * params are asserted locally against it — both unscoped and resource-scoped forms.
107
+ * params are asserted locally against it — both unscoped and resource-scoped forms, and a set
108
+ * bound to an organization only for the organization the request acts in.
108
109
  * 2. Otherwise (Keycloak mode — its tokens never produce a conforming claim) the
109
110
  * @-suffixes are stripped and the check delegates to the UMA2 gate model from
110
111
  * @owlmeans/server-oidc-rp, byte-equivalent to makeOidcGate.
@@ -134,6 +135,11 @@ export const makeIamGate = (alias: string = OIDC_GATE, opts?: IamGateOptions): G
134
135
  // repaired in completely different places.
135
136
  const structural: string[] = []
136
137
 
138
+ // The organization the request acts in. A relying party of a tenanted client attaches it
139
+ // through the guard; otherwise it is the token's. A set bound to any other organization
140
+ // never satisfies the check.
141
+ const entitySlug = req.entity?.slug ?? entitySlugOf(auth)
142
+
137
143
  const granted = params.some(param => {
138
144
  const { permission, resource, error } = parseGateParam(param)
139
145
  // Grants are stored against the entity's stable id wherever one is resolvable; the
@@ -158,10 +164,10 @@ export const makeIamGate = (alias: string = OIDC_GATE, opts?: IamGateOptions): G
158
164
  return false
159
165
  }
160
166
 
161
- return hasPermission(auth, fixed, { resourceId: resolution.id })
167
+ return hasPermission(auth, fixed, { resourceId: resolution.id, entitySlug })
162
168
  }
163
169
 
164
- return hasPermission(auth, fixed)
170
+ return hasPermission(auth, fixed, { entitySlug })
165
171
  })
166
172
 
167
173
  if (!granted) {
package/src/index.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  export type * from '@owlmeans/iam'
2
2
  export { hasPermission } from '@owlmeans/iam'
3
+ export { ORGANIZATION_REFUSAL, ORGANIZATION_OWNER_REFUSAL } from '@owlmeans/oidc'
3
4
  export * from './consts.js'
4
5
  export * from './append.js'
5
6
  export * from './gate.js'
7
+ export { organizationOf, organizationsOf } from './organization.js'
8
+ export { iamRuntime } from './runtime.js'
9
+ export type { IamRuntimeClient } from './runtime.js'