@syndew/oluso-sdk 0.0.0-stage → 1.30.1

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.
@@ -0,0 +1,535 @@
1
+ /**
2
+ * Field definition from a journey policy
3
+ */
4
+ interface JourneyField {
5
+ name: string;
6
+ type: 'text' | 'email' | 'password' | 'number' | 'date' | 'tel' | 'url' | 'textarea' | 'select' | 'radio' | 'checkbox';
7
+ label: string;
8
+ placeholder?: string;
9
+ description?: string;
10
+ required: boolean;
11
+ options?: Array<{
12
+ value: string;
13
+ label: string;
14
+ }>;
15
+ validation?: {
16
+ minLength?: number;
17
+ maxLength?: number;
18
+ pattern?: string;
19
+ min?: number;
20
+ max?: number;
21
+ };
22
+ }
23
+ /**
24
+ * A journey running step by step through the headless API (start, step, session).
25
+ * Every headless call returns the session as it stands after the call.
26
+ */
27
+ interface JourneySession {
28
+ sessionId: string;
29
+ /** The journey's slug */
30
+ slug: string;
31
+ status: 'in_progress' | 'completed' | 'failed' | 'cancelled' | 'expired';
32
+ success: boolean;
33
+ completed: boolean;
34
+ /** The step waiting for input, while in progress */
35
+ currentStep?: string;
36
+ /** Same as currentStep: set while there is a step to complete */
37
+ nextStep?: string;
38
+ completedSteps: string[];
39
+ expiresAt?: string;
40
+ /** What to render for the current step */
41
+ step?: JourneyStepView;
42
+ /** The step continues elsewhere (e.g. an external identity provider) */
43
+ redirectUrl?: string;
44
+ /** Authentication journeys: the authorization code to redeem with the PKCE verifier */
45
+ code?: string;
46
+ /** Data-collection journeys: the success message once completed */
47
+ message?: string;
48
+ error?: string;
49
+ errorDescription?: string;
50
+ }
51
+ /** A step to render: its definition and the model the server renders it from */
52
+ interface JourneyStepView {
53
+ id: string;
54
+ /** Step type, e.g. local_login, mfa, dynamic_form */
55
+ type: string;
56
+ displayName?: string;
57
+ /** The server view the step renders as */
58
+ view?: string;
59
+ /** Fields, options, messages and errors for the step */
60
+ model?: Record<string, unknown>;
61
+ /** The step as a form any client can draw (see JourneyStepForm) */
62
+ form?: JourneyStepForm;
63
+ /** The hosted view's markup, for a step that has no form description (custom pages, plugin views) */
64
+ html?: string;
65
+ actions: JourneyStepAction[];
66
+ }
67
+ /**
68
+ * A step as a form. Posting its fields with the clicked action's name and value is exactly what the hosted page
69
+ * posts, so any journey step can be drawn by any client.
70
+ */
71
+ interface JourneyStepForm {
72
+ title?: string;
73
+ description?: string;
74
+ /** What went wrong with the last submission */
75
+ error?: string;
76
+ /** Something to tell the person that isn't an error */
77
+ notice?: string;
78
+ fields: JourneyFormField[];
79
+ /** The buttons, in order; each posts the fields with its own name and value */
80
+ actions: JourneyFormAction[];
81
+ links: JourneyFormLink[];
82
+ /** Browser work for the step: passkey, captcha, QR code, recovery codes or waiting */
83
+ widget?: JourneyFormWidget;
84
+ }
85
+ type JourneyFormFieldType = 'text' | 'email' | 'password' | 'number' | 'date' | 'tel' | 'url' | 'textarea' | 'select' | 'radio' | 'checkbox' | 'file' | 'hidden' | 'otp' | 'content';
86
+ interface JourneyFormField {
87
+ name: string;
88
+ type: JourneyFormFieldType | string;
89
+ label?: string;
90
+ placeholder?: string;
91
+ description?: string;
92
+ /** Guidance under the input, e.g. upload limits */
93
+ hint?: string;
94
+ required: boolean;
95
+ readOnly: boolean;
96
+ value?: string;
97
+ error?: string;
98
+ options?: JourneyFormOption[];
99
+ pattern?: string;
100
+ patternError?: string;
101
+ minLength?: number;
102
+ maxLength?: number;
103
+ min?: string;
104
+ max?: string;
105
+ rows?: number;
106
+ autocomplete?: string;
107
+ autofocus: boolean;
108
+ accept?: string;
109
+ maxFiles?: number;
110
+ maxFileSizeMb?: number;
111
+ capture?: string;
112
+ /** Phone: countries to pick from; the country is posted as `${name}__country` */
113
+ countries?: JourneyFormCountry[];
114
+ country?: string;
115
+ showWhen?: JourneyFormCondition;
116
+ /** Shown only while every one of these is met too */
117
+ showWhenAll?: JourneyFormCondition[];
118
+ group?: string;
119
+ }
120
+ interface JourneyFormOption {
121
+ value: string;
122
+ label: string;
123
+ description?: string;
124
+ disabled: boolean;
125
+ }
126
+ interface JourneyFormCountry {
127
+ code: string;
128
+ name: string;
129
+ dialCode: string;
130
+ flag?: string;
131
+ }
132
+ interface JourneyFormCondition {
133
+ field: string;
134
+ operator: string;
135
+ value?: string;
136
+ }
137
+ interface JourneyFormAction {
138
+ /** The posted name; absent for a button that posts no name of its own */
139
+ name?: string;
140
+ value?: string;
141
+ label: string;
142
+ /** Text shown with the button, e.g. "Didn't get the code?" before a resend link */
143
+ description?: string;
144
+ /** A question to confirm before the button posts; the renderer asks inline */
145
+ confirm?: string;
146
+ /** More values the button posts */
147
+ values?: Record<string, string>;
148
+ /** False: the button posts only its own name, value and values, none of the step's fields */
149
+ postsFields: boolean;
150
+ kind: 'primary' | 'secondary' | 'link' | 'cancel' | string;
151
+ skipValidation: boolean;
152
+ }
153
+ interface JourneyFormLink {
154
+ label: string;
155
+ url: string;
156
+ }
157
+ interface JourneyFormWidget {
158
+ type: 'webauthn_get' | 'webauthn_create' | 'captcha' | 'qr' | 'codes' | 'poll' | string;
159
+ data?: Record<string, unknown>;
160
+ /** Where the widget's result is posted */
161
+ resultField?: string;
162
+ /** The action posted with the result */
163
+ submit?: JourneyFormAction;
164
+ }
165
+ interface JourneyStepAction {
166
+ id: string;
167
+ label: string;
168
+ isPrimary: boolean;
169
+ isCancel: boolean;
170
+ }
171
+
172
+ /** Text the renderer shows itself; everything about a step comes from the server in the person's language. */
173
+ interface JourneyLabels {
174
+ loading: string;
175
+ completed: string;
176
+ failed: string;
177
+ expired: string;
178
+ restart: string;
179
+ takePhoto: string;
180
+ copy: string;
181
+ copied: string;
182
+ passkey: string;
183
+ retry: string;
184
+ waiting: string;
185
+ unavailable: string;
186
+ }
187
+ interface JourneyMount {
188
+ /** The session as it stands. */
189
+ readonly session: JourneySession | undefined;
190
+ /** Start again from the first step (only for journeys started by slug or `start`). */
191
+ restart(): Promise<void>;
192
+ destroy(): void;
193
+ }
194
+
195
+ interface OidcEndpoints {
196
+ /** Authorization endpoint */
197
+ authorization?: string;
198
+ /** Token endpoint */
199
+ token?: string;
200
+ /** UserInfo endpoint */
201
+ userinfo?: string;
202
+ /** End session / logout endpoint */
203
+ endSession?: string;
204
+ /** JWKS URI */
205
+ jwks?: string;
206
+ }
207
+ interface OlusoAuthConfig {
208
+ /** Base URL of the Oluso server */
209
+ serverUrl: string;
210
+ /** Tenant identifier */
211
+ tenant: string;
212
+ /** OAuth2 client ID (public client) */
213
+ clientId: string;
214
+ /** Redirect URI for OIDC code flow (optional if using inline/modal auth) */
215
+ redirectUri?: string;
216
+ /** Scopes to request (default: "openid profile email") */
217
+ scopes?: string;
218
+ /**
219
+ * Manual OIDC endpoint overrides. If not provided, endpoints are
220
+ * discovered from the server's .well-known/openid-configuration document.
221
+ */
222
+ endpoints?: OidcEndpoints;
223
+ /** Slug of the sign-in journey (default: "signin") */
224
+ signInJourney?: string;
225
+ /** Slug of the sign-up journey (default: "signup") */
226
+ signUpJourney?: string;
227
+ /** Slug of the password-reset journey (default: "password-reset") */
228
+ passwordResetJourney?: string;
229
+ /** Token storage strategy */
230
+ storage?: 'memory' | 'localStorage' | 'sessionStorage';
231
+ /** Public API key for journey endpoints */
232
+ apiKey?: string;
233
+ /** Callback when auth state changes */
234
+ onAuthStateChange?: (user: OlusoUser | null) => void;
235
+ }
236
+ interface OlusoUser {
237
+ /** Subject (user ID) */
238
+ sub: string;
239
+ /** Display name */
240
+ name?: string;
241
+ /** Email address */
242
+ email?: string;
243
+ /** Whether email is verified */
244
+ emailVerified?: boolean;
245
+ /** User roles */
246
+ roles: string[];
247
+ /** User permissions */
248
+ permissions: string[];
249
+ /** Tenant ID */
250
+ tenantId?: string;
251
+ /** Raw ID token claims */
252
+ claims: Record<string, unknown>;
253
+ }
254
+ interface TokenSet {
255
+ accessToken: string;
256
+ idToken?: string;
257
+ refreshToken?: string;
258
+ expiresAt: number;
259
+ tokenType: string;
260
+ scope?: string;
261
+ }
262
+ interface AuthResult {
263
+ success: boolean;
264
+ user?: OlusoUser;
265
+ tokens?: TokenSet;
266
+ error?: string;
267
+ code?: string;
268
+ /** The journey needs another step (e.g. "mfa"): continue it with continueStep(sessionId, ...) */
269
+ requiresStep?: string;
270
+ sessionId?: string;
271
+ /** What to render for that step */
272
+ step?: JourneyStepView;
273
+ /** The step continues at an external identity provider */
274
+ redirectUrl?: string;
275
+ }
276
+ interface JourneyStepData {
277
+ /** Current step identifier */
278
+ step: string;
279
+ /** Fields to display for this step */
280
+ fields: JourneyField[];
281
+ /** Step title */
282
+ title?: string;
283
+ /** Step description */
284
+ description?: string;
285
+ }
286
+ /** Options for signInWithPasskey */
287
+ interface PasskeyOptions {
288
+ /** Username for username-based flow. Omit for discoverable credential (usernameless) flow. */
289
+ username?: string;
290
+ /** Whether to use conditional mediation (passkey autofill in input fields) */
291
+ conditional?: boolean;
292
+ /** Abort signal to cancel the passkey prompt */
293
+ signal?: AbortSignal;
294
+ }
295
+ /** Options for signInWithFedCM */
296
+ interface FedCmOptions {
297
+ /** Whether to use auto-reauthentication (silent sign-in if user previously approved) */
298
+ autoReauth?: boolean;
299
+ /** Nonce for token binding */
300
+ nonce?: string;
301
+ /** Login hint (email or username) to pre-select an account */
302
+ loginHint?: string;
303
+ }
304
+ /** WebAuthn assertion options returned by the server */
305
+ interface WebAuthnAssertionOptions {
306
+ assertionId: string;
307
+ challenge: string;
308
+ timeout: number;
309
+ rpId?: string;
310
+ allowCredentials?: Array<{
311
+ type: string;
312
+ id: string;
313
+ transports?: string[];
314
+ }>;
315
+ userVerification: string;
316
+ }
317
+ /** Options for OlusoAuth.mountSignIn. */
318
+ interface MountSignInOptions {
319
+ /** Slug of the journey to run (default: the configured sign-in journey) */
320
+ journey?: string;
321
+ /** Called with the outcome once the journey completes and the code is redeemed */
322
+ onSignedIn?: (result: AuthResult) => void;
323
+ onError?: (error: Error) => void;
324
+ /** A step continues at an external identity provider; defaults to navigating there */
325
+ onRedirect?: (url: string) => void;
326
+ theme?: 'light' | 'dark' | 'auto';
327
+ unstyled?: boolean;
328
+ labels?: Partial<JourneyLabels>;
329
+ }
330
+
331
+ /**
332
+ * Oluso Auth Client
333
+ *
334
+ * Handles authentication via OIDC (redirect), the hosted journey in a modal, or the
335
+ * journey run step by step from your own UI (inline). Inline journeys complete with an
336
+ * authorization code bound to this client's PKCE verifier, redeemed for tokens here.
337
+ *
338
+ * @example
339
+ * ```typescript
340
+ * const auth = new OlusoAuth({
341
+ * serverUrl: 'https://auth.yoursite.com',
342
+ * tenant: 'acme',
343
+ * clientId: 'my-spa-client',
344
+ * });
345
+ *
346
+ * // Inline auth (no redirect)
347
+ * const result = await auth.signIn({ username: 'user@example.com', password: '...' });
348
+ * if (result.success) {
349
+ * console.log('Logged in as', result.user.name);
350
+ * }
351
+ *
352
+ * // Check roles
353
+ * if (auth.hasRole('admin')) { ... }
354
+ * ```
355
+ */
356
+ declare class OlusoAuth {
357
+ private config;
358
+ private storage;
359
+ private user;
360
+ private resolvedEndpoints;
361
+ private discoveryPromise;
362
+ /** PKCE verifiers of inline journeys still in progress, by session */
363
+ private readonly journeyVerifiers;
364
+ constructor(config: OlusoAuthConfig);
365
+ /**
366
+ * Sign in.
367
+ * - With data: run the sign-in journey inline with these inputs (programmatic, first-party
368
+ * only). If another step follows (e.g. MFA), the result has requiresStep, sessionId and
369
+ * the step to render; continue with continueStep().
370
+ * - Without data: open the hosted journey in a secure iframe modal (safe for any site)
371
+ */
372
+ signIn(data?: Record<string, unknown>): Promise<AuthResult>;
373
+ /**
374
+ * Sign up.
375
+ * - With data: run the sign-up journey inline (programmatic, first-party only)
376
+ * - Without data: open the hosted journey in a secure iframe modal
377
+ */
378
+ signUp(data?: Record<string, unknown>): Promise<AuthResult>;
379
+ /**
380
+ * Sign in with a passkey (WebAuthn).
381
+ * No form or iframe needed — the browser shows a native biometric/key prompt.
382
+ * Works with discoverable credentials (usernameless) or username-based flow.
383
+ *
384
+ * @example
385
+ * ```typescript
386
+ * // Discoverable credential (passkey autofill or prompt)
387
+ * const result = await auth.signInWithPasskey();
388
+ *
389
+ * // Username-based (shows credentials for specific user)
390
+ * const result = await auth.signInWithPasskey({ username: 'user@example.com' });
391
+ *
392
+ * // Conditional mediation (passkey suggestions in input fields)
393
+ * const result = await auth.signInWithPasskey({ conditional: true });
394
+ * ```
395
+ */
396
+ signInWithPasskey(options?: PasskeyOptions): Promise<AuthResult>;
397
+ /**
398
+ * Sign in with FedCM (Federated Credential Management).
399
+ * Opens a browser-native account picker — no redirect or popup needed.
400
+ * Requires the user to be already authenticated on the Oluso server (has session cookie).
401
+ *
402
+ * Browser support: Chrome/Edge 132+ (the provider `params` that carry the PKCE challenge).
403
+ * The page origin must be registered for the client (CORS origin or redirect URI).
404
+ *
405
+ * @example
406
+ * ```typescript
407
+ * const result = await auth.signInWithFedCM();
408
+ * if (result.success) {
409
+ * console.log('Signed in as', result.user.name);
410
+ * }
411
+ * ```
412
+ */
413
+ signInWithFedCM(options?: FedCmOptions): Promise<AuthResult>;
414
+ /**
415
+ * Render the sign-in journey into `container` and sign in without leaving the page, e.g. in a modal when the
416
+ * session has expired. Every step the journey has (password, MFA, passkey, consent) is shown; when it completes,
417
+ * the code is redeemed and `onSignedIn` gets the result.
418
+ */
419
+ mountSignIn(container: HTMLElement, options?: MountSignInOptions): JourneyMount;
420
+ /**
421
+ * Continue an inline journey with the input for its current step (e.g. an MFA code)
422
+ */
423
+ continueStep(sessionId: string, data: Record<string, unknown>): Promise<AuthResult>;
424
+ /**
425
+ * Get the fields/config of a data-collection journey (for rendering in UI)
426
+ */
427
+ getJourneyStep(journeySlug: string): Promise<JourneyStepData | null>;
428
+ /**
429
+ * Redirect to the Oluso login page (standard OIDC Authorization Code + PKCE)
430
+ */
431
+ loginWithRedirect(options?: {
432
+ prompt?: 'login' | 'consent' | 'none';
433
+ state?: string;
434
+ }): Promise<void>;
435
+ /**
436
+ * Handle the OIDC callback (exchange code for tokens)
437
+ */
438
+ handleCallback(callbackUrl?: string): Promise<AuthResult>;
439
+ /**
440
+ * Get the current user (null if not authenticated)
441
+ */
442
+ getUser(): OlusoUser | null;
443
+ /**
444
+ * Check if user is authenticated (has valid tokens)
445
+ */
446
+ isAuthenticated(): boolean;
447
+ /**
448
+ * Get the current access token (for API calls)
449
+ */
450
+ getAccessToken(): string | null;
451
+ /**
452
+ * Check if user has a specific role
453
+ */
454
+ hasRole(role: string): boolean;
455
+ /**
456
+ * Check if user has any of the specified roles
457
+ */
458
+ hasAnyRole(...roles: string[]): boolean;
459
+ /**
460
+ * Check if user has a specific permission
461
+ */
462
+ hasPermission(permission: string): boolean;
463
+ /**
464
+ * Refresh the access token using the refresh token
465
+ */
466
+ refreshToken(): Promise<boolean>;
467
+ /**
468
+ * Log out — clear tokens and optionally redirect to end session.
469
+ * If redirect is true, uses discovered or configured end_session_endpoint.
470
+ */
471
+ logout(options?: {
472
+ redirect?: boolean;
473
+ postLogoutRedirectUri?: string;
474
+ }): void;
475
+ /**
476
+ * Fetch the user's profile from the userinfo endpoint
477
+ */
478
+ fetchUserInfo(): Promise<OlusoUser | null>;
479
+ /**
480
+ * Opens a secure iframe modal for authentication.
481
+ * Credentials are entered inside the iframe (auth server origin).
482
+ * Returns tokens via postMessage + PKCE code exchange.
483
+ */
484
+ private openAuthModal;
485
+ /**
486
+ * Get OIDC endpoints — uses manual overrides, falls back to discovery.
487
+ */
488
+ private getEndpoints;
489
+ private discoverEndpoints;
490
+ private defaultEndpoints;
491
+ private executeJourney;
492
+ /**
493
+ * Completed: redeem the code with the journey's verifier. In progress: hand back the step
494
+ * to render (an error from the step's last input, e.g. a wrong password, rides along).
495
+ */
496
+ private handleJourneyResult;
497
+ private processTokenResponse;
498
+ private parseIdToken;
499
+ private claimsToUser;
500
+ private parseArrayClaim;
501
+ private buildHeaders;
502
+ private generatePkce;
503
+ private base64UrlEncode;
504
+ private base64UrlDecode;
505
+ /**
506
+ * Exchange an authorization code for tokens via the token endpoint.
507
+ * Used by passkey, FedCM, and modal auth flows.
508
+ */
509
+ private exchangeCodeForTokens;
510
+ }
511
+
512
+ /**
513
+ * Token storage abstraction
514
+ */
515
+ interface TokenStorage {
516
+ getTokens(): TokenSet | null;
517
+ setTokens(tokens: TokenSet): void;
518
+ clear(): void;
519
+ }
520
+ declare class MemoryStorage implements TokenStorage {
521
+ private tokens;
522
+ getTokens(): TokenSet | null;
523
+ setTokens(tokens: TokenSet): void;
524
+ clear(): void;
525
+ }
526
+ declare class BrowserStorage implements TokenStorage {
527
+ private store;
528
+ constructor(type: 'localStorage' | 'sessionStorage');
529
+ getTokens(): TokenSet | null;
530
+ setTokens(tokens: TokenSet): void;
531
+ clear(): void;
532
+ }
533
+ declare function createStorage(type: 'memory' | 'localStorage' | 'sessionStorage'): TokenStorage;
534
+
535
+ export { type AuthResult, BrowserStorage, type FedCmOptions, type JourneyStepData, MemoryStorage, type OidcEndpoints, OlusoAuth, type OlusoAuthConfig, type OlusoUser, type PasskeyOptions, type TokenSet, type TokenStorage, type WebAuthnAssertionOptions, createStorage };