@bentoforge/umami-iam 0.1.1 → 0.2.0
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/LICENSE +21 -0
- package/dist/client.d.ts +44 -36
- package/dist/client.js +125 -72
- package/dist/types.d.ts +126 -96
- package/package.json +1 -1
- package/src/client.ts +144 -91
- package/src/types.ts +134 -104
package/src/types.ts
CHANGED
|
@@ -9,9 +9,7 @@ export interface AccessClaims {
|
|
|
9
9
|
aud?: string;
|
|
10
10
|
/** The active tenant this token is scoped to. */
|
|
11
11
|
tenant: string;
|
|
12
|
-
name: string;
|
|
13
12
|
email: string;
|
|
14
|
-
locale: string;
|
|
15
13
|
permissions: string[];
|
|
16
14
|
iat: number;
|
|
17
15
|
exp: number;
|
|
@@ -61,13 +59,30 @@ export interface TotpSetup {
|
|
|
61
59
|
secret: string;
|
|
62
60
|
/** `otpauth://` URL for QR rendering. */
|
|
63
61
|
otpauthUrl: string;
|
|
62
|
+
/** Ready-to-render QR-code SVG of `otpauthUrl` (dark on white). */
|
|
63
|
+
qrSvg: string;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
// ── Users ───────────────────────────────────────────────────────────────────
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
/** How to address a user; the rendered word is composed server-side from the config labels. */
|
|
69
|
+
export type Salutation = "" | "SIR" | "MADAM";
|
|
70
|
+
|
|
71
|
+
/** Structured name parts (editable) plus the server-composed display names (read-only). */
|
|
72
|
+
export interface NameParts {
|
|
73
|
+
title: string | null;
|
|
74
|
+
salutation: Salutation;
|
|
75
|
+
firstname: string | null;
|
|
76
|
+
lastname: string | null;
|
|
77
|
+
/** `title firstname lastname`. */
|
|
78
|
+
name: string;
|
|
79
|
+
/** `salutation title firstname lastname`. */
|
|
80
|
+
fullName: string;
|
|
81
|
+
/** `salutation title lastname`. */
|
|
82
|
+
addressableName: string;
|
|
83
|
+
}
|
|
69
84
|
|
|
70
|
-
export interface UserView {
|
|
85
|
+
export interface UserView extends NameParts {
|
|
71
86
|
userId: string;
|
|
72
87
|
tenantId: string;
|
|
73
88
|
roles: string[];
|
|
@@ -75,95 +90,131 @@ export interface UserView {
|
|
|
75
90
|
username: string;
|
|
76
91
|
/** Optional contact email — not unique, may be null/absent. */
|
|
77
92
|
email: string | null;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
status: UserStatus;
|
|
93
|
+
/** Admin lock — a locked user cannot log in. */
|
|
94
|
+
locked: boolean;
|
|
81
95
|
customFields: Record<string, unknown>;
|
|
82
96
|
/** RFC3339 creation timestamp. */
|
|
83
97
|
created: string;
|
|
84
|
-
/** RFC3339 timestamp of the
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
98
|
+
/** RFC3339 timestamp of the last change to this record. */
|
|
99
|
+
lastUpdated: string;
|
|
100
|
+
/** RFC3339 timestamp of the user's last authentication (login/refresh); null until first active. */
|
|
101
|
+
lastSeen: string | null;
|
|
102
|
+
/** User id that created / last changed this user (audit; not surfaced in the UI yet). */
|
|
103
|
+
createdBy?: string | null;
|
|
104
|
+
lastChangedBy?: string | null;
|
|
105
|
+
/** Whether TOTP MFA is configured (never exposes the secret). */
|
|
106
|
+
mfaEnabled: boolean;
|
|
107
|
+
/** Whether the current password came from an admin reset and hasn't been changed since. */
|
|
108
|
+
passwordGenerated: boolean;
|
|
109
|
+
/** Whether the user has at least one registered passkey. */
|
|
110
|
+
hasPasskey: boolean;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** The editable structured name parts (all optional; omitted = unset, `""` clears). */
|
|
114
|
+
export interface NameInput {
|
|
115
|
+
title?: string;
|
|
116
|
+
salutation?: Salutation;
|
|
117
|
+
firstname?: string;
|
|
118
|
+
lastname?: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface CreateUserRequest extends NameInput {
|
|
89
122
|
/** Login username (unique). If omitted, `email` is used as the username. */
|
|
90
123
|
username?: string;
|
|
91
124
|
/** Optional contact email (not unique). */
|
|
92
125
|
email?: string;
|
|
93
|
-
password
|
|
94
|
-
|
|
95
|
-
|
|
126
|
+
/** Optional initial password. Omit (the normal case) to have a temporary one generated and
|
|
127
|
+
* returned once in {@link CreateUserResponse.temporaryPassword}. */
|
|
128
|
+
password?: string;
|
|
96
129
|
roles?: string[];
|
|
97
130
|
customFields?: Record<string, unknown>;
|
|
98
131
|
}
|
|
99
132
|
|
|
100
|
-
|
|
133
|
+
/** The created user, plus the one-time temporary password when one was generated. */
|
|
134
|
+
export type CreateUserResponse = UserView & {
|
|
135
|
+
/** Present only when the server generated the initial password — shown once. */
|
|
136
|
+
temporaryPassword?: string | null;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export interface PatchUserRequest extends NameInput {
|
|
140
|
+
/** New login username (globally unique). Omit to leave unchanged; must not be empty. */
|
|
141
|
+
username?: string;
|
|
142
|
+
/** Contact email. Omit to leave unchanged; empty string clears it. */
|
|
143
|
+
email?: string;
|
|
101
144
|
roles?: string[];
|
|
102
|
-
|
|
145
|
+
locked?: boolean;
|
|
103
146
|
customFields?: Record<string, unknown>;
|
|
104
147
|
}
|
|
105
148
|
|
|
106
149
|
// ── Me ──────────────────────────────────────────────────────────────────────
|
|
107
150
|
|
|
151
|
+
export interface MeUser extends NameParts {
|
|
152
|
+
userId: string;
|
|
153
|
+
tenantId: string;
|
|
154
|
+
roles: string[];
|
|
155
|
+
username: string;
|
|
156
|
+
email: string | null;
|
|
157
|
+
locked: boolean;
|
|
158
|
+
customFields: Record<string, unknown>;
|
|
159
|
+
/** Whether TOTP MFA is configured (never exposes the secret). */
|
|
160
|
+
mfaEnabled: boolean;
|
|
161
|
+
/** Whether the caller has at least one registered passkey. */
|
|
162
|
+
hasPasskey: boolean;
|
|
163
|
+
}
|
|
164
|
+
|
|
108
165
|
export interface MeResponse {
|
|
109
|
-
user:
|
|
110
|
-
userId: string;
|
|
111
|
-
tenantId: string;
|
|
112
|
-
roles: string[];
|
|
113
|
-
username: string;
|
|
114
|
-
email: string | null;
|
|
115
|
-
name: string;
|
|
116
|
-
locale: string;
|
|
117
|
-
status: UserStatus;
|
|
118
|
-
};
|
|
166
|
+
user: MeUser;
|
|
119
167
|
tenant: Tenant | null;
|
|
120
168
|
}
|
|
121
169
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
active: boolean;
|
|
170
|
+
/** One of the caller's active login sessions (device). Never exposes the refresh secret. */
|
|
171
|
+
export interface SessionView {
|
|
172
|
+
sessionId: string;
|
|
173
|
+
userAgent?: string;
|
|
174
|
+
ip?: string;
|
|
175
|
+
/** RFC3339 creation timestamp. */
|
|
176
|
+
created: string;
|
|
177
|
+
/** RFC3339 timestamp of the last refresh. */
|
|
178
|
+
lastSeen: string;
|
|
179
|
+
/** RFC3339 absolute expiry. */
|
|
180
|
+
expiresAt: string;
|
|
181
|
+
/** Whether this is the session making the request. */
|
|
182
|
+
current: boolean;
|
|
136
183
|
}
|
|
137
184
|
|
|
185
|
+
// ── Tenants ──────────────────────────────────────────────────────────────────
|
|
186
|
+
|
|
138
187
|
export interface Tenant {
|
|
139
188
|
tenantId: string;
|
|
140
189
|
version: number;
|
|
141
|
-
packages: PackageAssignment[];
|
|
142
|
-
limitOverrides: Record<string, string>;
|
|
143
|
-
featureOverrides: Record<string, FeatureToggle>;
|
|
144
190
|
/** Authorization features granted to the tenant (`feature:*`), fed to the token broker. */
|
|
145
191
|
features: string[];
|
|
146
192
|
customFields: Record<string, unknown>;
|
|
147
193
|
name: string;
|
|
148
194
|
slug: string;
|
|
149
|
-
status: TenantStatus;
|
|
150
|
-
plan: string;
|
|
151
|
-
billedUntil?: string | null;
|
|
152
|
-
seatsLimit?: number | null;
|
|
153
195
|
created: string;
|
|
154
196
|
lastUpdated: string;
|
|
197
|
+
/** RFC3339 timestamp of the last token activity (refresh / exchange) scoped to this tenant;
|
|
198
|
+
* null until the first activity. */
|
|
199
|
+
lastActive?: string | null;
|
|
200
|
+
/** Sort key backing the tenant listing: `lastActive` when present, else `created`. */
|
|
201
|
+
lastActiveOrCreated?: string;
|
|
202
|
+
/** User id that created this tenant (audit; not surfaced in the UI yet). */
|
|
203
|
+
createdBy?: string | null;
|
|
204
|
+
/** User id of the last change to this tenant (audit; not surfaced in the UI yet). */
|
|
205
|
+
lastChangedBy?: string | null;
|
|
155
206
|
}
|
|
156
207
|
|
|
157
208
|
export interface CreateTenantRequest {
|
|
158
209
|
name: string;
|
|
159
|
-
owner
|
|
210
|
+
/** Optional first owner. Omit to create an empty tenant (add users afterwards by impersonating
|
|
211
|
+
* it on the Tenants screen). */
|
|
212
|
+
owner?: {
|
|
160
213
|
/** Owner login username (unique). If omitted, `email` is used as the username. */
|
|
161
214
|
username?: string;
|
|
162
215
|
/** Optional contact email (not unique). */
|
|
163
216
|
email?: string;
|
|
164
217
|
password: string;
|
|
165
|
-
name: string;
|
|
166
|
-
locale?: string;
|
|
167
218
|
};
|
|
168
219
|
/** Custom-field values, validated against `customTenantFields`. */
|
|
169
220
|
customFields?: Record<string, unknown>;
|
|
@@ -171,26 +222,8 @@ export interface CreateTenantRequest {
|
|
|
171
222
|
|
|
172
223
|
export interface CreateTenantResponse {
|
|
173
224
|
tenantId: string;
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
export interface EntitlementsResponse {
|
|
178
|
-
limits: Record<string, string>;
|
|
179
|
-
features: string[];
|
|
180
|
-
monthlyTotal: string;
|
|
181
|
-
packages: PackageAssignment[];
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export interface MetricUsage {
|
|
185
|
-
metric: string;
|
|
186
|
-
used: number;
|
|
187
|
-
limit?: string;
|
|
188
|
-
overQuota: boolean;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
export interface UsageResponse {
|
|
192
|
-
period: string;
|
|
193
|
-
metrics: MetricUsage[];
|
|
225
|
+
/** The created owner's user id — only present when an `owner` was supplied. */
|
|
226
|
+
ownerUserId: string | null;
|
|
194
227
|
}
|
|
195
228
|
|
|
196
229
|
// ── Config ────────────────────────────────────────────────────────────────────
|
|
@@ -199,6 +232,8 @@ export interface UsageResponse {
|
|
|
199
232
|
export interface RoleDef {
|
|
200
233
|
code: string;
|
|
201
234
|
name: string;
|
|
235
|
+
/** Optional human-readable description (shown muted under the name in the admin UI). */
|
|
236
|
+
description?: string | null;
|
|
202
237
|
/** Boolean expression over the tenant's `feature:*`/`is:*` gating whether it may be assigned. */
|
|
203
238
|
assignableIf?: string | null;
|
|
204
239
|
}
|
|
@@ -206,38 +241,21 @@ export interface RoleDef {
|
|
|
206
241
|
export interface ScopeDef {
|
|
207
242
|
code: string;
|
|
208
243
|
name: string;
|
|
244
|
+
/** Optional human-readable description (shown muted under the name in the admin UI). */
|
|
245
|
+
description?: string | null;
|
|
209
246
|
assignableIf?: string | null;
|
|
210
247
|
}
|
|
211
248
|
/** An authorization feature granted to a tenant (`feature:*`). */
|
|
212
249
|
export interface FeatureDef {
|
|
213
250
|
code: string;
|
|
214
251
|
name: string;
|
|
252
|
+
/** Optional human-readable description (shown muted under the name in the admin UI). */
|
|
253
|
+
description?: string | null;
|
|
215
254
|
/** Boolean expression over the tenant's current features gating whether it may be granted. */
|
|
216
255
|
assignableIf?: string | null;
|
|
217
256
|
}
|
|
218
|
-
export interface LimitDef {
|
|
219
|
-
code: string;
|
|
220
|
-
name: string;
|
|
221
|
-
unit?: string;
|
|
222
|
-
default?: string;
|
|
223
|
-
}
|
|
224
|
-
export interface PackageLimit {
|
|
225
|
-
code: string;
|
|
226
|
-
value: string;
|
|
227
|
-
}
|
|
228
|
-
export interface PriceEntry {
|
|
229
|
-
validFrom: string;
|
|
230
|
-
price: string;
|
|
231
|
-
}
|
|
232
|
-
export interface PackageDef {
|
|
233
|
-
code: string;
|
|
234
|
-
name: string;
|
|
235
|
-
features: string[];
|
|
236
|
-
limits: PackageLimit[];
|
|
237
|
-
prices: PriceEntry[];
|
|
238
|
-
}
|
|
239
257
|
export interface CustomFieldDef {
|
|
240
|
-
|
|
258
|
+
code: string;
|
|
241
259
|
label: string;
|
|
242
260
|
/** `"string"` | `"number"` | `"bool"` | `"select"`. */
|
|
243
261
|
type: string;
|
|
@@ -246,6 +264,8 @@ export interface CustomFieldDef {
|
|
|
246
264
|
required: boolean;
|
|
247
265
|
/** Whether admin list tables surface this field as a column. */
|
|
248
266
|
showInTable?: boolean;
|
|
267
|
+
/** Whether the user may edit this field on themselves via `PATCH /auth/me`. */
|
|
268
|
+
selfEditable?: boolean;
|
|
249
269
|
}
|
|
250
270
|
|
|
251
271
|
/** The custom-field schemas for rendering user/tenant forms (`GET /config/custom-fields`). */
|
|
@@ -275,7 +295,9 @@ export interface ApiDef {
|
|
|
275
295
|
eligibility?: string | null;
|
|
276
296
|
/** Ordered rules mapping subjects → granted permissions (accumulated top-to-bottom). */
|
|
277
297
|
permissions: PermissionRule[];
|
|
278
|
-
/** Claim mapping: claimName → source
|
|
298
|
+
/** Claim mapping: claimName → source. A source is a literal string, a `$user.<field>` /
|
|
299
|
+
* `$tenant.<field>` reference (`id`, `username`, `email`, `name`, `fullName`, `addressableName`,
|
|
300
|
+
* `roles`, `name`/`slug`/`features`, …), or `$user.custom.<key>` / `$tenant.custom.<key>`. */
|
|
279
301
|
claims?: Record<string, string>;
|
|
280
302
|
}
|
|
281
303
|
|
|
@@ -285,10 +307,10 @@ export interface Config {
|
|
|
285
307
|
/** Scopes assignable to M2M service keys. */
|
|
286
308
|
scopes: ScopeDef[];
|
|
287
309
|
features: FeatureDef[];
|
|
288
|
-
limits: LimitDef[];
|
|
289
|
-
packages: PackageDef[];
|
|
290
310
|
customTenantFields: CustomFieldDef[];
|
|
291
311
|
customUserFields: CustomFieldDef[];
|
|
312
|
+
/** Salutation labels (`salutationCode → word`) in the deployment's language. */
|
|
313
|
+
salutations: Record<string, string>;
|
|
292
314
|
security: SecuritySettings;
|
|
293
315
|
/** Messaging integration (Telegram/WhatsApp) settings. */
|
|
294
316
|
messaging?: MessagingConfig;
|
|
@@ -312,8 +334,8 @@ export interface ApiKeyView {
|
|
|
312
334
|
roles: string[];
|
|
313
335
|
/** Service-key `scope:*` subjects (empty for PATs). */
|
|
314
336
|
scopes: string[];
|
|
315
|
-
/**
|
|
316
|
-
|
|
337
|
+
/** Whether the raw-secret (Mode 1) exchange is allowed; false ⇒ HMAC-only (Mode 2). */
|
|
338
|
+
allowSecretLogin: boolean;
|
|
317
339
|
status: ApiKeyStatus;
|
|
318
340
|
allowedOrigins: string[];
|
|
319
341
|
expiresAt?: string | null;
|
|
@@ -326,8 +348,8 @@ export interface CreateApiKeyRequest {
|
|
|
326
348
|
name: string;
|
|
327
349
|
/** The `scope:*` subjects this key carries (must be assignable given the tenant's features). */
|
|
328
350
|
scopes?: string[];
|
|
329
|
-
/**
|
|
330
|
-
|
|
351
|
+
/** Allow the raw-secret (Mode 1) exchange; omitted/false ⇒ HMAC-only (Mode 2). */
|
|
352
|
+
allowSecretLogin?: boolean;
|
|
331
353
|
allowedOrigins?: string[];
|
|
332
354
|
expiresAt?: string;
|
|
333
355
|
}
|
|
@@ -337,8 +359,6 @@ export interface CreatePatRequest {
|
|
|
337
359
|
name: string;
|
|
338
360
|
/** Restrict the token to this subset of your own `role:*` (empty = all your roles). */
|
|
339
361
|
roles?: string[];
|
|
340
|
-
/** Target API codes this PAT may mint for; defaults to `["umami"]`. */
|
|
341
|
-
apis?: string[];
|
|
342
362
|
expiresAt?: string;
|
|
343
363
|
}
|
|
344
364
|
|
|
@@ -360,6 +380,9 @@ export interface BrandingConfig {
|
|
|
360
380
|
/** Logo for dark backgrounds; falls back to logoLight, then default. */
|
|
361
381
|
logoDark?: string;
|
|
362
382
|
favicon?: string;
|
|
383
|
+
/** Browser tab title (document `<title>`); served at /app/branding.json, applied at runtime.
|
|
384
|
+
* Empty → "umami". */
|
|
385
|
+
title?: string;
|
|
363
386
|
}
|
|
364
387
|
|
|
365
388
|
/** Messaging integration settings (Telegram/WhatsApp). */
|
|
@@ -394,9 +417,7 @@ export interface MessagingLink {
|
|
|
394
417
|
export interface ResolvedMessagingUser {
|
|
395
418
|
userId: string;
|
|
396
419
|
tenantId: string;
|
|
397
|
-
name: string;
|
|
398
420
|
email?: string | null;
|
|
399
|
-
locale: string;
|
|
400
421
|
roles: string[];
|
|
401
422
|
}
|
|
402
423
|
|
|
@@ -405,6 +426,12 @@ export interface ResolvedMessagingUser {
|
|
|
405
426
|
/** Outcome flavour of an audited event. */
|
|
406
427
|
export type AuditSeverity = "good" | "neutral" | "bad";
|
|
407
428
|
|
|
429
|
+
/** One page of audit entries plus the cursor to fetch the next (absent when the trail is exhausted). */
|
|
430
|
+
export interface AuditPage {
|
|
431
|
+
entries: AuditEntry[];
|
|
432
|
+
nextCursor?: string;
|
|
433
|
+
}
|
|
434
|
+
|
|
408
435
|
export interface AuditEntry {
|
|
409
436
|
id: string;
|
|
410
437
|
/** RFC3339 event time. */
|
|
@@ -413,6 +440,9 @@ export interface AuditEntry {
|
|
|
413
440
|
user?: string | null;
|
|
414
441
|
severity: AuditSeverity;
|
|
415
442
|
message: string;
|
|
443
|
+
/** Best-effort client IP, present on security-relevant events (logins, credential/account
|
|
444
|
+
* changes). Absent on events with no request IP. */
|
|
445
|
+
ip?: string | null;
|
|
416
446
|
}
|
|
417
447
|
|
|
418
448
|
/** Result of an admin password reset — `temporaryPassword` is set (once) only when generated. */
|