@owlmeans/iam 0.1.18-rc.46 → 0.1.18-rc.48

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
@@ -15,7 +15,7 @@ Provider-agnostic IAM abstraction for the OwlMeans framework — `IamService` in
15
15
  ## Installation
16
16
 
17
17
  ```bash
18
- bun add @owlmeans/iam@^0.1.18-rc.46
18
+ bun add @owlmeans/iam@^0.1.18-rc.48
19
19
  ```
20
20
 
21
21
  ## Usage
@@ -38,7 +38,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
38
38
  your project's skill store (`.agents/skills/`):
39
39
 
40
40
  ```sh
41
- npx @owlmeans/agent-skills@^0.1.18-rc.45
41
+ npx @owlmeans/agent-skills@^0.1.18-rc.47
42
42
  ```
43
43
 
44
44
  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/iam",
4
- "version": "0.1.18-rc.46",
5
- "generatedAt": "2026-10-01T23:27:48.730Z",
4
+ "version": "0.1.18-rc.48",
5
+ "generatedAt": "2026-10-03T20:05:07.591Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -8,7 +8,7 @@ metadata:
8
8
 
9
9
  # Using `@owlmeans/iam`
10
10
 
11
- **Install:** `"@owlmeans/iam": "^0.1.18-rc.46"` in `dependencies`
11
+ **Install:** `"@owlmeans/iam": "^0.1.18-rc.48"` in `dependencies`
12
12
 
13
13
  Provider-agnostic IAM abstraction: the `IamService` interface, the permission and grant shapes, the
14
14
  gate-param grammar, `hasPermission`, and the declarations of the provider's runtime IAM API. It
@@ -16,41 +16,13 @@ contains no implementation and talks to no provider —
16
16
  concrete backends (a Keycloak proxy, an integrated backend built on the OwlMeans OIDC provider) are
17
17
  separate packages, and every consumer depends on this interface rather than on one of them.
18
18
 
19
- ## The browser half — `@owlmeans/client-iam`
20
-
21
- `@owlmeans/client-iam` has no skill of its own because it is a wiring package: it re-exports these
22
- types and `hasPermission`, and adds the two calls that turn "this application uses IAM" into two
23
- lines.
24
-
25
- | Export | Description |
26
- |--------|-------------|
27
- | `appendIam(context, opts?)` | Wires the OIDC guard onto a web context **and** installs the consent-before-sign-in precondition |
28
- | `withIamGuard(protocolTree, coguards?)` | Returns a decorated immutable protocol tree with IAM's OIDC guard applied |
29
- | `iamEntrypoints(dispatcherProps?)` | Browser-local OIDC bindings and dispatcher-screen binding |
30
- | `requireConsentForLogin(ctx, opts?)` | That precondition on its own, for a context wired some other way |
31
- | `CONSENT_LOGIN_PRECONDITION` / `CLIENT_IAM_SERVICE` | The precondition alias and the service alias |
32
- | `useLogin` / `useLogout`, `LoginOutcome` / `LoginIntent`, `LoginPlugin` / `LoginService` / … | Re-exported sign-in surface, so an app has one IAM import rather than three |
33
- | `listOrganizations(ctx)` | The signed-in subject's organizations (`OidcOrganizationItem[]`, the acting one marked `acting`) over `oidcProtocols.organizations` |
34
- | `switchOrganization(ctx, entitySlug)` | Moves the session into another of its organizations over `oidcProtocols.organization` and adopts the re-signed token with `adoptToken` |
35
- | `ORGANIZATION_REFUSAL` / `ORGANIZATION_OWNER_REFUSAL` | Re-exported from `@owlmeans/oidc`: the `AuthForbidden` reasons a screen phrases (not a member; not the owner) |
36
- | Everything from `@owlmeans/iam` | The types above, plus `hasPermission` |
37
-
38
- The protocol tree is immutable. Decorate it once with `withIamGuard(protocols)` before binding local
39
- screens and API callers, then spread `iamEntrypoints()` once into that browser binding list. The
40
- helper deliberately does not add declarations to a flattened entrypoint array.
41
-
42
- And the consent precondition sits on `LoginService.begin` rather than on a hook, a screen or a login
43
- plugin, because `begin` is the single funnel every sign-in mechanic passes through and the one place
44
- the user's gesture is still live — a refusal resolves as `LoginOutcome.Gesture` and opens the consent dialog in
45
- the same gesture. An application that genuinely sets no cookie passes `{ consent: { disabled: true } }`.
46
-
47
- Both organization calls need the bindings `iamEntrypoints()` spreads, and both are answered by the
48
- application's own server from the session it holds — there is no per-request organization selector,
49
- so a screen that offers "act in another organization" calls `switchOrganization` and re-reads `auth`.
50
-
51
- The server half of the same story is `@owlmeans/server-iam` — the IAM gate that asserts the grants
52
- this package's screens hand out, and the runtime IAM client. Permission decisions are made there,
53
- never in the browser.
19
+ ## Where the halves live
20
+
21
+ This package is contracts only. The browser half — `appendIam`, the session's organizations and the
22
+ switch, `hasPermission` re-exported — is `@owlmeans/client-iam` (`client-iam` skill); the server half
23
+ — the IAM gate, the session's organizations on the server and the runtime IAM client — is
24
+ `@owlmeans/server-iam` (`server-iam` skill). Permission decisions are made on the server, never in
25
+ the browser.
54
26
 
