@drmhse/sso-sdk 0.2.8 → 0.3.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/index.mjs CHANGED
@@ -49,6 +49,12 @@ var HttpClient = class {
49
49
  timeout: 3e4
50
50
  };
51
51
  }
52
+ /**
53
+ * Allow injecting session manager after construction to avoid circular dep
54
+ */
55
+ setSessionManager(manager) {
56
+ this.sessionManager = manager;
57
+ }
52
58
  /**
53
59
  * Build query string from params object
54
60
  */
@@ -82,6 +88,12 @@ var HttpClient = class {
82
88
  ...this.defaults.headers.common,
83
89
  ...options.headers
84
90
  };
91
+ if (this.sessionManager) {
92
+ const token = await this.sessionManager.getToken();
93
+ if (token) {
94
+ headers["Authorization"] = `Bearer ${token}`;
95
+ }
96
+ }
85
97
  const controller = new AbortController();
86
98
  const timeoutId = setTimeout(() => controller.abort(), timeout);
87
99
  try {
@@ -92,6 +104,17 @@ var HttpClient = class {
92
104
  signal: controller.signal
93
105
  });
94
106
  clearTimeout(timeoutId);
107
+ if (response.status === 401 && this.sessionManager && !options._retry && !path.includes("/auth/login") && !path.includes("/auth/refresh")) {
108
+ try {
109
+ const newToken = await this.sessionManager.refreshSession();
110
+ return this.request(path, {
111
+ ...options,
112
+ _retry: true,
113
+ headers: { ...options.headers, Authorization: `Bearer ${newToken}` }
114
+ });
115
+ } catch (refreshError) {
116
+ }
117
+ }
95
118
  let data;
96
119
  const contentType = response.headers.get("content-type");
97
120
  if (contentType?.includes("application/json")) {
@@ -160,6 +183,16 @@ var HttpClient = class {
160
183
  headers: config?.headers
161
184
  });
162
185
  }
186
+ /**
187
+ * PUT request
188
+ */
189
+ async put(path, data, config) {
190
+ return this.request(path, {
191
+ method: "PUT",
192
+ body: data,
193
+ headers: config?.headers
194
+ });
195
+ }
163
196
  /**
164
197
  * PATCH request
165
198
  */
@@ -184,6 +217,125 @@ function createHttpAgent(baseURL) {
184
217
  return new HttpClient(baseURL);
185
218
  }
186
219
 
220
+ // src/session.ts
221
+ var SessionManager = class {
222
+ constructor(storage, refreshHandler, config = { storageKeyPrefix: "sso_" }) {
223
+ this.storage = storage;
224
+ this.refreshHandler = refreshHandler;
225
+ this.config = config;
226
+ this.accessToken = null;
227
+ this.refreshToken = null;
228
+ this.refreshPromise = null;
229
+ this.listeners = [];
230
+ }
231
+ /**
232
+ * Initialize session from storage
233
+ */
234
+ async loadSession() {
235
+ this.accessToken = await this.storage.getItem(`${this.config.storageKeyPrefix}access_token`);
236
+ this.refreshToken = await this.storage.getItem(`${this.config.storageKeyPrefix}refresh_token`);
237
+ }
238
+ /**
239
+ * Set the session data (used after login/register)
240
+ */
241
+ async setSession(tokens) {
242
+ this.accessToken = tokens.access_token;
243
+ await this.storage.setItem(`${this.config.storageKeyPrefix}access_token`, tokens.access_token);
244
+ if (tokens.refresh_token) {
245
+ this.refreshToken = tokens.refresh_token;
246
+ await this.storage.setItem(`${this.config.storageKeyPrefix}refresh_token`, tokens.refresh_token);
247
+ }
248
+ this.notifyListeners(true);
249
+ }
250
+ /**
251
+ * Clear session (logout)
252
+ */
253
+ async clearSession() {
254
+ this.accessToken = null;
255
+ this.refreshToken = null;
256
+ await this.storage.removeItem(`${this.config.storageKeyPrefix}access_token`);
257
+ await this.storage.removeItem(`${this.config.storageKeyPrefix}refresh_token`);
258
+ this.notifyListeners(false);
259
+ }
260
+ /**
261
+ * Get the current access token, refreshing it if necessary/possible
262
+ */
263
+ async getToken() {
264
+ return this.accessToken;
265
+ }
266
+ /**
267
+ * Handle logic for when a 401 occurs
268
+ */
269
+ async refreshSession() {
270
+ if (!this.refreshToken) {
271
+ throw new Error("No refresh token available");
272
+ }
273
+ if (this.refreshPromise) {
274
+ return this.refreshPromise;
275
+ }
276
+ this.refreshPromise = (async () => {
277
+ try {
278
+ const tokens = await this.refreshHandler(this.refreshToken);
279
+ await this.setSession(tokens);
280
+ return tokens.access_token;
281
+ } catch (err) {
282
+ await this.clearSession();
283
+ throw err;
284
+ } finally {
285
+ this.refreshPromise = null;
286
+ }
287
+ })();
288
+ return this.refreshPromise;
289
+ }
290
+ isAuthenticated() {
291
+ return !!this.accessToken;
292
+ }
293
+ /**
294
+ * Subscribe to auth state changes (useful for UI updates)
295
+ */
296
+ subscribe(listener) {
297
+ this.listeners.push(listener);
298
+ return () => {
299
+ this.listeners = this.listeners.filter((l) => l !== listener);
300
+ };
301
+ }
302
+ notifyListeners(isAuth) {
303
+ this.listeners.forEach((l) => l(isAuth));
304
+ }
305
+ };
306
+
307
+ // src/storage.ts
308
+ var MemoryStorage = class {
309
+ constructor() {
310
+ this.store = /* @__PURE__ */ new Map();
311
+ }
312
+ getItem(key) {
313
+ return this.store.get(key) || null;
314
+ }
315
+ setItem(key, value) {
316
+ this.store.set(key, value);
317
+ }
318
+ removeItem(key) {
319
+ this.store.delete(key);
320
+ }
321
+ };
322
+ var BrowserStorage = class {
323
+ getItem(key) {
324
+ return typeof window !== "undefined" ? window.localStorage.getItem(key) : null;
325
+ }
326
+ setItem(key, value) {
327
+ if (typeof window !== "undefined") window.localStorage.setItem(key, value);
328
+ }
329
+ removeItem(key) {
330
+ if (typeof window !== "undefined") window.localStorage.removeItem(key);
331
+ }
332
+ };
333
+ function resolveStorage(userStorage) {
334
+ if (userStorage) return userStorage;
335
+ if (typeof window !== "undefined" && window.localStorage) return new BrowserStorage();
336
+ return new MemoryStorage();
337
+ }
338
+
187
339
  // src/modules/analytics.ts
