@bentoforge/umami-iam 0.1.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/README.md +60 -0
- package/dist/client.d.ts +180 -0
- package/dist/client.js +429 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/types.d.ts +371 -0
- package/dist/types.js +2 -0
- package/dist/webauthn.d.ts +12 -0
- package/dist/webauthn.js +73 -0
- package/package.json +50 -0
- package/src/client.ts +560 -0
- package/src/index.ts +11 -0
- package/src/types.ts +428 -0
- package/src/webauthn.ts +83 -0
package/dist/index.js
ADDED
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/** Claims carried by an umami access token (JWT payload). */
|
|
2
|
+
export interface AccessClaims {
|
|
3
|
+
iss: string;
|
|
4
|
+
sub: string;
|
|
5
|
+
aud?: string;
|
|
6
|
+
/** The active tenant this token is scoped to. */
|
|
7
|
+
tenant: string;
|
|
8
|
+
name: string;
|
|
9
|
+
email: string;
|
|
10
|
+
locale: string;
|
|
11
|
+
permissions: string[];
|
|
12
|
+
iat: number;
|
|
13
|
+
exp: number;
|
|
14
|
+
/** user.tokenVersion snapshot. */
|
|
15
|
+
ver: number;
|
|
16
|
+
/** Present on machine tokens issued via API-key exchange. */
|
|
17
|
+
kind?: "api_key";
|
|
18
|
+
/** Effective tenant features, when the config requests the `features` claim. */
|
|
19
|
+
features?: string[];
|
|
20
|
+
[claim: string]: unknown;
|
|
21
|
+
}
|
|
22
|
+
export interface LoginRequest {
|
|
23
|
+
username: string;
|
|
24
|
+
password: string;
|
|
25
|
+
totpCode?: string;
|
|
26
|
+
/** Optional target API to mint the access token for directly (default: `umami`). The session
|
|
27
|
+
* remembers it, so refreshes keep the same audience. See `docs/AUDIENCES.md`. */
|
|
28
|
+
api?: string;
|
|
29
|
+
}
|
|
30
|
+
/** Either an MFA challenge (`mfaRequired: true`, no token) or success (an access token). */
|
|
31
|
+
export interface LoginResponse {
|
|
32
|
+
mfaRequired: boolean;
|
|
33
|
+
accessToken?: string;
|
|
34
|
+
tenants: string[];
|
|
35
|
+
}
|
|
36
|
+
export interface TokenResponse {
|
|
37
|
+
accessToken: string;
|
|
38
|
+
tenants: string[];
|
|
39
|
+
}
|
|
40
|
+
export interface ExchangeResponse {
|
|
41
|
+
accessToken: string;
|
|
42
|
+
expiresIn: number;
|
|
43
|
+
}
|
|
44
|
+
export interface MfaStatus {
|
|
45
|
+
enabled: boolean;
|
|
46
|
+
}
|
|
47
|
+
export interface TotpSetup {
|
|
48
|
+
/** Base32 secret for manual entry. */
|
|
49
|
+
secret: string;
|
|
50
|
+
/** `otpauth://` URL for QR rendering. */
|
|
51
|
+
otpauthUrl: string;
|
|
52
|
+
}
|
|
53
|
+
export type UserStatus = "Active" | "Locked" | "Invited";
|
|
54
|
+
export interface UserView {
|
|
55
|
+
userId: string;
|
|
56
|
+
tenantId: string;
|
|
57
|
+
roles: string[];
|
|
58
|
+
/** Login identifier — unique. */
|
|
59
|
+
username: string;
|
|
60
|
+
/** Optional contact email — not unique, may be null/absent. */
|
|
61
|
+
email: string | null;
|
|
62
|
+
name: string;
|
|
63
|
+
locale: string;
|
|
64
|
+
status: UserStatus;
|
|
65
|
+
customFields: Record<string, unknown>;
|
|
66
|
+
/** RFC3339 creation timestamp. */
|
|
67
|
+
created: string;
|
|
68
|
+
/** RFC3339 timestamp of the user's last authentication (login/refresh). */
|
|
69
|
+
lastSeen: string;
|
|
70
|
+
}
|
|
71
|
+
export interface CreateUserRequest {
|
|
72
|
+
/** Login username (unique). If omitted, `email` is used as the username. */
|
|
73
|
+
username?: string;
|
|
74
|
+
/** Optional contact email (not unique). */
|
|
75
|
+
email?: string;
|
|
76
|
+
password: string;
|
|
77
|
+
name: string;
|
|
78
|
+
locale?: string;
|
|
79
|
+
roles?: string[];
|
|
80
|
+
customFields?: Record<string, unknown>;
|
|
81
|
+
}
|
|
82
|
+
export interface PatchUserRequest {
|
|
83
|
+
roles?: string[];
|
|
84
|
+
status?: UserStatus;
|
|
85
|
+
customFields?: Record<string, unknown>;
|
|
86
|
+
}
|
|
87
|
+
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
|
+
};
|
|
98
|
+
tenant: Tenant | null;
|
|
99
|
+
}
|
|
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;
|
|
110
|
+
}
|
|
111
|
+
export interface Tenant {
|
|
112
|
+
tenantId: string;
|
|
113
|
+
version: number;
|
|
114
|
+
packages: PackageAssignment[];
|
|
115
|
+
limitOverrides: Record<string, string>;
|
|
116
|
+
featureOverrides: Record<string, FeatureToggle>;
|
|
117
|
+
/** Authorization features granted to the tenant (`feature:*`), fed to the token broker. */
|
|
118
|
+
features: string[];
|
|
119
|
+
customFields: Record<string, unknown>;
|
|
120
|
+
name: string;
|
|
121
|
+
slug: string;
|
|
122
|
+
status: TenantStatus;
|
|
123
|
+
plan: string;
|
|
124
|
+
billedUntil?: string | null;
|
|
125
|
+
seatsLimit?: number | null;
|
|
126
|
+
created: string;
|
|
127
|
+
lastUpdated: string;
|
|
128
|
+
}
|
|
129
|
+
export interface CreateTenantRequest {
|
|
130
|
+
name: string;
|
|
131
|
+
owner: {
|
|
132
|
+
/** Owner login username (unique). If omitted, `email` is used as the username. */
|
|
133
|
+
username?: string;
|
|
134
|
+
/** Optional contact email (not unique). */
|
|
135
|
+
email?: string;
|
|
136
|
+
password: string;
|
|
137
|
+
name: string;
|
|
138
|
+
locale?: string;
|
|
139
|
+
};
|
|
140
|
+
/** Custom-field values, validated against `customTenantFields`. */
|
|
141
|
+
customFields?: Record<string, unknown>;
|
|
142
|
+
}
|
|
143
|
+
export interface CreateTenantResponse {
|
|
144
|
+
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[];
|
|
162
|
+
}
|
|
163
|
+
/** A role assignable to a user (`role:*`). Permissions come from the per-API rules, not here. */
|
|
164
|
+
export interface RoleDef {
|
|
165
|
+
code: string;
|
|
166
|
+
name: string;
|
|
167
|
+
/** Boolean expression over the tenant's `feature:*`/`is:*` gating whether it may be assigned. */
|
|
168
|
+
assignableIf?: string | null;
|
|
169
|
+
}
|
|
170
|
+
/** A scope carried by an M2M service key (`scope:*`); same assignability gating as roles. */
|
|
171
|
+
export interface ScopeDef {
|
|
172
|
+
code: string;
|
|
173
|
+
name: string;
|
|
174
|
+
assignableIf?: string | null;
|
|
175
|
+
}
|
|
176
|
+
/** An authorization feature granted to a tenant (`feature:*`). */
|
|
177
|
+
export interface FeatureDef {
|
|
178
|
+
code: string;
|
|
179
|
+
name: string;
|
|
180
|
+
/** Boolean expression over the tenant's current features gating whether it may be granted. */
|
|
181
|
+
assignableIf?: string | null;
|
|
182
|
+
}
|
|
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
|
+
export interface CustomFieldDef {
|
|
205
|
+
key: string;
|
|
206
|
+
label: string;
|
|
207
|
+
/** `"string"` | `"number"` | `"bool"` | `"select"`. */
|
|
208
|
+
type: string;
|
|
209
|
+
/** Allowed values for a `select` field (ignored otherwise). */
|
|
210
|
+
options?: string[];
|
|
211
|
+
required: boolean;
|
|
212
|
+
/** Whether admin list tables surface this field as a column. */
|
|
213
|
+
showInTable?: boolean;
|
|
214
|
+
}
|
|
215
|
+
/** The custom-field schemas for rendering user/tenant forms (`GET /config/custom-fields`). */
|
|
216
|
+
export interface CustomFieldsSchema {
|
|
217
|
+
user: CustomFieldDef[];
|
|
218
|
+
tenant: CustomFieldDef[];
|
|
219
|
+
}
|
|
220
|
+
export interface SecuritySettings {
|
|
221
|
+
minPasswordLength: number;
|
|
222
|
+
accessTtlSecs: number;
|
|
223
|
+
refreshTtlSecs: number;
|
|
224
|
+
/** Validity window for a messaging link code (seconds); older codes rotate on read / reject on link. */
|
|
225
|
+
messagingCodeTtlSecs?: number;
|
|
226
|
+
}
|
|
227
|
+
/** An ordered permission rule: when `when` holds against the accumulated subject set, `grant` is
|
|
228
|
+
* folded in (later rules see earlier grants). An empty `when` always applies. */
|
|
229
|
+
export interface PermissionRule {
|
|
230
|
+
when: string;
|
|
231
|
+
grant: string[];
|
|
232
|
+
}
|
|
233
|
+
/** A target API in the config catalog: its `aud`, eligibility gate, ordered permission projection,
|
|
234
|
+
* and claim mapping. See `docs/PERMISSIONS.md`. */
|
|
235
|
+
export interface ApiDef {
|
|
236
|
+
code: string;
|
|
237
|
+
audience: string;
|
|
238
|
+
/** Boolean expression (`,`=OR, `+`=AND, `!`=NOT) over the final subject set gating the exchange. */
|
|
239
|
+
eligibility?: string | null;
|
|
240
|
+
/** Ordered rules mapping subjects → granted permissions (accumulated top-to-bottom). */
|
|
241
|
+
permissions: PermissionRule[];
|
|
242
|
+
/** Claim mapping: claimName → source (`customUser:<k>`, `customTenant:<k>`, or a literal). */
|
|
243
|
+
claims?: Record<string, string>;
|
|
244
|
+
}
|
|
245
|
+
export interface Config {
|
|
246
|
+
version: number;
|
|
247
|
+
roles: RoleDef[];
|
|
248
|
+
/** Scopes assignable to M2M service keys. */
|
|
249
|
+
scopes: ScopeDef[];
|
|
250
|
+
features: FeatureDef[];
|
|
251
|
+
limits: LimitDef[];
|
|
252
|
+
packages: PackageDef[];
|
|
253
|
+
customTenantFields: CustomFieldDef[];
|
|
254
|
+
customUserFields: CustomFieldDef[];
|
|
255
|
+
security: SecuritySettings;
|
|
256
|
+
/** Messaging integration (Telegram/WhatsApp) settings. */
|
|
257
|
+
messaging?: MessagingConfig;
|
|
258
|
+
/** White-labeling for the management UI. */
|
|
259
|
+
branding?: BrandingConfig;
|
|
260
|
+
/** The catalog of target APIs umami can mint tokens for. */
|
|
261
|
+
apis: ApiDef[];
|
|
262
|
+
}
|
|
263
|
+
export type ApiKeyStatus = "Active" | "Revoked";
|
|
264
|
+
export interface ApiKeyView {
|
|
265
|
+
keyId: string;
|
|
266
|
+
tenantId: string;
|
|
267
|
+
/** Present for personal access tokens (the user the token acts as); null for service keys. */
|
|
268
|
+
userId: string | null;
|
|
269
|
+
name: string;
|
|
270
|
+
/** PAT role restriction — subset of the user's `role:*` (empty for service keys / all roles). */
|
|
271
|
+
roles: string[];
|
|
272
|
+
/** Service-key `scope:*` subjects (empty for PATs). */
|
|
273
|
+
scopes: string[];
|
|
274
|
+
/** Target API codes this key may mint tokens for. */
|
|
275
|
+
apis: string[];
|
|
276
|
+
status: ApiKeyStatus;
|
|
277
|
+
allowedOrigins: string[];
|
|
278
|
+
expiresAt?: string | null;
|
|
279
|
+
lastUsedAt?: string | null;
|
|
280
|
+
created: string;
|
|
281
|
+
}
|
|
282
|
+
/** Create a tenant **service** key (M2M machine principal; subjects are its `scope:*`). */
|
|
283
|
+
export interface CreateApiKeyRequest {
|
|
284
|
+
name: string;
|
|
285
|
+
/** The `scope:*` subjects this key carries (must be assignable given the tenant's features). */
|
|
286
|
+
scopes?: string[];
|
|
287
|
+
/** Target API codes this key may mint for; defaults to `["umami"]`. */
|
|
288
|
+
apis?: string[];
|
|
289
|
+
allowedOrigins?: string[];
|
|
290
|
+
expiresAt?: string;
|
|
291
|
+
}
|
|
292
|
+
/** Create a **personal access token** (acts as the current user; optionally role-restricted). */
|
|
293
|
+
export interface CreatePatRequest {
|
|
294
|
+
name: string;
|
|
295
|
+
/** Restrict the token to this subset of your own `role:*` (empty = all your roles). */
|
|
296
|
+
roles?: string[];
|
|
297
|
+
/** Target API codes this PAT may mint for; defaults to `["umami"]`. */
|
|
298
|
+
apis?: string[];
|
|
299
|
+
expiresAt?: string;
|
|
300
|
+
}
|
|
301
|
+
export interface CreateApiKeyResponse {
|
|
302
|
+
keyId: string;
|
|
303
|
+
/** The full `umk_…` secret — returned only once. */
|
|
304
|
+
apiKey: string;
|
|
305
|
+
name: string;
|
|
306
|
+
}
|
|
307
|
+
/** White-labeling for the management UI. All optional; empty → built-in defaults. `logo`/`favicon`
|
|
308
|
+
* may be a `data:` URI or an `http(s)` URL. Served at /app/branding.css, /app/logo, /app/favicon. */
|
|
309
|
+
export interface BrandingConfig {
|
|
310
|
+
/** Extra CSS injected after the app stylesheet — override the accent via
|
|
311
|
+
* `:root{--brand: <r> <g> <b>; --brand-dark: <r> <g> <b>}` (space-separated RGB channels). */
|
|
312
|
+
customCss?: string;
|
|
313
|
+
/** Logo for light backgrounds (data: URI or http(s) URL); falls back to logoDark, then default. */
|
|
314
|
+
logoLight?: string;
|
|
315
|
+
/** Logo for dark backgrounds; falls back to logoLight, then default. */
|
|
316
|
+
logoDark?: string;
|
|
317
|
+
favicon?: string;
|
|
318
|
+
}
|
|
319
|
+
/** Messaging integration settings (Telegram/WhatsApp). */
|
|
320
|
+
export interface MessagingConfig {
|
|
321
|
+
/** WhatsApp business number (digits) for click-to-chat links. */
|
|
322
|
+
whatsappNumber?: string;
|
|
323
|
+
/** Telegram bot username (without `@`) for deep links. */
|
|
324
|
+
telegramBot?: string;
|
|
325
|
+
}
|
|
326
|
+
/** The caller's link code plus ready-made deep links (when the deployment is configured). */
|
|
327
|
+
export interface MessagingCodeResponse {
|
|
328
|
+
code: string;
|
|
329
|
+
telegramUrl?: string;
|
|
330
|
+
whatsappUrl?: string;
|
|
331
|
+
}
|
|
332
|
+
/** An external messaging identity mapped to a user. */
|
|
333
|
+
export interface MessagingLink {
|
|
334
|
+
linkKey: string;
|
|
335
|
+
userId: string;
|
|
336
|
+
tenantId: string;
|
|
337
|
+
/** `"telegram"` | `"whatsapp"`. */
|
|
338
|
+
platform: string;
|
|
339
|
+
externalId: string;
|
|
340
|
+
created: string;
|
|
341
|
+
}
|
|
342
|
+
/** Resolve output (default): compact user info for a messaging identity. */
|
|
343
|
+
export interface ResolvedMessagingUser {
|
|
344
|
+
userId: string;
|
|
345
|
+
tenantId: string;
|
|
346
|
+
name: string;
|
|
347
|
+
email?: string | null;
|
|
348
|
+
locale: string;
|
|
349
|
+
roles: string[];
|
|
350
|
+
}
|
|
351
|
+
/** Outcome flavour of an audited event. */
|
|
352
|
+
export type AuditSeverity = "good" | "neutral" | "bad";
|
|
353
|
+
export interface AuditEntry {
|
|
354
|
+
id: string;
|
|
355
|
+
/** RFC3339 event time. */
|
|
356
|
+
timestamp: string;
|
|
357
|
+
tenant?: string | null;
|
|
358
|
+
user?: string | null;
|
|
359
|
+
severity: AuditSeverity;
|
|
360
|
+
message: string;
|
|
361
|
+
}
|
|
362
|
+
/** Result of an admin password reset — `temporaryPassword` is set (once) only when generated. */
|
|
363
|
+
export interface ResetPasswordResponse {
|
|
364
|
+
status: string;
|
|
365
|
+
temporaryPassword?: string;
|
|
366
|
+
}
|
|
367
|
+
/** Error shape returned by the server (wasabi `ApiError`). */
|
|
368
|
+
export interface ApiErrorBody {
|
|
369
|
+
message?: string;
|
|
370
|
+
[k: string]: unknown;
|
|
371
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Decodes a base64url string to an ArrayBuffer. */
|
|
2
|
+
export declare function b64urlToBuffer(value: string): ArrayBuffer;
|
|
3
|
+
/** Encodes an ArrayBuffer as a base64url string (no padding). */
|
|
4
|
+
export declare function bufferToB64url(buffer: ArrayBuffer): string;
|
|
5
|
+
/** Turns the server's `publicKey` creation options into browser `PublicKeyCredentialCreationOptions`. */
|
|
6
|
+
export declare function toCreationOptions(publicKey: any): PublicKeyCredentialCreationOptions;
|
|
7
|
+
/** Turns the server's `publicKey` request options into browser `PublicKeyCredentialRequestOptions`. */
|
|
8
|
+
export declare function toRequestOptions(publicKey: any): PublicKeyCredentialRequestOptions;
|
|
9
|
+
/** Serializes a registration credential into the JSON the server's `register/finish` expects. */
|
|
10
|
+
export declare function registrationToJSON(credential: PublicKeyCredential): unknown;
|
|
11
|
+
/** Serializes an assertion credential into the JSON the server's `login/finish` expects. */
|
|
12
|
+
export declare function assertionToJSON(credential: PublicKeyCredential): unknown;
|
package/dist/webauthn.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Browser WebAuthn helpers: convert the server's base64url challenge JSON to/from the ArrayBuffers
|
|
2
|
+
// that `navigator.credentials` requires. The server (webauthn-rs) emits/consumes standard WebAuthn
|
|
3
|
+
// JSON with base64url-encoded binary fields.
|
|
4
|
+
/** Decodes a base64url string to an ArrayBuffer. */
|
|
5
|
+
export function b64urlToBuffer(value) {
|
|
6
|
+
const padded = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
7
|
+
const binary = atob(padded.padEnd(padded.length + ((4 - (padded.length % 4)) % 4), "="));
|
|
8
|
+
const bytes = new Uint8Array(binary.length);
|
|
9
|
+
for (let i = 0; i < binary.length; i++)
|
|
10
|
+
bytes[i] = binary.charCodeAt(i);
|
|
11
|
+
return bytes.buffer;
|
|
12
|
+
}
|
|
13
|
+
/** Encodes an ArrayBuffer as a base64url string (no padding). */
|
|
14
|
+
export function bufferToB64url(buffer) {
|
|
15
|
+
const bytes = new Uint8Array(buffer);
|
|
16
|
+
let binary = "";
|
|
17
|
+
for (let i = 0; i < bytes.length; i++)
|
|
18
|
+
binary += String.fromCharCode(bytes[i]);
|
|
19
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
20
|
+
}
|
|
21
|
+
/** Turns the server's `publicKey` creation options into browser `PublicKeyCredentialCreationOptions`. */
|
|
22
|
+
export function toCreationOptions(publicKey) {
|
|
23
|
+
return {
|
|
24
|
+
...publicKey,
|
|
25
|
+
challenge: b64urlToBuffer(publicKey.challenge),
|
|
26
|
+
user: { ...publicKey.user, id: b64urlToBuffer(publicKey.user.id) },
|
|
27
|
+
excludeCredentials: (publicKey.excludeCredentials ?? []).map((c) => ({
|
|
28
|
+
...c,
|
|
29
|
+
id: b64urlToBuffer(c.id),
|
|
30
|
+
})),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** Turns the server's `publicKey` request options into browser `PublicKeyCredentialRequestOptions`. */
|
|
34
|
+
export function toRequestOptions(publicKey) {
|
|
35
|
+
return {
|
|
36
|
+
...publicKey,
|
|
37
|
+
challenge: b64urlToBuffer(publicKey.challenge),
|
|
38
|
+
allowCredentials: (publicKey.allowCredentials ?? []).map((c) => ({
|
|
39
|
+
...c,
|
|
40
|
+
id: b64urlToBuffer(c.id),
|
|
41
|
+
})),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Serializes a registration credential into the JSON the server's `register/finish` expects. */
|
|
45
|
+
export function registrationToJSON(credential) {
|
|
46
|
+
const response = credential.response;
|
|
47
|
+
return {
|
|
48
|
+
id: credential.id,
|
|
49
|
+
rawId: bufferToB64url(credential.rawId),
|
|
50
|
+
type: credential.type,
|
|
51
|
+
response: {
|
|
52
|
+
attestationObject: bufferToB64url(response.attestationObject),
|
|
53
|
+
clientDataJSON: bufferToB64url(response.clientDataJSON),
|
|
54
|
+
},
|
|
55
|
+
extensions: credential.getClientExtensionResults(),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** Serializes an assertion credential into the JSON the server's `login/finish` expects. */
|
|
59
|
+
export function assertionToJSON(credential) {
|
|
60
|
+
const response = credential.response;
|
|
61
|
+
return {
|
|
62
|
+
id: credential.id,
|
|
63
|
+
rawId: bufferToB64url(credential.rawId),
|
|
64
|
+
type: credential.type,
|
|
65
|
+
response: {
|
|
66
|
+
authenticatorData: bufferToB64url(response.authenticatorData),
|
|
67
|
+
clientDataJSON: bufferToB64url(response.clientDataJSON),
|
|
68
|
+
signature: bufferToB64url(response.signature),
|
|
69
|
+
userHandle: response.userHandle ? bufferToB64url(response.userHandle) : null,
|
|
70
|
+
},
|
|
71
|
+
extensions: credential.getClientExtensionResults(),
|
|
72
|
+
};
|
|
73
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bentoforge/umami-iam",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Typed client SDK for the umami micro-IAM service",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"src",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"iam",
|
|
22
|
+
"auth",
|
|
23
|
+
"jwt",
|
|
24
|
+
"oidc",
|
|
25
|
+
"multi-tenant",
|
|
26
|
+
"umami"
|
|
27
|
+
],
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/bentoforge/umami.git",
|
|
31
|
+
"directory": "clients/typescript"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/bentoforge/umami#readme",
|
|
34
|
+
"bugs": "https://github.com/bentoforge/umami/issues",
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc -p tsconfig.json",
|
|
40
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
41
|
+
"clean": "rm -rf dist",
|
|
42
|
+
"lint": "biome check",
|
|
43
|
+
"format": "biome check --write"
|
|
44
|
+
},
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@biomejs/biome": "^2.5.8",
|
|
48
|
+
"typescript": "^5.6.0"
|
|
49
|
+
}
|
|
50
|
+
}
|