@passflow/core 0.0.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.
Files changed (67) hide show
  1. package/README.md +1087 -0
  2. package/dist/index.js +2 -0
  3. package/dist/index.js.map +1 -0
  4. package/dist/index.mjs +2149 -0
  5. package/dist/index.mjs.map +1 -0
  6. package/dist/lib/api/app.d.ts +8 -0
  7. package/dist/lib/api/app.d.ts.map +1 -0
  8. package/dist/lib/api/auth.d.ts +23 -0
  9. package/dist/lib/api/auth.d.ts.map +1 -0
  10. package/dist/lib/api/axios-client.d.ts +36 -0
  11. package/dist/lib/api/axios-client.d.ts.map +1 -0
  12. package/dist/lib/api/index.d.ts +8 -0
  13. package/dist/lib/api/index.d.ts.map +1 -0
  14. package/dist/lib/api/invitation.d.ts +77 -0
  15. package/dist/lib/api/invitation.d.ts.map +1 -0
  16. package/dist/lib/api/model.d.ts +459 -0
  17. package/dist/lib/api/model.d.ts.map +1 -0
  18. package/dist/lib/api/setting.d.ts +10 -0
  19. package/dist/lib/api/setting.d.ts.map +1 -0
  20. package/dist/lib/api/tenant.d.ts +213 -0
  21. package/dist/lib/api/tenant.d.ts.map +1 -0
  22. package/dist/lib/api/user.d.ts +19 -0
  23. package/dist/lib/api/user.d.ts.map +1 -0
  24. package/dist/lib/constants/index.d.ts +8 -0
  25. package/dist/lib/constants/index.d.ts.map +1 -0
  26. package/dist/lib/device-service/index.d.ts +7 -0
  27. package/dist/lib/device-service/index.d.ts.map +1 -0
  28. package/dist/lib/index.d.ts +8 -0
  29. package/dist/lib/index.d.ts.map +1 -0
  30. package/dist/lib/passflow.d.ts +115 -0
  31. package/dist/lib/passflow.d.ts.map +1 -0
  32. package/dist/lib/services/auth-service.d.ts +67 -0
  33. package/dist/lib/services/auth-service.d.ts.map +1 -0
  34. package/dist/lib/services/index.d.ts +7 -0
  35. package/dist/lib/services/index.d.ts.map +1 -0
  36. package/dist/lib/services/invitation-service.d.ts +44 -0
  37. package/dist/lib/services/invitation-service.d.ts.map +1 -0
  38. package/dist/lib/services/logger.d.ts +24 -0
  39. package/dist/lib/services/logger.d.ts.map +1 -0
  40. package/dist/lib/services/tenant-service.d.ts +200 -0
  41. package/dist/lib/services/tenant-service.d.ts.map +1 -0
  42. package/dist/lib/services/tenant-user-membership.d.ts +76 -0
  43. package/dist/lib/services/tenant-user-membership.d.ts.map +1 -0
  44. package/dist/lib/services/token-cache-service.d.ts +26 -0
  45. package/dist/lib/services/token-cache-service.d.ts.map +1 -0
  46. package/dist/lib/services/user-service.d.ts +39 -0
  47. package/dist/lib/services/user-service.d.ts.map +1 -0
  48. package/dist/lib/storage-manager/index.d.ts +37 -0
  49. package/dist/lib/storage-manager/index.d.ts.map +1 -0
  50. package/dist/lib/store.d.ts +89 -0
  51. package/dist/lib/store.d.ts.map +1 -0
  52. package/dist/lib/token-service/index.d.ts +4 -0
  53. package/dist/lib/token-service/index.d.ts.map +1 -0
  54. package/dist/lib/token-service/membership.d.ts +37 -0
  55. package/dist/lib/token-service/membership.d.ts.map +1 -0
  56. package/dist/lib/token-service/service.d.ts +35 -0
  57. package/dist/lib/token-service/service.d.ts.map +1 -0
  58. package/dist/lib/token-service/token.d.ts +34 -0
  59. package/dist/lib/token-service/token.d.ts.map +1 -0
  60. package/dist/lib/types/index.d.ts +22 -0
  61. package/dist/lib/types/index.d.ts.map +1 -0
  62. package/dist/tests/storage-manager/fake-storage.d.ts +7 -0
  63. package/dist/tests/storage-manager/fake-storage.d.ts.map +1 -0
  64. package/dist/tests/storage-manager/storage-manager.test.d.ts +2 -0
  65. package/dist/tests/storage-manager/storage-manager.test.d.ts.map +1 -0
  66. package/dist/tsconfig.tsbuildinfo +1 -0
  67. package/package.json +81 -0
