@owlmeans/server-iam 0.1.18-rc.7 → 0.1.18

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.
Files changed (53) hide show
  1. package/README.md +13 -4
  2. package/agent-meta/manifest.json +3 -3
  3. package/agent-meta/skills/server-iam/SKILL.md +142 -16
  4. package/build/append.d.ts +1 -5
  5. package/build/append.d.ts.map +1 -1
  6. package/build/append.js.map +1 -1
  7. package/build/consts.local.d.ts +3 -0
  8. package/build/consts.local.d.ts.map +1 -0
  9. package/build/consts.local.js +3 -0
  10. package/build/consts.local.js.map +1 -0
  11. package/build/gate.d.ts +13 -14
  12. package/build/gate.d.ts.map +1 -1
  13. package/build/gate.js +128 -35
  14. package/build/gate.js.map +1 -1
  15. package/build/index.d.ts +6 -0
  16. package/build/index.d.ts.map +1 -1
  17. package/build/index.js +3 -0
  18. package/build/index.js.map +1 -1
  19. package/build/organization/types.d.ts +21 -0
  20. package/build/organization/types.d.ts.map +1 -0
  21. package/build/organization/types.js +2 -0
  22. package/build/organization/types.js.map +1 -0
  23. package/build/organization.d.ts +9 -0
  24. package/build/organization.d.ts.map +1 -0
  25. package/build/organization.js +30 -0
  26. package/build/organization.js.map +1 -0
  27. package/build/runtime.d.ts +15 -0
  28. package/build/runtime.d.ts.map +1 -0
  29. package/build/runtime.js +167 -0
  30. package/build/runtime.js.map +1 -0
  31. package/build/types.d.ts +42 -0
  32. package/build/types.d.ts.map +1 -0
  33. package/build/types.js +2 -0
  34. package/build/types.js.map +1 -0
  35. package/build/types.local.d.ts +16 -0
  36. package/build/types.local.d.ts.map +1 -0
  37. package/build/types.local.js +2 -0
  38. package/build/types.local.js.map +1 -0
  39. package/package.json +12 -10
  40. package/src/append.ts +1 -4
  41. package/src/consts.local.ts +2 -0
  42. package/src/gate.ts +157 -45
  43. package/src/index.ts +6 -0
  44. package/src/organization/types.ts +21 -0
  45. package/src/organization.ts +49 -0
  46. package/src/runtime.ts +193 -0
  47. package/src/types.local.ts +18 -0
  48. package/src/types.ts +43 -0
  49. package/tests/context.ts +100 -0
  50. package/tests/gate.spec.ts +132 -4
  51. package/tests/organization.spec.ts +52 -0
  52. package/tests/runtime.spec.ts +169 -0
  53. package/tests/tsconfig.json +12 -0
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
+ - `makeOrganizationScope(context, request)` — `.organizationOf(slug)` / `.organizationsOf()`: the organizations of a tenanted session, from its session record
11
+ - `makeIamRuntimeClient(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
17
+ bun add @owlmeans/server-iam@^0.1.18
16
18
  ```
17
19
 
18
20
  ## Usage
@@ -20,12 +22,19 @@ bun add @owlmeans/server-iam
20
22
  ```typescript
21
23
  import { appendIam, makeIamGate } from '@owlmeans/server-iam'
22
24
  import { IAM_GATE } from '@owlmeans/server-iam'
25
+ import { contract, protocol, typed } from '@owlmeans/entrypoint'
26
+ import { route } from '@owlmeans/route'
27
+ import { DEFAULT_GUARD } from '@owlmeans/auth-common'
23
28
 
24
29
  // Wire IAM into a server context
25
30
  appendIam(context)
26
31
 
27
- // Gate a module with a permission check
28
- module.gate(IAM_GATE, { permission: 'manage-users' })
32
+ // Gate a protocol with a permission check
33
+ protocol(
34
+ route('users', '/users'),
35
+ contract(typed<User[]>()),
36
+ { guards: DEFAULT_GUARD, gate: { alias: IAM_GATE, params: ['manage-users@{entity}'] } },
37
+ )
29
38
  ```
30
39
 
31
40
  Requires `@owlmeans/oidc` OIDC shared config and a running IAM provider configured via `@owlmeans/server-oidc-rp`.
@@ -38,7 +47,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
38
47
  your project's skill store (`.agents/skills/`):
39
48
 
40
49
  ```sh
41
- npx @owlmeans/agent-skills
50
+ npx @owlmeans/agent-skills@^0.1.18
42
51
  ```
43
52
 
44
53
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
- "schemaVersion": 2,
2
+ "schemaVersion": 3,
3
3
  "package": "@owlmeans/server-iam",
