@fabrika/iam-contract 0.9.0-dev.1 → 0.9.0-dev.3
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/package.json +6 -5
- package/types/src/access.d.ts +25 -0
- package/types/src/index.d.ts +603 -0
- package/types/src/oauth.d.ts +136 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fabrika/iam-contract",
|
|
3
|
-
"version": "0.9.0-dev.
|
|
3
|
+
"version": "0.9.0-dev.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -11,12 +11,13 @@
|
|
|
11
11
|
"access": "public"
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
|
-
"src"
|
|
14
|
+
"src",
|
|
15
|
+
"types"
|
|
15
16
|
],
|
|
16
17
|
"exports": {
|
|
17
18
|
".": {
|
|
18
19
|
"bun": "./src/index.ts",
|
|
19
|
-
"types": "./src/index.ts",
|
|
20
|
+
"types": "./types/src/index.d.ts",
|
|
20
21
|
"default": "./src/index.ts"
|
|
21
22
|
}
|
|
22
23
|
},
|
|
@@ -24,7 +25,7 @@
|
|
|
24
25
|
"typecheck": "tsc --noEmit"
|
|
25
26
|
},
|
|
26
27
|
"dependencies": {
|
|
27
|
-
"@fabrika/app": "0.9.0-dev.
|
|
28
|
-
"@fabrika/auth-core": "0.9.0-dev.
|
|
28
|
+
"@fabrika/app": "0.9.0-dev.3",
|
|
29
|
+
"@fabrika/auth-core": "0.9.0-dev.3"
|
|
29
30
|
}
|
|
30
31
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type AppSchema, type PermissionEntry } from '@fabrika/auth-core';
|
|
2
|
+
/** Durable IAM application id of IAM itself — a deployed identity, not configuration branding (ADR-0024). */
|
|
3
|
+
export declare const IAM_APP_ID = "propustka";
|
|
4
|
+
export declare const IAM_ACTIONS: Readonly<{
|
|
5
|
+
/** Administer identity and access for the installation. Global only; it reaches every application. */
|
|
6
|
+
ADMIN: "iam.admin";
|
|
7
|
+
/** Manage the people of applications, never of the installation's own apps. Global, or in `app` values. */
|
|
8
|
+
USERS_MANAGE: "iam.users.manage";
|
|
9
|
+
}>;
|
|
10
|
+
export declare const IAM_SCOPES: Readonly<{
|
|
11
|
+
/** One application environment, by its IAM id `<app>@<env>`. */
|
|
12
|
+
APP: "app";
|
|
13
|
+
}>;
|
|
14
|
+
/** The preset role a user administrator holds. */
|
|
15
|
+
export declare const USER_ADMIN_ROLE = "user-admin";
|
|
16
|
+
/** What a holder of IAM permissions administers: the installation, or the people of some applications (`null` = all). */
|
|
17
|
+
export type IamAdministrationScope = {
|
|
18
|
+
readonly kind: 'installation';
|
|
19
|
+
} | {
|
|
20
|
+
readonly kind: 'users';
|
|
21
|
+
readonly apps: readonly string[] | null;
|
|
22
|
+
};
|
|
23
|
+
/** The administration IAM permissions confer, or null for none. IAM adds that a user administrator acts from a session. */
|
|
24
|
+
export declare function iamAdministrationScope(permissions: PermissionEntry[]): IamAdministrationScope | null;
|
|
25
|
+
export declare const IAM_APP_SCHEMA: AppSchema;
|
|
@@ -0,0 +1,603 @@
|
|
|
1
|
+
import type { RpcProcedureContract } from '@fabrika/app';
|
|
2
|
+
import type { AppActionDef, AppDelegationDef, AppLinkMountDef, AppMemberItem, AppSchema, AppScopeDef, Locale, PermissionEntry, PermissionSource, PrincipalAffiliation, PrincipalType, RoleDef, WorkloadKind } from '@fabrika/auth-core';
|
|
3
|
+
/** A plain list response. */
|
|
4
|
+
export interface ListResponse<T> {
|
|
5
|
+
items: T[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* A cursor-paginated list. `nextCursor` is the opaque cursor to pass as `before`
|
|
9
|
+
* for the next page; null when there are no more rows.
|
|
10
|
+
*/
|
|
11
|
+
export interface CursorList<T> {
|
|
12
|
+
items: T[];
|
|
13
|
+
nextCursor: string | null;
|
|
14
|
+
}
|
|
15
|
+
/** Derived principal status — invited (unclaimed) → active → disabled. */
|
|
16
|
+
export type PrincipalStatus = 'invited' | 'active' | 'disabled';
|
|
17
|
+
export interface PrincipalListItem {
|
|
18
|
+
id: string;
|
|
19
|
+
type: PrincipalType;
|
|
20
|
+
label: string;
|
|
21
|
+
email: string | null;
|
|
22
|
+
externalId: string | null;
|
|
23
|
+
status: PrincipalStatus;
|
|
24
|
+
/** `external`: a person from outside the organization, who holds only roles declared external (ADR-0051). */
|
|
25
|
+
affiliation: PrincipalAffiliation;
|
|
26
|
+
createdAt: number;
|
|
27
|
+
}
|
|
28
|
+
export interface GrantDto {
|
|
29
|
+
id: string;
|
|
30
|
+
principalId: string;
|
|
31
|
+
/** Named role/policy key; null for an inline grant (then `permissions` is set). */
|
|
32
|
+
roleKey: string | null;
|
|
33
|
+
/** Inline action-pattern set; null for a role-based grant (then `roleKey` is set). */
|
|
34
|
+
permissions: string[] | null;
|
|
35
|
+
/** Scope dimension; null = global (both rise/fall together). */
|
|
36
|
+
scopeType: string | null;
|
|
37
|
+
/** Opaque, app-owned scope value; null = global. */
|
|
38
|
+
scopeValue: string | null;
|
|
39
|
+
/** App id this grant applies to; null = all apps (cross-app). */
|
|
40
|
+
app: string | null;
|
|
41
|
+
grantedBy: string | null;
|
|
42
|
+
expiresAt: number | null;
|
|
43
|
+
createdAt: number;
|
|
44
|
+
/** True when `roleKey` is set but no longer resolves to a known role (zero perms). */
|
|
45
|
+
dangling: boolean;
|
|
46
|
+
}
|
|
47
|
+
export interface PrincipalDetail extends PrincipalListItem {
|
|
48
|
+
/** The mailbox is listed in `IAM_BOOTSTRAP_ADMINS`: an internal user with it is an installation admin. */
|
|
49
|
+
bootstrapMailbox: boolean;
|
|
50
|
+
grants: GrantDto[];
|
|
51
|
+
/** Effective permissions with `source` (grant / bootstrap / group:<ref>). */
|
|
52
|
+
permissions: PermissionEntry[];
|
|
53
|
+
authentication: {
|
|
54
|
+
/** OIDC is linked when this user has claimed an identity-provider subject. */
|
|
55
|
+
oidc: {
|
|
56
|
+
state: 'unavailable' | 'unlinked' | 'linked';
|
|
57
|
+
};
|
|
58
|
+
/** Password access is globally opt-in and then enabled per user. */
|
|
59
|
+
password: {
|
|
60
|
+
state: 'unavailable' | 'disabled' | 'pending' | 'enabled';
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export interface InviteRequest {
|
|
65
|
+
email: string;
|
|
66
|
+
/** Omitted: `internal`. An `external` user signs in by password only, and OIDC never claims it. */
|
|
67
|
+
affiliation?: PrincipalAffiliation;
|
|
68
|
+
}
|
|
69
|
+
export interface UpdatePrincipalRequest {
|
|
70
|
+
disabled: boolean;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* A browser session, as an operator sees it. Never the cookie or its hash — a session is addressed
|
|
74
|
+
* by `id`, which is not a credential.
|
|
75
|
+
*/
|
|
76
|
+
export interface SessionDto {
|
|
77
|
+
id: string;
|
|
78
|
+
principalId: string;
|
|
79
|
+
/** How the human proved who they were; `local_dev` is IAM's `LOCAL_DEV_LOGIN` bypass. */
|
|
80
|
+
authenticationMethod: 'oidc' | 'password' | 'local_dev';
|
|
81
|
+
/** The app this session is bound to (an ADR-0022 child); null = the IAM session itself. */
|
|
82
|
+
app: string | null;
|
|
83
|
+
/** The IAM session this was derived from; null on an IAM session. Revoking that one revokes this. */
|
|
84
|
+
parentSessionId: string | null;
|
|
85
|
+
/** `active` until it is revoked or its expiry passes. A revoked session reads `revoked`. */
|
|
86
|
+
status: 'active' | 'revoked' | 'expired';
|
|
87
|
+
createdAt: number;
|
|
88
|
+
expiresAt: number;
|
|
89
|
+
revokedAt: number | null;
|
|
90
|
+
}
|
|
91
|
+
export interface CreateGrantRequest {
|
|
92
|
+
principalId: string;
|
|
93
|
+
/** A named role/policy key — XOR `permissions` (supply exactly one). */
|
|
94
|
+
roleKey?: string;
|
|
95
|
+
/** An inline action-pattern set — XOR `roleKey` (supply exactly one). */
|
|
96
|
+
permissions?: string[];
|
|
97
|
+
/** Scope dimension; null / omitted = global. Both-or-neither with `scopeValue`. */
|
|
98
|
+
scopeType?: string | null;
|
|
99
|
+
/** Opaque, app-owned scope value; null / omitted = global. */
|
|
100
|
+
scopeValue?: string | null;
|
|
101
|
+
/** App id (a registered app id); null / omitted = all apps (cross-app). */
|
|
102
|
+
app?: string | null;
|
|
103
|
+
expiresAt?: number | null;
|
|
104
|
+
}
|
|
105
|
+
/** The set of app ids propustka knows about — the choices for a grant's `app`. */
|
|
106
|
+
export interface AppDto {
|
|
107
|
+
id: string;
|
|
108
|
+
/** The name the app's schema declares for people; null when it declares none. */
|
|
109
|
+
displayName: string | null;
|
|
110
|
+
/** One of the installation's own apps (a platform audience): a user administrator grants nothing there (ADR-0058). */
|
|
111
|
+
installation: boolean;
|
|
112
|
+
}
|
|
113
|
+
/** A role available for the calling app — a built-in or a DB row (app/custom). */
|
|
114
|
+
export interface RoleDto {
|
|
115
|
+
key: string;
|
|
116
|
+
name: string;
|
|
117
|
+
description?: string;
|
|
118
|
+
permissions: string[];
|
|
119
|
+
/** 'builtin' (cross-app, e.g. admin), 'app' (reconciled), or 'custom' (admin-made). */
|
|
120
|
+
origin: 'builtin' | 'app' | 'custom';
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The body of `PUT /admin/apps/:app/schema` — one of the two surviving REST operations, because a
|
|
124
|
+
* deploy step reconciles an app's vocabulary from outside the installation. It IS the core
|
|
125
|
+
* `AppSchema`; `reconcileSchema` in `@fabrika/auth` sends exactly this.
|
|
126
|
+
*/
|
|
127
|
+
export type PutAppSchemaRequest = AppSchema;
|
|
128
|
+
/** GET schema response — the app's scopes, actions, and origin='app' roles. */
|
|
129
|
+
export interface AppSchemaDto {
|
|
130
|
+
app: string;
|
|
131
|
+
scopes: AppScopeDef[];
|
|
132
|
+
actions: AppActionDef[];
|
|
133
|
+
/** role_key -> def, origin='app' only (reconciled from code). */
|
|
134
|
+
roles: Record<string, RoleDef>;
|
|
135
|
+
/** Share-link mounts; absent when the app declares none. */
|
|
136
|
+
links?: Record<string, AppLinkMountDef>;
|
|
137
|
+
/** Endpoints that may spend membership authority; absent when the app declares none. */
|
|
138
|
+
delegations?: Record<string, AppDelegationDef>;
|
|
139
|
+
/** The language IAM speaks to the app's people; absent when the app declares none. */
|
|
140
|
+
locale?: Locale;
|
|
141
|
+
/** The name IAM shows for the app; absent when the app declares none. */
|
|
142
|
+
displayName?: string;
|
|
143
|
+
/** The actions that manage and list the app's own people; absent when the app declares none. */
|
|
144
|
+
members?: {
|
|
145
|
+
manage: string;
|
|
146
|
+
list: string;
|
|
147
|
+
};
|
|
148
|
+
/** `labels` when the app's directory shows labels to `members.list` holders. */
|
|
149
|
+
directory?: 'labels';
|
|
150
|
+
}
|
|
151
|
+
/** A custom policy (an origin='custom' role row) for an app. */
|
|
152
|
+
export interface PolicyDto {
|
|
153
|
+
app: string;
|
|
154
|
+
key: string;
|
|
155
|
+
name: string;
|
|
156
|
+
description?: string;
|
|
157
|
+
permissions: string[];
|
|
158
|
+
createdAt: number;
|
|
159
|
+
}
|
|
160
|
+
export interface CreatePolicyRequest {
|
|
161
|
+
key: string;
|
|
162
|
+
name: string;
|
|
163
|
+
description?: string;
|
|
164
|
+
permissions: string[];
|
|
165
|
+
}
|
|
166
|
+
export interface UpdatePolicyRequest {
|
|
167
|
+
name: string;
|
|
168
|
+
description?: string;
|
|
169
|
+
permissions: string[];
|
|
170
|
+
}
|
|
171
|
+
/** Metadata view of a service-principal API key (the secret is never returned here). */
|
|
172
|
+
export interface ApiKeyDto {
|
|
173
|
+
principalId: string;
|
|
174
|
+
label: string;
|
|
175
|
+
status: PrincipalStatus;
|
|
176
|
+
grants: GrantDto[];
|
|
177
|
+
createdAt: number;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* One API key with its live credential. Its permissions are its grants, edited with `grants.create` and
|
|
181
|
+
* `grants.delete` by an installation administrator and read again at every token exchange.
|
|
182
|
+
*/
|
|
183
|
+
export interface ApiKeyDetail extends ApiKeyDto {
|
|
184
|
+
/** The key's live credential, or null when none is live (a revoked key). */
|
|
185
|
+
credential: ApiKeyCredentialDto | null;
|
|
186
|
+
}
|
|
187
|
+
export interface ApiKeyCredentialDto {
|
|
188
|
+
/** The app the key mints tokens for; null = every app. A grant at another app never reaches the key's token. */
|
|
189
|
+
app: string | null;
|
|
190
|
+
expiresAt: number | null;
|
|
191
|
+
}
|
|
192
|
+
export interface ProvisionApiKeyRequest {
|
|
193
|
+
label: string;
|
|
194
|
+
type: 'service';
|
|
195
|
+
/** A named role/policy key — XOR `permissions` (supply exactly one). */
|
|
196
|
+
roleKey?: string;
|
|
197
|
+
/** An inline action-pattern set — XOR `roleKey` (supply exactly one). */
|
|
198
|
+
permissions?: string[];
|
|
199
|
+
/** Scope dimension; null / omitted = global. Both-or-neither with `scopeValue`. */
|
|
200
|
+
scopeType?: string | null;
|
|
201
|
+
/** Opaque, app-owned scope value; null / omitted = global. */
|
|
202
|
+
scopeValue?: string | null;
|
|
203
|
+
/** App id (a registered app id); null / omitted = all apps. */
|
|
204
|
+
app?: string | null;
|
|
205
|
+
expiresAt?: number | null;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Provisioning result. `apiKey` (a `px_…` credential bound to the new service principal) is shown
|
|
209
|
+
* exactly ONCE — copy it now, it is not retrievable later. Carried as a bearer (or a declared
|
|
210
|
+
* header) directly to apps on the propustka-native path (`mintFromKey`), no Cloudflare Access in
|
|
211
|
+
* front.
|
|
212
|
+
*/
|
|
213
|
+
export interface ProvisionApiKeyResponse {
|
|
214
|
+
principalId: string;
|
|
215
|
+
apiKey: string;
|
|
216
|
+
}
|
|
217
|
+
/** Rotation result — new key shown once; principal + grants unchanged, old key stops working. */
|
|
218
|
+
export interface RotateApiKeyResponse {
|
|
219
|
+
principalId: string;
|
|
220
|
+
apiKey: string;
|
|
221
|
+
}
|
|
222
|
+
/** Metadata only — never the token hash or plaintext. A share link is an anonymous credential. */
|
|
223
|
+
export interface ShareLinkListItem {
|
|
224
|
+
id: string;
|
|
225
|
+
label: string | null;
|
|
226
|
+
/** The app this link mints for. Never null on a link issued after the app binding (SEC-2). */
|
|
227
|
+
app: string | null;
|
|
228
|
+
issuedBy: string | null;
|
|
229
|
+
expiresAt: number | null;
|
|
230
|
+
revokedAt: number | null;
|
|
231
|
+
createdAt: number;
|
|
232
|
+
/** Frozen inline grants — matched by `permits` (action + optional scope), not exact resource. */
|
|
233
|
+
grants: {
|
|
234
|
+
action: string;
|
|
235
|
+
scope: {
|
|
236
|
+
type: string;
|
|
237
|
+
value: string;
|
|
238
|
+
} | null;
|
|
239
|
+
}[];
|
|
240
|
+
}
|
|
241
|
+
export interface IssueShareLinkRequest {
|
|
242
|
+
/**
|
|
243
|
+
* The app the link mints for. REQUIRED: an anonymous credential's grants are frozen at issue, so
|
|
244
|
+
* the app they were delegated against is the only thing that keeps them from being spent at every
|
|
245
|
+
* other app behind the proxy.
|
|
246
|
+
*/
|
|
247
|
+
app: string;
|
|
248
|
+
/** Each grant is delegation-checked against the issuer on its `scope` (omitted → global). */
|
|
249
|
+
grants: {
|
|
250
|
+
action: string;
|
|
251
|
+
scope?: {
|
|
252
|
+
type: string;
|
|
253
|
+
value: string;
|
|
254
|
+
} | null;
|
|
255
|
+
}[];
|
|
256
|
+
label?: string;
|
|
257
|
+
/** Absolute expiry (unix seconds). REQUIRED, and at most `MAX_KEY_LIFETIME_SECONDS` ahead: no key is permanent. */
|
|
258
|
+
expiresAt: number;
|
|
259
|
+
}
|
|
260
|
+
/** Issued share-link result — the plaintext `px_` token returned ONCE. */
|
|
261
|
+
export interface IssuedShareLinkResponse {
|
|
262
|
+
id: string;
|
|
263
|
+
token: string;
|
|
264
|
+
}
|
|
265
|
+
export interface AuditEventDto {
|
|
266
|
+
id: string;
|
|
267
|
+
requestId: string;
|
|
268
|
+
principalId: string | null;
|
|
269
|
+
principalLabel: string;
|
|
270
|
+
credentialId: string | null;
|
|
271
|
+
app: string;
|
|
272
|
+
action: string;
|
|
273
|
+
resourceType: string;
|
|
274
|
+
resourceId: string | null;
|
|
275
|
+
diff: unknown;
|
|
276
|
+
metadata: unknown;
|
|
277
|
+
createdAt: number;
|
|
278
|
+
}
|
|
279
|
+
export interface AuthLogDto {
|
|
280
|
+
id: number;
|
|
281
|
+
requestId: string;
|
|
282
|
+
app: string;
|
|
283
|
+
/** A login attempt or a mint (`authenticate`), or the end of a session (`logout`). */
|
|
284
|
+
kind: 'authenticate' | 'logout';
|
|
285
|
+
principalId: string | null;
|
|
286
|
+
credentialId: string | null;
|
|
287
|
+
decision: 'allow' | 'deny';
|
|
288
|
+
reason: string | null;
|
|
289
|
+
/** The login opened a session of an installation administrator. */
|
|
290
|
+
privileged: boolean;
|
|
291
|
+
createdAt: number;
|
|
292
|
+
}
|
|
293
|
+
export interface MeDto {
|
|
294
|
+
id: string;
|
|
295
|
+
type: PrincipalType;
|
|
296
|
+
label: string;
|
|
297
|
+
permissions: PermissionEntry[];
|
|
298
|
+
}
|
|
299
|
+
export interface OkResponse {
|
|
300
|
+
ok: true;
|
|
301
|
+
}
|
|
302
|
+
/** Every admin list takes the same page coordinates: an opaque `before` cursor and a row `limit`. */
|
|
303
|
+
export interface PageInput {
|
|
304
|
+
before?: string;
|
|
305
|
+
limit?: number;
|
|
306
|
+
}
|
|
307
|
+
export interface ListPrincipalsInput extends PageInput {
|
|
308
|
+
type?: PrincipalType;
|
|
309
|
+
status?: PrincipalStatus;
|
|
310
|
+
q?: string;
|
|
311
|
+
}
|
|
312
|
+
export type ListApiKeysInput = PageInput;
|
|
313
|
+
export interface ListShareLinksInput extends PageInput {
|
|
314
|
+
/** Only links minting for this app. */
|
|
315
|
+
app?: string;
|
|
316
|
+
/** Revoked links are hidden by default; the console asks for them explicitly. */
|
|
317
|
+
includeRevoked?: boolean;
|
|
318
|
+
}
|
|
319
|
+
export interface RotateApiKeyInput extends ApiKeyIdInput {
|
|
320
|
+
/**
|
|
321
|
+
* New absolute expiry (unix seconds), or `null` for none. OMITTED carries the replaced
|
|
322
|
+
* credential's expiry forward — rotation replaces a secret, it does not extend a lifetime.
|
|
323
|
+
*/
|
|
324
|
+
expiresAt?: number | null;
|
|
325
|
+
}
|
|
326
|
+
/** Every procedure that names a principal keys it as `principalId`; a bare `id` names some other resource. */
|
|
327
|
+
export interface PrincipalIdInput {
|
|
328
|
+
principalId: string;
|
|
329
|
+
}
|
|
330
|
+
export interface ListSessionsInput extends PageInput {
|
|
331
|
+
/** Whose sessions to list. */
|
|
332
|
+
principalId: string;
|
|
333
|
+
}
|
|
334
|
+
export interface SessionIdInput {
|
|
335
|
+
id: string;
|
|
336
|
+
}
|
|
337
|
+
/** How many rows the call actually revoked; an already-revoked session is not counted again. */
|
|
338
|
+
export interface RevokedSessionsResponse {
|
|
339
|
+
revoked: number;
|
|
340
|
+
}
|
|
341
|
+
export interface UpdatePrincipalInput extends UpdatePrincipalRequest, PrincipalIdInput {
|
|
342
|
+
}
|
|
343
|
+
/** Password action sent by email, or a one-time URL shown only in this response. */
|
|
344
|
+
export type PasswordActionDelivery = {
|
|
345
|
+
delivery: 'email';
|
|
346
|
+
email: string;
|
|
347
|
+
expiresAt: number;
|
|
348
|
+
} | {
|
|
349
|
+
delivery: 'manual';
|
|
350
|
+
url: string;
|
|
351
|
+
expiresAt: number;
|
|
352
|
+
};
|
|
353
|
+
export interface GrantIdInput {
|
|
354
|
+
id: string;
|
|
355
|
+
}
|
|
356
|
+
export interface AppInput {
|
|
357
|
+
app: string;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* The public origins IAM will hand this app a session at (ADR-0022). Declarative: the whole set is
|
|
361
|
+
* stated, so removing one is the same call as adding one and a stale entry cannot survive a deploy.
|
|
362
|
+
*/
|
|
363
|
+
export interface SetReturnOriginsRequest {
|
|
364
|
+
app: string;
|
|
365
|
+
origins: string[];
|
|
366
|
+
}
|
|
367
|
+
export interface ReturnOriginsDto {
|
|
368
|
+
app: string;
|
|
369
|
+
/** Canonicalized: lower-cased scheme and host, default port dropped, no path. */
|
|
370
|
+
origins: string[];
|
|
371
|
+
}
|
|
372
|
+
export interface ListRolesInput {
|
|
373
|
+
app?: string | null;
|
|
374
|
+
}
|
|
375
|
+
export interface CreatePolicyInput {
|
|
376
|
+
app: string;
|
|
377
|
+
policy: CreatePolicyRequest;
|
|
378
|
+
}
|
|
379
|
+
export interface UpdatePolicyInput {
|
|
380
|
+
app: string;
|
|
381
|
+
key: string;
|
|
382
|
+
policy: UpdatePolicyRequest;
|
|
383
|
+
}
|
|
384
|
+
export interface PolicyIdInput {
|
|
385
|
+
app: string;
|
|
386
|
+
key: string;
|
|
387
|
+
}
|
|
388
|
+
export interface ApiKeyIdInput {
|
|
389
|
+
principalId: string;
|
|
390
|
+
}
|
|
391
|
+
export interface ShareLinkIdInput {
|
|
392
|
+
id: string;
|
|
393
|
+
}
|
|
394
|
+
export interface ListAuditInput {
|
|
395
|
+
resourceType?: string;
|
|
396
|
+
resourceId?: string;
|
|
397
|
+
principalId?: string;
|
|
398
|
+
action?: string;
|
|
399
|
+
requestId?: string;
|
|
400
|
+
before?: string;
|
|
401
|
+
limit?: number;
|
|
402
|
+
}
|
|
403
|
+
export interface ListAuthLogInput {
|
|
404
|
+
principalId?: string;
|
|
405
|
+
requestId?: string;
|
|
406
|
+
decision?: 'allow' | 'deny';
|
|
407
|
+
before?: string;
|
|
408
|
+
limit?: number;
|
|
409
|
+
}
|
|
410
|
+
type RpcProcedure<TInput, TOutput> = RpcProcedureContract<TInput, TOutput>;
|
|
411
|
+
/** Browser-safe typed contract for the IAM administration API. */
|
|
412
|
+
/**
|
|
413
|
+
* Issue a fresh key for one workload of an application (ADR-0022 "Principal kinds"). The key resolves to the actions
|
|
414
|
+
* the app's reconciled schema declares for the workload. Every other key of the workload is revoked except `keep`.
|
|
415
|
+
*/
|
|
416
|
+
export interface IssueWorkloadKeyRequest {
|
|
417
|
+
/** The IAM application id `<app>@<env>`. */
|
|
418
|
+
app: string;
|
|
419
|
+
/**
|
|
420
|
+
* `public` is a public procedure, named by its endpoint id; `app` is the connector of the application named `name`,
|
|
421
|
+
* which consumes an export of `app`.
|
|
422
|
+
*/
|
|
423
|
+
kind: WorkloadKind;
|
|
424
|
+
name: string;
|
|
425
|
+
/** Absolute expiry (unix seconds), after now and at most 30 days ahead. */
|
|
426
|
+
expiresAt: number;
|
|
427
|
+
/**
|
|
428
|
+
* The credential id of the key the caller has recorded as active, which stays valid until the caller replaces it; null
|
|
429
|
+
* when it has none. A key issued earlier and never recorded is an orphan, and is revoked.
|
|
430
|
+
*/
|
|
431
|
+
keep: string | null;
|
|
432
|
+
}
|
|
433
|
+
/** The plaintext key is returned once. */
|
|
434
|
+
export interface IssueWorkloadKeyResponse {
|
|
435
|
+
key: string;
|
|
436
|
+
credentialId: string;
|
|
437
|
+
principalId: string;
|
|
438
|
+
expiresAt: number;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Revoke every key of one workload of `app`, the key a rotation kept included: what Control asks when a consumer stops
|
|
442
|
+
* importing from `app`. A workload with no principal has nothing to revoke.
|
|
443
|
+
*/
|
|
444
|
+
export interface RevokeWorkloadKeysRequest {
|
|
445
|
+
app: string;
|
|
446
|
+
kind: WorkloadKind;
|
|
447
|
+
name: string;
|
|
448
|
+
}
|
|
449
|
+
export interface RevokeWorkloadKeysResponse {
|
|
450
|
+
revoked: number;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Issue a fresh membership transport key for one application environment (ADR-0022 rule 6). The key authenticates
|
|
454
|
+
* IAM's `/membership/*` surface and pins the application id; it carries no authority. Every other key of the
|
|
455
|
+
* application environment is deleted except `keep`.
|
|
456
|
+
*/
|
|
457
|
+
export interface IssueMembershipKeyRequest {
|
|
458
|
+
/** The IAM application id `<app>@<env>`. */
|
|
459
|
+
app: string;
|
|
460
|
+
/** Absolute expiry (unix seconds), after now and at most 30 days ahead. */
|
|
461
|
+
expiresAt: number;
|
|
462
|
+
/** The id of the key the caller has recorded as active, which stays valid until the caller replaces it; null when it has none. */
|
|
463
|
+
keep: string | null;
|
|
464
|
+
}
|
|
465
|
+
/** The issued key, shown once. `id` is what a later issuance keeps. */
|
|
466
|
+
export interface IssueMembershipKeyResponse {
|
|
467
|
+
key: string;
|
|
468
|
+
id: string;
|
|
469
|
+
expiresAt: number;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* An application whose people the caller administers: it holds the application's `members.manage` globally there.
|
|
473
|
+
* `roles` are the application's global roles the caller may grant, those whose every action it holds itself.
|
|
474
|
+
*/
|
|
475
|
+
export interface AdministeredAppDto {
|
|
476
|
+
app: string;
|
|
477
|
+
displayName: string | null;
|
|
478
|
+
roles: {
|
|
479
|
+
key: string;
|
|
480
|
+
name: string;
|
|
481
|
+
permissions: string[];
|
|
482
|
+
}[];
|
|
483
|
+
}
|
|
484
|
+
export interface AppPeopleInput {
|
|
485
|
+
app: string;
|
|
486
|
+
/** The previous page's `next`. */
|
|
487
|
+
after?: string;
|
|
488
|
+
}
|
|
489
|
+
/** One page of the application's people, as its administrator sees them: labels, addresses and last sign-ins. */
|
|
490
|
+
export interface AppPeopleDto {
|
|
491
|
+
app: string;
|
|
492
|
+
members: AppMemberItem[];
|
|
493
|
+
next: string | null;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Give a person who already has an IAM principal one of the application's global roles, by its address (the people
|
|
497
|
+
* list answers each person's). An address IAM knows no person for is a 404: an IAM administrator invites people, an
|
|
498
|
+
* application's administrator does not. Holding the role already answers the same.
|
|
499
|
+
*/
|
|
500
|
+
export interface GrantAppRoleRequest {
|
|
501
|
+
app: string;
|
|
502
|
+
role: string;
|
|
503
|
+
email: string;
|
|
504
|
+
}
|
|
505
|
+
export interface GrantAppRoleResponse {
|
|
506
|
+
principalId: string;
|
|
507
|
+
role: string;
|
|
508
|
+
/** False when the person held the role already. */
|
|
509
|
+
granted: boolean;
|
|
510
|
+
}
|
|
511
|
+
/** Remove the global grant of `role` from a person. Repeating it answers the same. */
|
|
512
|
+
export interface RevokeAppRoleRequest {
|
|
513
|
+
app: string;
|
|
514
|
+
role: string;
|
|
515
|
+
principalId: string;
|
|
516
|
+
}
|
|
517
|
+
export interface RevokeAppRoleResponse {
|
|
518
|
+
/** False when the person held no such grant. */
|
|
519
|
+
revoked: boolean;
|
|
520
|
+
}
|
|
521
|
+
export interface IamAdminRpcContract {
|
|
522
|
+
me: RpcProcedure<void, MeDto>;
|
|
523
|
+
principals: {
|
|
524
|
+
list: RpcProcedure<ListPrincipalsInput, CursorList<PrincipalListItem>>;
|
|
525
|
+
get: RpcProcedure<PrincipalIdInput, PrincipalDetail>;
|
|
526
|
+
invite: RpcProcedure<InviteRequest, PrincipalListItem>;
|
|
527
|
+
update: RpcProcedure<UpdatePrincipalInput, PrincipalListItem>;
|
|
528
|
+
/**
|
|
529
|
+
* Make an external user internal, revoking its sessions and pending password actions. A bootstrap mailbox becomes
|
|
530
|
+
* an installation admin. There is no way back; promoting an internal principal is a 409.
|
|
531
|
+
*/
|
|
532
|
+
promote: RpcProcedure<PrincipalIdInput, PrincipalListItem>;
|
|
533
|
+
};
|
|
534
|
+
/**
|
|
535
|
+
* Sessions an operator no longer trusts. Revoking an IAM session also ends every app session
|
|
536
|
+
* derived from it — a child is only valid while its parent is — so this is one call, not a sweep.
|
|
537
|
+
*/
|
|
538
|
+
sessions: {
|
|
539
|
+
list: RpcProcedure<ListSessionsInput, CursorList<SessionDto>>;
|
|
540
|
+
revoke: RpcProcedure<SessionIdInput, RevokedSessionsResponse>;
|
|
541
|
+
revokeAll: RpcProcedure<PrincipalIdInput, RevokedSessionsResponse>;
|
|
542
|
+
};
|
|
543
|
+
passwords: {
|
|
544
|
+
issueEnrollment: RpcProcedure<PrincipalIdInput, PasswordActionDelivery>;
|
|
545
|
+
issueReset: RpcProcedure<PrincipalIdInput, PasswordActionDelivery>;
|
|
546
|
+
disable: RpcProcedure<PrincipalIdInput, OkResponse>;
|
|
547
|
+
};
|
|
548
|
+
grants: {
|
|
549
|
+
/** A role grant the principal already holds — same role, scope and app — answers 409 naming it. */
|
|
550
|
+
create: RpcProcedure<CreateGrantRequest, GrantDto>;
|
|
551
|
+
delete: RpcProcedure<GrantIdInput, OkResponse>;
|
|
552
|
+
};
|
|
553
|
+
apps: {
|
|
554
|
+
list: RpcProcedure<void, ListResponse<AppDto>>;
|
|
555
|
+
getSchema: RpcProcedure<AppInput, AppSchemaDto>;
|
|
556
|
+
setReturnOrigins: RpcProcedure<SetReturnOriginsRequest, ReturnOriginsDto>;
|
|
557
|
+
};
|
|
558
|
+
roles: {
|
|
559
|
+
list: RpcProcedure<ListRolesInput, ListResponse<RoleDto>>;
|
|
560
|
+
};
|
|
561
|
+
policies: {
|
|
562
|
+
list: RpcProcedure<AppInput, ListResponse<PolicyDto>>;
|
|
563
|
+
create: RpcProcedure<CreatePolicyInput, PolicyDto>;
|
|
564
|
+
update: RpcProcedure<UpdatePolicyInput, PolicyDto>;
|
|
565
|
+
delete: RpcProcedure<PolicyIdInput, OkResponse>;
|
|
566
|
+
};
|
|
567
|
+
apiKeys: {
|
|
568
|
+
list: RpcProcedure<ListApiKeysInput, CursorList<ApiKeyDto>>;
|
|
569
|
+
get: RpcProcedure<ApiKeyIdInput, ApiKeyDetail>;
|
|
570
|
+
provision: RpcProcedure<ProvisionApiKeyRequest, ProvisionApiKeyResponse>;
|
|
571
|
+
rotate: RpcProcedure<RotateApiKeyInput, RotateApiKeyResponse>;
|
|
572
|
+
revoke: RpcProcedure<ApiKeyIdInput, OkResponse>;
|
|
573
|
+
};
|
|
574
|
+
workloads: {
|
|
575
|
+
issueKey: RpcProcedure<IssueWorkloadKeyRequest, IssueWorkloadKeyResponse>;
|
|
576
|
+
revokeKeys: RpcProcedure<RevokeWorkloadKeysRequest, RevokeWorkloadKeysResponse>;
|
|
577
|
+
};
|
|
578
|
+
membershipKeys: {
|
|
579
|
+
issue: RpcProcedure<IssueMembershipKeyRequest, IssueMembershipKeyResponse>;
|
|
580
|
+
};
|
|
581
|
+
shareLinks: {
|
|
582
|
+
list: RpcProcedure<ListShareLinksInput, CursorList<ShareLinkListItem>>;
|
|
583
|
+
issue: RpcProcedure<IssueShareLinkRequest, IssuedShareLinkResponse>;
|
|
584
|
+
revoke: RpcProcedure<ShareLinkIdInput, OkResponse>;
|
|
585
|
+
};
|
|
586
|
+
audit: {
|
|
587
|
+
list: RpcProcedure<ListAuditInput, CursorList<AuditEventDto>>;
|
|
588
|
+
listAuthLog: RpcProcedure<ListAuthLogInput, CursorList<AuthLogDto>>;
|
|
589
|
+
};
|
|
590
|
+
/**
|
|
591
|
+
* An application's own people, administered by whoever holds the application's `members.manage` globally there: no
|
|
592
|
+
* installation admin needed. Each change writes its audit row under the application.
|
|
593
|
+
*/
|
|
594
|
+
appPeople: {
|
|
595
|
+
apps: RpcProcedure<void, ListResponse<AdministeredAppDto>>;
|
|
596
|
+
list: RpcProcedure<AppPeopleInput, AppPeopleDto>;
|
|
597
|
+
grant: RpcProcedure<GrantAppRoleRequest, GrantAppRoleResponse>;
|
|
598
|
+
revoke: RpcProcedure<RevokeAppRoleRequest, RevokeAppRoleResponse>;
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
export type { AppActionDef, AppMemberItem, AppSchema, AppScopeDef, PermissionEntry, PermissionSource, PrincipalAffiliation, PrincipalType, RoleDef };
|
|
602
|
+
export * from './access';
|
|
603
|
+
export * from './oauth';
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { KeyGrant } from '@fabrika/auth-core';
|
|
2
|
+
/** Managed CIMD registrations only; public dynamic registration is not part of this contract. */
|
|
3
|
+
export interface OAuthManagedClient {
|
|
4
|
+
provider: 'chatgpt' | 'claude';
|
|
5
|
+
clientId: string;
|
|
6
|
+
redirectUris: string[];
|
|
7
|
+
}
|
|
8
|
+
/** RFC 7523 §2.2: the `client_assertion_type` of a JWT client assertion (`private_key_jwt`). */
|
|
9
|
+
export declare const OAUTH_CLIENT_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
|
|
10
|
+
/** The signing algorithms IAM verifies a client assertion with. */
|
|
11
|
+
export declare const OAUTH_CLIENT_ASSERTION_ALGORITHMS: readonly ["RS256"];
|
|
12
|
+
export type OAuthClientAssertionAlgorithm = (typeof OAUTH_CLIENT_ASSERTION_ALGORITHMS)[number];
|
|
13
|
+
interface OAuthClientMetadataFields {
|
|
14
|
+
client_id: string;
|
|
15
|
+
client_name?: string;
|
|
16
|
+
redirect_uris: string[];
|
|
17
|
+
grant_types: ('authorization_code' | 'refresh_token')[];
|
|
18
|
+
response_types: ['code'];
|
|
19
|
+
}
|
|
20
|
+
/** A public client, or one that authenticates with a JWT signed by a key its document publishes. */
|
|
21
|
+
export type OAuthClientMetadataDocument = (OAuthClientMetadataFields & {
|
|
22
|
+
token_endpoint_auth_method: 'none';
|
|
23
|
+
}) | (OAuthClientMetadataFields & {
|
|
24
|
+
token_endpoint_auth_method: 'private_key_jwt';
|
|
25
|
+
/** HTTPS, on the client id's own origin. */
|
|
26
|
+
jwks_uri: string;
|
|
27
|
+
token_endpoint_auth_signing_alg: OAuthClientAssertionAlgorithm;
|
|
28
|
+
});
|
|
29
|
+
/** How a token or revocation request authenticates its client: no credential, or a JWT assertion. */
|
|
30
|
+
export type OAuthClientCredential = {
|
|
31
|
+
method: 'none';
|
|
32
|
+
} | {
|
|
33
|
+
method: 'private_key_jwt';
|
|
34
|
+
assertion: string;
|
|
35
|
+
};
|
|
36
|
+
export interface OAuthResourceDto {
|
|
37
|
+
app: string;
|
|
38
|
+
resource: string;
|
|
39
|
+
/** IAM resolves these app-owned grants; OAuth scope text alone never authorizes an action. */
|
|
40
|
+
scopeGrants: {
|
|
41
|
+
scope: string;
|
|
42
|
+
permissions: KeyGrant[];
|
|
43
|
+
}[];
|
|
44
|
+
/** Used only when scope is omitted. No configured default means invalid_scope, never all scopes. */
|
|
45
|
+
defaultScopes?: string[];
|
|
46
|
+
}
|
|
47
|
+
/** No access token, refresh token, hash, or authorization code appears in a grant DTO. */
|
|
48
|
+
export interface OAuthGrantDto {
|
|
49
|
+
id: string;
|
|
50
|
+
principalId: string;
|
|
51
|
+
clientId: string;
|
|
52
|
+
app: string;
|
|
53
|
+
resource: string;
|
|
54
|
+
scopes: string[];
|
|
55
|
+
createdAt: number;
|
|
56
|
+
expiresAt: number;
|
|
57
|
+
revokedAt: number | null;
|
|
58
|
+
}
|
|
59
|
+
export interface OAuthAuthorizationRequest {
|
|
60
|
+
response_type: 'code';
|
|
61
|
+
client_id: string;
|
|
62
|
+
redirect_uri: string;
|
|
63
|
+
resource: string;
|
|
64
|
+
scope?: string;
|
|
65
|
+
state?: string;
|
|
66
|
+
code_challenge: string;
|
|
67
|
+
code_challenge_method: 'S256';
|
|
68
|
+
}
|
|
69
|
+
export type OAuthAuthorizationCoordinates = Omit<OAuthAuthorizationRequest, 'scope'>;
|
|
70
|
+
export type OAuthAuthorizationParseResult = {
|
|
71
|
+
kind: 'valid';
|
|
72
|
+
request: OAuthAuthorizationRequest;
|
|
73
|
+
} | {
|
|
74
|
+
kind: 'invalid_scope';
|
|
75
|
+
coordinates: OAuthAuthorizationCoordinates;
|
|
76
|
+
rawScope: string;
|
|
77
|
+
} | {
|
|
78
|
+
kind: 'invalid_request';
|
|
79
|
+
};
|
|
80
|
+
export type OAuthTokenRequest = {
|
|
81
|
+
grant_type: 'authorization_code';
|
|
82
|
+
client_id: string;
|
|
83
|
+
code: string;
|
|
84
|
+
redirect_uri: string;
|
|
85
|
+
code_verifier: string;
|
|
86
|
+
resource: string;
|
|
87
|
+
} | {
|
|
88
|
+
grant_type: 'refresh_token';
|
|
89
|
+
client_id: string;
|
|
90
|
+
refresh_token: string;
|
|
91
|
+
resource: string;
|
|
92
|
+
};
|
|
93
|
+
export interface OAuthTokenResponse {
|
|
94
|
+
access_token: string;
|
|
95
|
+
token_type: 'Bearer';
|
|
96
|
+
expires_in: number;
|
|
97
|
+
refresh_token?: string;
|
|
98
|
+
scope: string;
|
|
99
|
+
}
|
|
100
|
+
export interface OAuthRevocationRequest {
|
|
101
|
+
client_id: string;
|
|
102
|
+
token: string;
|
|
103
|
+
token_type_hint?: 'access_token' | 'refresh_token';
|
|
104
|
+
}
|
|
105
|
+
export interface OAuthAuthorizationServerMetadata {
|
|
106
|
+
issuer: string;
|
|
107
|
+
authorization_endpoint: string;
|
|
108
|
+
token_endpoint: string;
|
|
109
|
+
revocation_endpoint: string;
|
|
110
|
+
jwks_uri: string;
|
|
111
|
+
response_types_supported: ['code'];
|
|
112
|
+
grant_types_supported: ['authorization_code', 'refresh_token'];
|
|
113
|
+
code_challenge_methods_supported: ['S256'];
|
|
114
|
+
token_endpoint_auth_methods_supported: ['none', 'private_key_jwt'];
|
|
115
|
+
token_endpoint_auth_signing_alg_values_supported: OAuthClientAssertionAlgorithm[];
|
|
116
|
+
revocation_endpoint_auth_methods_supported: ['none', 'private_key_jwt'];
|
|
117
|
+
revocation_endpoint_auth_signing_alg_values_supported: OAuthClientAssertionAlgorithm[];
|
|
118
|
+
/** RFC 9207: every authorization response carries `iss`. */
|
|
119
|
+
authorization_response_iss_parameter_supported: true;
|
|
120
|
+
client_id_metadata_document_supported: true;
|
|
121
|
+
}
|
|
122
|
+
export declare function parseOAuthManagedClient(value: unknown): OAuthManagedClient | null;
|
|
123
|
+
/** A fetched document cannot enlarge the managed registration's exact redirect allowlist. */
|
|
124
|
+
export declare function parseOAuthClientMetadata(value: unknown, registration: OAuthManagedClient): OAuthClientMetadataDocument | null;
|
|
125
|
+
export declare function parseOAuthResourceDto(value: unknown): OAuthResourceDto | null;
|
|
126
|
+
export declare function resolveOAuthAuthorizationScopes(scope: string | undefined, resource: OAuthResourceDto): string[] | null;
|
|
127
|
+
export declare function parseOAuthAuthorizationAttempt(value: unknown): OAuthAuthorizationParseResult;
|
|
128
|
+
export declare function parseOAuthAuthorizationRequest(value: unknown): OAuthAuthorizationRequest | null;
|
|
129
|
+
export declare function parseOAuthTokenRequest(value: unknown): OAuthTokenRequest | null;
|
|
130
|
+
/**
|
|
131
|
+
* The client credential of a token or revocation request (RFC 7521 §4.2). Neither assertion field is no
|
|
132
|
+
* credential; a half-present pair, another assertion type or an oversized assertion is null.
|
|
133
|
+
*/
|
|
134
|
+
export declare function parseOAuthClientCredential(value: unknown): OAuthClientCredential | null;
|
|
135
|
+
export declare function parseOAuthRevocationRequest(value: unknown): OAuthRevocationRequest | null;
|
|
136
|
+
export {};
|