55
27
  ## Public API surface
56
28
 
@@ -220,7 +192,8 @@ interface IamService extends InitializedService {
220
192
  // A rename and a merge — never a delete, never an invention.
221
193
  normalizePermissions: (entityId: string, clientId: string, args?: IamNormalizeArgs) => Promise<IamNormalizationReport>
222
194
 
223
- // Provision a resource and assign it to the tenant's owner role
195
+ // Provision a resource and assign it to the tenant's owner role (keycloak). A no-op on the
196
+ // integrated backend, which has no owner role to assign to.
224
197
  ensureResourceOwnership: (entityId: string, clientId: string, resource: IamResourceSpec) => Promise<void>
225
198
 
226
199
  // --- Authorization (permission definitions & grants) ---
@@ -298,7 +271,7 @@ promise would hand it the wrong type — an awaited call rejects either way.
298
271
  past the membership cap), `updateMember`, `removeMember`. The last owner cannot be demoted or
299
272
  removed (`IamOrganizationError`).
300
273
  - **`groups`** — a group is (organization, client, key), kept inside the organization record; one
301
- client never reads or writes another's. `ensure`, `remove`, `members`, `addMembers`,
274
+ client never reads or writes another's. `list`, `ensure`, `remove`, `members`, `addMembers`,
302
275
  `removeMembers`, `grant` / `revoke` (bound to the group's organization), `grants`, and
303
276
  `setBundles` — a bundle (`{ filter?, permissions? }`) grants every definition it selects to the
304
277
  group's members at claim time. `IAM_MEMBERS_GROUP` is the managed group `syncStaff` keeps in the
@@ -359,8 +332,8 @@ cannot namespace it on a consumer's behalf. Uniqueness has to be *in the id*.
359
332
 
360
333
  ## `IamClient.realm` field
361
334
 
362
- `realm` is set by the provider to the entity id, so read `client.realm ?? fallbackEntityId` and never
363
- reach into an adapter's own internals (`(client as any)._realm`) for the same value.
335
+ Every implementation sets `realm` to the entity id, so read `client.realm ?? fallbackEntityId` and
336
+ never reach into an adapter's own internals (`(client as any)._realm`) for the same value.
364
337
 
365
338
  ## Issuer & redirect URIs
366
339
 
@@ -429,7 +402,6 @@ the same options — otherwise they disagree about the issuer, and only one of t
429
402
  - Do not expose `getCredentialsPair` to request handlers — it returns a raw admin token.
430
403
  - `IamClientError` means the provisioned client was invalid (missing secret, null token). The adapter
431
404
  already throws it; callers handle it rather than re-throwing a wrapped error.
432
- - `IamClient.realm` is set by every concrete implementation.
433
405
  - An operation a backend cannot support throws `IamUnsupported('<what>')`; a whole facet it cannot
434
406
  support is `unsupportedFacet(what)`. Callers turn that into a fallback — an "external console"
435
407
  link, a disabled control — rather than an error screen.
@@ -440,6 +412,8 @@ the same options — otherwise they disagree about the issuer, and only one of t
440
412
 
441
413
  - `@owlmeans/server-iam` — the IAM gate that asserts every permission kind, the gate-param grammar,
442
414
  the session's organizations and the runtime IAM client; see the `server-iam` skill
415
+ - `@owlmeans/client-iam` — browser wiring, the organization switch and `hasPermission`; see the
416
+ `client-iam` skill
443
417
  - `@owlmeans/oidc` — provider descriptors and the `iamMode` config seam; see the `oidc` skill
444
418
  - The concrete adapters (a Keycloak proxy, and the integrated backend built on
445
419
  `@owlmeans/server-oidc-provider` plus `@owlmeans/server-auth-identity`) ship separately and are
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/iam",
3
- "version": "0.1.18-rc.46",
3
+ "version": "0.1.18-rc.48",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -22,11 +22,11 @@
22
22
  }
23
23
  },
24
24
  "dependencies": {
25
- "@owlmeans/auth": "^0.1.18-rc.37",
25
+ "@owlmeans/auth": "^0.1.18-rc.38",
26
26
  "@owlmeans/context": "^0.1.18-rc.32",
27
- "@owlmeans/entrypoint": "^0.1.18-rc.38",
27
+ "@owlmeans/entrypoint": "^0.1.18-rc.39",
28
28
  "@owlmeans/error": "^0.1.18-rc.36",
29
- "@owlmeans/oidc": "^0.1.18-rc.51",
29
+ "@owlmeans/oidc": "^0.1.18-rc.53",
30
30
  "@owlmeans/route": "^0.1.18-rc.33"
31
31
  },
32
32
  "peerDependencies": {