4
- "version": "0.1.18-rc.6",
5
- "generatedAt": "2026-08-18T14:48:00.502Z",
4
+ "version": "0.1.18",
5
+ "generatedAt": "2026-10-28T00:31:34.720Z",
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). Use when gating server endpoints in IAM consumers (e.g. the viable target template backend). 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 (makeOrganizationScope: organizationOf/organizationsOf), the request-bound runtime IAM client (makeIamRuntimeClient), 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,34 +8,91 @@ metadata:
8
8
 
9
9
  # Using `@owlmeans/server-iam`
10
10
 
11
+ **Install:** `"@owlmeans/server-iam": "^0.1.18"` in `dependencies`
12
+
11
13
  Boilerplate-less server-side consumer of the OwlMeans IAM. One call wires the OIDC RP stack and the
12
- 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.
13
17
 
14
18
  ## Public API surface
15
19
 
16
20
  | Symbol | Kind | Purpose |
17
21
  |--------|------|---------|
18
22
  | `appendIam(context)` | fn | Registers `makeOidcClientService()`, `makeOidcWrappingService()`, `makeIamGate()` (under `OIDC_GATE`), and the OIDC guard |
19
- | `makeIamGate(alias?)` | fn | The IAM `GateService` — claims-first assertion with UMA2 fallback |
20
- | `parseGateParam(param)` | fn | Splits `'perm@requestParam'` into `{ permission, resourceParam? }` |
21
- | `RESOURCE_PARAM_SEPARATOR` | const | `'@'` |
23
+ | `makeIamGate(alias?, opts?)` | fn | The IAM `GateService` — claims-first assertion with UMA2 fallback |
24
+ | `IamGateOptions` | type | `{ strictResourceScope?: boolean }` — refuse a scoped param on the fallback path instead of widening it |
25
+ | `gateParamHelper` (`.parseGateParam` / `.parseGateSelector` / `.formatGateParam`) / `gateValidationHelper.validateGateParams` / `resolveGateResource` | helper / fn | Re-exports from `@owlmeans/iam`, which owns the grammar |
26
+ | `RESOURCE_PARAM_SEPARATOR` / `RESOURCE_SOURCE_SEPARATOR` / `RESOURCE_PATH_SEPARATOR` | const | `'@'`, `':'` and `'.'`, re-exported |
27
+ | `GateParamSource` / `GateParamErrorCode` / `GateResolutionFailure` | enum | Re-exported; the sources a selector may name, and why one failed |
22
28
  | `hasPermission` | fn | Re-export from `@owlmeans/iam` |
29
+ | `makeOrganizationScope(context, request).organizationOf(entitySlug)` | scope | 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
+ | `makeOrganizationScope(context, request).organizationsOf()` | scope | Every organization of the request's subject, as `ResolvedEntity[]` |
31
+ | `makeIamRuntimeClient(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 |
34
+ | `SERVER_IAM_SERVICE` | const | `'server-iam-service'` — exported and referenced nowhere; the gate registers under `OIDC_GATE`, not under this |
35
+ | Every `@owlmeans/iam` type | type | Re-exported wholesale, so a server needs one IAM import |
36
+
37
+ The grammar lives in **`@owlmeans/iam`**, not here — the browser, both IAM adapters and
38
+ code-generation tooling all have to read the same rules. This package re-exports it because it is the
39
+ documented import path for a server, so one IAM import covers the gate and its grammar.
40
+
41
+ `appendIam` reads the provider configuration from `cfg.oidc.providers`, so populate that before
42
+ calling it. It is the IAM-flavoured replacement for wiring `@owlmeans/server-oidc-rp` by hand: the
43
+ one difference is the gate, which is `makeIamGate()` rather than `makeOidcGate()`.
23
44
 
24
45
  ## Gate-param syntax
25
46
 
26
47
  ```