188
340
  var AnalyticsModule = class {
189
341
  constructor(http) {
@@ -289,8 +441,9 @@ var AnalyticsModule = class {
289
441
 
290
442
  // src/modules/auth.ts
291
443
  var AuthModule = class {
292
- constructor(http) {
444
+ constructor(http, session) {
293
445
  this.http = http;
446
+ this.session = session;
294
447
  /**
295
448
  * Device Flow: Request a device code for CLI/device authentication.
296
449
  *
@@ -337,6 +490,9 @@ var AuthModule = class {
337
490
  /**
338
491
  * Exchange a device code for a JWT token.
339
492
  * This should be polled by the device/CLI after displaying the user code.
493
+ * Note: This returns a TokenResponse (not RefreshTokenResponse) and typically
494
+ * only includes access_token. For device flows that need persistence,
495
+ * manually call sso.session.setSession() if needed.
340
496
  *
341
497
  * @param payload Token request payload
342
498
  * @returns Token response with JWT
@@ -352,7 +508,7 @@ var AuthModule = class {
352
508
  * client_id: 'service-client-id'
353
509
  * });
354
510
  * clearInterval(interval);
355
- * sso.setAuthToken(token.access_token);
511
+ * // Session is automatically configured
356
512
  * } catch (error) {
357
513
  * if (error.errorCode !== 'authorization_pending') {
358
514
  * clearInterval(interval);
@@ -374,17 +530,29 @@ var AuthModule = class {
374
530
  * should redirect the user's browser to this URL.
375
531
  *
376
532
  * @param provider The OAuth provider to use
377
- * @param params Login parameters (org, service, redirect_uri)
533
+ * @param params Login parameters (org, service, redirect_uri, connection_id)
378
534
  * @returns The full URL to redirect the user to
379
535
  *
380
536
  * @example
381
537
  * ```typescript
538
+ * // Standard OAuth login
382
539
  * const url = sso.auth.getLoginUrl('github', {
383
540
  * org: 'acme-corp',
384
541
  * service: 'main-app',
385
542
  * redirect_uri: 'https://app.acme.com/callback'
386
543
  * });
387
544
  * window.location.href = url;
545
+ *
546
+ * // Enterprise IdP login (after HRD lookup)
547
+ * const hrd = await sso.auth.lookupEmail('user@enterprise.com');
548
+ * if (hrd.connection_id) {
549
+ * const url = sso.auth.getLoginUrl('github', {
550
+ * org: 'acme-corp',
551
+ * service: 'main-app',
552
+ * connection_id: hrd.connection_id
553
+ * });
554
+ * window.location.href = url;
555
+ * }
388
556
  * ```
389
557
  */
390
558
  getLoginUrl(provider, params) {
@@ -399,6 +567,9 @@ var AuthModule = class {
399
567
  if (params.user_code) {
400
568
  searchParams.append("user_code", params.user_code);
401
569
  }
570
+ if (params.connection_id) {
571
+ searchParams.append("connection_id", params.connection_id);
572
+ }
402
573
  return `${baseURL}/auth/${provider}?${searchParams.toString()}`;
403
574
  }
404
575
  /**
@@ -431,19 +602,20 @@ var AuthModule = class {
431
602
  }
432
603
  /**
433
604
  * Logout the current user by revoking their JWT.
434
- * After calling this, you should clear the token from storage
435
- * and call `sso.setAuthToken(null)`.
605
+ * Automatically clears the session and tokens from storage.
436
606
  *
437
607
  * @example
438
608
  * ```typescript
439
609
  * await sso.auth.logout();
440
- * sso.setAuthToken(null);
441
- * localStorage.removeItem('sso_access_token');
442
- * localStorage.removeItem('sso_refresh_token');
610
+ * // Session is automatically cleared - no need for manual cleanup
443
611
  * ```
444
612
  */
445
613
  async logout() {
446
- await this.http.post("/api/auth/logout");
614
+ try {
615
+ await this.http.post("/api/auth/logout");
616
+ } finally {
617
+ await this.session.clearSession();
618
+ }
447
619
  }
448
620
  /**
449
621
  * Refresh an expired JWT access token using a refresh token.
@@ -516,10 +688,26 @@ var AuthModule = class {
516
688
  const response = await this.http.post("/api/auth/register", payload);
517
689
  return response.data;
518
690
  }
691
+ /**
692
+ * Verify an email address using the token from the verification email.
693
+ *
694
+ * @param token Verification token
695
+ * @returns HTML success page string
696
+ *
697
+ * @example
698
+ * ```typescript
699
+ * const html = await sso.auth.verifyEmail('token-from-email');
700
+ * ```
701
+ */
702
+ async verifyEmail(token) {
703
+ const response = await this.http.get("/auth/verify-email", {
704
+ params: { token }
705
+ });
706
+ return response.data;
707
+ }
519
708
  /**
520
709
  * Login with email and password.
521
- * Returns access token and refresh token on successful authentication.
522
- * The user's email must be verified before login.
710
+ * Automatically persists the session and configures the client.
523
711
  *
524
712
  * @param payload Login credentials (email and password)
525
713
  * @returns Access token, refresh token, and expiration info
@@ -530,13 +718,15 @@ var AuthModule = class {
530
718
  * email: 'user@example.com',
531
719
  * password: 'SecurePassword123!'
532
720
  * });
533
- * sso.setAuthToken(tokens.access_token);
534
- * localStorage.setItem('sso_access_token', tokens.access_token);
535
- * localStorage.setItem('sso_refresh_token', tokens.refresh_token);
721
+ * // Session is automatically saved - no need for manual token management
536
722
  * ```
537
723
  */
538
724
  async login(payload) {
539
725
  const response = await this.http.post("/api/auth/login", payload);
726
+ await this.session.setSession({
727
+ access_token: response.data.access_token,
728
+ refresh_token: response.data.refresh_token
729
+ });
540
730
  return response.data;
541
731
  }
542
732
  /**
@@ -544,6 +734,7 @@ var AuthModule = class {
544
734
  * This method should be called after login when the user has MFA enabled.
545
735
  * The login will return a pre-auth token with a short expiration (5 minutes).
546
736
  * Exchange the pre-auth token and TOTP code for a full session.
737
+ * Automatically persists the session after successful MFA verification.
547
738
  *
548
739
  * @param preauthToken The pre-authentication token received from login
549
740
  * @param code The TOTP code from the user's authenticator app or a backup code
@@ -562,9 +753,7 @@ var AuthModule = class {
562
753
  * // User needs to provide MFA code
563
754
  * const mfaCode = prompt('Enter your 6-digit code from authenticator app');
564
755
  * const tokens = await sso.auth.verifyMfa(loginResponse.access_token, mfaCode);
565
- * sso.setAuthToken(tokens.access_token);
566
- * localStorage.setItem('sso_access_token', tokens.access_token);
567
- * localStorage.setItem('sso_refresh_token', tokens.refresh_token);
756
+ * // Session is automatically saved - no need for manual token management
568
757
  * }
569
758
  * ```
570
759
  */
@@ -574,6 +763,10 @@ var AuthModule = class {
574
763
  code,
575
764
  ...deviceCodeId && { device_code_id: deviceCodeId }
576
765
  });
766
+ await this.session.setSession({
767
+ access_token: response.data.access_token,
768
+ refresh_token: response.data.refresh_token
769
+ });
577
770
  return response.data;
578
771
  }
579
772
  /**
@@ -616,6 +809,52 @@ var AuthModule = class {
616
809
  const response = await this.http.post("/api/auth/reset-password", payload);
617
810
  return response.data;
618
811
  }
812
+ // ============================================================================
813
+ // HOME REALM DISCOVERY (HRD)
814
+ // ============================================================================
815
+ /**
816
+ * Lookup an email address to determine which authentication method to use.
817
+ * This implements Home Realm Discovery (HRD), allowing users to simply enter
818
+ * their email address and be automatically routed to the correct identity provider.
819
+ *
820
+ * The system will:
821
+ * 1. Extract the domain from the email address
822
+ * 2. Check if the domain is verified and mapped to an enterprise IdP
823
+ * 3. Return routing information (connection_id) if found
824
+ * 4. Otherwise, indicate to use default authentication (password or OAuth)
825
+ *
826
+ * @param email The user's email address
827
+ * @returns HRD response with routing information
828
+ *
829
+ * @example
830
+ * ```typescript
831
+ * // Lookup email to determine authentication flow
832
+ * const result = await sso.auth.lookupEmail('john@acmecorp.com');
833
+ *
834
+ * if (result.auth_method === 'upstream' && result.connection_id) {
835
+ * // Route to enterprise IdP
836
+ * console.log(`Redirecting to ${result.provider_name}`);
837
+ * const url = sso.auth.getLoginUrl('github', {
838
+ * org: 'acme-corp',
839
+ * service: 'main-app',
840
+ * connection_id: result.connection_id
841
+ * });
842
+ * window.location.href = url;
843
+ * } else if (result.auth_method === 'password') {
844
+ * // Show password login form
845
+ * showPasswordForm();
846
+ * } else {
847
+ * // Show default OAuth provider buttons (GitHub, Google, Microsoft)
848
+ * showOAuthButtons();
849
+ * }
850
+ * ```
851
+ */
852
+ async lookupEmail(email) {
853
+ const response = await this.http.post("/api/auth/lookup-email", {
854
+ email
855
+ });
856
+ return response.data;
857
+ }
619
858
  };
620
859
 
621
860
  // src/modules/user.ts
@@ -752,11 +991,127 @@ var MfaModule = class {
752
991
  return response.data;
753
992
  }
754
993
  };
994
+ var DevicesModule = class {
995
+ constructor(http) {
996
+ this.http = http;
997
+ }
998
+ /**
999
+ * List all devices associated with the authenticated user.
1000
+ *
1001
+ * @param options Optional query parameters for pagination
1002
+ * @returns Array of user devices
1003
+ *
1004
+ * @example
1005
+ * ```typescript
1006
+ * const { devices, total } = await sso.user.devices.list();
1007
+ * console.log(devices); // Array of trusted devices
1008
+ * ```
1009
+ */
1010
+ async list(options) {
1011
+ const params = new URLSearchParams();
1012
+ if (options?.page) params.append("page", options.page.toString());
1013
+ if (options?.limit) params.append("limit", options.limit.toString());
1014
+ if (options?.sort_by) params.append("sort_by", options.sort_by);
1015
+ if (options?.sort_order) params.append("sort_order", options.sort_order);
1016
+ const query = params.toString();
1017
+ const url = `/api/user/devices${query ? `?${query}` : ""}`;
1018
+ const response = await this.http.get(url);
1019
+ return response.data;
1020
+ }
1021
+ /**
1022
+ * Get details for a specific device.
1023
+ *
1024
+ * @param deviceId The device ID to retrieve
1025
+ * @returns Device details
1026
+ *
1027
+ * @example
1028
+ * ```typescript
1029
+ * const device = await sso.user.devices.get('device-123');
1030
+ * console.log(device.device_name, device.is_trusted);
1031
+ * ```
1032
+ */
1033
+ async get(deviceId) {
1034
+ const response = await this.http.get(`/api/user/devices/${deviceId}`);
1035
+ return response.data;
1036
+ }
1037
+ /**
1038
+ * Revoke access for a specific device.
1039
+ * This will remove the device's trust and require re-authentication.
1040
+ *
1041
+ * @param deviceId The device ID to revoke
1042
+ * @param reason Optional reason for revocation
1043
+ * @returns Confirmation message
1044
+ *
1045
+ * @example
1046
+ * ```typescript
1047
+ * const result = await sso.user.devices.revoke('device-123', 'Device lost');
1048
+ * console.log(result.message);
1049
+ * ```
1050
+ */
1051
+ async revoke(deviceId, reason) {
1052
+ const payload = reason ? { reason } : {};
1053
+ const response = await this.http.post(`/api/user/devices/${deviceId}/revoke`, payload);
1054
+ return response.data;
1055
+ }
1056
+ /**
1057
+ * Revoke all devices except the current one.
1058
+ * This is useful when you suspect account compromise or want to force re-authentication on all devices.
1059
+ *
1060
+ * @returns Confirmation message
1061
+ *
1062
+ * @example
1063
+ * ```typescript
1064
+ * const result = await sso.user.devices.revokeAll();
1065
+ * console.log(result.message); // "All other devices have been revoked"
1066
+ * ```
1067
+ */
1068
+ async revokeAll() {
1069
+ const response = await this.http.post("/api/user/devices/revoke-all", {});
1070
+ return response.data;
1071
+ }
1072
+ /**
1073
+ * Update the name of a device.
1074
+ *
1075
+ * @param deviceId The device ID to update
1076
+ * @param deviceName New device name
1077
+ * @returns Updated device information
1078
+ *
1079
+ * @example
1080
+ * ```typescript
1081
+ * const device = await sso.user.devices.updateName('device-123', 'My Laptop');
1082
+ * console.log(device.device_name); // "My Laptop"
1083
+ * ```
1084
+ */
1085
+ async updateName(deviceId, deviceName) {
1086
+ const response = await this.http.patch(`/api/user/devices/${deviceId}`, {
1087
+ device_name: deviceName
1088
+ });
1089
+ return response.data;
1090
+ }
1091
+ /**
1092
+ * Mark a device as trusted manually.
1093
+ * This is useful for devices that you want to explicitly trust regardless of risk assessment.
1094
+ *
1095
+ * @param deviceId The device ID to trust
1096
+ * @returns Updated device information
1097
+ *
1098
+ * @example
1099
+ * ```typescript
1100
+ * const device = await sso.user.devices.trust('device-123');
1101
+ * console.log(device.is_trusted); // true
1102
+ * ```
1103
+ */
1104
+ async trust(deviceId) {
1105
+ const response = await this.http.post(`/api/user/devices/${deviceId}/trust`, {});
1106
+ return response.data;
1107
+ }
1108
+ };
755
1109
  var UserModule = class {
756
1110
  constructor(http) {
757
1111
  this.http = http;
758
1112
  this.identities = new IdentitiesModule(http);
759
1113
  this.mfa = new MfaModule(http);
1114
+ this.devices = new DevicesModule(http);
760
1115
  }
761
1116
  /**
762
1117
  * Get the profile of the currently authenticated user.
@@ -1109,6 +1464,43 @@ var WebhooksModule = class {
1109
1464
  var OrganizationsModule = class {
1110
1465
  constructor(http) {
1111
1466
  this.http = http;
1467
+ /**
1468
+ * SCIM token management methods
1469
+ */
1470
+ this.scim = {
1471
+ /**
1472
+ * Create a new SCIM token.
1473
+ * The token is only returned once upon creation.
1474
+ */
1475
+ createToken: async (orgSlug, payload) => {
1476
+ const response = await this.http.post(
1477
+ `/api/organizations/${orgSlug}/scim-tokens`,
1478
+ payload
1479
+ );
1480
+ return response.data;
1481
+ },
1482
+ /**
1483
+ * List all SCIM tokens.
1484
+ */
1485
+ listTokens: async (orgSlug) => {
1486
+ const response = await this.http.get(
1487
+ `/api/organizations/${orgSlug}/scim-tokens`
1488
+ );
1489
+ return response.data;
1490
+ },
1491
+ /**
1492
+ * Revoke a SCIM token.
1493
+ */
1494
+ revokeToken: async (orgSlug, tokenId) => {
1495
+ await this.http.post(`/api/organizations/${orgSlug}/scim-tokens/${tokenId}/revoke`);
1496
+ },
1497
+ /**
1498
+ * Delete a SCIM token.
1499
+ */
1500
+ deleteToken: async (orgSlug, tokenId) => {
1501
+ await this.http.delete(`/api/organizations/${orgSlug}/scim-tokens/${tokenId}`);
1502
+ }
1503
+ };
1112
1504
  /**
1113
1505
  * Member management methods
1114
1506
  */
@@ -1132,6 +1524,24 @@ var OrganizationsModule = class {
1132
1524
  );
1133
1525
  return response.data;
1134
1526
  },
1527
+ /**
1528
+ * Add a member to the organization (Invite + Accept).
1529
+ * This is a convenience method that creates an invitation and immediately accepts it.
1530
+ * Useful for testing and admin operations.
1531
+ *
1532
+ * @param orgSlug Organization slug
1533
+ * @param payload Member details (email, role)
1534
+ * @returns The created invitation
1535
+ */
1536
+ add: async (orgSlug, payload) => {
1537
+ const response = await this.http.post(
1538
+ `/api/organizations/${orgSlug}/invitations`,
1539
+ payload
1540
+ );
1541
+ const invitation = response.data;
1542
+ await this.http.post("/api/invitations/accept", { token: invitation.token });
1543
+ return invitation;
1544
+ },
1135
1545
  /**
1136
1546
  * Update a member's role.
1137
1547
  * Requires 'owner' role.
@@ -1318,81 +1728,296 @@ var OrganizationsModule = class {
1318
1728
  return response.data;
1319
1729
  }
1320
1730
  };
1321
- this.auditLogs = new AuditLogsModule(http);
1322
- this.webhooks = new WebhooksModule(http);
1323
- }
1324
- /**
1325
- * Create a new organization (requires authentication).
1326
- * The authenticated user becomes the organization owner.
1327
- * Returns JWT tokens with organization context, eliminating the need to re-authenticate.
1328
- *
1329
- * @param payload Organization creation payload
1330
- * @returns Created organization with owner, membership, and JWT tokens
1331
- *
1332
- * @example
1333
- * ```typescript
1334
- * const result = await sso.organizations.create({
1335
- * slug: 'acme-corp',
1336
- * name: 'Acme Corporation'
1337
- * });
1338
- * // Store the new tokens with org context
1339
- * authStore.setTokens(result.access_token, result.refresh_token);
1340
- * ```
1341
- */
1342
- async create(payload) {
1343
- const response = await this.http.post("/api/organizations", payload);
1344
- return response.data;
1345
- }
1346
- /**
1347
- * List all organizations the authenticated user is a member of.
1348
- *
1349
- * @param params Optional query parameters for filtering and pagination
1350
- * @returns Array of organization responses
1351
- *
1352
- * @example
1353
- * ```typescript
1354
- * const orgs = await sso.organizations.list({
1355
- * status: 'active',
1356
- * page: 1,
1357
- * limit: 20
1358
- * });
1359
- * ```
1360
- */
1361
- async list(params) {
1362
- const response = await this.http.get("/api/organizations", { params });
1363
- return response.data;
1364
- }
1365
- /**
1366
- * Get detailed information for a specific organization.
1367
- *
1368
- * @param orgSlug Organization slug
1369
- * @returns Organization details
1370
- *
1371
- * @example
1372
- * ```typescript
1373
- * const org = await sso.organizations.get('acme-corp');
1374
- * console.log(org.organization.name, org.membership_count);
1375
- * ```
1376
- */
1377
- async get(orgSlug) {
1378
- const response = await this.http.get(`/api/organizations/${orgSlug}`);
1379
- return response.data;
1380
- }
1381
- /**
1382
- * Update organization details.
1383
- * Requires 'owner' or 'admin' role.
1384
- *
1385
- * @param orgSlug Organization slug
1386
- * @param payload Update payload
1387
- * @returns Updated organization details
1388
- *
1389
- * @example
1390
- * ```typescript
1391
- * const updated = await sso.organizations.update('acme-corp', {
1392
- * name: 'Acme Corporation Inc.',
1393
- * max_services: 20
1394
- * });
1395
- * ```
1731
+ // ============================================================================
1732
+ // RISK SETTINGS
1733
+ // ============================================================================
1734
+ /**
1735
+ * Risk settings management methods
1736
+ */
1737
+ this.riskSettings = {
1738
+ /**
1739
+ * Get risk settings for an organization.
1740
+ * Requires 'owner' or 'admin' role.
1741
+ *
1742
+ * @param orgSlug Organization slug
1743
+ * @returns Risk settings configuration
1744
+ *
1745
+ * @example
1746
+ * ```typescript
1747
+ * const settings = await sso.organizations.riskSettings.get('acme-corp');
1748
+ * console.log('Enforcement mode:', settings.enforcement_mode);
1749
+ * console.log('Low threshold:', settings.low_threshold);
1750
+ * ```
1751
+ */
1752
+ get: async (orgSlug) => {
1753
+ const response = await this.http.get(
1754
+ `/api/organizations/${orgSlug}/risk-settings`
1755
+ );
1756
+ return response.data;
1757
+ },
1758
+ /**
1759
+ * Update risk settings for an organization.
1760
+ * Requires 'owner' or 'admin' role.
1761
+ *
1762
+ * @param orgSlug Organization slug
1763
+ * @param payload Risk settings update payload
1764
+ * @returns Updated risk settings
1765
+ *
1766
+ * @example
1767
+ * ```typescript
1768
+ * const result = await sso.organizations.riskSettings.update('acme-corp', {
1769
+ * enforcement_mode: 'challenge',
1770
+ * low_threshold: 30,
1771
+ * medium_threshold: 70,
1772
+ * new_device_score: 20,
1773
+ * impossible_travel_score: 50
1774
+ * });
1775
+ * console.log(result.message);
1776
+ * ```
1777
+ */
1778
+ update: async (orgSlug, payload) => {
1779
+ const response = await this.http.put(
1780
+ `/api/organizations/${orgSlug}/risk-settings`,
1781
+ payload
1782
+ );
1783
+ return response.data;
1784
+ },
1785
+ /**
1786
+ * Reset risk settings to default values.
1787
+ * Requires 'owner' or 'admin' role.
1788
+ *
1789
+ * @param orgSlug Organization slug
1790
+ * @returns Reset confirmation with default values
1791
+ *
1792
+ * @example
1793
+ * ```typescript
1794
+ * const result = await sso.organizations.riskSettings.reset('acme-corp');
1795
+ * console.log('Risk settings reset to defaults');
1796
+ * ```
1797
+ */
1798
+ reset: async (orgSlug) => {
1799
+ const response = await this.http.post(
1800
+ `/api/organizations/${orgSlug}/risk-settings/reset`
1801
+ );
1802
+ return response.data;
1803
+ }
1804
+ };
1805
+ // ============================================================================
1806
+ // SIEM CONFIGURATIONS
1807
+ // ============================================================================
1808
+ /**
1809
+ * SIEM (Security Information and Event Management) configuration methods
1810
+ */
1811
+ this.siem = {
1812
+ /**
1813
+ * Create a new SIEM configuration.
1814
+ * Requires 'owner' or 'admin' role.
1815
+ *
1816
+ * @param orgSlug Organization slug
1817
+ * @param payload SIEM configuration payload
1818
+ * @returns Created SIEM configuration
1819
+ *
1820
+ * @example
1821
+ * ```typescript
1822
+ * const config = await sso.organizations.siem.create('acme-corp', {
1823
+ * name: 'Datadog Integration',
1824
+ * provider_type: 'Datadog',
1825
+ * endpoint_url: 'https://http-intake.logs.datadoghq.com/v1/input',
1826
+ * api_key: 'dd-api-key',
1827
+ * batch_size: 100
1828
+ * });
1829
+ * ```
1830
+ */
1831
+ create: async (orgSlug, payload) => {
1832
+ const response = await this.http.post(
1833
+ `/api/organizations/${orgSlug}/siem-configs`,
1834
+ payload
1835
+ );
1836
+ return response.data;
1837
+ },
1838
+ /**
1839
+ * List all SIEM configurations for an organization.
1840
+ * Requires 'owner' or 'admin' role.
1841
+ *
1842
+ * @param orgSlug Organization slug
1843
+ * @returns List of SIEM configurations
1844
+ *
1845
+ * @example
1846
+ * ```typescript
1847
+ * const result = await sso.organizations.siem.list('acme-corp');
1848
+ * console.log(`Total SIEM configs: ${result.total}`);
1849
+ * result.siem_configs.forEach(config => {
1850
+ * console.log(config.name, config.provider_type, config.enabled);
1851
+ * });
1852
+ * ```
1853
+ */
1854
+ list: async (orgSlug) => {
1855
+ const response = await this.http.get(
1856
+ `/api/organizations/${orgSlug}/siem-configs`
1857
+ );
1858
+ return response.data;
1859
+ },
1860
+ /**
1861
+ * Get a specific SIEM configuration.
1862
+ * Requires 'owner' or 'admin' role.
1863
+ *
1864
+ * @param orgSlug Organization slug
1865
+ * @param configId SIEM configuration ID
1866
+ * @returns SIEM configuration
1867
+ *
1868
+ * @example
1869
+ * ```typescript
1870
+ * const config = await sso.organizations.siem.get('acme-corp', 'config-id');
1871
+ * console.log(config.name, config.endpoint_url);
1872
+ * ```
1873
+ */
1874
+ get: async (orgSlug, configId) => {
1875
+ const response = await this.http.get(
1876
+ `/api/organizations/${orgSlug}/siem-configs/${configId}`
1877
+ );
1878
+ return response.data;
1879
+ },
1880
+ /**
1881
+ * Update a SIEM configuration.
1882
+ * Requires 'owner' or 'admin' role.
1883
+ *
1884
+ * @param orgSlug Organization slug
1885
+ * @param configId SIEM configuration ID
1886
+ * @param payload Update payload
1887
+ * @returns Updated SIEM configuration
1888
+ *
1889
+ * @example
1890
+ * ```typescript
1891
+ * const updated = await sso.organizations.siem.update('acme-corp', 'config-id', {
1892
+ * enabled: false,
1893
+ * batch_size: 200
1894
+ * });
1895
+ * ```
1896
+ */
1897
+ update: async (orgSlug, configId, payload) => {
1898
+ const response = await this.http.patch(
1899
+ `/api/organizations/${orgSlug}/siem-configs/${configId}`,
1900
+ payload
1901
+ );
1902
+ return response.data;
1903
+ },
1904
+ /**
1905
+ * Delete a SIEM configuration.
1906
+ * Requires 'owner' or 'admin' role.
1907
+ *
1908
+ * @param orgSlug Organization slug
1909
+ * @param configId SIEM configuration ID
1910
+ *
1911
+ * @example
1912
+ * ```typescript
1913
+ * await sso.organizations.siem.delete('acme-corp', 'config-id');
1914
+ * console.log('SIEM configuration deleted');
1915
+ * ```
1916
+ */
1917
+ delete: async (orgSlug, configId) => {
1918
+ await this.http.delete(`/api/organizations/${orgSlug}/siem-configs/${configId}`);
1919
+ },
1920
+ /**
1921
+ * Test connection to a SIEM endpoint.
1922
+ * Sends a test event to verify the configuration.
1923
+ * Requires 'owner' or 'admin' role.
1924
+ *
1925
+ * @param orgSlug Organization slug
1926
+ * @param configId SIEM configuration ID
1927
+ * @returns Test result
1928
+ *
1929
+ * @example
1930
+ * ```typescript
1931
+ * const result = await sso.organizations.siem.test('acme-corp', 'config-id');
1932
+ * if (result.success) {
1933
+ * console.log('Connection successful:', result.message);
1934
+ * } else {
1935
+ * console.error('Connection failed:', result.message);
1936
+ * }
1937
+ * ```
1938
+ */
1939
+ test: async (orgSlug, configId) => {
1940
+ const response = await this.http.post(
1941
+ `/api/organizations/${orgSlug}/siem-configs/${configId}/test`
1942
+ );
1943
+ return response.data;
1944
+ }
1945
+ };
1946
+ this.auditLogs = new AuditLogsModule(http);
1947
+ this.webhooks = new WebhooksModule(http);
1948
+ }
1949
+ /**
1950
+ * Create a new organization (requires authentication).
1951
+ * The authenticated user becomes the organization owner.
1952
+ * Returns JWT tokens with organization context, eliminating the need to re-authenticate.
1953
+ *
1954
+ * @param payload Organization creation payload
1955
+ * @returns Created organization with owner, membership, and JWT tokens
1956
+ *
1957
+ * @example
1958
+ * ```typescript
1959
+ * const result = await sso.organizations.create({
1960
+ * slug: 'acme-corp',
1961
+ * name: 'Acme Corporation'
1962
+ * });
1963
+ * // Store the new tokens with org context
1964
+ * authStore.setTokens(result.access_token, result.refresh_token);
1965
+ * ```
1966
+ */
1967
+ async create(payload) {
1968
+ const response = await this.http.post("/api/organizations", payload);
1969
+ return response.data;
1970
+ }
1971
+ /**
1972
+ * List all organizations the authenticated user is a member of.
1973
+ *
1974
+ * @param params Optional query parameters for filtering and pagination
1975
+ * @returns Array of organization responses
1976
+ *
1977
+ * @example
1978
+ * ```typescript
1979
+ * const orgs = await sso.organizations.list({
1980
+ * status: 'active',
1981
+ * page: 1,
1982
+ * limit: 20
1983
+ * });
1984
+ * ```
1985
+ */
1986
+ async list(params) {
1987
+ const response = await this.http.get("/api/organizations", { params });
1988
+ return response.data;
1989
+ }
1990
+ /**
1991
+ * Get detailed information for a specific organization.
1992
+ *
1993
+ * @param orgSlug Organization slug
1994
+ * @returns Organization details
1995
+ *
1996
+ * @example
1997
+ * ```typescript
1998
+ * const org = await sso.organizations.get('acme-corp');
1999
+ * console.log(org.organization.name, org.membership_count);
2000
+ * ```
2001
+ */
2002
+ async get(orgSlug) {
2003
+ const response = await this.http.get(`/api/organizations/${orgSlug}`);
2004
+ return response.data;
2005
+ }
2006
+ /**
2007
+ * Update organization details.
2008
+ * Requires 'owner' or 'admin' role.
2009
+ *
2010
+ * @param orgSlug Organization slug
2011
+ * @param payload Update payload
2012
+ * @returns Updated organization details
2013
+ *
2014
+ * @example
2015
+ * ```typescript
2016
+ * const updated = await sso.organizations.update('acme-corp', {
2017
+ * name: 'Acme Corporation Inc.',
2018
+ * max_services: 20
2019
+ * });
2020
+ * ```
1396
2021
  */
1397
2022
  async update(orgSlug, payload) {
1398
2023
  const response = await this.http.patch(
@@ -1958,6 +2583,26 @@ var ServicesModule = class {
1958
2583
  );
1959
2584
  return response.data;
1960
2585
  },
2586
+ /**
2587
+ * Initiate an IdP-initiated SAML login.
2588
+ * Returns an HTML page with an auto-submitting form that POSTs the SAML assertion to the Service Provider.
2589
+ *
2590
+ * @param orgSlug Organization slug
2591
+ * @param serviceSlug Service slug
2592
+ * @returns HTML page with auto-submitting form
2593
+ *
2594
+ * @example
2595
+ * ```typescript
2596
+ * const html = await sso.services.saml.initiateLogin('acme-corp', 'salesforce');
2597
+ * document.body.innerHTML = html; // Auto-submits
2598
+ * ```
2599
+ */
2600
+ initiateLogin: async (orgSlug, serviceSlug) => {
2601
+ const response = await this.http.get(
2602
+ `/api/organizations/${orgSlug}/services/${serviceSlug}/saml/login`
2603
+ );
2604
+ return response.data;
2605
+ },
1961
2606
  /**
1962
2607
  * Generate a new SAML signing certificate for the IdP.
1963
2608
  * Requires 'owner' or 'admin' role.
@@ -2054,6 +2699,41 @@ var ServicesModule = class {
2054
2699
  return `${baseURL}/saml/${orgSlug}/${serviceSlug}/sso`;
2055
2700
  }
2056
2701
  };
2702
+ /**
2703
+ * Stripe billing and checkout methods
2704
+ */
2705
+ this.billing = {
2706
+ /**
2707
+ * Create a Stripe checkout session for the authenticated user to subscribe to a plan.
2708
+ * Requires organization membership.
2709
+ *
2710
+ * IMPORTANT: The plan must have a `stripe_price_id` configured to be available for purchase.
2711
+ *
2712
+ * @param orgSlug Organization slug
2713
+ * @param serviceSlug Service slug
2714
+ * @param payload Checkout payload containing plan_id and redirect URLs
2715
+ * @returns Checkout session with URL to redirect user to
2716
+ *
2717
+ * @example
2718
+ * ```typescript
2719
+ * const checkout = await sso.services.billing.createCheckout('acme-corp', 'main-app', {
2720
+ * plan_id: 'plan_pro',
2721
+ * success_url: 'https://app.acme.com/billing/success?session_id={CHECKOUT_SESSION_ID}',
2722
+ * cancel_url: 'https://app.acme.com/billing/cancel'
2723
+ * });
2724
+ *
2725
+ * // Redirect user to Stripe checkout
2726
+ * window.location.href = checkout.checkout_url;
2727
+ * ```
2728
+ */
2729
+ createCheckout: async (orgSlug, serviceSlug, payload) => {
2730
+ const response = await this.http.post(
2731
+ `/api/organizations/${orgSlug}/services/${serviceSlug}/checkout`,
2732
+ payload
2733
+ );
2734
+ return response.data;
2735
+ }
2736
+ };
2057
2737
  }
2058
2738
  /**
2059
2739
  * Create a new service for an organization.
@@ -2176,7 +2856,7 @@ var InvitationsModule = class {
2176
2856
  * @example
2177
2857
  * ```typescript
2178
2858
  * const invitation = await sso.invitations.create('acme-corp', {
2179
- * invitee_email: 'newuser@example.com',
2859
+ * email: 'newuser@example.com',
2180
2860
  * role: 'member'
2181
2861
  * });
2182
2862
  * ```
@@ -2670,6 +3350,41 @@ var PlatformModule = class {
2670
3350
  const response = await this.http.get("/api/platform/audit-log", { params });
2671
3351
  return response.data;
2672
3352
  }
3353
+ /**
3354
+ * Impersonate a user (Platform Owner or Org Admin only).
3355
+ * Returns a short-lived JWT (15 minutes) that allows acting as the target user.
3356
+ *
3357
+ * Security:
3358
+ * - Platform Owners can impersonate any user
3359
+ * - Organization Admins can only impersonate users within their organization
3360
+ * - All impersonation actions are logged to the platform audit log with HIGH severity
3361
+ * - Tokens contain RFC 8693 actor claim for full audit trail
3362
+ *
3363
+ * @param payload Impersonation details (user_id and reason)
3364
+ * @returns Impersonation token and user context
3365
+ *
3366
+ * @example
3367
+ * ```typescript
3368
+ * const result = await sso.platform.impersonateUser({
3369
+ * user_id: 'user-uuid-123',
3370
+ * reason: 'Investigating support ticket #456'
3371
+ * });
3372
+ *
3373
+ * // Use the returned token to create a new client acting as the user
3374
+ * const userClient = new SsoClient({
3375
+ * baseURL: 'https://sso.example.com',
3376
+ * token: result.token
3377
+ * });
3378
+ *
3379
+ * // Now all requests with userClient are made as the target user
3380
+ * const profile = await userClient.user.getProfile();
3381
+ * console.log('Acting as:', result.target_user.email);
3382
+ * ```
3383
+ */
3384
+ async impersonateUser(payload) {
3385
+ const response = await this.http.post("/api/platform/impersonate", payload);
3386
+ return response.data;
3387
+ }
2673
3388
  };
2674
3389
 
2675
3390
  // src/modules/serviceApi.ts
@@ -2678,14 +3393,86 @@ var ServiceApiModule = class {
2678
3393
  this.http = http;
2679
3394
  }
2680
3395
  /**
2681
- * Create a new user
2682
- * Requires 'write:users' permission on the API key
3396
+ * List all users for the service
3397
+ * Requires 'read:users' permission on the API key
2683
3398
  *
2684
- * @param request User creation request
2685
- * @returns Created user
3399
+ * @param params Optional pagination parameters
3400
+ * @returns List of users with total count
2686
3401
  */
2687
- async createUser(request) {
2688
- const response = await this.http.post("/api/service/users", request);
3402
+ async listUsers(params) {
3403
+ const queryParams = new URLSearchParams();
3404
+ if (params?.limit) queryParams.set("limit", params.limit.toString());
3405
+ if (params?.offset) queryParams.set("offset", params.offset.toString());
3406
+ const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
3407
+ const response = await this.http.get(`/api/service/users${query}`);
3408
+ return response.data;
3409
+ }
3410
+ /**
3411
+ * Get a specific user by ID
3412
+ * Requires 'read:users' permission on the API key
3413
+ *
3414
+ * @param userId User ID to retrieve
3415
+ * @returns User details
3416
+ */
3417
+ async getUser(userId) {
3418
+ const response = await this.http.get(`/api/service/users/${userId}`);
3419
+ return response.data;
3420
+ }
3421
+ /**
3422
+ * List all subscriptions for the service
3423
+ * Requires 'read:subscriptions' permission on the API key
3424
+ *
3425
+ * @param params Optional pagination parameters
3426
+ * @returns List of subscriptions with total count
3427
+ */
3428
+ async listSubscriptions(params) {
3429
+ const queryParams = new URLSearchParams();
3430
+ if (params?.limit) queryParams.set("limit", params.limit.toString());
3431
+ if (params?.offset) queryParams.set("offset", params.offset.toString());
3432
+ const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
3433
+ const response = await this.http.get(`/api/service/subscriptions${query}`);
3434
+ return response.data;
3435
+ }
3436
+ /**
3437
+ * Get subscription for a specific user
3438
+ * Requires 'read:subscriptions' permission on the API key
3439
+ *
3440
+ * @param userId User ID whose subscription to retrieve
3441
+ * @returns User's subscription
3442
+ */
3443
+ async getSubscription(userId) {
3444
+ const response = await this.http.get(`/api/service/subscriptions/${userId}`);
3445
+ return response.data;
3446
+ }
3447
+ /**
3448
+ * Get analytics for the service
3449
+ * Requires 'read:analytics' permission on the API key
3450
+ *
3451
+ * @returns Service analytics data
3452
+ */
3453
+ async getAnalytics() {
3454
+ const response = await this.http.get("/api/service/analytics");
3455
+ return response.data;
3456
+ }
3457
+ /**
3458
+ * Get service information
3459
+ * Requires 'read:service' permission on the API key
3460
+ *
3461
+ * @returns Service information
3462
+ */
3463
+ async getServiceInfo() {
3464
+ const response = await this.http.get("/api/service/info");
3465
+ return response.data;
3466
+ }
3467
+ /**
3468
+ * Create a new user
3469
+ * Requires 'write:users' permission on the API key
3470
+ *
3471
+ * @param request User creation request
3472
+ * @returns Created user
3473
+ */
3474
+ async createUser(request) {
3475
+ const response = await this.http.post("/api/service/users", request);
2689
3476
  return response.data;
2690
3477
  }
2691
3478
  /**
@@ -2754,24 +3541,676 @@ var ServiceApiModule = class {
2754
3541
  }
2755
3542
  };
2756
3543
 
3544
+ // src/modules/permissions.ts
3545
+ var PermissionsModule = class {
3546
+ constructor(http) {
3547
+ this.http = http;
3548
+ }
3549
+ /**
3550
+ * Check if user has a specific permission.
3551
+ * Fetches from user profile API (which uses cached permissions).
3552
+ *
3553
+ * @param permission Permission in format "namespace:object_id#relation"
3554
+ * @returns true if the permission is present
3555
+ *
3556
+ * @example
3557
+ * ```typescript
3558
+ * const hasAccess = await sso.permissions.hasPermission('organization:acme#owner');
3559
+ * ```
3560
+ */
3561
+ async hasPermission(permission) {
3562
+ const response = await this.http.get("/api/user");
3563
+ return response.data.permissions.includes(permission);
3564
+ }
3565
+ /**
3566
+ * Get all user permissions.
3567
+ * Fetches from user profile API (which uses cached permissions).
3568
+ *
3569
+ * @returns Array of permission strings
3570
+ *
3571
+ * @example
3572
+ * ```typescript
3573
+ * const permissions = await sso.permissions.listPermissions();
3574
+ * // ["organization:acme#owner", "service:api#admin"]
3575
+ * ```
3576
+ */
3577
+ async listPermissions() {
3578
+ const response = await this.http.get("/api/user");
3579
+ return response.data.permissions;
3580
+ }
3581
+ /**
3582
+ * Check if user has access to a feature.
3583
+ *
3584
+ * @param feature Feature name to check
3585
+ * @returns true if the feature is available
3586
+ *
3587
+ * @example
3588
+ * ```typescript
3589
+ * const canExport = await sso.permissions.hasFeature('advanced-export');
3590
+ * ```
3591
+ */
3592
+ async hasFeature(feature) {
3593
+ const response = await this.http.get("/api/user");
3594
+ return response.data.features?.includes(feature) ?? false;
3595
+ }
3596
+ /**
3597
+ * Get current plan name.
3598
+ *
3599
+ * @returns Current plan name or null if not in org/service context
3600
+ *
3601
+ * @example
3602
+ * ```typescript
3603
+ * const plan = await sso.permissions.getPlan();
3604
+ * console.log(plan); // "pro", "enterprise", etc.
3605
+ * ```
3606
+ */
3607
+ async getPlan() {
3608
+ const response = await this.http.get("/api/user");
3609
+ return response.data.plan;
3610
+ }
3611
+ /**
3612
+ * Check if user has a specific permission on a resource.
3613
+ *
3614
+ * @param namespace The permission namespace (e.g., "organization", "service")
3615
+ * @param objectId The object ID (e.g., organization slug, service slug)
3616
+ * @param relation The relation type (e.g., "owner", "admin", "member")
3617
+ * @returns true if the user has the permission
3618
+ *
3619
+ * @example
3620
+ * ```typescript
3621
+ * const isOwner = await sso.permissions.can('organization', 'acme-corp', 'owner');
3622
+ * ```
3623
+ */
3624
+ async can(namespace, objectId, relation) {
3625
+ return this.hasPermission(`${namespace}:${objectId}#${relation}`);
3626
+ }
3627
+ /**
3628
+ * Check if user is a member of an organization.
3629
+ *
3630
+ * @param orgId The organization ID or slug
3631
+ * @returns true if the user is a member
3632
+ *
3633
+ * @example
3634
+ * ```typescript
3635
+ * if (await sso.permissions.isOrgMember('acme-corp')) {
3636
+ * // User is a member
3637
+ * }
3638
+ * ```
3639
+ */
3640
+ async isOrgMember(orgId) {
3641
+ return this.hasPermission(`organization:${orgId}#member`);
3642
+ }
3643
+ /**
3644
+ * Check if user is an admin of an organization.
3645
+ *
3646
+ * @param orgId The organization ID or slug
3647
+ * @returns true if the user is an admin
3648
+ *
3649
+ * @example
3650
+ * ```typescript
3651
+ * if (await sso.permissions.isOrgAdmin('acme-corp')) {
3652
+ * // User is an admin
3653
+ * }
3654
+ * ```
3655
+ */
3656
+ async isOrgAdmin(orgId) {
3657
+ return this.hasPermission(`organization:${orgId}#admin`);
3658
+ }
3659
+ /**
3660
+ * Check if user is an owner of an organization.
3661
+ *
3662
+ * @param orgId The organization ID or slug
3663
+ * @returns true if the user is an owner
3664
+ *
3665
+ * @example
3666
+ * ```typescript
3667
+ * if (await sso.permissions.isOrgOwner('acme-corp')) {
3668
+ * // User is an owner
3669
+ * }
3670
+ * ```
3671
+ */
3672
+ async isOrgOwner(orgId) {
3673
+ return this.hasPermission(`organization:${orgId}#owner`);
3674
+ }
3675
+ /**
3676
+ * Check if user has access to a service.
3677
+ *
3678
+ * @param serviceId The service ID or slug
3679
+ * @returns true if the user has access
3680
+ *
3681
+ * @example
3682
+ * ```typescript
3683
+ * if (await sso.permissions.hasServiceAccess('api-service')) {
3684
+ * // User has access to the service
3685
+ * }
3686
+ * ```
3687
+ */
3688
+ async hasServiceAccess(serviceId) {
3689
+ return this.hasPermission(`service:${serviceId}#member`);
3690
+ }
3691
+ /**
3692
+ * Filter permissions by namespace.
3693
+ *
3694
+ * @param namespace The namespace to filter by (e.g., "organization", "service")
3695
+ * @returns Array of permissions matching the namespace
3696
+ *
3697
+ * @example
3698
+ * ```typescript
3699
+ * const orgPermissions = await sso.permissions.getPermissionsByNamespace('organization');
3700
+ * ```
3701
+ */
3702
+ async getPermissionsByNamespace(namespace) {
3703
+ const allPermissions = await this.listPermissions();
3704
+ return allPermissions.filter((p) => p.startsWith(`${namespace}:`));
3705
+ }
3706
+ // ============================================================================
3707
+ // DEPRECATED METHODS - Token-based permission checking (legacy)
3708
+ // ============================================================================
3709
+ /**
3710
+ * @deprecated Use `hasPermission()` instead (without token parameter)
3711
+ * Decode a JWT token to extract claims (including permissions)
3712
+ * Note: This does NOT verify the signature - it only decodes the payload
3713
+ *
3714
+ * @param token The JWT access token
3715
+ * @returns The decoded JWT claims
3716
+ * @throws Error if the token is malformed
3717
+ */
3718
+ decodeToken(token) {
3719
+ try {
3720
+ const parts = token.split(".");
3721
+ if (parts.length !== 3) {
3722
+ throw new Error("Invalid JWT format");
3723
+ }
3724
+ const payload = parts[1];
3725
+ const decoded = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
3726
+ return JSON.parse(decoded);
3727
+ } catch (error) {
3728
+ throw new Error(`Failed to decode JWT: ${error instanceof Error ? error.message : "Unknown error"}`);
3729
+ }
3730
+ }
3731
+ /**
3732
+ * @deprecated JWT tokens no longer contain permissions. Use `hasPermission(permission)` instead.
3733
+ * Check if a JWT token contains a specific permission
3734
+ *
3735
+ * @param token The JWT access token (ignored)
3736
+ * @param permission Permission in format "namespace:object_id#relation"
3737
+ * @returns true if the permission is present in the token
3738
+ */
3739
+ hasPermissionFromToken(token, permission) {
3740
+ const claims = this.decodeToken(token);
3741
+ return claims.permissions?.includes(permission) ?? false;
3742
+ }
3743
+ /**
3744
+ * @deprecated JWT tokens no longer contain permissions. Use `can(namespace, objectId, relation)` instead.
3745
+ * Check if a user has a specific permission on a resource
3746
+ *
3747
+ * @param token The JWT access token (ignored)
3748
+ * @param namespace The permission namespace (e.g., "organization", "service")
3749
+ * @param objectId The object ID (e.g., organization slug, service slug)
3750
+ * @param relation The relation type (e.g., "owner", "admin", "member")
3751
+ * @returns true if the user has the permission
3752
+ */
3753
+ canFromToken(token, namespace, objectId, relation) {
3754
+ return this.hasPermissionFromToken(token, `${namespace}:${objectId}#${relation}`);
3755
+ }
3756
+ /**
3757
+ * @deprecated JWT tokens no longer contain permissions. Use `isOrgMember(orgId)` instead.
3758
+ * Check if user is a member of an organization
3759
+ *
3760
+ * @param token The JWT access token (ignored)
3761
+ * @param orgId The organization ID or slug
3762
+ * @returns true if the user is a member
3763
+ */
3764
+ isOrgMemberFromToken(token, orgId) {
3765
+ return this.hasPermissionFromToken(token, `organization:${orgId}#member`);
3766
+ }
3767
+ /**
3768
+ * @deprecated JWT tokens no longer contain permissions. Use `isOrgAdmin(orgId)` instead.
3769
+ * Check if user is an admin of an organization
3770
+ *
3771
+ * @param token The JWT access token (ignored)
3772
+ * @param orgId The organization ID or slug
3773
+ * @returns true if the user is an admin
3774
+ */
3775
+ isOrgAdminFromToken(token, orgId) {
3776
+ return this.hasPermissionFromToken(token, `organization:${orgId}#admin`);
3777
+ }
3778
+ /**
3779
+ * @deprecated JWT tokens no longer contain permissions. Use `isOrgOwner(orgId)` instead.
3780
+ * Check if user is an owner of an organization
3781
+ *
3782
+ * @param token The JWT access token (ignored)
3783
+ * @param orgId The organization ID or slug
3784
+ * @returns true if the user is an owner
3785
+ */
3786
+ isOrgOwnerFromToken(token, orgId) {
3787
+ return this.hasPermissionFromToken(token, `organization:${orgId}#owner`);
3788
+ }
3789
+ /**
3790
+ * @deprecated JWT tokens no longer contain permissions. Use `hasServiceAccess(serviceId)` instead.
3791
+ * Check if user has access to a service
3792
+ *
3793
+ * @param token The JWT access token (ignored)
3794
+ * @param serviceId The service ID or slug
3795
+ * @returns true if the user has access
3796
+ */
3797
+ hasServiceAccessFromToken(token, serviceId) {
3798
+ return this.hasPermissionFromToken(token, `service:${serviceId}#member`);
3799
+ }
3800
+ /**
3801
+ * @deprecated JWT tokens no longer contain permissions. Use `listPermissions()` instead.
3802
+ * Get all permissions from a JWT token
3803
+ *
3804
+ * @param token The JWT access token
3805
+ * @returns Array of permission strings, or empty array if none
3806
+ */
3807
+ getAllPermissionsFromToken(token) {
3808
+ const claims = this.decodeToken(token);
3809
+ return claims.permissions ?? [];
3810
+ }
3811
+ /**
3812
+ * Parse a permission string into its components
3813
+ *
3814
+ * @param permission Permission string in format "namespace:object_id#relation"
3815
+ * @returns Parsed permission components or null if invalid format
3816
+ *
3817
+ * @example
3818
+ * ```typescript
3819
+ * const parsed = sso.permissions.parsePermission('organization:acme#owner');
3820
+ * // { namespace: 'organization', objectId: 'acme', relation: 'owner' }
3821
+ * ```
3822
+ */
3823
+ parsePermission(permission) {
3824
+ const match = permission.match(/^([^:]+):([^#]+)#(.+)$/);
3825
+ if (!match) {
3826
+ return null;
3827
+ }
3828
+ return {
3829
+ namespace: match[1],
3830
+ objectId: match[2],
3831
+ relation: match[3]
3832
+ };
3833
+ }
3834
+ /**
3835
+ * @deprecated JWT tokens no longer contain permissions. Use `getPermissionsByNamespace(namespace)` instead.
3836
+ * Filter permissions by namespace
3837
+ *
3838
+ * @param token The JWT access token (ignored)
3839
+ * @param namespace The namespace to filter by (e.g., "organization", "service")
3840
+ * @returns Array of permissions matching the namespace
3841
+ */
3842
+ getPermissionsByNamespaceFromToken(token, namespace) {
3843
+ const allPermissions = this.getAllPermissionsFromToken(token);
3844
+ return allPermissions.filter((p) => p.startsWith(`${namespace}:`));
3845
+ }
3846
+ };
3847
+
3848
+ // src/modules/passkeys.ts
3849
+ var PasskeysModule = class {
3850
+ constructor(http) {
3851
+ this.http = http;
3852
+ }
3853
+ /**
3854
+ * Check if WebAuthn is supported in the current browser
3855
+ */
3856
+ isSupported() {
3857
+ return typeof window !== "undefined" && window.PublicKeyCredential !== void 0 && typeof window.PublicKeyCredential === "function";
3858
+ }
3859
+ /**
3860
+ * Check if platform authenticator (like Touch ID, Face ID, Windows Hello) is available
3861
+ */
3862
+ async isPlatformAuthenticatorAvailable() {
3863
+ if (!this.isSupported()) {
3864
+ return false;
3865
+ }
3866
+ return PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
3867
+ }
3868
+ /**
3869
+ * Register a new passkey for the authenticated user
3870
+ *
3871
+ * This method requires an authenticated session (JWT token must be set).
3872
+ * It starts the WebAuthn registration ceremony, prompts the user to create
3873
+ * a passkey using their device's authenticator (e.g., Touch ID, Face ID,
3874
+ * Windows Hello, or hardware security key), and stores the credential.
3875
+ *
3876
+ * @param displayName Optional display name for the passkey
3877
+ * @returns Promise resolving to the registered passkey ID
3878
+ * @throws {Error} If WebAuthn is not supported or registration fails
3879
+ *
3880
+ * @example
3881
+ * ```typescript
3882
+ * try {
3883
+ * const passkeyId = await sso.passkeys.register('My MacBook Pro');
3884
+ * console.log('Passkey registered:', passkeyId);
3885
+ * } catch (error) {
3886
+ * console.error('Passkey registration failed:', error);
3887
+ * }
3888
+ * ```
3889
+ */
3890
+ /**
3891
+ * Start the passkey registration ceremony.
3892
+ * returns the options required to create credentials in the browser.
3893
+ */
3894
+ async registerStart(displayName) {
3895
+ const response = await this.http.post(
3896
+ "/api/auth/passkeys/register/start",
3897
+ { name: displayName }
3898
+ );
3899
+ return response.data;
3900
+ }
3901
+ /**
3902
+ * Finish the passkey registration ceremony.
3903
+ * Verifies the credential created by the browser.
3904
+ */
3905
+ async registerFinish(challengeId, credential) {
3906
+ const response = await this.http.post(
3907
+ "/api/auth/passkeys/register/finish",
3908
+ {
3909
+ challenge_id: challengeId,
3910
+ credential
3911
+ }
3912
+ );
3913
+ return response.data;
3914
+ }
3915
+ /**
3916
+ * Register a new passkey for the authenticated user
3917
+ * ...
3918
+ */
3919
+ async register(displayName) {
3920
+ if (!this.isSupported()) {
3921
+ throw new Error("WebAuthn is not supported in this browser");
3922
+ }
3923
+ const startData = await this.registerStart(displayName);
3924
+ const createOptions = {
3925
+ publicKey: {
3926
+ ...startData.options,
3927
+ challenge: this.base64UrlToUint8Array(startData.options.challenge),
3928
+ user: {
3929
+ ...startData.options.user,
3930
+ id: this.base64UrlToUint8Array(startData.options.user.id)
3931
+ },
3932
+ excludeCredentials: startData.options.excludeCredentials?.map((cred) => ({
3933
+ ...cred,
3934
+ id: this.base64UrlToUint8Array(cred.id)
3935
+ }))
3936
+ }
3937
+ };
3938
+ const credential = await navigator.credentials.create(createOptions);
3939
+ if (!credential || !(credential instanceof PublicKeyCredential)) {
3940
+ throw new Error("Failed to create passkey");
3941
+ }
3942
+ if (!(credential.response instanceof AuthenticatorAttestationResponse)) {
3943
+ throw new Error("Invalid credential response type");
3944
+ }
3945
+ const credentialJSON = {
3946
+ id: credential.id,
3947
+ rawId: this.uint8ArrayToBase64Url(new Uint8Array(credential.rawId)),
3948
+ response: {
3949
+ clientDataJSON: this.uint8ArrayToBase64Url(new Uint8Array(credential.response.clientDataJSON)),
3950
+ attestationObject: this.uint8ArrayToBase64Url(new Uint8Array(credential.response.attestationObject)),
3951
+ transports: credential.response.getTransports?.()
3952
+ },
3953
+ authenticatorAttachment: credential.authenticatorAttachment,
3954
+ clientExtensionResults: credential.getClientExtensionResults(),
3955
+ type: credential.type
3956
+ };
3957
+ const finishResponse = await this.registerFinish(startData.challenge_id, credentialJSON);
3958
+ return finishResponse.passkey_id;
3959
+ }
3960
+ /**
3961
+ * Authenticate with a passkey and obtain a JWT token
3962
+ *
3963
+ * This method prompts the user to authenticate using their passkey.
3964
+ * Upon successful authentication, a JWT token is returned which can
3965
+ * be used to make authenticated API requests.
3966
+ *
3967
+ * @param email User's email address
3968
+ * @returns Promise resolving to authentication response with JWT token
3969
+ * @throws {Error} If WebAuthn is not supported or authentication fails
3970
+ *
3971
+ * @example
3972
+ * ```typescript
3973
+ * try {
3974
+ * const { token, user_id } = await sso.passkeys.login('user@example.com');
3975
+ * sso.setToken(token);
3976
+ * console.log('Logged in as:', user_id);
3977
+ * } catch (error) {
3978
+ * console.error('Passkey login failed:', error);
3979
+ * }
3980
+ * ```
3981
+ */
3982
+ /**
3983
+ * Start the passkey authentication ceremony.
3984
+ * Returns the options required to get credentials from the browser.
3985
+ */
3986
+ async authenticateStart(email) {
3987
+ const response = await this.http.post(
3988
+ "/api/auth/passkeys/authenticate/start",
3989
+ { email }
3990
+ );
3991
+ return response.data;
3992
+ }
3993
+ /**
3994
+ * Finish the passkey authentication ceremony.
3995
+ * Verifies the assertion returned by the browser.
3996
+ */
3997
+ async authenticateFinish(challengeId, credential) {
3998
+ const response = await this.http.post(
3999
+ "/api/auth/passkeys/authenticate/finish",
4000
+ {
4001
+ challenge_id: challengeId,
4002
+ credential
4003
+ }
4004
+ );
4005
+ return response.data;
4006
+ }
4007
+ /**
4008
+ * Authenticate with a passkey and obtain a JWT token
4009
+ * ...
4010
+ */
4011
+ async login(email) {
4012
+ if (!this.isSupported()) {
4013
+ throw new Error("WebAuthn is not supported in this browser");
4014
+ }
4015
+ const startData = await this.authenticateStart(email);
4016
+ const getOptions = {
4017
+ publicKey: {
4018
+ ...startData.options,
4019
+ challenge: this.base64UrlToUint8Array(startData.options.challenge),
4020
+ allowCredentials: startData.options.allowCredentials?.map((cred) => ({
4021
+ ...cred,
4022
+ id: this.base64UrlToUint8Array(cred.id)
4023
+ }))
4024
+ }
4025
+ };
4026
+ const credential = await navigator.credentials.get(getOptions);
4027
+ if (!credential || !(credential instanceof PublicKeyCredential)) {
4028
+ throw new Error("Failed to get passkey");
4029
+ }
4030
+ if (!(credential.response instanceof AuthenticatorAssertionResponse)) {
4031
+ throw new Error("Invalid credential response type");
4032
+ }
4033
+ const credentialJSON = {
4034
+ id: credential.id,
4035
+ rawId: this.uint8ArrayToBase64Url(new Uint8Array(credential.rawId)),
4036
+ response: {
4037
+ clientDataJSON: this.uint8ArrayToBase64Url(new Uint8Array(credential.response.clientDataJSON)),
4038
+ authenticatorData: this.uint8ArrayToBase64Url(new Uint8Array(credential.response.authenticatorData)),
4039
+ signature: this.uint8ArrayToBase64Url(new Uint8Array(credential.response.signature)),
4040
+ userHandle: credential.response.userHandle ? this.uint8ArrayToBase64Url(new Uint8Array(credential.response.userHandle)) : void 0
4041
+ },
4042
+ authenticatorAttachment: credential.authenticatorAttachment,
4043
+ clientExtensionResults: credential.getClientExtensionResults(),
4044
+ type: credential.type
4045
+ };
4046
+ return this.authenticateFinish(startData.challenge_id, credentialJSON);
4047
+ }
4048
+ /**
4049
+ * Convert Base64URL string to Uint8Array
4050
+ */
4051
+ base64UrlToUint8Array(base64url) {
4052
+ const padding = "=".repeat((4 - base64url.length % 4) % 4);
4053
+ const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/") + padding;
4054
+ const rawData = atob(base64);
4055
+ const outputArray = new Uint8Array(rawData.length);
4056
+ for (let i = 0; i < rawData.length; ++i) {
4057
+ outputArray[i] = rawData.charCodeAt(i);
4058
+ }
4059
+ return outputArray;
4060
+ }
4061
+ /**
4062
+ * Convert Uint8Array to Base64URL string
4063
+ */
4064
+ uint8ArrayToBase64Url(array) {
4065
+ let binary = "";
4066
+ for (let i = 0; i < array.byteLength; i++) {
4067
+ binary += String.fromCharCode(array[i]);
4068
+ }
4069
+ const base64 = btoa(binary);
4070
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
4071
+ }
4072
+ };
4073
+
4074
+ // src/modules/magic.ts
4075
+ var MagicLinks = class {
4076
+ constructor(http) {
4077
+ this.http = http;
4078
+ }
4079
+ /**
4080
+ * Request a magic link to be sent to the user's email
4081
+ *
4082
+ * @param data Magic link request data
4083
+ * @returns Promise resolving to magic link response
4084
+ */
4085
+ async request(data) {
4086
+ const response = await this.http.post("/api/auth/magic-link/request", data);
4087
+ return response.data;
4088
+ }
4089
+ /**
4090
+ * Verify a magic link token and complete authentication
4091
+ * Note: This is typically handled by redirecting to the magic link URL
4092
+ * The backend will handle verification and either redirect or return tokens
4093
+ *
4094
+ * @param token The magic link token to verify
4095
+ * @param redirectUri Optional where to redirect after success
4096
+ * @returns URL to redirect to for verification
4097
+ */
4098
+ getVerificationUrl(token, redirectUri) {
4099
+ const params = new URLSearchParams({ token });
4100
+ if (redirectUri) {
4101
+ params.append("redirect_uri", redirectUri);
4102
+ }
4103
+ return `/api/auth/magic-link/verify?${params.toString()}`;
4104
+ }
4105
+ /**
4106
+ * Verify a magic link token via API call
4107
+ * This is an alternative to redirect-based verification
4108
+ *
4109
+ * @param token The magic link token
4110
+ * @param redirectUri Optional redirect URI
4111
+ * @returns Promise resolving to authentication response
4112
+ */
4113
+ async verify(token, redirectUri) {
4114
+ const params = new URLSearchParams({ token });
4115
+ if (redirectUri) {
4116
+ params.append("redirect_uri", redirectUri);
4117
+ }
4118
+ return this.http.get(`/api/auth/magic-link/verify?${params.toString()}`);
4119
+ }
4120
+ /**
4121
+ * Construct the complete magic link URL that would be sent via email
4122
+ *
4123
+ * @param token The magic link token
4124
+ * @param redirectUri Optional redirect URI
4125
+ * @returns Complete magic link URL
4126
+ */
4127
+ constructMagicLink(token, redirectUri) {
4128
+ return this.getVerificationUrl(token, redirectUri);
4129
+ }
4130
+ };
4131
+
4132
+ // src/modules/privacy.ts
4133
+ var PrivacyModule = class {
4134
+ constructor(http) {
4135
+ this.http = http;
4136
+ }
4137
+ /**
4138
+ * Export all user data (GDPR Right to Access).
4139
+ * Users can export their own data, or organization owners can export their members' data.
4140
+ *
4141
+ * @param userId User ID to export data for
4142
+ * @returns Complete user data export including memberships, login events, identities, MFA events, and passkeys
4143
+ *
4144
+ * @example
4145
+ * ```typescript
4146
+ * const userData = await sso.privacy.exportData('user-id');
4147
+ * console.log(`Exported ${userData.login_events_count} login events`);
4148
+ * console.log(`User has ${userData.memberships.length} organization memberships`);
4149
+ * ```
4150
+ */
4151
+ async exportData(userId) {
4152
+ const response = await this.http.get(`/api/privacy/export/${userId}`);
4153
+ return response.data;
4154
+ }
4155
+ /**
4156
+ * Anonymize user data (GDPR Right to be Forgotten).
4157
+ * Requires organization owner permission for all organizations the user is a member of.
4158
+ * Platform owners cannot be anonymized.
4159
+ *
4160
+ * This operation:
4161
+ * - Soft-deletes the user account
4162
+ * - Hard-deletes PII from identities and passkeys tables
4163
+ * - Preserves audit logs for compliance
4164
+ *
4165
+ * @param userId User ID to anonymize
4166
+ * @returns Anonymization confirmation response
4167
+ *
4168
+ * @example
4169
+ * ```typescript
4170
+ * const result = await sso.privacy.forgetUser('user-id');
4171
+ * console.log(result.message);
4172
+ * // "User data has been anonymized. PII has been removed while preserving audit logs."
4173
+ * ```
4174
+ */
4175
+ async forgetUser(userId) {
4176
+ const response = await this.http.delete(`/api/privacy/forget/${userId}`);
4177
+ return response.data;
4178
+ }
4179
+ };
4180
+
2757
4181
  // src/client.ts
2758
4182
  var SsoClient = class {
2759
4183
  constructor(options) {
2760
4184
  this.http = createHttpAgent(options.baseURL);
2761
- if (options.token) {
2762
- this.setAuthToken(options.token);
2763
- }
2764
- if (options.apiKey) {
2765
- this.setApiKey(options.apiKey);
2766
- }
4185
+ this.session = new SessionManager(
4186
+ resolveStorage(options.storage),
4187
+ async (refreshToken) => {
4188
+ const res = await this.http.post("/api/auth/refresh", { refresh_token: refreshToken });
4189
+ return res.data;
4190
+ },
4191
+ { storageKeyPrefix: options.storagePrefix || "sso_" }
4192
+ );
4193
+ this.http.setSessionManager(this.session);
2767
4194
  this.analytics = new AnalyticsModule(this.http);
2768
- this.auth = new AuthModule(this.http);
4195
+ this.auth = new AuthModule(this.http, this.session);
2769
4196
  this.user = new UserModule(this.http);
2770
4197
  this.organizations = new OrganizationsModule(this.http);
2771
4198
  this.services = new ServicesModule(this.http);
2772
4199
  this.invitations = new InvitationsModule(this.http);
2773
4200
  this.platform = new PlatformModule(this.http);
2774
4201
  this.serviceApi = new ServiceApiModule(this.http);
4202
+ this.permissions = new PermissionsModule(this.http);
4203
+ this.passkeys = new PasskeysModule(this.http);
4204
+ this.magicLinks = new MagicLinks(this.http);
4205
+ this.privacy = new PrivacyModule(this.http);
4206
+ if (options.apiKey) {
4207
+ this.setApiKey(options.apiKey);
4208
+ }
4209
+ if (options.token) {
4210
+ this.session.setSession({ access_token: options.token });
4211
+ } else {
4212
+ this.session.loadSession().catch(console.error);
4213
+ }
2775
4214
  }
2776
4215
  /**
2777
4216
  * Sets the JWT for all subsequent authenticated requests.
@@ -2823,12 +4262,95 @@ var SsoClient = class {
2823
4262
  getBaseURL() {
2824
4263
  return this.http.defaults.baseURL || "";
2825
4264
  }
4265
+ /**
4266
+ * Check if the user is currently authenticated
4267
+ */
4268
+ isAuthenticated() {
4269
+ return this.session.isAuthenticated();
4270
+ }
4271
+ /**
4272
+ * Subscribe to authentication state changes.
4273
+ * Useful for updating UI when login/logout/expiration occurs.
4274
+ *
4275
+ * @param listener Callback function that receives the authentication state
4276
+ * @returns Unsubscribe function
4277
+ *
4278
+ * @example
4279
+ * ```typescript
4280
+ * const unsubscribe = sso.onAuthStateChange((isAuth) => {
4281
+ * console.log(isAuth ? 'User is logged in' : 'User is logged out');
4282
+ * });
4283
+ *
4284
+ * // Later, to stop listening
4285
+ * unsubscribe();
4286
+ * ```
4287
+ */
4288
+ onAuthStateChange(listener) {
4289
+ return this.session.subscribe(listener);
4290
+ }
4291
+ /**
4292
+ * Manually retrieve the current access token
4293
+ *
4294
+ * @returns The current access token, or null if not authenticated
4295
+ */
4296
+ async getToken() {
4297
+ return this.session.getToken();
4298
+ }
2826
4299
  };
4300
+
4301
+ // src/types/risk.ts
4302
+ var RiskAction = /* @__PURE__ */ ((RiskAction2) => {
4303
+ RiskAction2["ALLOW"] = "allow";
4304
+ RiskAction2["LOG_ONLY"] = "log_only";
4305
+ RiskAction2["CHALLENGE_MFA"] = "challenge_mfa";
4306
+ RiskAction2["BLOCK"] = "block";
4307
+ return RiskAction2;
4308
+ })(RiskAction || {});
4309
+ var RiskFactorType = /* @__PURE__ */ ((RiskFactorType2) => {
4310
+ RiskFactorType2["NEW_IP"] = "new_ip";
4311
+ RiskFactorType2["HIGH_RISK_LOCATION"] = "high_risk_location";
4312
+ RiskFactorType2["IMPOSSIBLE_TRAVEL"] = "impossible_travel";
4313
+ RiskFactorType2["NEW_DEVICE"] = "new_device";
4314
+ RiskFactorType2["FAILED_ATTEMPTS"] = "failed_attempts";
4315
+ RiskFactorType2["UNUSUAL_TIME"] = "unusual_time";
4316
+ RiskFactorType2["SUSPICIOUS_USER_AGENT"] = "suspicious_user_agent";
4317
+ RiskFactorType2["ANONYMOUS_NETWORK"] = "anonymous_network";
4318
+ RiskFactorType2["NEW_ACCOUNT"] = "new_account";
4319
+ RiskFactorType2["SUSPICIOUS_HISTORY"] = "suspicious_history";
4320
+ RiskFactorType2["HIGH_VELOCITY"] = "high_velocity";
4321
+ RiskFactorType2["CUSTOM_RULE"] = "custom_rule";
4322
+ return RiskFactorType2;
4323
+ })(RiskFactorType || {});
4324
+ var AuthMethod = /* @__PURE__ */ ((AuthMethod2) => {
4325
+ AuthMethod2["PASSWORD"] = "password";
4326
+ AuthMethod2["OAUTH"] = "oauth";
4327
+ AuthMethod2["PASSKEY"] = "passkey";
4328
+ AuthMethod2["MAGIC_LINK"] = "magic_link";
4329
+ AuthMethod2["MFA"] = "mfa";
4330
+ AuthMethod2["SAML"] = "saml";
4331
+ return AuthMethod2;
4332
+ })(AuthMethod || {});
4333
+ var RiskEventOutcome = /* @__PURE__ */ ((RiskEventOutcome2) => {
4334
+ RiskEventOutcome2["ALLOWED"] = "allowed";
4335
+ RiskEventOutcome2["BLOCKED"] = "blocked";
4336
+ RiskEventOutcome2["CHALLENGED"] = "challenged";
4337
+ RiskEventOutcome2["LOGGED"] = "logged";
4338
+ return RiskEventOutcome2;
4339
+ })(RiskEventOutcome || {});
2827
4340
  export {
4341
+ AuthMethod,
2828
4342
  AuthModule,
4343
+ BrowserStorage,
2829
4344
  InvitationsModule,
4345
+ MagicLinks,
4346
+ MemoryStorage,
2830
4347
  OrganizationsModule,
4348
+ PasskeysModule,
4349
+ PermissionsModule,
2831
4350
  PlatformModule,
4351
+ RiskAction,
4352
+ RiskEventOutcome,
4353
+ RiskFactorType,
2832
4354
  ServiceApiModule,
2833
4355
  ServicesModule,
2834
4356
  SsoApiError,