package/README.md ADDED
@@ -0,0 +1,1087 @@
1
+ # Passflow JavaScript SDK Documentation
2
+
3
+ ## Changelog
4
+
5
+ ### Version 0.1.45
6
+
7
+ - Fixed bug related to null/undefined checks in the codebase
8
+
9
+ ## Table of Contents
10
+
11
+ - [Passflow JavaScript SDK Documentation](#passflow-javascript-sdk-documentation)
12
+ - [Changelog](#changelog)
13
+ - [Version 0.1.45](#version-0145)
14
+ - [Table of Contents](#table-of-contents)
15
+ - [Quick Start Examples](#quick-start-examples)
16
+ - [Basic Initialization](#basic-initialization)
17
+ - [Simple Authentication](#simple-authentication)
18
+ - [Quick Passwordless Authentication](#quick-passwordless-authentication)
19
+ - [Basic Passkey Usage](#basic-passkey-usage)
20
+ - [Quick Password Reset](#quick-password-reset)
21
+ - [Basic Tenant Creation](#basic-tenant-creation)
22
+ - [Simple Event Subscription](#simple-event-subscription)
23
+ - [Introduction](#introduction)
24
+ - [Installation](#installation)
25
+ - [Getting Started](#getting-started)
26
+ - [Initialization](#initialization)
27
+ - [Session Management](#session-management)
28
+ - [Authentication](#authentication)
29
+ - [Sign In](#sign-in)
30
+ - [Sign Up](#sign-up)
31
+ - [Sign Out](#sign-out)
32
+ - [Passwordless Authentication](#passwordless-authentication)
33
+ - [Federated Authentication](#federated-authentication)
34
+ - [Passkey Authentication](#passkey-authentication)
35
+ - [Password Reset](#password-reset)
36
+ - [Token Management](#token-management)
37
+ - [Tenant Management](#tenant-management)
38
+ - [Invitation Management](#invitation-management)
39
+ - [Events and Subscriptions](#events-and-subscriptions)
40
+ - [Error Handling](#error-handling)
41
+ - [API Reference](#api-reference)
42
+ - [Passflow Class](#passflow-class)
43
+ - [Constructor](#constructor)
44
+ - [Methods](#methods)
45
+ - [Services](#services)
46
+ - [Types](#types)
47
+ - [PassflowConfig](#passflowconfig)
48
+ - [Tokens](#tokens)
49
+ - [Token](#token)
50
+ - [PassflowSignInPayload](#passflowsigninpayload)
51
+ - [PassflowSignUpPayload](#passflowsignuppayload)
52
+ - [UserMembership](#usermembership)
53
+ - [PassflowEvent](#passflowevent)
54
+ - [Detailed Examples](#detailed-examples)
55
+ - [Complete Sign In Flow](#complete-sign-in-flow)
56
+ - [Authentication with Passkeys](#authentication-with-passkeys)
57
+ - [Multi-tenant Application](#multi-tenant-application)
58
+
59
+ ## Quick Start Examples
60
+
61
+ ### Basic Initialization
62
+
63
+ ```javascript
64
+ import { Passflow } from "passflow-js";
65
+
66
+ // Minimal initialization with just the required appId
67
+ const passflow = new Passflow({
68
+ appId: "your-app-id",
69
+ });
70
+
71
+ // Simple session setup
72
+ passflow.session({
73
+ createSession: () => console.log("User is authenticated"),
74
+ expiredSession: () => console.log("User session expired"),
75
+ });
76
+ ```
77
+
78
+ ### Simple Authentication
79
+
80
+ ```javascript
81
+ // Basic sign in with just email and password
82
+ const simpleSignIn = async () => {
83
+ try {
84
+ await passflow.signIn({
85
+ email: "user@example.com",
86
+ password: "password123",
87
+ });
88
+ console.log("Signed in successfully");
89
+ } catch (error) {
90
+ console.error("Sign in failed", error);
91
+ }
92
+ };
93
+
94
+ // Basic sign up with minimal user info
95
+ const simpleSignUp = async () => {
96
+ try {
97
+ await passflow.signUp({
98
+ user: {
99
+ email: "user@example.com",
100
+ password: "password123",
101
+ },
102
+ });
103
+ console.log("Registered successfully");
104
+ } catch (error) {
105
+ console.error("Registration failed", error);
106
+ }
107
+ };
108
+
109
+ // Simple logout
110
+ const simpleSignOut = async () => {
111
+ await passflow.logOut();
112
+ };
113
+ ```
114
+
115
+ ### Quick Passwordless Authentication
116
+
117
+ ```javascript
118
+ // Start passwordless flow with just email
119
+ const simplePasswordless = async () => {
120
+ try {
121
+ const response = await passflow.passwordlessSignIn({
122
+ email: "user@example.com",
123
+ challenge_type: "otp",
124
+ redirect_url: window.location.origin,
125
+ });
126
+ console.log("Check your email for the code");
127
+ return response.challenge_id;
128
+ } catch (error) {
129
+ console.error("Failed to start passwordless flow", error);
130
+ }
131
+ };
132
+
133
+ // Complete with just the challenge ID and OTP
134
+ const completeSimplePasswordless = async (challengeId, otp) => {
135
+ try {
136
+ await passflow.passwordlessSignInComplete({
137
+ challenge_id: challengeId,
138
+ otp: otp,
139
+ });
140
+ console.log("Authentication successful");
141
+ } catch (error) {
142
+ console.error("Failed to complete passwordless flow", error);
143
+ }
144
+ };
145
+ ```
146
+
147
+ ### Basic Passkey Usage
148
+
149
+ ```javascript
150
+ // Register a passkey with minimal options
151
+ const simplePasskeyRegister = async () => {
152
+ try {
153
+ await passflow.passkeyRegister({
154
+ relying_party_id: window.location.hostname,
155
+ redirect_url: window.location.origin,
156
+ scopes: ["id", "offline"],
157
+ });
158
+ console.log("Passkey registered");
159
+ } catch (error) {
160
+ console.error("Passkey registration failed", error);
161
+ }
162
+ };
163
+
164
+ // Authenticate with minimal options
165
+ const simplePasskeyAuthenticate = async () => {
166
+ try {
167
+ await passflow.passkeyAuthenticate({
168
+ relying_party_id: window.location.hostname,
169
+ });
170
+ console.log("Authenticated with passkey");
171
+ } catch (error) {
172
+ console.error("Passkey authentication failed", error);
173
+ }
174
+ };
175
+ ```
176
+
177
+ ### Quick Password Reset
178
+
179
+ ```javascript
180
+ // Send reset email with just the email address
181
+ const simplePasswordReset = async () => {
182
+ try {
183
+ await passflow.sendPasswordResetEmail({
184
+ email: "user@example.com",
185
+ });
186
+ console.log("Password reset email sent");
187
+ } catch (error) {
188
+ console.error("Failed to send reset email", error);
189
+ }
190
+ };
191
+ ```
192
+
193
+ ### Basic Tenant Creation
194
+
195
+ ```javascript
196
+ // Create a tenant with just a name
197
+ const simpleCreateTenant = async () => {
198
+ try {
199
+ await passflow.createTenant("My Organization");
200
+ console.log("Tenant created");
201
+ } catch (error) {
202
+ console.error("Failed to create tenant", error);
203
+ }
204
+ };
205
+ ```
206
+
207
+ ### Simple Event Subscription
208
+
209
+ ```javascript
210
+ // Subscribe to authentication events with minimal setup
211
+ passflow.subscribe({
212
+ onAuthChange: (eventType) => {
213
+ console.log(`Auth event occurred: ${eventType}`);
214
+ },
215
+ });
216
+ ```
217
+
218
+ ## Introduction
219
+
220
+ Passflow JavaScript SDK is a client library for interacting with the Passflow authentication service. It provides a comprehensive set of features for user authentication, token management, tenant management, and more. The SDK supports various authentication methods including email/password, passwordless, passkeys (WebAuthn), and federated identity providers.
221
+
222
+ ## Installation
223
+
224
+ ```bash
225
+ npm install passflow-js
226
+ # or
227
+ yarn add passflow-js
228
+ ```
229
+
230
+ ## Getting Started
231
+
232
+ ### Initialization
233
+
234
+ Import and initialize the Passflow client with your configuration:
235
+
236
+ ```javascript
237
+ import { Passflow } from "passflow-js";
238
+
239
+ const passflow = new Passflow({
240
+ url: "https://auth.passflow.cloud", // or your custom URL
241
+ appId: "your-app-id",
242
+ scopes: [
243
+ "id",
244
+ "offline",
245
+ "tenant",
246
+ "email",
247
+ "oidc",
248
+ "openid",
249
+ "access:tenant:all",
250
+ ], // optional, these are the defaults
251
+ createTenantForNewUser: false, // optional
252
+ parseQueryParams: true, // optional, will parse tokens from URL query params
253
+ keyStoragePrefix: "myapp", // optional, prefix for localStorage keys
254
+ });
255
+ ```
256
+
257
+ ### Session Management
258
+
259
+ Setup session management to handle authentication state:
260
+
261
+ ```javascript
262
+ passflow.session({
263
+ createSession: (tokens) => {
264
+ console.log("Session created", tokens);
265
+ // Set your app's authenticated state
266
+ },
267
+ expiredSession: () => {
268
+ console.log("Session expired");
269
+ // Clear your app's authenticated state
270
+ },
271
+ doRefresh: true, // automatically refresh tokens when expired
272
+ });
273
+ ```
274
+
275
+ ## Authentication
276
+
277
+ ### Sign In
278
+
279
+ ```javascript
280
+ // Sign in with email and password
281
+ const signIn = async () => {
282
+ try {
283
+ const response = await passflow.signIn({
284
+ email: "user@example.com",
285
+ password: "password123",
286
+ scopes: ["id", "offline", "tenant", "email"], // optional
287
+ });
288
+ console.log("Signed in successfully", response);
289
+ } catch (error) {
290
+ console.error("Sign in failed", error);
291
+ }
292
+ };
293
+ ```
294
+
295
+ ### Sign Up
296
+
297
+ ```javascript
298
+ // Register a new user
299
+ const signUp = async () => {
300
+ try {
301
+ const response = await passflow.signUp({
302
+ user: {
303
+ email: "user@example.com",
304
+ password: "password123",
305
+ given_name: "John",
306
+ family_name: "Doe",
307
+ // Additional optional user fields
308
+ },
309
+ scopes: ["id", "offline", "tenant", "email"], // optional
310
+ create_tenant: true, // optional
311
+ });
312
+ console.log("Registered successfully", response);
313
+ } catch (error) {
314
+ console.error("Registration failed", error);
315
+ }
316
+ };
317
+ ```
318
+
319
+ ### Sign Out
320
+
321
+ ```javascript
322
+ // Log out the current user
323
+ const signOut = async () => {
324
+ try {
325
+ await passflow.logOut();
326
+ console.log("Signed out successfully");
327
+ } catch (error) {
328
+ console.error("Sign out failed", error);
329
+ }
330
+ };
331
+ ```
332
+
333
+ ### Passwordless Authentication
334
+
335
+ ```javascript
336
+ // Start passwordless authentication flow
337
+ const startPasswordless = async () => {
338
+ try {
339
+ const response = await passflow.passwordlessSignIn({
340
+ email: "user@example.com",
341
+ challenge_type: "otp", // or 'magic_link'
342
+ redirect_url: "https://yourapp.com/auth/callback",
343
+ });
344
+ console.log("Passwordless authentication started", response);
345
+ // Store the challenge_id for the next step
346
+ } catch (error) {
347
+ console.error("Passwordless authentication failed", error);
348
+ }
349
+ };
350
+
351
+ // Complete passwordless authentication flow
352
+ const completePasswordless = async (challengeId, otp) => {
353
+ try {
354
+ const response = await passflow.passwordlessSignInComplete({
355
+ challenge_id: challengeId,
356
+ otp: otp,
357
+ });
358
+ console.log("Passwordless authentication completed", response);
359
+ } catch (error) {
360
+ console.error("Passwordless authentication completion failed", error);
361
+ }
362
+ };
363
+ ```
364
+
365
+ ### Federated Authentication
366
+
367
+ ```javascript
368
+ // Sign in with a provider using popup
369
+ passflow.federatedAuthWithPopup(
370
+ "google", // or 'facebook'
371
+ "https://yourapp.com/auth/callback",
372
+ ["id", "offline", "tenant", "email"] // optional scopes
373
+ );
374
+
375
+ // Sign in with a provider using redirect
376
+ passflow.federatedAuthWithRedirect(
377
+ "google", // or 'facebook'
378
+ "https://yourapp.com/auth/callback",
379
+ ["id", "offline", "tenant", "email"] // optional scopes
380
+ );
381
+ ```
382
+
383
+ ### Passkey Authentication
384
+
385
+ ```javascript
386
+ // Register a new passkey
387
+ const registerPasskey = async () => {
388
+ try {
389
+ const response = await passflow.passkeyRegister({
390
+ passkey_display_name: "My Passkey",
391
+ passkey_username: "user@example.com",
392
+ relying_party_id: window.location.hostname,
393
+ redirect_url: "https://yourapp.com/auth/callback",
394
+ scopes: ["id", "offline", "tenant", "email"], // optional
395
+ });
396
+ console.log("Passkey registered successfully", response);
397
+ } catch (error) {
398
+ console.error("Passkey registration failed", error);
399
+ }
400
+ };
401
+
402
+ // Authenticate with a passkey
403
+ const authenticateWithPasskey = async () => {
404
+ try {
405
+ const response = await passflow.passkeyAuthenticate({
406
+ relying_party_id: window.location.hostname,
407
+ scopes: ["id", "offline", "tenant", "email"], // optional
408
+ });
409
+ console.log("Passkey authentication successful", response);
410
+ } catch (error) {
411
+ console.error("Passkey authentication failed", error);
412
+ }
413
+ };
414
+
415
+ // Add additional passkey to user account
416
+ const addPasskey = async () => {
417
+ try {
418
+ await passflow.addUserPasskey({
419
+ relyingPartyId: window.location.hostname,
420
+ passkeyUsername: "user@example.com",
421
+ passkeyDisplayName: "My Secondary Passkey",
422
+ });
423
+ console.log("Passkey added successfully");
424
+ } catch (error) {
425
+ console.error("Adding passkey failed", error);
426
+ }
427
+ };
428
+
429
+ // Manage user passkeys
430
+ const managePasskeys = async () => {
431
+ try {
432
+ // Get all passkeys
433
+ const passkeys = await passflow.getUserPasskeys();
434
+ console.log("User passkeys", passkeys);
435
+
436
+ // Rename a passkey
437
+ await passflow.renameUserPasskey("New Name", "passkey-id");
438
+
439
+ // Delete a passkey
440
+ await passflow.deleteUserPasskey("passkey-id");
441
+ } catch (error) {
442
+ console.error("Passkey management failed", error);
443
+ }
444
+ };
445
+ ```
446
+
447
+ ### Password Reset
448
+
449
+ ```javascript
450
+ // Send password reset email
451
+ const sendResetEmail = async () => {
452
+ try {
453
+ await passflow.sendPasswordResetEmail({
454
+ email: "user@example.com",
455
+ reset_page_url: "https://yourapp.com/reset-password",
456
+ });
457
+ console.log("Password reset email sent");
458
+ } catch (error) {
459
+ console.error("Sending password reset email failed", error);
460
+ }
461
+ };
462
+
463
+ // Reset password (after receiving reset token)
464
+ const resetPassword = async (newPassword) => {
465
+ try {
466
+ // The token should be in the URL query parameters
467
+ const response = await passflow.resetPassword(newPassword);
468
+ console.log("Password reset successful", response);
469
+ } catch (error) {
470
+ console.error("Password reset failed", error);
471
+ }
472
+ };
473
+ ```
474
+
475
+ ## Token Management
476
+
477
+ ```javascript
478
+ // Check if user is authenticated
479
+ const isAuthenticated = passflow.isAuthenticated();
480
+
481
+ // Get current tokens
482
+ const tokens = passflow.getTokensCache();
483
+
484
+ // Get parsed tokens (decoded JWT payload)
485
+ const parsedTokens = passflow.getParsedTokenCache();
486
+
487
+ // Manually refresh token
488
+ const refreshToken = async () => {
489
+ try {
490
+ const response = await passflow.refreshToken();
491
+ console.log("Token refreshed", response);
492
+ } catch (error) {
493
+ console.error("Token refresh failed", error);
494
+ }
495
+ };
496
+
497
+ // Set tokens manually
498
+ const setTokens = async (tokens) => {
499
+ try {
500
+ await passflow.setTokens({
501
+ access_token: tokens.access_token,
502
+ refresh_token: tokens.refresh_token,
503
+ id_token: tokens.id_token,
504
+ scopes: tokens.scopes,
505
+ });
506
+ } catch (error) {
507
+ console.error("Setting tokens failed", error);
508
+ }
509
+ };
510
+
511
+ // Handle tokens from redirect
512
+ const handleRedirect = () => {
513
+ const tokens = passflow.handleTokensRedirect();
514
+ if (tokens) {
515
+ console.log("Tokens received from redirect", tokens);
516
+ }
517
+ };
518
+ ```
519
+
520
+ ## Tenant Management
521
+
522
+ ```javascript
523
+ // Create a new tenant
524
+ const createTenant = async () => {
525
+ try {
526
+ const response = await passflow.createTenant("My Organization", true);
527
+ console.log("Tenant created", response);
528
+ } catch (error) {
529
+ console.error("Tenant creation failed", error);
530
+ }
531
+ };
532
+
533
+ // Join a tenant via invitation
534
+ const joinTenant = async (invitationToken) => {
535
+ try {
536
+ const response = await passflow.joinInvitation(invitationToken);
537
+ console.log("Joined tenant", response);
538
+ } catch (error) {
539
+ console.error("Joining tenant failed", error);
540
+ }
541
+ };
542
+ ```
543
+
544
+ ## Invitation Management
545
+
546
+ ```javascript
547
+ // Request an invitation link
548
+ const requestInvite = async () => {
549
+ try {
550
+ const response = await passflow.requestInviteLink({
551
+ email: "newuser@example.com",
552
+ tenant: "tenant-id", // optional
553
+ group: "group-id", // optional
554
+ role: "role-name", // optional
555
+ callback: "https://yourapp.com/onboarding", // optional
556
+ send_to_email: true, // optional
557
+ });
558
+ console.log("Invitation link created", response);
559
+ } catch (error) {
560
+ console.error("Creating invitation link failed", error);
561
+ }
562
+ };
563
+
564
+ // Get all active invitations
565
+ const getInvitations = async () => {
566
+ try {
567
+ const invitations = await passflow.getInvitations({
568
+ tenant_id: "tenant-id", // optional
569
+ group_id: "group-id", // optional
570
+ skip: 0, // optional
571
+ limit: 10, // optional
572
+ });
573
+ console.log("Active invitations", invitations);
574
+ } catch (error) {
575
+ console.error("Getting invitations failed", error);
576
+ }
577
+ };
578
+
579
+ // Delete an invitation
580
+ const deleteInvitation = async (token) => {
581
+ try {
582
+ await passflow.deleteInvitation(token);
583
+ console.log("Invitation deleted");
584
+ } catch (error) {
585
+ console.error("Deleting invitation failed", error);
586
+ }
587
+ };
588
+ ```
589
+
590
+ ## Events and Subscriptions
591
+
592
+ ```javascript
593
+ // Define a subscriber
594
+ const subscriber = {
595
+ onAuthChange: (eventType, source) => {
596
+ console.log(`Auth event: ${eventType}`, source);
597
+
598
+ // Handle different event types
599
+ switch (eventType) {
600
+ case "signin":
601
+ // Handle sign in
602
+ break;
603
+ case "signout":
604
+ // Handle sign out
605
+ break;
606
+ case "register":
607
+ // Handle registration
608
+ break;
609
+ case "error":
610
+ // Handle error
611
+ break;
612
+ case "refresh":
613
+ // Handle token refresh
614
+ break;
615
+ }
616
+ },
617
+ };
618
+
619
+ // Subscribe to all events
620
+ passflow.subscribe(subscriber);
621
+
622
+ // Subscribe to specific events
623
+ passflow.subscribe(subscriber, ["signin", "signout"]);
624
+
625
+ // Unsubscribe from all events
626
+ passflow.unsubscribe(subscriber);
627
+
628
+ // Unsubscribe from specific events
629
+ passflow.unsubscribe(subscriber, ["error"]);
630
+ ```
631
+
632
+ ## Error Handling
633
+
634
+ Errors thrown by the SDK are typically instances of `PassflowError` which include details about the error:
635
+
636
+ ```javascript
637
+ try {
638
+ await passflow.signIn({
639
+ email: "user@example.com",
640
+ password: "wrong-password",
641
+ });
642
+ } catch (error) {
643
+ if (error instanceof PassflowError) {
644
+ console.error(`Error ID: ${error.id}`);
645
+ console.error(`Error Message: ${error.message}`);
646
+ console.error(`Status Code: ${error.status}`);
647
+ console.error(`Location: ${error.location}`);
648
+ console.error(`Time: ${error.time}`);
649
+ } else {
650
+ console.error("Unknown error:", error);
651
+ }
652
+ }
653
+ ```
654
+
655
+ ## API Reference
656
+
657
+ ### Passflow Class
658
+
659
+ The main class that provides access to all functionality of the SDK.
660
+
661
+ #### Constructor
662
+
663
+ ```typescript
664
+ constructor(config: PassflowConfig)
665
+ ```
666
+
667
+ Configuration options:
668
+
669
+ - `url`: The URL of the Passflow service (default: 'https://auth.passflow.cloud')
670
+ - `appId`: Your application ID
671
+ - `scopes`: Token scopes to request (default: ['id', 'offline', 'tenant', 'email', 'oidc', 'openid', 'access:tenant:all'])
672
+ - `createTenantForNewUser`: Whether to create a tenant for new users (default: false)
673
+ - `parseQueryParams`: Whether to parse tokens from URL query parameters (default: false)
674
+ - `keyStoragePrefix`: Prefix for localStorage keys
675
+
676
+ #### Methods
677
+
678
+ **Authentication Methods**
679
+
680
+ | Method | Description |
681
+ | ---------------------------------------------------------- | -------------------------------------- |
682
+ | `session({ createSession, expiredSession, doRefresh })` | Set up session management |
683
+ | `signIn(payload)` | Sign in with email/password |
684
+ | `signUp(payload)` | Register a new user |
685
+ | `logOut()` | Sign out the current user |
686
+ | `passwordlessSignIn(payload)` | Start passwordless authentication |
687
+ | `passwordlessSignInComplete(payload)` | Complete passwordless authentication |
688
+ | `federatedAuthWithPopup(provider, redirectUrl, scopes)` | Sign in with a provider using popup |
689
+ | `federatedAuthWithRedirect(provider, redirectUrl, scopes)` | Sign in with a provider using redirect |
690
+ | `passkeyRegister(payload)` | Register a new passkey |
691
+ | `passkeyAuthenticate(payload)` | Authenticate with a passkey |
692
+ | `sendPasswordResetEmail(payload)` | Send password reset email |
693
+ | `resetPassword(newPassword, scopes)` | Reset password |
694
+
695
+ **Token Methods**
696
+
697
+ | Method | Description |
698
+ | -------------------------- | ------------------------------ |
699
+ | `isAuthenticated()` | Check if user is authenticated |
700
+ | `getTokensCache()` | Get current tokens |
701
+ | `getParsedTokenCache()` | Get parsed tokens |
702
+ | `refreshToken()` | Refresh the access token |
703
+ | `setTokens(tokens)` | Set tokens manually |
704
+ | `handleTokensRedirect()` | Handle tokens from redirect |
705
+ | `authRedirectUrl(options)` | Generate an auth redirect URL |
706
+ | `authRedirect(options)` | Redirect to the auth page |
707
+
708
+ **Tenant Methods**
709
+
710
+ | Method | Description |
711
+ | ---------------------------------- | ---------------------------- |
712
+ | `createTenant(name, refreshToken)` | Create a new tenant |
713
+ | `joinInvitation(token, scopes)` | Join a tenant via invitation |
714
+
715
+ **Invitation Methods**
716
+
717
+ | Method | Description |
718
+ | ---------------------------- | -------------------------- |
719
+ | `requestInviteLink(payload)` | Request an invitation link |
720
+ | `getInvitations(options)` | Get all active invitations |
721
+ | `deleteInvitation(token)` | Delete an invitation |
722
+
723
+ **Passkey Methods**
724
+
725
+ | Method | Description |
726
+ | ------------------------------------ | ----------------------------- |
727
+ | `getUserPasskeys()` | Get all user passkeys |
728
+ | `renameUserPasskey(name, passkeyId)` | Rename a passkey |
729
+ | `deleteUserPasskey(passkeyId)` | Delete a passkey |
730
+ | `addUserPasskey(options)` | Add a passkey to user account |
731
+
732
+ **Event Methods**
733
+
734
+ | Method | Description |
735
+ | --------------------------------- | ---------------------------- |
736
+ | `subscribe(subscriber, events)` | Subscribe to auth events |
737
+ | `unsubscribe(subscriber, events)` | Unsubscribe from auth events |
738
+
739
+ ### Services
740
+
741
+ The SDK includes several services that handle specific functionality:
742
+
743
+ - `AuthService`: Handles authentication and session management
744
+ - `UserService`: Handles user-related operations
745
+ - `TenantService`: Handles tenant operations
746
+ - `InvitationService`: Handles invitation operations
747
+ - `DeviceService`: Manages device identification
748
+ - `TokenService`: Handles token parsing and validation
749
+ - `StorageManager`: Manages token storage
750
+
751
+ ### Types
752
+
753
+ Key types used in the SDK:
754
+
755
+ #### PassflowConfig
756
+
757
+ ```typescript
758
+ type PassflowConfig = {
759
+ url?: string;
760
+ appId?: string;
761
+ scopes?: string[];
762
+ createTenantForNewUser?: boolean;
763
+ parseQueryParams?: boolean;
764
+ keyStoragePrefix?: string;
765
+ };
766
+ ```
767
+
768
+ #### Tokens
769
+
770
+ ```typescript
771
+ type Tokens = {
772
+ access_token: string;
773
+ id_token?: string;
774
+ refresh_token?: string;
775
+ scopes?: string[];
776
+ };
777
+ ```
778
+
779
+ #### Token
780
+
781
+ ```typescript
782
+ type Token = {
783
+ aud: string[];
784
+ exp: number;
785
+ iat: number;
786
+ iss: string;
787
+ jti: string;
788
+ sub: string;
789
+ type: string;
790
+ email?: string;
791
+ passflow_tm?: RawUserMembership;
792
+ payload?: unknown;
793
+ membership?: UserMembership;
794
+ };
795
+ ```
796
+
797
+ #### PassflowSignInPayload
798
+
799
+ ```typescript
800
+ type PassflowSignInPayload = {
801
+ password: string;
802
+ scopes?: string[];
803
+ email?: string;
804
+ phone?: string;
805
+ username?: string;
806
+ } & ({ email: string } | { phone: string } | { username: string });
807
+ ```
808
+
809
+ #### PassflowSignUpPayload
810
+
811
+ ```typescript
812
+ type PassflowSignUpPayload = {
813
+ user: PassflowUserPayload;
814
+ scopes?: string[];
815
+ create_tenant?: boolean;
816
+ anonymous?: boolean;
817
+ invite?: string;
818
+ };
819
+ ```
820
+
821
+ #### UserMembership
822
+
823
+ ```typescript
824
+ type UserMembership = {
825
+ raw: RawUserMembership;
826
+ tenants: TenantMembership[];
827
+ };
828
+ ```
829
+
830
+ #### PassflowEvent
831
+
832
+ ```typescript
833
+ enum PassflowEvent {
834
+ SignIn = "signin",
835
+ Register = "register",
836
+ SignOut = "signout",
837
+ Error = "error",
838
+ Refresh = "refresh",
839
+ }
840
+ ```
841
+
842
+ ## Detailed Examples
843
+
844
+ ### Complete Sign In Flow
845
+
846
+ ```javascript
847
+ import { Passflow, PassflowError } from "passflow-js";
848
+
849
+ // Initialize Passflow
850
+ const passflow = new Passflow({
851
+ appId: "your-app-id",
852
+ parseQueryParams: true,
853
+ });
854
+
855
+ // Set up session management
856
+ passflow.session({
857
+ createSession: (tokens) => {
858
+ // Store authentication state
859
+ localStorage.setItem("isAuthenticated", "true");
860
+
861
+ // Update UI
862
+ document.getElementById("login-form").style.display = "none";
863
+ document.getElementById("user-dashboard").style.display = "block";
864
+ },
865
+ expiredSession: () => {
866
+ // Clear authentication state
867
+ localStorage.removeItem("isAuthenticated");
868
+
869
+ // Update UI
870
+ document.getElementById("login-form").style.display = "block";
871
+ document.getElementById("user-dashboard").style.display = "none";
872
+ },
873
+ doRefresh: true,
874
+ });
875
+
876
+ // Handle form submission
877
+ document
878
+ .getElementById("login-form")
879
+ .addEventListener("submit", async (event) => {
880
+ event.preventDefault();
881
+
882
+ const email = document.getElementById("email").value;
883
+ const password = document.getElementById("password").value;
884
+
885
+ try {
886
+ await passflow.signIn({ email, password });
887
+ // Login successful - session callback will handle UI update
888
+ } catch (error) {
889
+ if (error instanceof PassflowError) {
890
+ document.getElementById("error-message").textContent = error.message;
891
+ } else {
892
+ document.getElementById("error-message").textContent =
893
+ "An unexpected error occurred";
894
+ console.error(error);
895
+ }
896
+ }
897
+ });
898
+
899
+ // Handle logout
900
+ document.getElementById("logout-button").addEventListener("click", async () => {
901
+ await passflow.logOut();
902
+ // Logout successful - session callback will handle UI update
903
+ });
904
+
905
+ // Check for redirect tokens on page load
906
+ window.addEventListener("load", () => {
907
+ const tokens = passflow.handleTokensRedirect();
908
+ if (tokens) {
909
+ console.log("Authenticated via redirect");
910
+ }
911
+ });
912
+ ```
913
+
914
+ ### Authentication with Passkeys
915
+
916
+ ```javascript
917
+ import { Passflow } from "passflow-js";
918
+
919
+ // Initialize Passflow
920
+ const passflow = new Passflow({
921
+ appId: "your-app-id",
922
+ });
923
+
924
+ // Set up session management
925
+ passflow.session({
926
+ createSession: (tokens) => {
927
+ console.log("Session created", tokens);
928
+ },
929
+ expiredSession: () => {
930
+ console.log("Session expired");
931
+ },
932
+ doRefresh: true,
933
+ });
934
+
935
+ // Handle passkey registration
936
+ document
937
+ .getElementById("register-passkey-button")
938
+ .addEventListener("click", async () => {
939
+ try {
940
+ const email = document.getElementById("email").value;
941
+
942
+ await passflow.passkeyRegister({
943
+ passkey_display_name: "My Passkey",
944
+ passkey_username: email,
945
+ relying_party_id: window.location.hostname,
946
+ redirect_url: window.location.origin,
947
+ scopes: ["id", "offline", "tenant", "email"],
948
+ });
949
+
950
+ alert("Passkey registered successfully!");
951
+ } catch (error) {
952
+ alert(`Passkey registration failed: ${error.message}`);
953
+ console.error(error);
954
+ }
955
+ });
956
+
957
+ // Handle passkey authentication
958
+ document
959
+ .getElementById("login-with-passkey-button")
960
+ .addEventListener("click", async () => {
961
+ try {
962
+ await passflow.passkeyAuthenticate({
963
+ relying_party_id: window.location.hostname,
964
+ });
965
+
966
+ alert("Authenticated with passkey successfully!");
967
+ } catch (error) {
968
+ alert(`Passkey authentication failed: ${error.message}`);
969
+ console.error(error);
970
+ }
971
+ });
972
+ ```
973
+
974
+ ### Multi-tenant Application
975
+
976
+ ```javascript
977
+ import { Passflow, PassflowEvent } from "passflow-js";
978
+
979
+ // Initialize Passflow
980
+ const passflow = new Passflow({
981
+ appId: "your-app-id",
982
+ });
983
+
984
+ // Track current tenant
985
+ let currentTenant = null;
986
+
987
+ // Set up session management
988
+ passflow.session({
989
+ createSession: (tokens) => {
990
+ const parsedTokens = passflow.getParsedTokenCache();
991
+ if (parsedTokens?.access_token?.membership?.tenants?.length > 0) {
992
+ currentTenant = parsedTokens.access_token.membership.tenants[0];
993
+ updateTenantUI();
994
+ } else {
995
+ showCreateTenantUI();
996
+ }
997
+ },
998
+ expiredSession: () => {
999
+ currentTenant = null;
1000
+ showLoginUI();
1001
+ },
1002
+ doRefresh: true,
1003
+ });
1004
+
1005
+ // Subscribe to auth events
1006
+ passflow.subscribe({
1007
+ onAuthChange: (eventType) => {
1008
+ if (
1009
+ eventType === PassflowEvent.SignIn ||
1010
+ eventType === PassflowEvent.Register
1011
+ ) {
1012
+ const parsedTokens = passflow.getParsedTokenCache();
1013
+ if (parsedTokens?.access_token?.membership?.tenants?.length > 0) {
1014
+ currentTenant = parsedTokens.access_token.membership.tenants[0];
1015
+ updateTenantUI();
1016
+ } else {
1017
+ showCreateTenantUI();
1018
+ }
1019
+ }
1020
+ },
1021
+ });
1022
+
1023
+ // Create tenant function
1024
+ async function createNewTenant() {
1025
+ const tenantName = document.getElementById("tenant-name").value;
1026
+ try {
1027
+ await passflow.createTenant(tenantName, true);
1028
+ const parsedTokens = passflow.getParsedTokenCache();
1029
+ currentTenant = parsedTokens.access_token.membership.tenants[0];
1030
+ updateTenantUI();
1031
+ } catch (error) {
1032
+ console.error("Failed to create tenant", error);
1033
+ alert(`Failed to create tenant: ${error.message}`);
1034
+ }
1035
+ }
1036
+
1037
+ // Invite user function
1038
+ async function inviteUser() {
1039
+ const email = document.getElementById("invite-email").value;
1040
+ try {
1041
+ const response = await passflow.requestInviteLink({
1042
+ email,
1043
+ tenant: currentTenant.tenant.id,
1044
+ send_to_email: true,
1045
+ });
1046
+ alert(`Invitation sent to ${email}`);
1047
+ console.log("Invitation link:", response.link);
1048
+ } catch (error) {
1049
+ console.error("Failed to invite user", error);
1050
+ alert(`Failed to invite user: ${error.message}`);
1051
+ }
1052
+ }
1053
+
1054
+ // UI update functions
1055
+ function updateTenantUI() {
1056
+ document.getElementById("tenant-name-display").textContent =
1057
+ currentTenant.tenant.name;
1058
+ document.getElementById("tenant-id-display").textContent =
1059
+ currentTenant.tenant.id;
1060
+ document.getElementById("tenant-section").style.display = "block";
1061
+ document.getElementById("create-tenant-section").style.display = "none";
1062
+ }
1063
+
1064
+ function showCreateTenantUI() {
1065
+ document.getElementById("tenant-section").style.display = "none";
1066
+ document.getElementById("create-tenant-section").style.display = "block";
1067
+ }
1068
+
1069
+ function showLoginUI() {
1070
+ document.getElementById("tenant-section").style.display = "none";
1071
+ document.getElementById("create-tenant-section").style.display = "none";
1072
+ document.getElementById("login-section").style.display = "block";
1073
+ }
1074
+
1075
+ // Event listeners
1076
+ document
1077
+ .getElementById("create-tenant-form")
1078
+ .addEventListener("submit", (e) => {
1079
+ e.preventDefault();
1080
+ createNewTenant();
1081
+ });
1082
+
1083
+ document.getElementById("invite-form").addEventListener("submit", (e) => {
1084
+ e.preventDefault();
1085
+ inviteUser();
1086
+ });
1087
+ ```