27
- <permission> → unscoped (project-wide): 'article--modify'
28
- <permission>@<requestParam> → resource-scoped: 'department--modify@departmentId'
48
+ <permission> → unscoped (project-wide): 'article--modify'
49
+ <permission>@<bareKey> → bare form: 'department--modify@departmentId'
50
+ <permission>@<source>:<path> → qualified form: 'order--modify@body:order.id'
29
51
  ```
30
52
 
31
- - `{entity}` substitution applies to the permission part in both forms.
32
- - For the resource-scoped form the gate resolves `req.params[requestParam] ?? req.query[requestParam]`
33
- at assert time and requires the grant's `resources[]` to contain it (an unscoped grant always passes).
34
- - Params are **any-of**: one granted param is enough.
53
+ Sources: `params`, `query`, `body`, `headers`, `auth`.
54
+
55
+ - **The bare form is lenient and flat**; the qualified form is strict and nested. `@a.b` in the bare
56
+ form is the literal key `"a.b"` — a query key may legally contain a dot, and splitting it would
57
+ change what already-deployed selectors mean. Dotted paths exist only after a `source:`.
58
+ - The separator is `:` and not a dot on purpose. With a dotted-only form, a route that gains a param
59
+ named like a source keyword silently changes a selector's meaning: `/report/:query` with
60
+ `@query.id` flips from "the route param named `query.id`" to "the query string's `id`" — an
61
+ authorization lookup redirected to attacker-supplied input by an unrelated route rename.
62
+ - The bare form searches `params` then `query`. That query fallback is legacy and deliberately kept:
63
+ removing it would silently deny deployed apps whose selector names a query key.
64
+ - `headers:` lowercases its first segment and unwraps a single-element array; a repeated header is
65
+ refused rather than silently narrowed to one of its values.
66
+ - `auth:` is the only source that is neither attacker-controlled nor subject to `removeAdditional`.
67
+ Prefer it whenever the rule is "bound to their own profile or entity".
68
+ - A resolved id must satisfy the grant's `resources[]` — **and a grant carrying no `resources` at all
69
+ covers every id**. That is the all-resources form of the same permission, not a separate one.
70
+ - Params are **any-of**: one granted param is enough. Which is why a malformed or unresolvable param
71
+ can only contribute `false` and never throw — otherwise one bad sibling would refuse a request the
72
+ subject is legitimately entitled to.
73
+
74
+ **The `@` belongs to the gate and nowhere else.** It is never part of a permission's stored NAME. A
75
+ definition or grant written as `enquiry--view@enquiryId` is a key nothing looks up, so the permission
76
+ reads as granted in every screen and every request is still refused, with nothing logged.
77
+ `ensurePermission` refuses such a name; `gateValidationHelper.validateGateParams` catches the mirror-image mistake at
78
+ generation time.
79
+
80
+ ### `gateValidationHelper.validateGateParams(params, audit?)`
81
+
82
+ Reports what is provable from an entrypoint's own declaration: a selector naming a route param the
83
+ path does not carry, or a `body`/`query`/`headers` key the `Filter` does not declare **while that
84
+ schema sets `additionalProperties: false`**. That qualifier matters — the server's AJV runs
85
+ `removeAdditional`, which strips only what a schema closes, so without it the check would fire on
86
+ every unfiltered endpoint and be ignored within a week. It never decides a request; the gate runs it
87
+ once per alias and only logs.
88
+
89
+ The route half of the audit comes from `entrypoint.mount()`, which needs a service with a resolvable
90
+ address. An entrypoint that has none is still audited against its filter — the route-param check is
91
+ simply skipped rather than the whole audit being lost.
35
92
 
36
93
  ```ts
37
94
  // module declaration
38
- gate(OIDC_GATE, ['department--modify@depId'])
95
+ { gate: { alias: OIDC_GATE, params: ['department--modify@depId'] } }
39
96
 
40
97
  // or imperative
41
98
  await ctx.service<GateService>(OIDC_GATE).assert(req, res, ['article--modify'])
@@ -46,10 +103,76 @@ await ctx.service<GateService>(OIDC_GATE).assert(req, res, ['article--modify'])
46
103
  1. `req.auth == null` → `AuthForbidden('auth')`.
47
104
  2. **Claims mode** — when `req.auth.permissions` is a valid `PermissionSet[]` (minted into the id_token
48
105
  by the integrated IAM provider and mapped into `Auth` by `@owlmeans/server-oidc-rp`), params are
49
- asserted locally via `hasPermission`.
106
+ asserted locally via `hasPermission`, with `entitySlug: req.entity?.slug ?? authHelper.entitySlugOf(auth)` —
107
+ the organization the request acts in.
50
108
  3. **Fallback** — otherwise `@…` suffixes are stripped and the check delegates to the UMA2 gate model
