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