@owlmeans/iam 0.1.18-rc.7 → 0.1.18-rc.9
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/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/iam",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-08-
|
|
4
|
+
"version": "0.1.18-rc.8",
|
|
5
|
+
"generatedAt": "2026-08-24T21:14:00.118Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -23,6 +23,9 @@ Provider-agnostic IAM abstraction. Defines `IamService` interface and related ty
|
|
|
23
23
|
| `IamPermissionDefinition` | type | Declared permission `{ name, resource, action?, resourceScoped?, title? }` |
|
|
24
24
|
| `IamGrantArgs` | type | `{ resources?: string[] }` — present = resource-scoped grant form |
|
|
25
25
|
| `IamGrant` | type | `{ profileId, clientId, permission, resources? }` |
|
|
26
|
+
| `IamUser` | type | End-user of an entity `{ profileId, email?, name?, role, disabled?, grantCount? }` |
|
|
27
|
+
| `IamUserInvite` | type | `{ email, name?, role? }` — find-or-create args |
|
|
28
|
+
| `IamUserUpdate` | type | `{ name?, role?, disabled? }` |
|
|
26
29
|
| `hasPermission` | fn | `(auth, permission, { scope?, resourceId? }?)` — checks `Authorization.permissions` PermissionSet[]; an unscoped set satisfies a resourceId check |
|
|
27
30
|
| `DEFAULT_ALIAS` | const | Default service alias `'iam-service'` |
|
|
28
31
|
| `IAM_MODE_KEYCLOAK` | const | `'keycloak'` |
|
|
@@ -62,8 +65,15 @@ interface IamService extends InitializedService {
|
|
|
62
65
|
|
|
63
66
|
// Provision an OIDC client for a tenant and return its credentials.
|
|
64
67
|
// options.redirectUris hardens the client; always pass them (see "Issuer & redirect URIs").
|
|
68
|
+
// MUST refuse an existing record owned by another entity — see "A client id is a global name".
|
|
65
69
|
ensureClient: (entityId: string, clientId: string, options?: IamClientOptions) => Promise<IamClient>
|
|
66
70
|
|
|
71
|
+
// Reserve a free client id without provisioning it; false when anyone else holds it.
|
|
72
|
+
claimClient: (entityId: string, clientId: string) => Promise<boolean>
|
|
73
|
+
|
|
74
|
+
// Release a client id and everything keyed by it (called when a project/slot is deleted).
|
|
75
|
+
deleteClient: (entityId: string, clientId: string) => Promise<void>
|
|
76
|
+
|
|
67
77
|
// Provision a named permission on a client resource; returns the resource name
|
|
68
78
|
ensurePermission: (entityId: string, clientId: string, resource?: string, args?: IamPermissionArgs) => Promise<string>
|
|
69
79
|
|
|
@@ -83,9 +93,49 @@ interface IamService extends InitializedService {
|
|
|
83
93
|
|
|
84
94
|
// List grants for the client, optionally for one subject
|
|
85
95
|
listGrants: (entityId: string, clientId: string, profileId?: string) => Promise<IamGrant[]>
|
|
96
|
+
|
|
97
|
+
// --- End-user management (customer-wide users, shared per entityId) ---
|
|
98
|
+
|
|
99
|
+
// Every end-user of the entity. clientId scopes the reported grantCount — it does NOT filter.
|
|
100
|
+
listUsers: (entityId: string, clientId?: string) => Promise<IamUser[]>
|
|
101
|
+
getUser: (entityId: string, profileId: string) => Promise<IamUser | null>
|
|
102
|
+
// Find-or-create by email — the one way an end-user record comes into existence.
|
|
103
|
+
inviteUser: (entityId: string, invite: IamUserInvite) => Promise<IamUser>
|
|
104
|
+
updateUser: (entityId: string, profileId: string, update: IamUserUpdate) => Promise<IamUser>
|
|
105
|
+
removeUser: (entityId: string, profileId: string) => Promise<void>
|
|
86
106
|
}
|
|
87
107
|
```
|
|
88
108
|
|
|
109
|
+
## End-users are customer-wide
|
|
110
|
+
|
|
111
|
+
An end-user belongs to the **entity**, not to a project: one customer's users are shared across every
|
|
112
|
+
project they own. `listUsers` therefore returns the entity's whole set and `clientId` only scopes the
|
|
113
|
+
reported `grantCount` to one project's client. Do not reintroduce a per-client filter — a person who
|
|
114
|
+
has authenticated against a project but holds no grant there is still that project's user, and the
|
|
115
|
+
screen that hands out grants is exactly where they have to be visible.
|
|
116
|
+
|
|
117
|
+
`inviteUser` is idempotent by email, which is what lets an invitation and a first sign-in converge on
|
|
118
|
+
one record instead of accumulating twins. Backends that have no user store of their own (keycloak,
|
|
119
|
+
where the customer manages realm users in its console) throw `IamUnsupported('user-management')` from
|
|
120
|
+
all five methods; callers turn that into an "external console" fallback rather than an error.
|
|
121
|
+
|
|
122
|
+
## A client id is a global name
|
|
123
|
+
|
|
124
|
+
A provider resolves a client from the bare `client_id` a relying party sends — the adapter gets no
|
|
125
|
+
tenant context — so the id is unique across the whole deployment, not per entity, and the store
|
|
126
|
+
cannot namespace it on a consumer's behalf. Uniqueness has to be *in the id*.
|
|
127
|
+
|
|
128
|
+
- Compose it with the owning entity in it. A name derived from a project's own name alone lets two
|
|
129
|
+
organizations share one registration: one secret, one redirect-URI list, one permission-definition
|
|
130
|
+
set, and one grant namespace, because `PermissionSet.scope` is that same string.
|
|
131
|
+
- `ensureClient` refuses a record belonging to another entity rather than returning it. The failure
|
|
132
|
+
is deliberate — a loud provisioning error is the only alternative to a silent cross-tenant handover.
|
|
133
|
+
- Assign an id **once** and reserve it with `claimClient`. Whatever a consumer derives ids from
|
|
134
|
+
(a project alias, a slug) may later be released and re-used by a sibling, so only the registry can
|
|
135
|
+
say whether an id is free. `deleteClient` gives it back, or a deleted project's name is burned.
|
|
136
|
+
- Backends whose clients are already per-tenant (keycloak realms) satisfy this by construction:
|
|
137
|
+
`claimClient` returns true and `deleteClient` may be `IamUnsupported`.
|
|
138
|
+
|
|
89
139
|
## `IamClient.realm` field
|
|
90
140
|
|
|
91
141
|
`realm` is set by the provider to the entity ID. Use it instead of accessing internal properties:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/iam",
|
|
3
|
-
"version": "0.1.18-rc.
|
|
3
|
+
"version": "0.1.18-rc.9",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"@owlmeans/auth": "^0.1.18-rc.6",
|
|
26
26
|
"@owlmeans/context": "^0.1.18-rc.6",
|
|
27
27
|
"@owlmeans/error": "^0.1.18-rc.6",
|
|
28
|
-
"@owlmeans/oidc": "^0.1.18-rc.
|
|
28
|
+
"@owlmeans/oidc": "^0.1.18-rc.8"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@owlmeans/dep-config": "workspace:*",
|