51
- from `@owlmeans/server-oidc-rp` (`createGateModel().loadPermissions`) — byte-equivalent to the old
52
- `makeOidcGate`, which keeps Keycloak-backed deployments unchanged.
109
+ from `@owlmeans/server-oidc-rp` (`createGateModel().loadPermissions`) — exactly the check
110
+ `makeOidcGate` performs, which is what a Keycloak-backed deployment gets.
111
+
112
+ In claims mode all four permission kinds (`iam` skill, "Permission kinds") are decided locally by
113
+ `hasPermission`; a bound set reaching the gate is a leak the relying party should have stripped, and
114
+ it counts only when the request acts in exactly its organization.
115
+
116
+ Which branch runs is decided per request by the token alone: a conforming `permissions` claim selects
117
+ claims mode, its absence selects the fallback. Nothing configures it, and there is no environment
118
+ variable behind it.
119
+
120
+ **The fallback path does not enforce resource scoping**, so one declaration means two different
121
+ things depending on which backend answered. It cannot be fixed by denying — a Keycloak-backed adapter
122
+ throws `IamUnsupported('resource-scoped-grant')`, so a scoped grant cannot exist there and denying
123
+ would lock every user out of every scoped endpoint. The gate logs each such param once per alias;
124
+ `makeIamGate(alias, { strictResourceScope: true })` opts into denial where the refusals are the
125
+ intended outcome.
126
+
127
+ A denial distinguishes its reasons in the log: a param that failed **structurally** (unparseable,
128
+ source absent, value not a scalar) is reported separately from one that resolved fine and simply was
129
+ not granted. The HTTP answer is identical — `AuthForbidden('permission')` either way — because the
130
+ caller must never learn which.
131
+
132
+ ## `{entity}` in a permission name
133
+
134
+ A gate param may carry the literal `{entity}`, which is substituted at assertion time with the
135
+ organization entity's **stable id** — `req.entity.id`, resolved once at the server boundary — falling
136
+ back to the token's `entitySlug` and then to `-`. So `my-service-account-{entity}` asserts a
137
+ per-organization permission without the declaration knowing which organization it is for.
138
+
139
+ Grants are stored against the id wherever one is resolvable. The slug fallback exists for deployments
140
+ with no organization store of their own, where the slug *is* the identifier; a deployment that has a
141
+ store must not grant against slugs, because a rename would then orphan every grant.
142
+
143
+ ## Organizations of the session
144
+
145
+ A relying party of a tenanted client has no organization registry of its own: the provider's
146
+ `organizations` claim, kept in the session record (`@owlmeans/server-oidc-rp`), is the only source,
147
+ re-read on every validation. `makeOrganizationScope(context, request).organizationsOf()` answers all of it and `.organizationOf(slug)` one entry by
148
+ slug, as `ResolvedEntity` values keyed by the frozen `entityKey` — use them for a handler that acts in
149
+ an organization its URL names rather than the session's acting one (which the guard already attaches
150
+ as `req.entity`). A session of a client without the scope acts in none and has none to offer; an
151
+ unauthenticated request or a session that is gone is `AuthorizationError` (401).
152
+
153
+ ## The runtime IAM client
154
+
155
+ `makeIamRuntimeClient(context, request)` calls the provider's runtime IAM API (`makeIamRuntimeProtocols` in
156
+ `@owlmeans/iam`) as that request's subject:
157
+
158
+ - **Base** — the provider's discovery field `IAM_API_METADATA` (`owlmeans_iam_api`), read through
159
+ the OIDC client service's adapter `getMetadata()` for the session's client, and cached per context
160
+ and client; a failed lookup is forgotten. A provider that advertises none (Keycloak) is
161
+ `IamClientError('runtime-api:<client>')`.
162
+ - **Bearer** — the provider access token of the request's session record, read afresh on every call
163
+ so a token the guard just refreshed is the one sent. A record without one is
164
+ `AuthForbidden('record')`, and nothing is sent.
165
+ - **Transport** — plain `fetch` over `IAM_RUNTIME_ROUTES`: the API is another service's tree, and a
166
+ relying party binds none of it.
167
+ - **Refusals** — a development server's marshalled error is rebuilt into its class. A production body
168
+ is only an incident id, so the status and the route decide: a 403 from an owner route is
169
+ `AuthForbidden(ORGANIZATION_OWNER_REFUSAL)`, from any other route `AuthForbidden(ORGANIZATION_REFUSAL)`;
170
+ a 401 is `AuthorizationError('iam-runtime')`; anything else `IamError('runtime:status:<n>')`. The
171
+ incident id travels on the error.
172
+
173
+ `members.add` is find-or-create by e-mail and idempotent — that is the provider's job, so a caller
174
+ never checks for an existing member first. Owner and member rights are decided by the provider from
175
+ the token; nothing sent here can widen them.
53
176
 
54
177
  ## Rules
55
178
 
@@ -57,7 +180,10 @@ await ctx.service<GateService>(OIDC_GATE).assert(req, res, ['article--modify'])
57
180
  `OIDC_GATE` alias and the IAM gate already covers the UMA2 path.
58
181
  - Never branch on the IAM backend in consumer code — the gate's claims/fallback split is the only seam.
59
182
  - The OIDC provider entry must request the `permissions` scope (`extraScopes`) for claims mode to
60
- engage; without it the gate transparently uses the fallback.
183
+ engage; without it the gate transparently uses the fallback. A tenanted client also requests
184
+ `organizations`.
185
+ - Reach the runtime IAM API only through `makeIamRuntimeClient`; never forward the browser's wrapped token or
186
+ build a bearer from anything but the session record.
61
187
 
62
188
  ## Related instructions
63
189
 
package/build/append.d.ts CHANGED
@@ -1,7 +1,4 @@
1
- import type { ServerConfig, ServerContext } from '@owlmeans/server-context';
2
- import type { WithSharedConfig } from '@owlmeans/oidc';
3
- type IamServerConfig = ServerConfig & WithSharedConfig;
4
- type IamServerContext<C extends IamServerConfig = IamServerConfig> = ServerContext<C>;
1
+ import type { IamServerConfig, IamServerContext } from './types.local.js';
5
2
  /**
6
3
  * One-call OIDC RP setup for a server context.
7
4
  *
@@ -15,5 +12,4 @@ type IamServerContext<C extends IamServerConfig = IamServerConfig> = ServerConte
15
12
  * context.cfg.oidc.providers[] — populate it before calling appendIam().
16
13
  */
17
14
  export declare const appendIam: <C extends IamServerConfig, T extends IamServerContext<C>>(context: T) => T;
18
- export {};
19
15
  //# sourceMappingURL=append.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"append.d.ts","sourceRoot":"","sources":["../src/append.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAC3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAItD,KAAK,eAAe,GAAG,YAAY,GAAG,gBAAgB,CAAA;AACtD,KAAK,gBAAgB,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,IAAI,aAAa,CAAC,CAAC,CAAC,CAAA;AAErF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,eAAe,EAAE,CAAC,SAAS,gBAAgB,CAAC,CAAC,CAAC,WACvE,CAAC,KACT,CAQF,CAAA"}
1
+ {"version":3,"file":"append.d.ts","sourceRoot":"","sources":["../src/append.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAGzE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,eAAe,EAAE,CAAC,SAAS,gBAAgB,CAAC,CAAC,CAAC,WACvE,CAAC,KACT,CAQF,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"append.js","sourceRoot":"","sources":["../src/append.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAC1G,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAKvC;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,OAAU,EACP,EAAE;IACL,OAAO,CAAC,eAAe,CAAC,qBAAqB,EAAE,CAAC,CAAA;IAChD,OAAO,CAAC,eAAe,CAAC,uBAAuB,EAAE,CAAC,CAAA;IAClD,sEAAsE;IACtE,gEAAgE;IAChE,OAAO,CAAC,eAAe,CAAC,WAAW,EAAE,CAAC,CAAA;IACtC,eAAe,CAAO,OAAO,CAAC,CAAA;IAC9B,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA"}
1
+ {"version":3,"file":"append.js","sourceRoot":"","sources":["../src/append.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAC1G,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAIvC;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,OAAU,EACP,EAAE;IACL,OAAO,CAAC,eAAe,CAAC,qBAAqB,EAAE,CAAC,CAAA;IAChD,OAAO,CAAC,eAAe,CAAC,uBAAuB,EAAE,CAAC,CAAA;IAClD,sEAAsE;IACtE,gEAAgE;IAChE,OAAO,CAAC,eAAe,CAAC,WAAW,EAAE,CAAC,CAAA;IACtC,eAAe,CAAO,OAAO,CAAC,CAAA;IAC9B,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA"}
@@ -0,0 +1,3 @@
1
+ /** `@owlmeans/server-api`'s incident header; a production error body is nothing but this id. */
2
+ export declare const INCIDENT_ID_HEADER = "x-incident-id";
3
+ //# sourceMappingURL=consts.local.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.local.d.ts","sourceRoot":"","sources":["../src/consts.local.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,eAAO,MAAM,kBAAkB,kBAAkB,CAAA"}
@@ -0,0 +1,3 @@
1
+ /** `@owlmeans/server-api`'s incident header; a production error body is nothing but this id. */
2
+ export const INCIDENT_ID_HEADER = 'x-incident-id';
3
+ //# sourceMappingURL=consts.local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.local.js","sourceRoot":"","sources":["../src/consts.local.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAA"}
package/build/gate.d.ts CHANGED
@@ -1,27 +1,26 @@
1
1
  import type { GateService } from '@owlmeans/entrypoint';
2
+ import type { IamGateOptions } from './types.js';
2
3
  /**
3
- * Gate-param syntax:
4
- * <permission> — unscoped (project-wide) check, e.g. 'article--modify'
5
- * <permission>@<requestParam> — resource-scoped: the resource id is resolved from
6
- * req.params[requestParam] ?? req.query[requestParam]
7
- * and must be listed in the grant's resources[]
8
- * '{entity}' substitution applies to the permission part in both forms.
4
+ * The gate-param grammar lives in `@owlmeans/iam` so the browser, the adapters and code-generation
5
+ * tooling can all read it. Re-exported here because this module was its home and is the documented
6
+ * import path.
9
7
  */
10
- export declare const RESOURCE_PARAM_SEPARATOR = "@";
11
- export interface ParsedGateParam {
12
- permission: string;
13
- resourceParam?: string;
14
- }
15
- export declare const parseGateParam: (param: string) => ParsedGateParam;
8
+ export { RESOURCE_PARAM_SEPARATOR, RESOURCE_SOURCE_SEPARATOR, RESOURCE_PATH_SEPARATOR, GateParamSource, GateParamErrorCode, GateResolutionFailure, gateParamHelper, gateValidationHelper, resolveGateResource } from '@owlmeans/iam';
9
+ export type { ParsedGateParam, GateResourceSelector, GateParamAudit, GateParamHelper, GateValidationHelper } from '@owlmeans/iam';
16
10
  /**
17
11
  * IAM gate: claims-first with UMA2 fallback. Registered under the OIDC_GATE alias by
18
12
  * appendIam() so target code never knows which IAM backend is active.
19
13
  *
20
14
  * 1. When the Auth carries a valid PermissionSet[] claim (integrated IAM mode),
21
- * params are asserted locally against it — both unscoped and resource-scoped forms.
15
+ * params are asserted locally against it — both unscoped and resource-scoped forms, and a set
16
+ * bound to an organization only for the organization the request acts in.
22
17
  * 2. Otherwise (Keycloak mode — its tokens never produce a conforming claim) the
23
18
  * @-suffixes are stripped and the check delegates to the UMA2 gate model from
24
19
  * @owlmeans/server-oidc-rp, byte-equivalent to makeOidcGate.
20
+ *
21
+ * Params are OR'd: holding any one of them admits the request. That is why a malformed or
22
+ * unresolvable param can only contribute `false` and never throw — a sibling that would otherwise
23
+ * have passed must not be refused because of it.
25
24
  */
26
- export declare const makeIamGate: (alias?: string) => GateService;
25
+ export declare const makeIamGate: (alias?: string, opts?: IamGateOptions) => GateService;
27
26
  //# sourceMappingURL=gate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAmB,MAAM,sBAAsB,CAAA;AAQxE;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,MAAM,CAAA;AAE3C,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,eAAO,MAAM,cAAc,UAAW,MAAM,KAAG,eAO9C,CAAA;AAUD;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,WAAW,MAAM,KAAe,WAgDvD,CAAA"}
1
+ {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAqC,MAAM,sBAAsB,CAAA;AAM1F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD;;;;GAIG;AACH,OAAO,EACL,wBAAwB,EAAE,yBAAyB,EAAE,uBAAuB,EAC5E,eAAe,EAAE,kBAAkB,EAAE,qBAAqB,EAC1D,eAAe,EAAE,oBAAoB,EAAE,mBAAmB,EAC3D,MAAM,eAAe,CAAA;AACtB,YAAY,EAAE,eAAe,EAAE,oBAAoB,EAAE,cAAc,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AAejI;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,WAAW,WAAW,MAAM,SAAqB,cAAc,KAAG,WAkK9E,CAAA"}
package/build/gate.js CHANGED
@@ -1,73 +1,166 @@
1
1
  import { createLazyService } from '@owlmeans/context';
2
+ import { authHelper, AuthForbidden } from '@owlmeans/auth';
2
3
  import { OIDC_GATE } from '@owlmeans/oidc';
3
- import { AuthForbidden } from '@owlmeans/auth';
4
4
  import { createGateModel, extractPermissionSets } from '@owlmeans/server-oidc-rp';
5
- import { hasPermission } from '@owlmeans/iam';
5
+ import { hasPermission, gateParamHelper, gateValidationHelper, resolveGateResource, GateResolutionFailure } from '@owlmeans/iam';
6
+ import { logger, logThrottle } from '@owlmeans/log';
6
7
  /**
7
- * Gate-param syntax:
8
- * <permission> — unscoped (project-wide) check, e.g. 'article--modify'
9
- * <permission>@<requestParam> — resource-scoped: the resource id is resolved from
10
- * req.params[requestParam] ?? req.query[requestParam]
11
- * and must be listed in the grant's resources[]
12
- * '{entity}' substitution applies to the permission part in both forms.
8
+ * The gate-param grammar lives in `@owlmeans/iam` so the browser, the adapters and code-generation
9
+ * tooling can all read it. Re-exported here because this module was its home and is the documented
10
+ * import path.
13
11
  */
14
- export const RESOURCE_PARAM_SEPARATOR = '@';
15
- export const parseGateParam = (param) => {
16
- const idx = param.indexOf(RESOURCE_PARAM_SEPARATOR);
17
- if (idx < 0) {
18
- return { permission: param };
19
- }
20
- return { permission: param.slice(0, idx), resourceParam: param.slice(idx + 1) };
21
- };
22
- const resolveResourceId = (req, resourceParam) => {
23
- const params = req.params;
24
- const query = req.query;
25
- const value = params?.[resourceParam] ?? query?.[resourceParam];
26
- return value != null ? `${value}` : undefined;
27
- };
12
+ export { RESOURCE_PARAM_SEPARATOR, RESOURCE_SOURCE_SEPARATOR, RESOURCE_PATH_SEPARATOR, GateParamSource, GateParamErrorCode, GateResolutionFailure, gateParamHelper, gateValidationHelper, resolveGateResource } from '@owlmeans/iam';
13
+ /**
14
+ * Structural faults are logged once per alias+param+reason.
15
+ *
16
+ * A gate on a hot endpoint runs on every request, and a misconfiguration is by definition permanent,
17
+ * so an un-deduplicated log turns one typo into the incident.
18
+ */
19
+ const reported = new Set();
20
+ const log = logger('server-iam:gate');
21
+ /** Aliases whose declarations have already been checked. */
22
+ const audited = new Set();
28
23
  /**
29
24
  * IAM gate: claims-first with UMA2 fallback. Registered under the OIDC_GATE alias by
30
25
  * appendIam() so target code never knows which IAM backend is active.
31
26
  *
32
27
  * 1. When the Auth carries a valid PermissionSet[] claim (integrated IAM mode),
33
- * params are asserted locally against it — both unscoped and resource-scoped forms.
28
+ * params are asserted locally against it — both unscoped and resource-scoped forms, and a set
29
+ * bound to an organization only for the organization the request acts in.
34
30
  * 2. Otherwise (Keycloak mode — its tokens never produce a conforming claim) the
35
31
  * @-suffixes are stripped and the check delegates to the UMA2 gate model from
36
32
  * @owlmeans/server-oidc-rp, byte-equivalent to makeOidcGate.
33
+ *
34
+ * Params are OR'd: holding any one of them admits the request. That is why a malformed or
35
+ * unresolvable param can only contribute `false` and never throw — a sibling that would otherwise
36
+ * have passed must not be refused because of it.
37
37
  */
38
- export const makeIamGate = (alias = OIDC_GATE) => {
38
+ export const makeIamGate = (alias = OIDC_GATE, opts) => {
39
+ const reportOnce = (key, message, data) => {
40
+ if (reported.has(key)) {
41
+ return;
42
+ }
43
+ reported.add(key);
44
+ log.error(message, data);
45
+ };
46
+ /**
47
+ * A refusal of the gate. Logged once a minute per route, reason and subject — a hot endpoint
48
+ * refuses on every request, and one line per window says the same thing.
49
+ */
50
+ const forbidden = (req, reason, params) => {
51
+ const auth = req.auth;
52
+ if (logThrottle(`access.forbidden\0${req.alias ?? ''}\0${reason}\0${auth?.userId ?? ''}`, 60_000)) {
53
+ log.warn('Access forbidden', {
54
+ alias: req.alias, reason, params, userId: auth?.userId, entitySlug: req.entity?.slug ?? (auth != null ? authHelper.entitySlugOf(auth) : undefined),
55
+ }, { event: 'access.forbidden' });
56
+ }
57
+ return new AuthForbidden(reason);
58
+ };
59
+ /**
60
+ * Check an entrypoint's gate params against what it declares, once, the first time it is reached.
61
+ *
62
+ * Structurally unable to affect the outcome: it only ever logs. A selector naming a key the
63
+ * entrypoint's filter does not declare is stripped by validation before the gate runs, so the
64
+ * endpoint denies every request with a clean build and nothing else to go on — this is the line that
65
+ * says why.
66
+ */
67
+ const auditEntrypoint = (ctx, req, params) => {
68
+ if (req.alias == null || audited.has(req.alias)) {
69
+ return;
70
+ }
71
+ audited.add(req.alias);
72
+ try {
73
+ const entrypoint = ctx.entrypoint(req.alias);
74
+ if (entrypoint == null) {
75
+ return;
76
+ }
77
+ let routePath;
78
+ try {
79
+ routePath = entrypoint.mount();
80
+ }
81
+ catch {
82
+ // The mount needs a service with an address; an entrypoint that has none still has a filter
83
+ // worth auditing.
84
+ }
85
+ const audit = {
86
+ ...(routePath != null ? { routePath } : {}),
87
+ ...(entrypoint.filter != null ? { filter: entrypoint.filter } : {})
88
+ };
89
+ for (const issue of gateValidationHelper.validateGateParams(params, audit)) {
90
+ log.error('Gate param is misconfigured', {
91
+ param: issue.param, alias: req.alias, code: issue.code, detail: issue.detail,
92
+ });
93
+ }
94
+ }
95
+ catch {
96
+ // An entrypoint that cannot be looked up is not this gate's problem to report.
97
+ }
98
+ };
39
99
  const service = createLazyService(alias, {
40
100
  assert: async (req, _, params) => {
41
101
  await service.ready();
42
102
  const ctx = service.assertCtx();
43
103
  if (req.auth == null) {
44
- throw new AuthForbidden('auth');
104
+ throw forbidden(req, 'auth', params);
45
105
  }
46
106
  const auth = req.auth;
47
107
  const sets = extractPermissionSets(auth.permissions);
48
108
  if (sets != null) {
109
+ auditEntrypoint(ctx, req, params);
110
+ // Why a param failed, kept aside so a denial can say whether the gate was misconfigured or
111
+ // the subject simply lacks the grant. The two look identical from the outside and are
112
+ // repaired in completely different places.
113
+ const structural = [];
114
+ // The organization the request acts in. A relying party of a tenanted client attaches it
115
+ // through the guard; otherwise it is the token's. A set bound to any other organization
116
+ // never satisfies the check.
117
+ const entitySlug = req.entity?.slug ?? authHelper.entitySlugOf(auth);
49
118
  const granted = params.some(param => {
50
- const { permission, resourceParam } = parseGateParam(param);
51
- const fixed = permission.replaceAll('{entity}', auth.entityId ?? '-');
52
- if (resourceParam != null) {
53
- const resourceId = resolveResourceId(req, resourceParam);
54
- if (resourceId == null) {
119
+ const { permission, resource, error } = gateParamHelper.parseGateParam(param);
120
+ // Grants are stored against the entity's stable id wherever one is resolvable; the
121
+ // slug is only a fallback for deployments with no organization store of their own,
122
+ // where the slug IS the identifier.
123
+ const fixed = permission.replaceAll('{entity}', req.entity?.id ?? authHelper.entitySlugOf(auth) ?? '-');
124
+ if (error != null) {
125
+ structural.push(`"${param}" does not parse [${error.code}]: ${error.detail}`);
126
+ return false;
127
+ }
128
+ if (resource != null) {
129
+ const resolution = resolveGateResource(req, resource);
130
+ if (resolution.id == null) {
131
+ if (resolution.reason !== GateResolutionFailure.NotProvided) {
132
+ structural.push(`"${param}" resolved no resource id [${resolution.reason}]`
133
+ + ` from ${resource.sources.join(', ')}`);
134
+ }
55
135
  return false;
56
136
  }
57
- return hasPermission(auth, fixed, { resourceId });
137
+ return hasPermission(auth, fixed, { resourceId: resolution.id, entitySlug });
58
138
  }
59
- return hasPermission(auth, fixed);
139
+ return hasPermission(auth, fixed, { entitySlug });
60
140
  });
61
141
  if (!granted) {
62
- throw new AuthForbidden('permission');
142
+ structural.forEach(detail => reportOnce(`${req.alias}\0${detail}`, 'Gate refused a request it could not evaluate', { alias: req.alias, detail }));
143
+ throw forbidden(req, 'permission', params);
63
144
  }
64
145
  return;
65
146
  }
66
- const stripped = params.map(param => parseGateParam(param).permission);
147
+ // UMA2 fallback. Resource scoping is NOT enforced here: a Keycloak-backed deployment cannot
148
+ // store a resource-scoped grant, so a scoped param widens to a project-wide check and the same
149
+ // declaration means two different things depending on which backend is active.
150
+ const scoped = params.filter(param => gateParamHelper.parseGateParam(param).resource != null);
151
+ scoped.forEach(param => reportOnce(`uma2\0${req.alias}\0${param}`, 'Gate param is resource-scoped, but resource scoping is not enforced in UMA2 mode — it is checked as a '
152
+ + 'project-wide permission', { param, alias: req.alias }));
153
+ const usable = opts?.strictResourceScope === true
154
+ ? params.filter(param => gateParamHelper.parseGateParam(param).resource == null)
155
+ : params;
156
+ if (usable.length < 1) {
157
+ throw forbidden(req, 'permission', params);
158
+ }
159
+ const stripped = usable.map(param => gateParamHelper.parseGateParam(param).permission);
67
160
  const model = createGateModel(ctx);
68
161
  const permissions = await model.loadPermissions(auth, stripped);
69
162
  if (permissions.length < 1) {
70
- throw new AuthForbidden('permission');
163
+ throw forbidden(req, 'permission', params);
71
164
  }
72
165
  }
73
166
  });
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,12 @@
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 type * from './types.js';
8
+ export { makeOrganizationScope, organizationOf, organizationsOf } from './organization.js';
9
+ export type * from './organization/types.js';
10
+ export { makeIamRuntimeClient, iamRuntime } from './runtime.js';
11
+ export type { IamRuntimeClient } from './types.js';
6
12
  //# 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,mBAAmB,YAAY,CAAA;AAC/B,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAC1F,mBAAmB,yBAAyB,CAAA;AAC5C,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC/D,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,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 { makeOrganizationScope, organizationOf, organizationsOf } from './organization.js';
7
+ export { makeIamRuntimeClient, 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;AAEzB,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAE1F,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA"}