@passflow/core 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/index.js +1 -1
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +3174 -574
  4. package/dist/index.mjs.map +1 -1
  5. package/dist/lib/api/app.d.ts +4 -1
  6. package/dist/lib/api/app.d.ts.map +1 -1
  7. package/dist/lib/api/auth.d.ts +6 -4
  8. package/dist/lib/api/auth.d.ts.map +1 -1
  9. package/dist/lib/api/axios-client.d.ts +26 -4
  10. package/dist/lib/api/axios-client.d.ts.map +1 -1
  11. package/dist/lib/api/index.d.ts +4 -3
  12. package/dist/lib/api/index.d.ts.map +1 -1
  13. package/dist/lib/api/invitation.d.ts +4 -1
  14. package/dist/lib/api/invitation.d.ts.map +1 -1
  15. package/dist/lib/api/model.d.ts +180 -11
  16. package/dist/lib/api/model.d.ts.map +1 -1
  17. package/dist/lib/api/setting.d.ts +4 -1
  18. package/dist/lib/api/setting.d.ts.map +1 -1
  19. package/dist/lib/api/tenant.d.ts +4 -1
  20. package/dist/lib/api/tenant.d.ts.map +1 -1
  21. package/dist/lib/api/two-factor.d.ts +75 -0
  22. package/dist/lib/api/two-factor.d.ts.map +1 -0
  23. package/dist/lib/api/user.d.ts +4 -1
  24. package/dist/lib/api/user.d.ts.map +1 -1
  25. package/dist/lib/constants/index.d.ts +25 -0
  26. package/dist/lib/constants/index.d.ts.map +1 -1
  27. package/dist/lib/device/index.d.ts +17 -0
  28. package/dist/lib/device/index.d.ts.map +1 -0
  29. package/dist/lib/index.d.ts +10 -3
  30. package/dist/lib/index.d.ts.map +1 -1
  31. package/dist/lib/m2m/client.d.ts +196 -0
  32. package/dist/lib/m2m/client.d.ts.map +1 -0
  33. package/dist/lib/m2m/errors.d.ts +107 -0
  34. package/dist/lib/m2m/errors.d.ts.map +1 -0
  35. package/dist/lib/m2m/index.d.ts +27 -0
  36. package/dist/lib/m2m/index.d.ts.map +1 -0
  37. package/dist/lib/m2m/types.d.ts +217 -0
  38. package/dist/lib/m2m/types.d.ts.map +1 -0
  39. package/dist/lib/passflow.d.ts +972 -11
  40. package/dist/lib/passflow.d.ts.map +1 -1
  41. package/dist/lib/services/auth-service.d.ts +29 -4
  42. package/dist/lib/services/auth-service.d.ts.map +1 -1
  43. package/dist/lib/services/index.d.ts +2 -1
  44. package/dist/lib/services/index.d.ts.map +1 -1
  45. package/dist/lib/services/invitation-service.d.ts +2 -2
  46. package/dist/lib/services/tenant-service.d.ts +2 -2
  47. package/dist/lib/services/token-cache-service.d.ts +14 -6
  48. package/dist/lib/services/token-cache-service.d.ts.map +1 -1
  49. package/dist/lib/services/two-factor-service.d.ts +120 -0
  50. package/dist/lib/services/two-factor-service.d.ts.map +1 -0
  51. package/dist/lib/services/user-service.d.ts +1 -1
  52. package/dist/lib/services/user-service.d.ts.map +1 -1
  53. package/dist/lib/storage/index.d.ts +96 -0
  54. package/dist/lib/storage/index.d.ts.map +1 -0
  55. package/dist/lib/store.d.ts +67 -1
  56. package/dist/lib/store.d.ts.map +1 -1
  57. package/dist/lib/token/delivery-manager.d.ts +121 -0
  58. package/dist/lib/token/delivery-manager.d.ts.map +1 -0
  59. package/dist/lib/{token-service → token}/index.d.ts +1 -1
  60. package/dist/lib/token/index.d.ts.map +1 -0
  61. package/dist/lib/token/membership.d.ts.map +1 -0
  62. package/dist/lib/{token-service → token}/service.d.ts +15 -3
  63. package/dist/lib/token/service.d.ts.map +1 -0
  64. package/dist/lib/token/token.d.ts +55 -0
  65. package/dist/lib/token/token.d.ts.map +1 -0
  66. package/dist/lib/types/index.d.ts +5 -3
  67. package/dist/lib/types/index.d.ts.map +1 -1
  68. package/dist/lib/utils/validation.d.ts +54 -0
  69. package/dist/lib/utils/validation.d.ts.map +1 -0
  70. package/dist/tests/storage/fake-storage.d.ts.map +1 -0
  71. package/dist/tests/storage/storage-manager.test.d.ts.map +1 -0
  72. package/dist/tsconfig.tsbuildinfo +1 -1
  73. package/package.json +14 -15
  74. package/dist/lib/device-service/index.d.ts +0 -7
  75. package/dist/lib/device-service/index.d.ts.map +0 -1
  76. package/dist/lib/storage-manager/index.d.ts +0 -37
  77. package/dist/lib/storage-manager/index.d.ts.map +0 -1
  78. package/dist/lib/token-service/index.d.ts.map +0 -1
  79. package/dist/lib/token-service/membership.d.ts.map +0 -1
  80. package/dist/lib/token-service/service.d.ts.map +0 -1
  81. package/dist/lib/token-service/token.d.ts +0 -34
  82. package/dist/lib/token-service/token.d.ts.map +0 -1
  83. package/dist/tests/storage-manager/fake-storage.d.ts.map +0 -1
  84. package/dist/tests/storage-manager/storage-manager.test.d.ts.map +0 -1
  85. /package/dist/lib/{token-service → token}/membership.d.ts +0 -0
  86. /package/dist/tests/{storage-manager → storage}/fake-storage.d.ts +0 -0
  87. /package/dist/tests/{storage-manager → storage}/storage-manager.test.d.ts +0 -0
package/dist/index.mjs CHANGED
@@ -1,7 +1,9 @@
1
- import T from "axios";
2
- import { v4 as R } from "uuid";
3
- import { startRegistration as _, startAuthentication as U } from "@simplewebauthn/browser";
4
- const P = "X-Passflow-Clientid", m = "Authorization", D = "X-Passflow-DeviceId", x = "X-Passflow-DeviceType", F = ["id", "offline", "tenant", "email", "oidc", "openid", "access:tenant:all"], A = "https://auth.passflow.cloud", re = "default", M = (i) => {
1
+ import D from "axios";
2
+ import { v4 as H } from "uuid";
3
+ import { startRegistration as K, startAuthentication as j } from "@simplewebauthn/browser";
4
+ const z = "0.1.47", X = {
5
+ version: z
6
+ }, C = "X-Passflow-Clientid", _ = "Authorization", W = "X-Passflow-DeviceId", J = "X-Passflow-DeviceType", Z = X.version, Ne = ["id", "offline", "openid"], Q = ["id", "offline", "tenant", "email", "oidc", "openid", "access:tenant:all"], G = "https://auth.passflow.cloud", Ke = "default", ee = 500, te = 600, se = 100, re = 6e4, V = 30, ie = 3, oe = 30, ne = 200, ae = (i) => {
5
7
  const e = [];
6
8
  let t;
7
9
  for (t in i) {
@@ -16,9 +18,16 @@ const P = "X-Passflow-Clientid", m = "Authorization", D = "X-Passflow-DeviceId",
16
18
  }
17
19
  return { raw: i, tenants: e };
18
20
  };
19
- class G {
20
- constructor() {
21
- this.storageManager = new b();
21
+ function ce(i) {
22
+ if (typeof window < "u" && typeof window.atob == "function")
23
+ return window.atob(i);
24
+ if (typeof Buffer < "u")
25
+ return Buffer.from(i, "base64").toString("utf-8");
26
+ throw new Error("No Base64 decoding method available in this environment");
27
+ }
28
+ class he {
29
+ constructor(e) {
30
+ this.storageManager = e;
22
31
  }
23
32
  /**
24
33
  * Checks if a token is not exists or expired.
@@ -29,8 +38,8 @@ class G {
29
38
  isTokenTypeExpired(e) {
30
39
  const t = this.storageManager.getToken(e);
31
40
  if (!t) return !0;
32
- const s = d(t);
33
- return s ? f(s) : !0;
41
+ const s = y(t);
42
+ return s ? S(s) : !0;
34
43
  }
35
44
  /**
36
45
  * Parse token from storage by type.
@@ -42,40 +51,209 @@ class G {
42
51
  parseTokenType(e) {
43
52
  const t = this.storageManager.getToken(e);
44
53
  if (t)
45
- return d(t);
54
+ return y(t);
46
55
  }
47
56
  }
48
- function f(i) {
49
- return Math.floor(Date.now() / 1e3) > i.exp;
57
+ function S(i, e = V) {
58
+ return Math.floor(Date.now() / 1e3) + e > i.exp;
50
59
  }
51
- function d(i) {
60
+ function y(i) {
52
61
  const e = i.split(".")[1];
53
62
  if (!e) throw new Error("Invalid token string");
54
- const t = e.replace(/-/g, "+").replace(/_/g, "/"), s = decodeURIComponent(
55
- window.atob(t).split("").map((o) => "%" + ("00" + o.charCodeAt(0).toString(16)).slice(-2)).join("")
56
- ), r = JSON.parse(s);
57
- return r.membership = r.passflow_tm && r.type !== "invite" ? M(r.passflow_tm) : void 0, r;
63
+ const t = e.replace(/-/g, "+").replace(/_/g, "/"), s = t + "=".repeat((4 - t.length % 4) % 4), r = ce(s), o = decodeURIComponent(
64
+ r.split("").map((d) => "%" + ("00" + d.charCodeAt(0).toString(16)).slice(-2)).join("")
65
+ ), n = JSON.parse(o);
66
+ return n.membership = n.passflow_tm && n.type !== "invite" ? ae(n.passflow_tm) : void 0, n;
67
+ }
68
+ var k = /* @__PURE__ */ ((i) => (i.id_token = "id_token", i.access_token = "access", i.refresh_token = "refresh", i.invite_token = "invite", i.reset_token = "reset", i.web_cookie = "web-cookie", i.management = "management", i.signin = "signin", i.actor = "actor", i.two_factor = "2fa", i))(k || {}), v = /* @__PURE__ */ ((i) => (i.JsonBody = "json_body", i.Cookie = "cookie", i.Mobile = "mobile", i.BFF = "bff", i))(v || {}), q = /* @__PURE__ */ ((i) => (i.Unknown = "unknown", i.Valid = "valid", i.Invalid = "invalid", i))(q || {});
69
+ class B {
70
+ constructor(e) {
71
+ this.storageManager = e, this.mode = "json_body", this.sessionState = "unknown", this.isInitializedFlag = !1, this.STORAGE_PREFIX = "passflow_", this.DELIVERY_MODE_KEY = `${this.STORAGE_PREFIX}delivery_mode`, this.SESSION_STATE_KEY = `${this.STORAGE_PREFIX}session_state`, this.loadPersistedMode(), this.loadPersistedSessionState();
72
+ }
73
+ /**
74
+ * Set the token delivery mode
75
+ */
76
+ setMode(e) {
77
+ this.mode = e, this.isInitializedFlag = !0, this.persistMode();
78
+ }
79
+ /**
80
+ * Get the current token delivery mode
81
+ */
82
+ getMode() {
83
+ return this.mode;
84
+ }
85
+ /**
86
+ * Check if currently in cookie mode
87
+ */
88
+ isCookieMode() {
89
+ return this.mode === "cookie";
90
+ }
91
+ /**
92
+ * Check if currently in JSON body mode
93
+ */
94
+ isJsonMode() {
95
+ return this.mode === "json_body";
96
+ }
97
+ /**
98
+ * Check if currently in mobile mode
99
+ */
100
+ isMobileMode() {
101
+ return this.mode === "mobile";
102
+ }
103
+ /**
104
+ * Check if currently in BFF mode
105
+ */
106
+ isBFFMode() {
107
+ return this.mode === "bff";
108
+ }
109
+ /**
110
+ * Check if delivery mode has been initialized from a server response
111
+ */
112
+ isInitialized() {
113
+ return this.isInitializedFlag;
114
+ }
115
+ /**
116
+ * Mark session as valid (successful authentication or token refresh)
117
+ */
118
+ setSessionValid() {
119
+ this.sessionState = "valid", this.persistSessionState();
120
+ }
121
+ /**
122
+ * Mark session as invalid (received 401 or logout)
123
+ */
124
+ setSessionInvalid() {
125
+ this.sessionState = "invalid", this.persistSessionState();
126
+ }
127
+ /**
128
+ * Reset session state to unknown (used during authentication flows)
129
+ */
130
+ setSessionUnknown() {
131
+ this.sessionState = "unknown", this.persistSessionState();
132
+ }
133
+ /**
134
+ * Check if session is valid
135
+ */
136
+ isSessionValid() {
137
+ return this.sessionState === "valid";
138
+ }
139
+ /**
140
+ * Check if session state is unknown (not yet determined)
141
+ */
142
+ isSessionUnknown() {
143
+ return this.sessionState === "unknown";
144
+ }
145
+ /**
146
+ * Check if session is invalid
147
+ */
148
+ isSessionInvalid() {
149
+ return this.sessionState === "invalid";
150
+ }
151
+ /**
152
+ * Get current session state
153
+ */
154
+ getSessionState() {
155
+ return this.sessionState;
156
+ }
157
+ /**
158
+ * Reset delivery manager to initial state
159
+ */
160
+ reset() {
161
+ this.mode = "json_body", this.sessionState = "unknown", this.isInitializedFlag = !1, this.clearPersistedMode(), this.clearPersistedSessionState();
162
+ }
163
+ /**
164
+ * Load persisted delivery mode from storage
165
+ */
166
+ loadPersistedMode() {
167
+ try {
168
+ const e = this.storageManager.storage.getItem(this.DELIVERY_MODE_KEY);
169
+ e && Object.values(v).includes(e) && (this.mode = e, this.isInitializedFlag = !0);
170
+ } catch {
171
+ }
172
+ }
173
+ /**
174
+ * Load persisted session state from storage
175
+ */
176
+ loadPersistedSessionState() {
177
+ try {
178
+ const e = this.storageManager.storage.getItem(this.SESSION_STATE_KEY);
179
+ e && Object.values(q).includes(e) && (this.sessionState = e);
180
+ } catch {
181
+ }
182
+ }
183
+ /**
184
+ * Persist delivery mode to storage
185
+ */
186
+ persistMode() {
187
+ try {
188
+ this.storageManager.storage.setItem(this.DELIVERY_MODE_KEY, this.mode);
189
+ } catch {
190
+ }
191
+ }
192
+ /**
193
+ * Persist session state to storage
194
+ */
195
+ persistSessionState() {
196
+ try {
197
+ this.storageManager.storage.setItem(this.SESSION_STATE_KEY, this.sessionState);
198
+ } catch {
199
+ }
200
+ }
201
+ /**
202
+ * Clear persisted delivery mode from storage
203
+ */
204
+ clearPersistedMode() {
205
+ try {
206
+ this.storageManager.storage.removeItem(this.DELIVERY_MODE_KEY);
207
+ } catch {
208
+ }
209
+ }
210
+ /**
211
+ * Clear persisted session state from storage
212
+ */
213
+ clearPersistedSessionState() {
214
+ try {
215
+ this.storageManager.storage.removeItem(this.SESSION_STATE_KEY);
216
+ } catch {
217
+ }
218
+ }
58
219
  }
59
- var g = /* @__PURE__ */ ((i) => (i.id_token = "id_token", i.access_token = "access", i.refresh_token = "refresh", i.invite_token = "invite", i.reset_token = "reset", i.web_cookie = "web-cookie", i.management = "management", i.signin = "signin", i.actor = "actor", i))(g || {});
60
- class b {
220
+ class $ {
61
221
  constructor({ storage: e, prefix: t } = {}) {
62
- this.keyStoragePrefix = "", this.scopes = `${this.keyStoragePrefix}tokens_scopes`, this.deviceId = `${this.keyStoragePrefix}passflowDeviceId`, this.invitationToken = `${this.keyStoragePrefix}passflowInvitationToken`, this.previousRedirectUrl = `${this.keyStoragePrefix}passflowPreviousRedirectUrl`, this.storage = e ?? localStorage, this.keyStoragePrefix = t ? `${t}_` : "";
222
+ this.keyStoragePrefix = "", this.scopes = `${this.keyStoragePrefix}tokens_scopes`, this.deviceId = `${this.keyStoragePrefix}passflowDeviceId`, this.invitationToken = `${this.keyStoragePrefix}passflowInvitationToken`, this.previousRedirectUrl = `${this.keyStoragePrefix}passflowPreviousRedirectUrl`, this.STORAGE_PREFIX = "passflow_", this.ID_TOKEN_KEY = `${this.STORAGE_PREFIX}id_token`, this.CSRF_TOKEN_KEY = `${this.STORAGE_PREFIX}csrf_token`, this.DELIVERY_MODE_KEY = `${this.STORAGE_PREFIX}delivery_mode`, this.storage = e ?? localStorage, this.keyStoragePrefix = t ? `${t}_` : "";
63
223
  }
64
- saveTokens(e) {
65
- const { id_token: t, access_token: s, refresh_token: r, scopes: o } = e;
66
- t && this.storage.setItem(this.getKeyForTokenType(g.id_token), t), s && this.storage.setItem(this.getKeyForTokenType(g.access_token), s), r && this.storage.setItem(this.getKeyForTokenType(g.refresh_token), r), o && this.storage.setItem(this.scopes, o.join(","));
224
+ /**
225
+ * Save tokens to storage with conditional logic based on delivery mode
226
+ * In cookie/BFF mode: ONLY save ID token (not access/refresh tokens)
227
+ * In JSON mode: save all tokens (existing behavior)
228
+ */
229
+ saveTokens(e, t) {
230
+ const { id_token: s, access_token: r, refresh_token: o, scopes: n } = e;
231
+ t === v.Cookie || t === v.BFF ? s && this.storage.setItem(this.ID_TOKEN_KEY, s) : (s && this.storage.setItem(this.getKeyForTokenType(k.id_token), s), r && this.storage.setItem(this.getKeyForTokenType(k.access_token), r), o && this.storage.setItem(this.getKeyForTokenType(k.refresh_token), o), n && this.storage.setItem(this.scopes, n.join(",")));
67
232
  }
68
233
  getToken(e) {
69
234
  const t = this.getKeyForTokenType(e);
70
235
  return this.storage.getItem(t) ?? void 0;
71
236
  }
237
+ /**
238
+ * Get tokens from storage with conditional logic based on delivery mode
239
+ * In cookie/BFF mode: return ID token only (access/refresh in HttpOnly cookies)
240
+ * In JSON mode: return all stored tokens (existing behavior)
241
+ */
72
242
  getTokens() {
73
- const e = this.storage.getItem(this.getKeyForTokenType(g.access_token));
74
- if (e)
243
+ const e = this.getDeliveryMode();
244
+ if (e === v.Cookie || e === v.BFF) {
245
+ const s = this.storage.getItem(this.ID_TOKEN_KEY);
246
+ return s ? {
247
+ id_token: s
248
+ // access_token and refresh_token are in HttpOnly cookies, not localStorage
249
+ } : void 0;
250
+ }
251
+ const t = this.storage.getItem(this.getKeyForTokenType(k.access_token));
252
+ if (t)
75
253
  return {
76
- access_token: e,
77
- id_token: this.storage.getItem(this.getKeyForTokenType(g.id_token)) ?? void 0,
78
- refresh_token: this.storage.getItem(this.getKeyForTokenType(g.refresh_token)) ?? void 0,
254
+ access_token: t,
255
+ id_token: this.storage.getItem(this.getKeyForTokenType(k.id_token)) ?? void 0,
256
+ refresh_token: this.storage.getItem(this.getKeyForTokenType(k.refresh_token)) ?? void 0,
79
257
  scopes: this.storage.getItem(this.scopes)?.split(",") ?? void 0
80
258
  };
81
259
  }
@@ -87,7 +265,7 @@ class b {
87
265
  this.storage.removeItem(t);
88
266
  }
89
267
  deleteTokens() {
90
- this.storage.removeItem(this.getKeyForTokenType(g.id_token)), this.storage.removeItem(this.getKeyForTokenType(g.access_token)), this.storage.removeItem(this.getKeyForTokenType(g.refresh_token)), this.storage.removeItem(this.scopes);
268
+ this.storage.removeItem(this.getKeyForTokenType(k.id_token)), this.storage.removeItem(this.getKeyForTokenType(k.access_token)), this.storage.removeItem(this.getKeyForTokenType(k.refresh_token)), this.storage.removeItem(this.scopes), this.clearIdToken();
91
269
  }
92
270
  getDeviceId() {
93
271
  return this.storage.getItem(this.deviceId) ?? void 0;
@@ -116,13 +294,101 @@ class b {
116
294
  deletePreviousRedirectUrl() {
117
295
  this.storage.removeItem(this.previousRedirectUrl);
118
296
  }
297
+ // Delivery mode storage methods
298
+ /**
299
+ * Set the token delivery mode in storage
300
+ */
301
+ setDeliveryMode(e) {
302
+ try {
303
+ this.storage.setItem(this.DELIVERY_MODE_KEY, e);
304
+ } catch {
305
+ }
306
+ }
307
+ /**
308
+ * Get the token delivery mode from storage
309
+ */
310
+ getDeliveryMode() {
311
+ try {
312
+ const e = this.storage.getItem(this.DELIVERY_MODE_KEY);
313
+ if (e && Object.values(v).includes(e))
314
+ return e;
315
+ } catch {
316
+ }
317
+ }
318
+ /**
319
+ * Clear the delivery mode from storage
320
+ */
321
+ clearDeliveryMode() {
322
+ try {
323
+ this.storage.removeItem(this.DELIVERY_MODE_KEY);
324
+ } catch {
325
+ }
326
+ }
327
+ // ID token storage methods (for cookie mode)
328
+ /**
329
+ * Get the ID token from storage (cookie mode)
330
+ */
331
+ getIdToken() {
332
+ try {
333
+ return this.storage.getItem(this.ID_TOKEN_KEY) ?? void 0;
334
+ } catch {
335
+ return;
336
+ }
337
+ }
338
+ /**
339
+ * Set the ID token in storage (cookie mode)
340
+ */
341
+ setIdToken(e) {
342
+ try {
343
+ this.storage.setItem(this.ID_TOKEN_KEY, e);
344
+ } catch {
345
+ }
346
+ }
347
+ /**
348
+ * Clear the ID token from storage
349
+ */
350
+ clearIdToken() {
351
+ try {
352
+ this.storage.removeItem(this.ID_TOKEN_KEY);
353
+ } catch {
354
+ }
355
+ }
356
+ // CSRF token storage methods
357
+ /**
358
+ * Get the CSRF token from storage
359
+ */
360
+ getCsrfToken() {
361
+ try {
362
+ return this.storage.getItem(this.CSRF_TOKEN_KEY) ?? void 0;
363
+ } catch {
364
+ return;
365
+ }
366
+ }
367
+ /**
368
+ * Set the CSRF token in storage
369
+ */
370
+ setCsrfToken(e) {
371
+ try {
372
+ this.storage.setItem(this.CSRF_TOKEN_KEY, e);
373
+ } catch {
374
+ }
375
+ }
376
+ /**
377
+ * Clear the CSRF token from storage
378
+ */
379
+ clearCsrfToken() {
380
+ try {
381
+ this.storage.removeItem(this.CSRF_TOKEN_KEY);
382
+ } catch {
383
+ }
384
+ }
119
385
  getKeyForTokenType(e) {
120
386
  return `${this.keyStoragePrefix}${e}`;
121
387
  }
122
388
  }
123
- class $ {
124
- constructor() {
125
- this.storageManager = new b();
389
+ class Y {
390
+ constructor(e) {
391
+ this.storageManager = e ?? new $();
126
392
  }
127
393
  getDeviceId() {
128
394
  const e = this.storageManager.getDeviceId();
@@ -133,84 +399,85 @@ class $ {
133
399
  return e;
134
400
  }
135
401
  generateUniqueDeviceId() {
136
- return R();
402
+ return H();
137
403
  }
138
404
  }
139
- var S = /* @__PURE__ */ ((i) => (i.GET = "get", i.POST = "post", i.PUT = "put", i.PATCH = "patch", i.DELETE = "delete", i))(S || {}), c = /* @__PURE__ */ ((i) => (i.signin = "/auth/login", i.signup = "/auth/register", i.signInWithProvider = "/auth/federated/start/", i.passwordless = "/auth/passwordless/start", i.passwordlessComplete = "/auth/passwordless/complete", i.logout = "/user/logout", i.refresh = "/auth/refresh", i.sendPasswordResetEmail = "/auth/password/reset", i.resetPassword = "/auth/password/change", i.appSettings = "/app/settings", i.passkeyRegisterStart = "/auth/passkey/register/start", i.passkeyRegisterComplete = "/auth/passkey/register/complete", i.passkeyAuthenticateStart = "/auth/passkey/authenticate/start", i.passkeyAuthenticateComplete = "/auth/passkey/authenticate/complete", i.passkeyValidate = "/auth/validate", i.settingsAll = "/settings", i.settingsPasswordPolicy = "/settings/password", i.settingsPasskey = "/settings/passkey", i.userPasskey = "/user/passkey", i.addUserPasskey = "/user/passkey/add/start", i.completeAddUserPasskey = "/user/passkey/add/complete", i.joinInvitation = "/user/tenant/join", i.tenantPath = "/user/tenant", i.invitationsPath = "/user/tenant/:tenantID/invitations", i.requestInvitation = "/user/invite", i.invitationDelete = "/user/invite/:invitationID", i.invitationResend = "/user/invite/:invitationID/resend", i.invitationGetLink = "/user/invite/:invitationID/link", i))(c || {}), y = /* @__PURE__ */ ((i) => (i.passkeyRegisterStart = "/admin/auth/passkey/register/start", i.passkeyRegisterComplete = "/admin/auth/passkey/register/complete", i.passkeyAuthenticateStart = "/admin/auth/passkey/authenticate/start", i.passkeyAuthenticateComplete = "/admin/auth/passkey/authenticate/complete", i.passkeyValidate = "/admin/auth/validate", i.logout = "/admin/auth/logout", i))(y || {});
405
+ var E = /* @__PURE__ */ ((i) => (i.GET = "get", i.POST = "post", i.PUT = "put", i.PATCH = "patch", i.DELETE = "delete", i))(E || {}), h = /* @__PURE__ */ ((i) => (i.signin = "/auth/login", i.signup = "/auth/register", i.signInWithProvider = "/auth/federated/start/", i.passwordless = "/auth/passwordless/start", i.passwordlessComplete = "/auth/passwordless/complete", i.logout = "/user/logout", i.refresh = "/auth/refresh", i.validateSession = "/user/me", i.sendPasswordResetEmail = "/auth/password/reset", i.resetPassword = "/auth/password/change", i.appSettings = "/app/settings", i.passkeyRegisterStart = "/auth/passkey/register/start", i.passkeyRegisterComplete = "/auth/passkey/register/complete", i.passkeyAuthenticateStart = "/auth/passkey/authenticate/start", i.passkeyAuthenticateComplete = "/auth/passkey/authenticate/complete", i.passkeyValidate = "/auth/validate", i.settingsAll = "/settings", i.settingsPasswordPolicy = "/settings/password", i.settingsPasskey = "/settings/passkey", i.userPasskey = "/user/passkey", i.addUserPasskey = "/user/passkey/add/start", i.completeAddUserPasskey = "/user/passkey/add/complete", i.joinInvitation = "/user/tenant/join", i.tenantPath = "/user/tenant", i.invitationsPath = "/user/tenant/:tenantID/invitations", i.requestInvitation = "/user/invite", i.invitationDelete = "/user/invite/:invitationID", i.invitationResend = "/user/invite/:invitationID/resend", i.invitationGetLink = "/user/invite/:invitationID/link", i.twoFactor = "/user/2fa", i.twoFactorStatus = "/user/2fa/status", i.twoFactorSetupBegin = "/user/2fa/setup/begin", i.twoFactorSetupConfirm = "/user/2fa/setup/confirm", i.twoFactorVerify = "/auth/2fa/verify", i.twoFactorRecovery = "/auth/2fa/recovery", i.twoFactorRegenerateCodes = "/user/2fa/recovery-codes/regenerate", i.twoFactorSetupMagicLink = "/auth/2fa-setup", i))(h || {}), w = /* @__PURE__ */ ((i) => (i.passkeyRegisterStart = "/admin/auth/passkey/register/start", i.passkeyRegisterComplete = "/admin/auth/passkey/register/complete", i.passkeyAuthenticateStart = "/admin/auth/passkey/authenticate/start", i.passkeyAuthenticateComplete = "/admin/auth/passkey/authenticate/complete", i.passkeyValidate = "/admin/auth/validate", i.logout = "/admin/auth/logout", i))(w || {});
140
406
  class u extends Error {
141
407
  constructor(e) {
142
408
  super(), this.id = e?.id ?? "unknown", this.message = e?.message ?? e ?? "Something went wrong", this.status = e?.status ?? 500, this.location = e?.location ?? "unknown", this.time = e?.time ?? (/* @__PURE__ */ new Date()).toISOString();
143
409
  }
144
410
  }
145
- var L = /* @__PURE__ */ ((i) => (i.google = "google", i.facebook = "facebook", i))(L || {}), w = /* @__PURE__ */ ((i) => (i.web = "web", i))(w || {});
146
- function C(i, e) {
411
+ var de = /* @__PURE__ */ ((i) => (i.google = "google", i.facebook = "facebook", i))(de || {}), I = /* @__PURE__ */ ((i) => (i.web = "web", i))(I || {});
412
+ function A(i, e) {
147
413
  let t = i;
148
414
  return Object.entries(e).forEach(([s, r]) => {
149
415
  t = t.replace(`:${s}`, r);
150
416
  }), t;
151
417
  }
152
- class I {
153
- constructor(e) {
154
- this.refreshPromise = null, this.origin = window.location.origin, this.defaultHeaders = {
418
+ var ue = /* @__PURE__ */ ((i) => (i.Disabled = "disabled", i.Optional = "optional", i.Required = "required", i))(ue || {});
419
+ const le = 3, ge = 1e3;
420
+ class T {
421
+ constructor(e, t, s) {
422
+ this.refreshPromise = null, this.isRefreshing = !1, this.origin = typeof window < "u" ? window.location.origin : "", this.defaultHeaders = {
155
423
  Accept: "application/json",
156
424
  "Content-Type": "application/json"
157
425
  }, this.nonAccessTokenEndpoints = ["/auth/", "/settings", "/settings/"], this.protectedEndpoints = ["logout", "refresh"];
158
- const { url: t, appId: s, keyStoragePrefix: r } = e;
159
- this.url = t || A, this.storageManager = new b({
160
- prefix: r ?? ""
161
- }), this.deviceService = new $(), this.tokenService = new G(), s && (this.appId = s, this.defaultHeaders = {
426
+ const { url: r, appId: o, keyStoragePrefix: n } = e;
427
+ this.url = r || G, this.storageManager = t ?? new $({
428
+ prefix: n ?? ""
429
+ }), this.deviceService = s ?? new Y(this.storageManager), this.tokenService = new he(this.storageManager), this.tokenDeliveryManager = new B(this.storageManager), o && (this.appId = o, this.defaultHeaders = {
162
430
  ...this.defaultHeaders,
163
- [P]: s
431
+ [C]: o
164
432
  });
165
- const o = this.deviceService.getDeviceId();
433
+ const d = this.deviceService.getDeviceId();
166
434
  this.defaultHeaders = {
167
435
  ...this.defaultHeaders,
168
- [D]: o,
169
- [x]: "web"
170
- }, this.instance = T.create({
436
+ [W]: d,
437
+ [J]: "web"
438
+ }, this.detectCookieSupport(), this.instance = D.create({
171
439
  baseURL: this.url,
172
440
  headers: { ...this.defaultHeaders }
173
- }), this.instance.interceptors.request.use(async (n) => {
174
- if (this.isNonAuthEndpoint(n.url))
175
- return n;
176
- if (n.url?.includes("refresh")) {
177
- if (this.refreshPromise) {
441
+ }), this.instance.interceptors.request.use(async (c) => {
442
+ if (this.isNonAuthEndpoint(c.url))
443
+ return c;
444
+ if (this.tokenDeliveryManager.isCookieMode()) {
445
+ c.withCredentials = !0;
446
+ const p = this.storageManager.getCsrfToken();
447
+ return p && (c.headers["X-CSRF-Token"] = p), c;
448
+ }
449
+ if (c.url?.includes("refresh")) {
450
+ if (this.isRefreshing) {
178
451
  const p = new AbortController();
179
- return p.abort(), n.signal = p.signal, n;
452
+ return p.abort(), c.signal = p.signal, c;
180
453
  }
181
- return n;
454
+ return c;
182
455
  }
183
- const h = this.storageManager.getTokens(), l = this.storageManager.getScopes();
184
- if (h?.access_token) {
185
- const p = d(h.access_token);
186
- if (f(p) && h.refresh_token)
456
+ const g = this.storageManager.getTokens();
457
+ if (g?.access_token) {
458
+ const p = y(g.access_token);
459
+ if (S(p, V) && g.refresh_token)
187
460
  try {
188
461
  if (this.refreshPromise) {
189
- const E = await this.refreshPromise;
190
- return E.data && (n.headers[m] = `Bearer ${E.data.access_token}`), n;
462
+ const f = await this.refreshPromise;
463
+ return f?.data?.access_token && (c.headers[_] = `Bearer ${f.data.access_token}`), c;
464
+ }
465
+ this.refreshPromise = this.refreshTokens();
466
+ try {
467
+ const f = await this.refreshPromise;
468
+ return f?.data?.access_token && (c.headers[_] = `Bearer ${f.data.access_token}`), c;
469
+ } finally {
470
+ this.refreshPromise = null;
191
471
  }
192
- const v = {
193
- refresh_token: h.refresh_token,
194
- scopes: l
195
- };
196
- this.refreshPromise = this.instance.post(c.refresh, v, {
197
- headers: {
198
- [m]: `Bearer ${h.refresh_token}`
199
- }
200
- });
201
- const k = await this.refreshPromise;
202
- return k.data && (this.storageManager.saveTokens(k.data), n.headers[m] = `Bearer ${k.data.access_token}`), n;
203
- } catch (v) {
204
- return this.refreshPromise = null, Promise.reject(v);
205
- } finally {
206
- this.refreshPromise = null;
472
+ } catch (f) {
473
+ return this.refreshPromise = null, this.isRefreshing = !1, this.storageManager.deleteTokens(), Promise.reject(f);
207
474
  }
208
- return n.headers[m] = `Bearer ${h.access_token}`, n;
475
+ return c.headers[_] = `Bearer ${g.access_token}`, c;
209
476
  }
210
- return n;
477
+ return c;
211
478
  }), this.instance.interceptors.response.use(
212
- (n) => n,
213
- (n) => this.handleAxiosError(n)
479
+ (c) => c,
480
+ async (c) => (c.response?.status === 401 && this.tokenDeliveryManager.setSessionInvalid(), c.response?.status === 429 ? await this.handleRateLimitError(c) : this.handleAxiosError(c))
214
481
  );
215
482
  }
216
483
  isProtectedEndpoint(e) {
@@ -219,6 +486,71 @@ class I {
219
486
  isNonAuthEndpoint(e) {
220
487
  return this.nonAccessTokenEndpoints.some((t) => e?.includes(t)) && !this.isProtectedEndpoint(e);
221
488
  }
489
+ /**
490
+ * Detect if cookies are supported/enabled in the browser
491
+ * Falls back to JSON mode if cookies are blocked
492
+ */
493
+ detectCookieSupport() {
494
+ if (!(typeof document > "u"))
495
+ try {
496
+ document.cookie = "passflow_test=1; SameSite=Lax";
497
+ const e = document.cookie.indexOf("passflow_test=1") !== -1;
498
+ document.cookie = "passflow_test=; expires=Thu, 01 Jan 1970 00:00:00 UTC", !e && this.tokenDeliveryManager.isCookieMode() && console.warn("[Passflow SDK] Cookies are blocked. Cookie mode may not work.");
499
+ } catch {
500
+ }
501
+ }
502
+ /**
503
+ * Refresh tokens using single-flight pattern to prevent race conditions
504
+ * Supports both cookie mode and JSON mode
505
+ */
506
+ async refreshTokens() {
507
+ if (this.tokenDeliveryManager.isCookieMode()) {
508
+ const e = await this.instance.post(
509
+ h.refresh,
510
+ {},
511
+ // Empty body
512
+ { withCredentials: !0 }
513
+ );
514
+ return this.tokenDeliveryManager.setSessionValid(), e.data.csrf_token && this.storageManager.setCsrfToken(e.data.csrf_token), e.data.id_token && this.storageManager.setIdToken(e.data.id_token), e;
515
+ } else {
516
+ const e = this.storageManager.getTokens(), t = this.storageManager.getScopes();
517
+ if (!e?.refresh_token)
518
+ throw new Error("No refresh token available");
519
+ this.isRefreshing = !0;
520
+ const s = {
521
+ refresh_token: e.refresh_token,
522
+ scopes: t
523
+ }, r = await this.instance.post(h.refresh, s, {
524
+ headers: {
525
+ [_]: `Bearer ${e.refresh_token}`
526
+ }
527
+ });
528
+ return r.data && this.storageManager.saveTokens(r.data), this.isRefreshing = !1, r;
529
+ }
530
+ }
531
+ async handleRateLimitError(e) {
532
+ const t = e.config;
533
+ if (!t)
534
+ return Promise.reject(e);
535
+ const s = t.method?.toUpperCase();
536
+ if (!["GET", "HEAD", "OPTIONS"].includes(s || ""))
537
+ return Promise.reject(e);
538
+ const o = t._retryCount || 0;
539
+ if (o >= le)
540
+ return Promise.reject(e);
541
+ let n = ge * Math.pow(2, o);
542
+ const d = e.response?.headers?.["retry-after"];
543
+ if (d) {
544
+ const c = Number.parseInt(d, 10);
545
+ if (!Number.isNaN(c))
546
+ n = c * 1e3;
547
+ else {
548
+ const g = new Date(d);
549
+ Number.isNaN(g.getTime()) || (n = Math.max(0, g.getTime() - Date.now()));
550
+ }
551
+ }
552
+ return await new Promise((c) => setTimeout(c, n)), t._retryCount = o + 1, this.instance.request(t);
553
+ }
222
554
  // eslint-disable-next-line complexity
223
555
  // biome-ignore lint/suspicious/useAwait: <explanation>
224
556
  async handleAxiosError(e) {
@@ -247,33 +579,59 @@ class I {
247
579
  })).data;
248
580
  }
249
581
  get(e, t) {
250
- return this.send(S.GET, e, t);
582
+ return this.send(E.GET, e, t);
251
583
  }
252
584
  post(e, t, s) {
253
- return this.send(S.POST, e, { data: t, ...s });
585
+ return this.send(E.POST, e, { data: t, ...s });
254
586
  }
255
587
  put(e, t, s) {
256
- return this.send(S.PUT, e, { data: t, ...s });
588
+ return this.send(E.PUT, e, { data: t, ...s });
257
589
  }
258
590
  patch(e, t, s) {
259
- return this.send(S.PATCH, e, { data: t, ...s });
591
+ return this.send(E.PATCH, e, { data: t, ...s });
260
592
  }
261
593
  delete(e, t) {
262
- return this.send(S.DELETE, e, t);
594
+ return this.send(E.DELETE, e, t);
595
+ }
596
+ /**
597
+ * Update the appId and propagate it to axios headers.
598
+ * This ensures that the APP_ID_HEADER_KEY is updated in all future requests.
599
+ *
600
+ * @param appId - The new application ID to set
601
+ */
602
+ setAppId(e) {
603
+ this.appId = e, this.defaultHeaders = {
604
+ ...this.defaultHeaders,
605
+ [C]: e
606
+ }, this.instance.defaults.headers.common[C] = e;
263
607
  }
264
608
  }
265
- class O {
266
- constructor(e) {
267
- this.storageManager = new b(), this.axiosClient = new I(e);
609
+ class pe {
610
+ constructor(e, t, s) {
611
+ this.axiosClient = new T(e, t, s);
612
+ }
613
+ setAppId(e) {
614
+ this.axiosClient.setAppId(e);
615
+ }
616
+ getAppSettings() {
617
+ return this.axiosClient.get(h.appSettings);
618
+ }
619
+ }
620
+ class fe {
621
+ constructor(e, t, s) {
622
+ this.axiosClient = new T(e, t, s);
623
+ }
624
+ setAppId(e) {
625
+ this.axiosClient.setAppId(e);
268
626
  }
269
627
  refreshToken(e, t, s) {
270
628
  const r = {
271
629
  access: s,
272
630
  scopes: t
273
631
  };
274
- return this.axiosClient.post(c.refresh, r, {
632
+ return this.axiosClient.post(h.refresh, r, {
275
633
  headers: {
276
- [m]: `Bearer ${e}`
634
+ [_]: `Bearer ${e}`
277
635
  }
278
636
  });
279
637
  }
@@ -284,7 +642,7 @@ class O {
284
642
  os: s
285
643
  };
286
644
  return this.axiosClient.post(
287
- c.signin,
645
+ h.signin,
288
646
  r
289
647
  );
290
648
  }
@@ -295,7 +653,7 @@ class O {
295
653
  anonymous: s ?? !1
296
654
  };
297
655
  return this.axiosClient.post(
298
- c.signup,
656
+ h.signup,
299
657
  r
300
658
  );
301
659
  }
@@ -307,23 +665,26 @@ class O {
307
665
  os: s
308
666
  };
309
667
  return this.axiosClient.post(
310
- c.passwordless,
668
+ h.passwordless,
311
669
  o
312
670
  );
313
671
  }
314
672
  passwordlessSignInComplete(e) {
315
673
  return this.axiosClient.post(
316
- c.passwordlessComplete,
674
+ h.passwordlessComplete,
317
675
  e
318
676
  );
319
677
  }
320
678
  logOut(e, t, s = !1) {
321
- const r = s ? void 0 : { refresh_token: t, device: e }, o = s ? y.logout : c.logout;
679
+ const r = s ? void 0 : { refresh_token: t, device: e }, o = s ? w.logout : h.logout;
322
680
  return this.axiosClient.post(o, r);
323
681
  }
682
+ validateSession() {
683
+ return this.axiosClient.get(h.validateSession);
684
+ }
324
685
  sendPasswordResetEmail(e) {
325
686
  return this.axiosClient.post(
326
- c.sendPasswordResetEmail,
687
+ h.sendPasswordResetEmail,
327
688
  e
328
689
  );
329
690
  }
@@ -332,10 +693,10 @@ class O {
332
693
  password: e,
333
694
  scopes: t
334
695
  };
335
- return this.axiosClient.post(c.resetPassword, r, {
696
+ return this.axiosClient.post(h.resetPassword, r, {
336
697
  headers: {
337
- [m]: `Bearer ${s}`,
338
- [P]: void 0
698
+ [_]: `Bearer ${s}`,
699
+ [C]: void 0
339
700
  }
340
701
  });
341
702
  }
@@ -345,15 +706,15 @@ class O {
345
706
  create_tenant: o ?? !1,
346
707
  device: t,
347
708
  os: s
348
- }, h = r ? y.passkeyRegisterStart : c.passkeyRegisterStart;
349
- return this.axiosClient.post(h, n);
709
+ }, d = r ? w.passkeyRegisterStart : h.passkeyRegisterStart;
710
+ return this.axiosClient.post(d, n);
350
711
  }
351
712
  passkeyRegisterComplete(e, t, s, r = !1) {
352
713
  const o = {
353
714
  challenge_id: s,
354
715
  device: t,
355
716
  passkey_data: e
356
- }, n = r ? y.passkeyRegisterComplete : c.passkeyRegisterComplete;
717
+ }, n = r ? w.passkeyRegisterComplete : h.passkeyRegisterComplete;
357
718
  return this.axiosClient.post(n, o);
358
719
  }
359
720
  passkeyAuthenticateStart(e, t, s, r = !1) {
@@ -362,7 +723,7 @@ class O {
362
723
  user_id: e.user_id ?? "",
363
724
  device: t,
364
725
  os: s
365
- }, n = r ? y.passkeyAuthenticateStart : c.passkeyAuthenticateStart;
726
+ }, n = r ? w.passkeyAuthenticateStart : h.passkeyAuthenticateStart;
366
727
  return this.axiosClient.post(
367
728
  n,
368
729
  o
@@ -373,7 +734,7 @@ class O {
373
734
  challenge_id: s,
374
735
  device: t,
375
736
  passkey_data: e
376
- }, n = r ? y.passkeyAuthenticateComplete : c.passkeyAuthenticateComplete;
737
+ }, n = r ? w.passkeyAuthenticateComplete : h.passkeyAuthenticateComplete;
377
738
  return this.axiosClient.post(n, o);
378
739
  }
379
740
  passkeyValidate(e, t, s, r = !1, o) {
@@ -382,79 +743,103 @@ class O {
382
743
  device: t,
383
744
  challenge_id: s
384
745
  };
385
- let h = c.passkeyValidate;
386
- !o && r && (h = y.passkeyValidate);
387
- const l = o ? { [P]: o } : {};
388
- return this.axiosClient.post(h, n, { headers: l });
746
+ let d = h.passkeyValidate;
747
+ !o && r && (d = w.passkeyValidate);
748
+ const c = o ? { [C]: o } : {};
749
+ return this.axiosClient.post(d, n, { headers: c });
389
750
  }
390
751
  }
391
- class j {
392
- constructor(e) {
393
- this.axiosClient = new I(e);
752
+ class ke {
753
+ constructor(e, t, s) {
754
+ this.axiosClient = new T(e, t, s);
394
755
  }
395
- getAppSettings() {
396
- return this.axiosClient.get(c.appSettings);
756
+ setAppId(e) {
757
+ this.axiosClient.setAppId(e);
758
+ }
759
+ /**
760
+ * Requests an invitation link that can be used to invite users
761
+ * @param payload Request invitation payload
762
+ * @returns Promise with invitation link and token
763
+ */
764
+ requestInviteLink(e) {
765
+ return this.axiosClient.post(
766
+ h.requestInvitation,
767
+ e
768
+ );
769
+ }
770
+ /**
771
+ * Gets a list of active invitations
772
+ * @param options Optional parameters for filtering and pagination
773
+ * @returns Promise with paginated list of invitations
774
+ */
775
+ getInvitations(e) {
776
+ const t = {};
777
+ e.groupID && (t.group_id = e.groupID.toString()), e.skip !== void 0 && (t.skip = e.skip.toString()), e.limit !== void 0 && (t.limit = e.limit.toString());
778
+ const s = A(h.invitationsPath, {
779
+ tenantID: e.tenantID
780
+ });
781
+ return this.axiosClient.get(s, { params: t }).then((r) => ({
782
+ invites: r.invites,
783
+ nextPageSkip: r.next_page_skip
784
+ }));
785
+ }
786
+ /**
787
+ * Deletes an invitation by token
788
+ * @param invitationID The invitation ID to delete
789
+ * @returns Promise with success response
790
+ */
791
+ deleteInvitation(e) {
792
+ const t = A(h.invitationDelete, {
793
+ invitationID: e
794
+ });
795
+ return this.axiosClient.delete(t);
796
+ }
797
+ /**
798
+ * Resend an invitation by token
799
+ * @param invitationID The invitation ID to resend
800
+ * @returns Promise with success response
801
+ */
802
+ resendInvitation(e) {
803
+ const t = A(h.invitationResend, {
804
+ invitationID: e
805
+ });
806
+ return this.axiosClient.post(t, {});
807
+ }
808
+ /**
809
+ * Get a link to an invitation by id
810
+ * @param invitationID The invitation ID to get link
811
+ * @returns Promise with the link
812
+ */
813
+ getInvitationLink(e) {
814
+ const t = A(h.invitationGetLink, {
815
+ invitationID: e
816
+ });
817
+ return this.axiosClient.get(t);
397
818
  }
398
819
  }
399
- class K {
400
- constructor(e) {
401
- this.axiosClient = new I(e);
820
+ class ye {
821
+ constructor(e, t, s) {
822
+ this.axiosClient = new T(e, t, s);
823
+ }
824
+ setAppId(e) {
825
+ this.axiosClient.setAppId(e);
402
826
  }
403
827
  getSettingsAll() {
404
- return this.axiosClient.get(c.settingsAll);
828
+ return this.axiosClient.get(h.settingsAll);
405
829
  }
406
830
  getPasswordPolicySettings() {
407
- return this.axiosClient.get(c.settingsPasswordPolicy);
831
+ return this.axiosClient.get(h.settingsPasswordPolicy);
408
832
  }
409
833
  getPasskeySettings() {
410
- return this.axiosClient.get(c.settingsPasskey);
834
+ return this.axiosClient.get(h.settingsPasskey);
411
835
  }
412
836
  }
413
- class N {
414
- constructor(e) {
415
- this.axiosClient = new I(e);
416
- }
417
- getUserPasskeys() {
418
- return this.axiosClient.get(c.userPasskey);
419
- }
420
- renameUserPasskey(e, t) {
421
- return this.axiosClient.patch(
422
- `${c.userPasskey}/${t}`,
423
- {
424
- name: e
425
- }
426
- );
837
+ class ve {
838
+ constructor(e, t, s) {
839
+ this.axiosClient = new T(e, t, s);
427
840
  }
428
- deleteUserPasskey(e) {
429
- return this.axiosClient.delete(`${c.userPasskey}/${e}`);
430
- }
431
- addUserPasskeyStart({
432
- relyingPartyId: e,
433
- deviceId: t,
434
- os: s,
435
- passkeyDisplayName: r,
436
- passkeyUsername: o
437
- }) {
438
- const n = {
439
- passkey_display_name: r,
440
- passkey_username: o,
441
- relying_party_id: e,
442
- deviceId: t,
443
- os: s
444
- };
445
- return this.axiosClient.post(c.addUserPasskey, n);
446
- }
447
- addUserPasskeyComplete(e, t, s) {
448
- return this.axiosClient.post(c.completeAddUserPasskey, {
449
- challenge_id: s,
450
- device: t,
451
- passkey_data: e
452
- });
453
- }
454
- }
455
- class B {
456
- constructor(e) {
457
- this.axiosClient = new I(e);
841
+ setAppId(e) {
842
+ this.axiosClient.setAppId(e);
458
843
  }
459
844
  joinInvitation(e, t) {
460
845
  const s = {
@@ -462,7 +847,7 @@ class B {
462
847
  scopes: t
463
848
  };
464
849
  return this.axiosClient.post(
465
- c.joinInvitation,
850
+ h.joinInvitation,
466
851
  s
467
852
  );
468
853
  }
@@ -471,7 +856,7 @@ class B {
471
856
  name: e
472
857
  };
473
858
  return this.axiosClient.post(
474
- c.tenantPath,
859
+ h.tenantPath,
475
860
  t
476
861
  );
477
862
  }
@@ -481,7 +866,7 @@ class B {
481
866
  * @param tenantId Tenant ID
482
867
  */
483
868
  getTenantDetails(e) {
484
- const t = `${c.tenantPath}/${e}`;
869
+ const t = `${h.tenantPath}/${e}`;
485
870
  return this.axiosClient.get(t);
486
871
  }
487
872
  /**
@@ -490,7 +875,7 @@ class B {
490
875
  * @param name New tenant name
491
876
  */
492
877
  updateTenant(e, t) {
493
- const s = `${c.tenantPath}/${e}`, r = { name: t };
878
+ const s = `${h.tenantPath}/${e}`, r = { name: t };
494
879
  return this.axiosClient.put(s, r);
495
880
  }
496
881
  /**
@@ -498,14 +883,14 @@ class B {
498
883
  * @param tenantId Tenant ID
499
884
  */
500
885
  deleteTenant(e) {
501
- const t = `${c.tenantPath}/${e}`;
886
+ const t = `${h.tenantPath}/${e}`;
502
887
  return this.axiosClient.delete(t);
503
888
  }
504
889
  /**
505
890
  * Get user's tenant memberships
506
891
  */
507
892
  getUserTenantMembership() {
508
- return this.axiosClient.get(c.tenantPath);
893
+ return this.axiosClient.get(h.tenantPath);
509
894
  }
510
895
  // 2. Group Management
511
896
  /**
@@ -514,7 +899,7 @@ class B {
514
899
  * @param name Group name
515
900
  */
516
901
  createGroup(e, t) {
517
- const s = `${c.tenantPath}/${e}/group`, r = { name: t };
902
+ const s = `${h.tenantPath}/${e}/group`, r = { name: t };
518
903
  return this.axiosClient.post(s, r);
519
904
  }
520
905
  /**
@@ -523,7 +908,7 @@ class B {
523
908
  * @param groupId Group ID
524
909
  */
525
910
  getGroupInfo(e, t) {
526
- const s = `${c.tenantPath}/${e}/group/${t}`;
911
+ const s = `${h.tenantPath}/${e}/group/${t}`;
527
912
  return this.axiosClient.get(s);
528
913
  }
529
914
  /**
@@ -533,7 +918,7 @@ class B {
533
918
  * @param name New group name
534
919
  */
535
920
  updateGroup(e, t, s) {
536
- const r = `${c.tenantPath}/${e}/group/${t}`, o = { name: s };
921
+ const r = `${h.tenantPath}/${e}/group/${t}`, o = { name: s };
537
922
  return this.axiosClient.put(r, o);
538
923
  }
539
924
  /**
@@ -542,7 +927,7 @@ class B {
542
927
  * @param groupId Group ID
543
928
  */
544
929
  deleteGroup(e, t) {
545
- const s = `${c.tenantPath}/${e}/group/${t}`;
930
+ const s = `${h.tenantPath}/${e}/group/${t}`;
546
931
  return this.axiosClient.delete(s);
547
932
  }
548
933
  /**
@@ -553,7 +938,7 @@ class B {
553
938
  * @param role Role to assign
554
939
  */
555
940
  addUserToGroup(e, t, s, r) {
556
- const o = `${c.tenantPath}/${e}/group/${t}/add`, n = { user_id: s, role: r };
941
+ const o = `${h.tenantPath}/${e}/group/${t}/add`, n = { user_id: s, role: r };
557
942
  return this.axiosClient.post(o, n);
558
943
  }
559
944
  /**
@@ -564,7 +949,7 @@ class B {
564
949
  * @param roles Roles to remove
565
950
  */
566
951
  removeUserRolesFromGroup(e, t, s, r) {
567
- const o = `${c.tenantPath}/${e}/group/${t}/remove_roles`, n = { user_id: s, roles: r };
952
+ const o = `${h.tenantPath}/${e}/group/${t}/remove_roles`, n = { user_id: s, roles: r };
568
953
  return this.axiosClient.post(o, n);
569
954
  }
570
955
  /**
@@ -575,7 +960,7 @@ class B {
575
960
  * @param roles New roles to assign
576
961
  */
577
962
  changeUserRoles(e, t, s, r) {
578
- const o = `${c.tenantPath}/${e}/group/${t}/change`, n = { user_id: s, roles: r };
963
+ const o = `${h.tenantPath}/${e}/group/${t}/change`, n = { user_id: s, roles: r };
579
964
  return this.axiosClient.post(o, n);
580
965
  }
581
966
  /**
@@ -585,7 +970,7 @@ class B {
585
970
  * @param userId User ID
586
971
  */
587
972
  deleteUserFromGroup(e, t, s) {
588
- const r = `${c.tenantPath}/${e}/group/${t}/${s}`;
973
+ const r = `${h.tenantPath}/${e}/group/${t}/${s}`;
589
974
  return this.axiosClient.delete(r);
590
975
  }
591
976
  // 3. Role Management
@@ -594,7 +979,7 @@ class B {
594
979
  * @param tenantId Tenant ID
595
980
  */
596
981
  getRolesForTenant(e) {
597
- const t = `${c.tenantPath}/${e}/role`;
982
+ const t = `${h.tenantPath}/${e}/role`;
598
983
  return this.axiosClient.get(t);
599
984
  }
600
985
  /**
@@ -603,7 +988,7 @@ class B {
603
988
  * @param name Role name
604
989
  */
605
990
  createRoleForTenant(e, t) {
606
- const s = `${c.tenantPath}/${e}/role`, r = { name: t };
991
+ const s = `${h.tenantPath}/${e}/role`, r = { name: t };
607
992
  return this.axiosClient.post(s, r);
608
993
  }
609
994
  /**
@@ -613,7 +998,7 @@ class B {
613
998
  * @param name New role name
614
999
  */
615
1000
  updateRole(e, t, s) {
616
- const r = `${c.tenantPath}/${e}/role/${t}`, o = { name: s };
1001
+ const r = `${h.tenantPath}/${e}/role/${t}`, o = { name: s };
617
1002
  return this.axiosClient.put(r, o);
618
1003
  }
619
1004
  /**
@@ -622,7 +1007,7 @@ class B {
622
1007
  * @param roleId Role ID
623
1008
  */
624
1009
  deleteRole(e, t) {
625
- const s = `${c.tenantPath}/${e}/role/${t}`;
1010
+ const s = `${h.tenantPath}/${e}/role/${t}`;
626
1011
  return this.axiosClient.delete(s);
627
1012
  }
628
1013
  // 4. User Management in Tenants
@@ -632,7 +1017,7 @@ class B {
632
1017
  * @param userId User ID
633
1018
  */
634
1019
  deleteUserFromTenant(e, t) {
635
- const s = `${c.tenantPath}/${e}/user/${t}`;
1020
+ const s = `${h.tenantPath}/${e}/user/${t}`;
636
1021
  return this.axiosClient.delete(s);
637
1022
  }
638
1023
  // 5. Invitation Management
@@ -644,7 +1029,7 @@ class B {
644
1029
  * @param skip Number of invitations to skip
645
1030
  */
646
1031
  getGroupInvitations(e, t, s, r) {
647
- const o = `${c.tenantPath}/${e}/group/${t}/invitations`;
1032
+ const o = `${h.tenantPath}/${e}/group/${t}/invitations`;
648
1033
  return this.axiosClient.get(o, {
649
1034
  params: { limit: s, skip: r }
650
1035
  });
@@ -656,7 +1041,7 @@ class B {
656
1041
  * @param skip Number of invitations to skip
657
1042
  */
658
1043
  getTenantInvitations(e, t, s) {
659
- const r = `${c.tenantPath}/${e}/invitations`;
1044
+ const r = `${h.tenantPath}/${e}/invitations`;
660
1045
  return this.axiosClient.get(r, {
661
1046
  params: { limit: t, skip: s }
662
1047
  });
@@ -668,7 +1053,7 @@ class B {
668
1053
  * @param inviteId Invitation ID
669
1054
  */
670
1055
  invalidateInviteById(e, t, s) {
671
- const r = `${c.tenantPath}/${e}/group/${t}/invite/${s}`;
1056
+ const r = `${h.tenantPath}/${e}/group/${t}/invite/${s}`;
672
1057
  return this.axiosClient.delete(r);
673
1058
  }
674
1059
  /**
@@ -678,77 +1063,224 @@ class B {
678
1063
  * @param email Email address
679
1064
  */
680
1065
  invalidateInviteByEmail(e, t, s) {
681
- const r = `${c.tenantPath}/${e}/group/${t}/invite/email/${s}`;
1066
+ const r = `${h.tenantPath}/${e}/group/${t}/invite/email/${s}`;
682
1067
  return this.axiosClient.delete(r);
683
1068
  }
684
1069
  }
685
- class W {
686
- constructor(e) {
687
- this.axiosClient = new I(e);
1070
+ class Se {
1071
+ constructor(e, t, s) {
1072
+ this.axiosClient = new T(e, t, s);
1073
+ }
1074
+ setAppId(e) {
1075
+ this.axiosClient.setAppId(e);
688
1076
  }
689
1077
  /**
690
- * Requests an invitation link that can be used to invite users
691
- * @param payload Request invitation payload
692
- * @returns Promise with invitation link and token
1078
+ * Get current 2FA enrollment status
1079
+ * GET /user/2fa/status
693
1080
  */
694
- requestInviteLink(e) {
1081
+ getStatus() {
1082
+ return this.axiosClient.get(h.twoFactorStatus);
1083
+ }
1084
+ /**
1085
+ * Begin 2FA setup process
1086
+ * POST /user/2fa/setup/begin
1087
+ * Returns secret and QR code for authenticator app
1088
+ */
1089
+ beginSetup() {
1090
+ return this.axiosClient.post(h.twoFactorSetupBegin, {});
1091
+ }
1092
+ /**
1093
+ * Confirm 2FA setup with TOTP code
1094
+ * POST /user/2fa/setup/confirm
1095
+ * Returns recovery codes on success
1096
+ */
1097
+ confirmSetup(e) {
695
1098
  return this.axiosClient.post(
696
- c.requestInvitation,
1099
+ h.twoFactorSetupConfirm,
697
1100
  e
698
1101
  );
699
1102
  }
700
1103
  /**
701
- * Gets a list of active invitations
702
- * @param options Optional parameters for filtering and pagination
703
- * @returns Promise with paginated list of invitations
1104
+ * Verify TOTP code during login
1105
+ * POST /auth/2fa/verify
1106
+ * Uses tfa_token as Bearer token for authentication
704
1107
  */
705
- getInvitations(e) {
706
- const t = {};
707
- e.groupID && (t.group_id = e.groupID.toString()), e.skip !== void 0 && (t.skip = e.skip.toString()), e.limit !== void 0 && (t.limit = e.limit.toString());
708
- const s = C(c.invitationsPath, {
709
- tenantID: e.tenantID
710
- });
711
- return this.axiosClient.get(s, { params: t }).then((r) => ({
712
- invites: r.invites,
713
- nextPageSkip: r.next_page_skip
714
- }));
1108
+ verify(e) {
1109
+ const { tfa_token: t, code: s } = e;
1110
+ return this.axiosClient.post(
1111
+ h.twoFactorVerify,
1112
+ { code: s },
1113
+ {
1114
+ headers: {
1115
+ Authorization: `Bearer ${t}`
1116
+ }
1117
+ }
1118
+ );
715
1119
  }
716
1120
  /**
717
- * Deletes an invitation by token
718
- * @param invitationID The invitation ID to delete
719
- * @returns Promise with success response
1121
+ * Use recovery code for authentication
1122
+ * POST /auth/2fa/recovery
1123
+ * Uses tfa_token as Bearer token for authentication
720
1124
  */
721
- deleteInvitation(e) {
722
- const t = C(c.invitationDelete, {
723
- invitationID: e
724
- });
725
- return this.axiosClient.delete(t);
1125
+ useRecoveryCode(e) {
1126
+ const { tfa_token: t, recovery_code: s } = e;
1127
+ return this.axiosClient.post(
1128
+ h.twoFactorRecovery,
1129
+ { recovery_code: s },
1130
+ {
1131
+ headers: {
1132
+ Authorization: `Bearer ${t}`
1133
+ }
1134
+ }
1135
+ );
726
1136
  }
727
1137
  /**
728
- * Resend an invitation by token
729
- * @param invitationID The invitation ID to resend
730
- * @returns Promise with success response
1138
+ * Disable 2FA (requires TOTP verification)
1139
+ * DELETE /user/2fa
731
1140
  */
732
- resendInvitation(e) {
733
- const t = C(c.invitationResend, {
734
- invitationID: e
1141
+ disable(e) {
1142
+ return this.axiosClient.delete(h.twoFactor, { data: e });
1143
+ }
1144
+ /**
1145
+ * Regenerate recovery codes
1146
+ * POST /user/2fa/recovery-codes/regenerate
1147
+ */
1148
+ regenerateRecoveryCodes(e) {
1149
+ return this.axiosClient.post(
1150
+ h.twoFactorRegenerateCodes,
1151
+ e
1152
+ );
1153
+ }
1154
+ /**
1155
+ * Validate magic link token for 2FA setup
1156
+ * GET /auth/2fa-setup/:token
1157
+ *
1158
+ * This endpoint validates an admin-generated magic link token
1159
+ * and returns a scoped session (scope: "2fa_setup") that can ONLY
1160
+ * be used for completing 2FA setup operations.
1161
+ *
1162
+ * This method never throws - it always returns a TwoFactorSetupMagicLinkValidationResponse
1163
+ * with either success=true and session data, or success=false and error details.
1164
+ *
1165
+ * @param token - Magic link token from URL parameter
1166
+ * @returns Validation response with scoped session token or error
1167
+ */
1168
+ validateTwoFactorSetupMagicLink(e) {
1169
+ const t = `${h.twoFactorSetupMagicLink}/${e}`;
1170
+ return this.axiosClient.get(t, {
1171
+ // Override default auth headers (this is a public endpoint)
1172
+ transformRequest: [
1173
+ (s, r) => (r && delete r.Authorization, s)
1174
+ ]
1175
+ }).then((s) => ({
1176
+ success: !0,
1177
+ sessionToken: s.session_token,
1178
+ userId: s.user_id,
1179
+ expiresIn: s.expires_in,
1180
+ appId: s.app_id
1181
+ })).catch((s) => {
1182
+ if (s.response) {
1183
+ const r = s.response.status, o = s.response.data || {}, n = s.response.headers?.["retry-after"] ? parseInt(s.response.headers["retry-after"], 10) : void 0;
1184
+ return {
1185
+ success: !1,
1186
+ error: {
1187
+ code: o.error || this.mapStatusToErrorCode(r),
1188
+ message: o.message || this.getDefaultErrorMessage(r),
1189
+ retryAfter: n
1190
+ }
1191
+ };
1192
+ }
1193
+ return {
1194
+ success: !1,
1195
+ error: {
1196
+ code: "SERVER_ERROR",
1197
+ message: s instanceof Error ? s.message : "Unable to connect to the server. Please check your connection."
1198
+ }
1199
+ };
735
1200
  });
736
- return this.axiosClient.post(t, {});
737
1201
  }
738
1202
  /**
739
- * Get a link to an invitation by id
740
- * @param invitationID The invitation ID to get link
741
- * @returns Promise with the link
1203
+ * Map HTTP status code to magic link error code
742
1204
  */
743
- getInvitationLink(e) {
744
- const t = C(c.invitationGetLink, {
745
- invitationID: e
1205
+ mapStatusToErrorCode(e) {
1206
+ switch (e) {
1207
+ case 400:
1208
+ return "INVALID_TOKEN";
1209
+ case 404:
1210
+ return "REVOKED_TOKEN";
1211
+ case 410:
1212
+ return "EXPIRED_TOKEN";
1213
+ case 429:
1214
+ return "RATE_LIMITED";
1215
+ default:
1216
+ return "SERVER_ERROR";
1217
+ }
1218
+ }
1219
+ /**
1220
+ * Get default error message for HTTP status code
1221
+ */
1222
+ getDefaultErrorMessage(e) {
1223
+ switch (e) {
1224
+ case 400:
1225
+ return "The provided magic link is invalid or malformed.";
1226
+ case 404:
1227
+ return "This magic link has been revoked or does not exist.";
1228
+ case 410:
1229
+ return "This magic link has expired. Please request a new one from your administrator.";
1230
+ case 429:
1231
+ return "Too many validation attempts. Please try again later.";
1232
+ default:
1233
+ return "An error occurred while validating the magic link.";
1234
+ }
1235
+ }
1236
+ }
1237
+ class me {
1238
+ constructor(e, t, s) {
1239
+ this.axiosClient = new T(e, t, s);
1240
+ }
1241
+ setAppId(e) {
1242
+ this.axiosClient.setAppId(e);
1243
+ }
1244
+ getUserPasskeys() {
1245
+ return this.axiosClient.get(h.userPasskey);
1246
+ }
1247
+ renameUserPasskey(e, t) {
1248
+ return this.axiosClient.patch(
1249
+ `${h.userPasskey}/${t}`,
1250
+ {
1251
+ name: e
1252
+ }
1253
+ );
1254
+ }
1255
+ deleteUserPasskey(e) {
1256
+ return this.axiosClient.delete(`${h.userPasskey}/${e}`);
1257
+ }
1258
+ addUserPasskeyStart({
1259
+ relyingPartyId: e,
1260
+ deviceId: t,
1261
+ os: s,
1262
+ passkeyDisplayName: r,
1263
+ passkeyUsername: o
1264
+ }) {
1265
+ const n = {
1266
+ passkey_display_name: r,
1267
+ passkey_username: o,
1268
+ relying_party_id: e,
1269
+ deviceId: t,
1270
+ os: s
1271
+ };
1272
+ return this.axiosClient.post(h.addUserPasskey, n);
1273
+ }
1274
+ addUserPasskeyComplete(e, t, s) {
1275
+ return this.axiosClient.post(h.completeAddUserPasskey, {
1276
+ challenge_id: s,
1277
+ device: t,
1278
+ passkey_data: e
746
1279
  });
747
- return this.axiosClient.get(t);
748
1280
  }
749
1281
  }
750
- var a = /* @__PURE__ */ ((i) => (i.SignIn = "signin", i.SignInStart = "signin:start", i.Register = "register", i.RegisterStart = "register:start", i.SignOut = "signout", i.Error = "error", i.Refresh = "refresh", i.RefreshStart = "refresh:start", i.TokenCacheExpired = "token-cache-expired", i))(a || {});
751
- class H {
1282
+ var a = /* @__PURE__ */ ((i) => (i.SignIn = "signin", i.SignInStart = "signin:start", i.Register = "register", i.RegisterStart = "register:start", i.SignOut = "signout", i.SessionRestored = "session:restored", i.Error = "error", i.Refresh = "refresh", i.RefreshStart = "refresh:start", i.TokenCacheExpired = "token-cache-expired", i.TwoFactorRequired = "2fa:required", i.TwoFactorSetupStarted = "2fa:setup_started", i.TwoFactorEnabled = "2fa:enabled", i.TwoFactorDisabled = "2fa:disabled", i.TwoFactorVerified = "2fa:verified", i.TwoFactorRecoveryUsed = "2fa:recovery_used", i.TwoFactorRecoveryCodesLow = "2fa:recovery_low", i.TwoFactorRecoveryCodesExhausted = "2fa:recovery_exhausted", i.TwoFactorSetupMagicLinkValidated = "2fa:magic_link_validated", i.TwoFactorSetupMagicLinkFailed = "2fa:magic_link_failed", i))(a || {});
1283
+ class we {
752
1284
  constructor() {
753
1285
  this.subscribers = /* @__PURE__ */ new Map();
754
1286
  }
@@ -788,20 +1320,156 @@ class H {
788
1320
  });
789
1321
  }
790
1322
  }
791
- class q {
792
- constructor(e, t, s, r, o, n, h, l, p, v, k) {
793
- this.authApi = e, this.deviceService = t, this.storageManager = s, this.subscribeStore = r, this.tokenCacheService = o, this.scopes = n, this.createTenantForNewUser = h, this.origin = l, this.url = p, this.sessionCallbacks = v, this.appId = k;
1323
+ function F(i) {
1324
+ if (!i || typeof i != "string") return !1;
1325
+ const e = i.split(".");
1326
+ if (e.length !== 3) return !1;
1327
+ const t = /^[A-Za-z0-9_-]+$/;
1328
+ return e.every((s) => t.test(s) && s.length > 0);
1329
+ }
1330
+ function Te(i) {
1331
+ return i.replace(/<[^>]*>/g, "").substring(0, ne);
1332
+ }
1333
+ function M(i) {
1334
+ if (!i || typeof i != "string") return !1;
1335
+ const e = i.trim();
1336
+ return e.length === 0 ? !1 : /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e);
1337
+ }
1338
+ function x(i) {
1339
+ if (!i || typeof i != "string") return !1;
1340
+ const e = i.trim();
1341
+ return /^\+[1-9]\d{1,14}$/.test(e);
1342
+ }
1343
+ function Ee(i) {
1344
+ if (!i || typeof i != "string") return !1;
1345
+ const e = i.trim();
1346
+ return e.length < ie || e.length > oe ? !1 : /^[a-zA-Z0-9_-]+$/.test(e);
1347
+ }
1348
+ function R(i, e = 6) {
1349
+ return !i || typeof i != "string" ? !1 : (e === 8 ? /^\d{8}$/ : /^\d{6}$/).test(i);
1350
+ }
1351
+ function _e(i) {
1352
+ if (!i || typeof i != "string") return null;
1353
+ const e = i.toUpperCase().replace(/\s+/g, "");
1354
+ return /^[A-Z0-9-]{4,16}$/.test(e) ? e : null;
1355
+ }
1356
+ class Ie {
1357
+ constructor(e, t, s, r, o, n, d, c, g, p, f, b) {
1358
+ this.authApi = e, this.deviceService = t, this.storageManager = s, this.subscribeStore = r, this.tokenCacheService = o, this.scopes = n, this.createTenantForNewUser = d, this.origin = c, this.url = g, this.sessionCallbacks = p, this.appId = f, this.tokenExchangeConfig = b, this.tokenDeliveryManager = new B(s), b?.enabled && this.tokenDeliveryManager.setMode(v.BFF), this.initializeSession();
1359
+ }
1360
+ /**
1361
+ * Initialize session state on page load for cookie/BFF mode
1362
+ */
1363
+ async initializeSession() {
1364
+ (this.tokenDeliveryManager.isCookieMode() || this.tokenDeliveryManager.isBFFMode()) && await this.restoreSession();
1365
+ }
1366
+ /**
1367
+ * Restore session for cookie/BFF mode on page load
1368
+ * Validates that HttpOnly cookies are still valid
1369
+ * @returns true if session is valid, false otherwise
1370
+ */
1371
+ async restoreSession() {
1372
+ if (this.tokenDeliveryManager.isBFFMode() && this.tokenExchangeConfig?.statusUrl)
1373
+ try {
1374
+ const e = await fetch(this.tokenExchangeConfig.statusUrl, {
1375
+ method: "GET",
1376
+ credentials: "include"
1377
+ // Include httpOnly cookies
1378
+ });
1379
+ return e.ok && (await e.json()).authenticated ? (this.tokenDeliveryManager.setSessionValid(), !0) : (this.tokenDeliveryManager.setSessionInvalid(), !1);
1380
+ } catch {
1381
+ return this.tokenDeliveryManager.setSessionInvalid(), !1;
1382
+ }
1383
+ if (!this.tokenDeliveryManager.isCookieMode())
1384
+ return !1;
1385
+ try {
1386
+ const e = await this.authApi.validateSession();
1387
+ return e.valid ? (this.tokenDeliveryManager.setSessionValid(), e.user && this.subscribeStore.notify(a.SessionRestored, e.user), !0) : (this.tokenDeliveryManager.setSessionInvalid(), !1);
1388
+ } catch {
1389
+ return this.tokenDeliveryManager.setSessionInvalid(), !1;
1390
+ }
1391
+ }
1392
+ /**
1393
+ * Process successful authentication response
1394
+ * Handles token storage, session state, CSRF tokens
1395
+ * In BFF mode, forwards tokens to BFF server
1396
+ */
1397
+ async processAuthResponse(e, t) {
1398
+ this.tokenExchangeConfig?.enabled || "token_delivery" in e && e.token_delivery && this.tokenDeliveryManager.setMode(e.token_delivery), this.tokenDeliveryManager.setSessionValid(), this.tokenDeliveryManager.isBFFMode() && this.tokenExchangeConfig?.callbackUrl && await this.forwardTokensToBFF(e), e.scopes = t, this.storageManager.saveTokens(e, this.tokenDeliveryManager.getMode()), this.tokenCacheService.setTokensCache(e), e.csrf_token && this.storageManager.setCsrfToken(e.csrf_token);
1399
+ }
1400
+ /**
1401
+ * Forward tokens to BFF server for httpOnly cookie storage
1402
+ */
1403
+ async forwardTokensToBFF(e) {
1404
+ if (!this.tokenExchangeConfig?.callbackUrl) {
1405
+ console.warn("[Passflow SDK] BFF mode enabled but callbackUrl not configured");
1406
+ return;
1407
+ }
1408
+ try {
1409
+ const t = await fetch(this.tokenExchangeConfig.callbackUrl, {
1410
+ method: "POST",
1411
+ credentials: "include",
1412
+ // Include/set httpOnly cookies
1413
+ headers: {
1414
+ "Content-Type": "application/json"
1415
+ },
1416
+ body: JSON.stringify({
1417
+ access_token: e.access_token,
1418
+ refresh_token: e.refresh_token,
1419
+ id_token: e.id_token,
1420
+ // expires_in is returned by the server but not typed in the SDK
1421
+ expires_in: e.expires_in
1422
+ })
1423
+ });
1424
+ if (!t.ok) {
1425
+ const s = await t.text();
1426
+ throw console.error("[Passflow SDK] Failed to forward tokens to BFF:", s), new Error(`BFF token storage failed: ${t.status}`);
1427
+ }
1428
+ console.log("[Passflow SDK] Tokens forwarded to BFF successfully");
1429
+ } catch (t) {
1430
+ throw console.error("[Passflow SDK] Error forwarding tokens to BFF:", t), t;
1431
+ }
794
1432
  }
795
1433
  async signIn(e) {
1434
+ if ("email" in e && e.email && !M(e.email)) {
1435
+ const r = new Error("Invalid email format"), o = {
1436
+ message: "Invalid email format",
1437
+ originalError: r,
1438
+ code: "VALIDATION_ERROR"
1439
+ };
1440
+ throw this.subscribeStore.notify(a.Error, o), r;
1441
+ }
1442
+ if ("username" in e && e.username && !Ee(e.username)) {
1443
+ const r = new Error(
1444
+ "Invalid username format. Username must be 3-30 characters and contain only letters, numbers, underscores, and hyphens"
1445
+ ), o = {
1446
+ message: "Invalid username format. Username must be 3-30 characters and contain only letters, numbers, underscores, and hyphens",
1447
+ originalError: r,
1448
+ code: "VALIDATION_ERROR"
1449
+ };
1450
+ throw this.subscribeStore.notify(a.Error, o), r;
1451
+ }
1452
+ if ("phone" in e && e.phone && !x(e.phone)) {
1453
+ const r = new Error("Invalid phone number format. Phone must be in E.164 format (e.g., +12345678901)"), o = {
1454
+ message: "Invalid phone number format. Phone must be in E.164 format (e.g., +12345678901)",
1455
+ originalError: r,
1456
+ code: "VALIDATION_ERROR"
1457
+ };
1458
+ throw this.subscribeStore.notify(a.Error, o), r;
1459
+ }
796
1460
  this.subscribeStore.notify(a.SignInStart, { email: e.email });
797
- const t = this.deviceService.getDeviceId(), s = w.web;
1461
+ const t = this.deviceService.getDeviceId(), s = I.web;
798
1462
  e.scopes = e.scopes ?? this.scopes;
799
1463
  try {
800
1464
  const r = await this.authApi.signIn(e, t, s);
801
- return r.scopes = e.scopes, this.storageManager.saveTokens(r), this.tokenCacheService.setTokensCache(r), this.subscribeStore.notify(a.SignIn, {
1465
+ return "requires_2fa" in r && r.requires_2fa === !0 || "tfa_token" in r && r.tfa_token ? (this.subscribeStore.notify(a.TwoFactorRequired, {
1466
+ email: e.email || "",
1467
+ challengeId: r.challenge_id || "",
1468
+ tfaToken: r.tfa_token || ""
1469
+ }), r) : (await this.processAuthResponse(r, e.scopes), this.subscribeStore.notify(a.SignIn, {
802
1470
  tokens: r,
803
- parsedTokens: this.tokenCacheService.getParsedTokenCache()
804
- }), await this.submitSessionCheck(), r;
1471
+ parsedTokens: this.tokenCacheService.getParsedTokens()
1472
+ }), await this.submitSessionCheck(), r);
805
1473
  } catch (r) {
806
1474
  const o = {
807
1475
  message: r instanceof Error ? r.message : "Sign in failed",
@@ -812,12 +1480,28 @@ class q {
812
1480
  }
813
1481
  }
814
1482
  async signUp(e) {
1483
+ if (e.user.email && !M(e.user.email)) {
1484
+ const t = new Error("Invalid email format"), s = {
1485
+ message: "Invalid email format",
1486
+ originalError: t,
1487
+ code: "VALIDATION_ERROR"
1488
+ };
1489
+ throw this.subscribeStore.notify(a.Error, s), t;
1490
+ }
1491
+ if (e.user.phone_number && !x(e.user.phone_number)) {
1492
+ const t = new Error("Invalid phone number format. Phone must be in E.164 format (e.g., +12345678901)"), s = {
1493
+ message: "Invalid phone number format. Phone must be in E.164 format (e.g., +12345678901)",
1494
+ originalError: t,
1495
+ code: "VALIDATION_ERROR"
1496
+ };
1497
+ throw this.subscribeStore.notify(a.Error, s), t;
1498
+ }
815
1499
  this.subscribeStore.notify(a.RegisterStart, { email: e.user.email }), e.scopes = e.scopes ?? this.scopes, e.create_tenant = this.createTenantForNewUser;
816
1500
  try {
817
1501
  const t = await this.authApi.signUp(e);
818
- return t.scopes = e.scopes, this.storageManager.saveTokens(t), this.tokenCacheService.setTokensCache(t), this.subscribeStore.notify(a.Register, {
1502
+ return await this.processAuthResponse(t, e.scopes), this.subscribeStore.notify(a.Register, {
819
1503
  tokens: t,
820
- parsedTokens: this.tokenCacheService.getParsedTokenCache()
1504
+ parsedTokens: this.tokenCacheService.getParsedTokens()
821
1505
  }), await this.submitSessionCheck(), t;
822
1506
  } catch (t) {
823
1507
  const s = {
@@ -829,8 +1513,24 @@ class q {
829
1513
  }
830
1514
  }
831
1515
  async passwordlessSignIn(e) {
1516
+ if (e.email && !M(e.email)) {
1517
+ const r = new Error("Invalid email format"), o = {
1518
+ message: "Invalid email format",
1519
+ originalError: r,
1520
+ code: "VALIDATION_ERROR"
1521
+ };
1522
+ throw this.subscribeStore.notify(a.Error, o), r;
1523
+ }
1524
+ if (e.phone && !x(e.phone)) {
1525
+ const r = new Error("Invalid phone number format. Phone must be in E.164 format (e.g., +12345678901)"), o = {
1526
+ message: "Invalid phone number format. Phone must be in E.164 format (e.g., +12345678901)",
1527
+ originalError: r,
1528
+ code: "VALIDATION_ERROR"
1529
+ };
1530
+ throw this.subscribeStore.notify(a.Error, o), r;
1531
+ }
832
1532
  this.subscribeStore.notify(a.SignInStart, { email: e.email }), e.scopes = e.scopes ?? this.scopes;
833
- const t = this.deviceService.getDeviceId(), s = w.web;
1533
+ const t = this.deviceService.getDeviceId(), s = I.web;
834
1534
  try {
835
1535
  return await this.authApi.passwordlessSignIn(e, t, s);
836
1536
  } catch (r) {
@@ -846,9 +1546,9 @@ class q {
846
1546
  this.subscribeStore.notify(a.SignInStart, {}), e.scopes = e.scopes ?? this.scopes, e.device = this.deviceService.getDeviceId();
847
1547
  try {
848
1548
  const t = await this.authApi.passwordlessSignInComplete(e);
849
- return t.scopes = e.scopes, this.storageManager.saveTokens(t), this.tokenCacheService.setTokensCache(t), this.subscribeStore.notify(a.SignIn, {
1549
+ return await this.processAuthResponse(t, e.scopes), this.subscribeStore.notify(a.SignIn, {
850
1550
  tokens: t,
851
- parsedTokens: this.tokenCacheService.getParsedTokenCache()
1551
+ parsedTokens: this.tokenCacheService.getParsedTokens()
852
1552
  }), await this.submitSessionCheck(), t;
853
1553
  } catch (t) {
854
1554
  const s = {
@@ -860,17 +1560,67 @@ class q {
860
1560
  }
861
1561
  }
862
1562
  async logOut() {
863
- const e = this.storageManager.getToken(g.refresh_token), t = this.storageManager.getDeviceId();
864
- try {
865
- if ((await this.authApi.logOut(t, e, !this.appId)).status !== "ok")
866
- throw new Error("Logout failed");
867
- this.storageManager.deleteTokens(), this.subscribeStore.notify(a.SignOut, {});
868
- } catch (s) {
869
- throw console.error(s), s;
1563
+ if (this.tokenDeliveryManager.isBFFMode() && this.tokenExchangeConfig?.logoutUrl)
1564
+ try {
1565
+ const e = await fetch(this.tokenExchangeConfig.logoutUrl, {
1566
+ method: "POST",
1567
+ credentials: "include"
1568
+ // Include httpOnly cookies
1569
+ });
1570
+ e.ok || console.warn("[Passflow SDK] BFF logout failed:", await e.text());
1571
+ } catch (e) {
1572
+ console.warn("[Passflow SDK] BFF logout error:", e);
1573
+ }
1574
+ else {
1575
+ const e = this.storageManager.getToken(k.refresh_token), t = this.storageManager.getDeviceId();
1576
+ try {
1577
+ if ((await this.authApi.logOut(t, e, !this.appId)).status !== "ok")
1578
+ throw new Error("Logout failed");
1579
+ } catch (s) {
1580
+ console.warn("[Passflow SDK] Logout API failed, clearing local state anyway:", s);
1581
+ }
870
1582
  }
1583
+ this.storageManager.deleteTokens(), this.storageManager.clearIdToken(), this.storageManager.clearCsrfToken(), this.tokenDeliveryManager.reset(), this.subscribeStore.notify(a.SignOut, {});
871
1584
  }
872
1585
  async refreshToken() {
873
- this.subscribeStore.notify(a.RefreshStart, {});
1586
+ if (this.subscribeStore.notify(a.RefreshStart, {}), this.tokenDeliveryManager.isBFFMode() && this.tokenExchangeConfig?.refreshUrl)
1587
+ try {
1588
+ const s = await fetch(this.tokenExchangeConfig.refreshUrl, {
1589
+ method: "POST",
1590
+ credentials: "include"
1591
+ // Include httpOnly cookies
1592
+ });
1593
+ if (!s.ok)
1594
+ throw this.tokenDeliveryManager.setSessionInvalid(), new Error("BFF token refresh failed");
1595
+ const r = await s.json();
1596
+ return this.tokenDeliveryManager.setSessionValid(), r.id_token && this.storageManager.setIdToken(r.id_token), this.subscribeStore.notify(a.Refresh, {
1597
+ tokens: r,
1598
+ parsedTokens: this.tokenCacheService.getParsedTokens()
1599
+ }), this.subscribeStore.notify(a.TokenCacheExpired, { isExpired: !1 }), this.tokenCacheService.isRefreshing = !1, this.tokenCacheService.tokenExpiredFlag = !1, r;
1600
+ } catch (s) {
1601
+ this.tokenDeliveryManager.setSessionInvalid();
1602
+ const r = {
1603
+ message: s instanceof Error ? s.message : "Token refresh failed",
1604
+ originalError: s
1605
+ };
1606
+ throw this.subscribeStore.notify(a.Error, r), s;
1607
+ }
1608
+ if (this.tokenDeliveryManager.isCookieMode())
1609
+ try {
1610
+ const s = await this.authApi.refreshToken("", this.scopes);
1611
+ return this.tokenDeliveryManager.setSessionValid(), await this.processAuthResponse(s, this.scopes), this.subscribeStore.notify(a.Refresh, {
1612
+ tokens: s,
1613
+ parsedTokens: this.tokenCacheService.getParsedTokens()
1614
+ }), this.subscribeStore.notify(a.TokenCacheExpired, { isExpired: !1 }), this.tokenCacheService.isRefreshing = !1, this.tokenCacheService.tokenExpiredFlag = !1, s;
1615
+ } catch (s) {
1616
+ this.tokenDeliveryManager.setSessionInvalid();
1617
+ const r = {
1618
+ message: s instanceof Error ? s.message : "Token refresh failed",
1619
+ originalError: s,
1620
+ code: s instanceof u ? s.id : void 0
1621
+ };
1622
+ throw this.subscribeStore.notify(a.Error, r), s;
1623
+ }
874
1624
  const e = this.storageManager.getTokens();
875
1625
  if (e) {
876
1626
  if (!e?.refresh_token) {
@@ -892,19 +1642,19 @@ class q {
892
1642
  const s = await this.authApi.refreshToken(e?.refresh_token ?? "", t, e?.access_token);
893
1643
  return s.scopes = t, this.storageManager.saveTokens(s), this.tokenCacheService.setTokensCache(s), this.subscribeStore.notify(a.Refresh, {
894
1644
  tokens: s,
895
- parsedTokens: this.tokenCacheService.getParsedTokenCache()
896
- }), this.subscribeStore.notify(a.TokenCacheExpired, { isExpired: !1 }), this.tokenCacheService.isRefreshing = !1, this.tokenCacheService.isExpired = !1, this.tokenCacheService.startTokenCheck(), s;
1645
+ parsedTokens: this.tokenCacheService.getParsedTokens()
1646
+ }), this.subscribeStore.notify(a.TokenCacheExpired, { isExpired: !1 }), this.tokenCacheService.isRefreshing = !1, this.tokenCacheService.tokenExpiredFlag = !1, this.tokenCacheService.startTokenCheck(), s;
897
1647
  } catch (s) {
898
1648
  const r = {
899
1649
  message: s instanceof Error ? s.message : "Token refresh failed",
900
1650
  originalError: s,
901
1651
  code: s instanceof u ? s.id : void 0,
902
- details: T.isAxiosError(s) && s.response ? {
1652
+ details: D.isAxiosError(s) && s.response ? {
903
1653
  status: s.response.status,
904
1654
  data: s.response.data
905
1655
  } : void 0
906
1656
  };
907
- throw this.subscribeStore.notify(a.Error, r), s instanceof u ? s : T.isAxiosError(s) && s.response && s.response?.status >= 400 && s.response?.status < 500 ? new Error(`Getting unknown error message from server with code:${s.response.status}`) : s;
1657
+ throw this.subscribeStore.notify(a.Error, r), s instanceof u ? s : D.isAxiosError(s) && s.response && s.response?.status >= 400 && s.response?.status < 500 ? new Error(`Getting unknown error message from server with code:${s.response.status}`) : s;
908
1658
  }
909
1659
  }
910
1660
  async sendPasswordResetEmail(e) {
@@ -924,38 +1674,38 @@ class q {
924
1674
  const r = new URLSearchParams(window.location.search).get("token") ?? void 0, o = t ?? this.scopes;
925
1675
  try {
926
1676
  const n = await this.authApi.resetPassword(e, o, r);
927
- return n.scopes = o, this.storageManager.saveTokens(n), this.tokenCacheService.setTokensCache(n), this.subscribeStore.notify(a.SignIn, {
1677
+ return await this.processAuthResponse(n, o), this.subscribeStore.notify(a.SignIn, {
928
1678
  tokens: n,
929
- parsedTokens: this.tokenCacheService.getParsedTokenCache()
1679
+ parsedTokens: this.tokenCacheService.getParsedTokens()
930
1680
  }), await this.submitSessionCheck(), n;
931
1681
  } catch (n) {
932
- const h = {
1682
+ const d = {
933
1683
  message: n instanceof Error ? n.message : "Password reset failed",
934
1684
  originalError: n,
935
1685
  code: n instanceof u ? n.id : void 0
936
1686
  };
937
- throw this.subscribeStore.notify(a.Error, h), n;
1687
+ throw this.subscribeStore.notify(a.Error, d), n;
938
1688
  }
939
1689
  }
940
1690
  async passkeyRegister(e) {
941
1691
  this.subscribeStore.notify(a.RegisterStart, {});
942
- const t = this.deviceService.getDeviceId(), s = w.web;
1692
+ const t = this.deviceService.getDeviceId(), s = I.web;
943
1693
  e.scopes = e.scopes ?? this.scopes, e.create_tenant = this.createTenantForNewUser;
944
1694
  try {
945
1695
  const { challenge_id: r, publicKey: o } = await this.authApi.passkeyRegisterStart(e, t, s, !this.appId);
946
1696
  o.user.id = btoa(o.user.id);
947
- const n = await _({
1697
+ const n = await K({
948
1698
  optionsJSON: o
949
- }), h = await this.authApi.passkeyRegisterComplete(
1699
+ }), d = await this.authApi.passkeyRegisterComplete(
950
1700
  n,
951
1701
  t,
952
1702
  r,
953
1703
  !this.appId
954
1704
  );
955
- return h.scopes = e.scopes, this.storageManager.saveTokens(h), this.tokenCacheService.setTokensCache(h), this.subscribeStore.notify(a.Register, {
956
- tokens: h,
957
- parsedTokens: this.tokenCacheService.getParsedTokenCache()
958
- }), await this.submitSessionCheck(), h;
1705
+ return await this.processAuthResponse(d, e.scopes), this.subscribeStore.notify(a.Register, {
1706
+ tokens: d,
1707
+ parsedTokens: this.tokenCacheService.getParsedTokens()
1708
+ }), await this.submitSessionCheck(), d;
959
1709
  } catch (r) {
960
1710
  const o = {
961
1711
  message: r instanceof Error ? r.message : "Passkey registration failed",
@@ -967,21 +1717,21 @@ class q {
967
1717
  }
968
1718
  async passkeyAuthenticate(e) {
969
1719
  this.subscribeStore.notify(a.SignInStart, {});
970
- const t = this.deviceService.getDeviceId(), s = w.web;
1720
+ const t = this.deviceService.getDeviceId(), s = I.web;
971
1721
  e.scopes = e.scopes ?? this.scopes;
972
1722
  try {
973
- const { challenge_id: r, publicKey: o } = await this.authApi.passkeyAuthenticateStart(e, t, s, !this.appId), n = await U({
1723
+ const { challenge_id: r, publicKey: o } = await this.authApi.passkeyAuthenticateStart(e, t, s, !this.appId), n = await j({
974
1724
  optionsJSON: o
975
- }), h = await this.authApi.passkeyAuthenticateComplete(
1725
+ }), d = await this.authApi.passkeyAuthenticateComplete(
976
1726
  n,
977
1727
  t,
978
1728
  r,
979
1729
  !this.appId
980
1730
  );
981
- return "access_token" in h && (h.scopes = e.scopes, this.storageManager.saveTokens(h), this.tokenCacheService.setTokensCache(h), this.subscribeStore.notify(a.SignIn, {
982
- tokens: h,
983
- parsedTokens: this.tokenCacheService.getParsedTokenCache()
984
- }), await this.submitSessionCheck()), h;
1731
+ return "access_token" in d && (await this.processAuthResponse(d, e.scopes), this.subscribeStore.notify(a.SignIn, {
1732
+ tokens: d,
1733
+ parsedTokens: this.tokenCacheService.getParsedTokens()
1734
+ }), await this.submitSessionCheck()), d;
985
1735
  } catch (r) {
986
1736
  const o = {
987
1737
  message: r instanceof Error ? r.message : "Passkey authentication failed",
@@ -1006,25 +1756,48 @@ class q {
1006
1756
  }
1007
1757
  federatedAuthWithPopup(e) {
1008
1758
  this.subscribeStore.notify(a.SignInStart, { provider: e.provider });
1009
- const t = e.scopes ?? this.scopes, s = this.deviceService.getDeviceId(), r = this.createFederatedAuthUrl({ ...e, scopes: t, device: s }), o = window.open(r, "_blank", "width=500,height=500");
1010
- if (!o)
1759
+ const t = e.scopes ?? this.scopes, s = this.deviceService.getDeviceId(), r = this.createFederatedAuthUrl({ ...e, scopes: t, device: s }), o = window.open(r, "_blank", `width=${ee},height=${te}`);
1760
+ if (!o) {
1011
1761
  this.federatedAuthWithRedirect(e);
1012
- else {
1013
- const n = setInterval(() => {
1762
+ return;
1763
+ }
1764
+ const n = Date.now(), d = setInterval(() => {
1765
+ if (o.closed) {
1766
+ clearInterval(d);
1767
+ const c = {
1768
+ message: "Authentication popup was closed",
1769
+ code: "POPUP_CLOSED"
1770
+ };
1771
+ this.subscribeStore.notify(a.Error, c);
1772
+ return;
1773
+ }
1774
+ if (Date.now() - n > re) {
1775
+ clearInterval(d), o.close();
1776
+ const c = {
1777
+ message: "Authentication popup timed out",
1778
+ code: "POPUP_TIMEOUT"
1779
+ };
1780
+ this.subscribeStore.notify(a.Error, c);
1781
+ return;
1782
+ }
1783
+ try {
1014
1784
  if (o.location.href.startsWith(this.origin)) {
1015
- const h = new URLSearchParams(o.location.search), l = h.get("access_token") || "", p = h.get("refresh_token") || "", v = h.get("id_token") || "", k = {
1016
- access_token: l,
1017
- refresh_token: p,
1018
- id_token: v,
1785
+ const c = new URLSearchParams(o.location.search), g = c.get("access_token") || "", p = c.get("refresh_token") || "", f = c.get("id_token") || "", b = {
1786
+ access_token: g,
1787
+ refresh_token: p || void 0,
1788
+ id_token: f || void 0,
1019
1789
  scopes: t
1020
1790
  };
1021
- this.storageManager.saveTokens(k), this.tokenCacheService.setTokensCache(k), this.subscribeStore.notify(a.SignIn, {
1022
- tokens: k,
1023
- parsedTokens: this.tokenCacheService.getParsedTokenCache()
1024
- }), window.location.href = `${this.origin}`, clearInterval(n), o.close();
1791
+ this.processAuthResponse(b, t).then(() => {
1792
+ this.subscribeStore.notify(a.SignIn, {
1793
+ tokens: b,
1794
+ parsedTokens: this.tokenCacheService.getParsedTokens()
1795
+ }), window.location.href = `${this.origin}`;
1796
+ }), clearInterval(d), o.close();
1025
1797
  }
1026
- }, 100);
1027
- }
1798
+ } catch {
1799
+ }
1800
+ }, se);
1028
1801
  }
1029
1802
  federatedAuthWithRedirect(e) {
1030
1803
  this.subscribeStore.notify(a.SignInStart, { provider: e.provider });
@@ -1036,12 +1809,12 @@ class q {
1036
1809
  try {
1037
1810
  const { url: t, redirectUrl: s, scopes: r, appId: o } = e ?? {}, n = new URL(t ?? this.url);
1038
1811
  n.pathname = (n.pathname.endsWith("/") ? n.pathname : n.pathname + "/") + "web";
1039
- const h = r ?? this.scopes, l = {
1812
+ const d = r ?? this.scopes, c = {
1040
1813
  appId: o ?? this.appId ?? "",
1041
1814
  redirectto: s ?? window.location.href,
1042
- scopes: h.join(",")
1043
- }, p = new URLSearchParams(l);
1044
- return n.search = p.toString(), n.toString();
1815
+ scopes: d.join(",")
1816
+ }, g = new URLSearchParams(c);
1817
+ return n.search = g.toString(), n.toString();
1045
1818
  } catch (t) {
1046
1819
  const s = {
1047
1820
  message: t instanceof Error ? t.message : "Failed to create auth redirect URL",
@@ -1063,10 +1836,15 @@ class q {
1063
1836
  }
1064
1837
  /**
1065
1838
  * Check if user is authenticated
1839
+ * CRITICAL: Cookie/BFF mode checks ID token + session state, NOT access_token
1066
1840
  */
1067
1841
  isAuthenticated(e) {
1068
1842
  try {
1069
- return e ? !f(e.access_token) || !!e.refresh_token && !f(e.refresh_token) : !1;
1843
+ if (this.tokenDeliveryManager.isCookieMode() || this.tokenDeliveryManager.isBFFMode()) {
1844
+ const t = !!e?.id_token || !!this.storageManager.getIdToken(), s = this.tokenDeliveryManager.isSessionValid(), r = this.tokenDeliveryManager.isSessionUnknown();
1845
+ return t && (s || r);
1846
+ }
1847
+ return !e || !e.access_token ? !1 : !S(e.access_token) || e.refresh_token !== void 0 && !S(e.refresh_token);
1070
1848
  } catch (t) {
1071
1849
  const s = {
1072
1850
  message: t instanceof Error ? t.message : "Failed to check authentication status",
@@ -1081,7 +1859,7 @@ class q {
1081
1859
  async submitSessionCheck(e = !1) {
1082
1860
  let t, s;
1083
1861
  try {
1084
- t = await this.getTokens(e), s = this.tokenCacheService.getParsedTokenCache();
1862
+ t = await this.getTokens(e), s = this.tokenCacheService.getParsedTokens();
1085
1863
  } catch (r) {
1086
1864
  const o = {
1087
1865
  message: r instanceof Error || r instanceof u ? r.message : "Session check failed",
@@ -1093,13 +1871,19 @@ class q {
1093
1871
  }
1094
1872
  /**
1095
1873
  * Get tokens and refresh if needed
1874
+ * Cookie/BFF mode: Returns ID token only (access/refresh in HttpOnly cookies)
1875
+ * JSON mode: Returns all tokens from localStorage
1096
1876
  */
1097
1877
  async getTokens(e) {
1098
1878
  try {
1879
+ if (this.tokenDeliveryManager.isCookieMode() || this.tokenDeliveryManager.isBFFMode()) {
1880
+ const r = this.storageManager.getTokens();
1881
+ return r?.id_token ? this.tokenDeliveryManager.isSessionInvalid() && e ? await this.refreshToken() : r : void 0;
1882
+ }
1099
1883
  const t = this.storageManager.getTokens();
1100
1884
  if (!t || !t.access_token) return;
1101
- const s = d(t.access_token);
1102
- return f(s) ? e ? await this.refreshToken() : void 0 : t;
1885
+ const s = y(t.access_token);
1886
+ return S(s) ? e ? await this.refreshToken() : void 0 : t;
1103
1887
  } catch (t) {
1104
1888
  const s = {
1105
1889
  message: t instanceof Error ? t.message : "Failed to get tokens",
@@ -1110,9 +1894,9 @@ class q {
1110
1894
  }
1111
1895
  }
1112
1896
  }
1113
- class V {
1897
+ class be {
1114
1898
  constructor(e) {
1115
- this.invitationAPI = e;
1899
+ this.invitationApi = e;
1116
1900
  }
1117
1901
  /**
1118
1902
  * Requests an invitation link that can be used to invite users
@@ -1120,7 +1904,7 @@ class V {
1120
1904
  * @returns Promise with invitation link and token
1121
1905
  */
1122
1906
  requestInviteLink(e) {
1123
- return this.invitationAPI.requestInviteLink(e);
1907
+ return this.invitationApi.requestInviteLink(e);
1124
1908
  }
1125
1909
  /**
1126
1910
  * Gets a list of active invitations
@@ -1128,7 +1912,7 @@ class V {
1128
1912
  * @returns Promise with paginated list of invitations
1129
1913
  */
1130
1914
  getInvitations(e) {
1131
- return this.invitationAPI.getInvitations(e);
1915
+ return this.invitationApi.getInvitations(e);
1132
1916
  }
1133
1917
  /**
1134
1918
  * Deletes an invitation by token
@@ -1136,7 +1920,7 @@ class V {
1136
1920
  * @returns Promise with success response
1137
1921
  */
1138
1922
  deleteInvitation(e) {
1139
- return this.invitationAPI.deleteInvitation(e);
1923
+ return this.invitationApi.deleteInvitation(e);
1140
1924
  }
1141
1925
  /**
1142
1926
  * Resends an invitation by token
@@ -1144,7 +1928,7 @@ class V {
1144
1928
  * @returns Promise with success response
1145
1929
  */
1146
1930
  resendInvitation(e) {
1147
- return this.invitationAPI.resendInvitation(e);
1931
+ return this.invitationApi.resendInvitation(e);
1148
1932
  }
1149
1933
  /**
1150
1934
  * Gets a link to an invitation by id
@@ -1152,10 +1936,10 @@ class V {
1152
1936
  * @returns Promise with the link
1153
1937
  */
1154
1938
  getInvitationLink(e) {
1155
- return this.invitationAPI.getInvitationLink(e);
1939
+ return this.invitationApi.getInvitationLink(e);
1156
1940
  }
1157
1941
  }
1158
- class z {
1942
+ class Ce {
1159
1943
  error(e, ...t) {
1160
1944
  console.error(e, ...t);
1161
1945
  }
@@ -1169,10 +1953,10 @@ class z {
1169
1953
  console.debug(e, ...t);
1170
1954
  }
1171
1955
  }
1172
- function J() {
1173
- return new z();
1956
+ function Ae() {
1957
+ return new Ce();
1174
1958
  }
1175
- class Y {
1959
+ class Re {
1176
1960
  constructor(e) {
1177
1961
  this.data = this.normalize(e);
1178
1962
  }
@@ -1193,16 +1977,16 @@ class Y {
1193
1977
  name: n.name
1194
1978
  });
1195
1979
  }), e.users_in_groups?.forEach((n) => {
1196
- const h = n.user;
1197
- h && !t.has(h.id) && t.set(h.id, {
1198
- id: h.id,
1199
- name: h.name ?? null,
1200
- email: h.email ?? null,
1201
- phone: h.phone ?? null
1202
- }), h && n.group_id && s.has(n.group_id) && o.push({
1203
- userId: h.id,
1980
+ const d = n.user;
1981
+ d && !t.has(d.id) && t.set(d.id, {
1982
+ id: d.id,
1983
+ name: d.name ?? null,
1984
+ email: d.email ?? null,
1985
+ phone: d.phone ?? null
1986
+ }), d && n.group_id && s.has(n.group_id) && o.push({
1987
+ userId: d.id,
1204
1988
  groupId: n.group_id,
1205
- roleIds: n.roles?.map((l) => l.id) ?? []
1989
+ roleIds: n.roles?.map((c) => c.id) ?? []
1206
1990
  });
1207
1991
  }), {
1208
1992
  tenant_id: e.tenant_id,
@@ -1242,9 +2026,9 @@ class Y {
1242
2026
  return this.data;
1243
2027
  }
1244
2028
  }
1245
- class X {
2029
+ class Pe {
1246
2030
  constructor(e, t, s) {
1247
- this.tenantAPI = e, this.scopes = t, this.logger = s || J();
2031
+ this.tenantApi = e, this.scopes = t, this.logger = s || Ae();
1248
2032
  }
1249
2033
  /**
1250
2034
  * Handle Passflow API errors
@@ -1253,7 +2037,7 @@ class X {
1253
2037
  * @throws Formatted error with Passflow API error details
1254
2038
  */
1255
2039
  handlePassflowError(e, t) {
1256
- if (T.isAxiosError(e) && e.response?.data) {
2040
+ if (D.isAxiosError(e) && e.response?.data) {
1257
2041
  const s = e.response.data;
1258
2042
  if (typeof s == "object" && s !== null && "error" in s && typeof s.error == "object" && s.error !== null) {
1259
2043
  const r = s.error;
@@ -1271,7 +2055,7 @@ class X {
1271
2055
  async joinInvitation(e, t) {
1272
2056
  try {
1273
2057
  const s = t ?? this.scopes;
1274
- return await this.tenantAPI.joinInvitation(e, s);
2058
+ return await this.tenantApi.joinInvitation(e, s);
1275
2059
  } catch (s) {
1276
2060
  this.handlePassflowError(s, "Join invitation failed");
1277
2061
  }
@@ -1283,7 +2067,7 @@ class X {
1283
2067
  */
1284
2068
  async createTenant(e) {
1285
2069
  try {
1286
- return await this.tenantAPI.createTenant(e);
2070
+ return await this.tenantApi.createTenant(e);
1287
2071
  } catch (t) {
1288
2072
  this.handlePassflowError(t, "Tenant creation failed");
1289
2073
  }
@@ -1301,7 +2085,7 @@ class X {
1301
2085
  */
1302
2086
  async getTenantDetails(e) {
1303
2087
  try {
1304
- return await this.tenantAPI.getTenantDetails(e);
2088
+ return await this.tenantApi.getTenantDetails(e);
1305
2089
  } catch (t) {
1306
2090
  this.handlePassflowError(t, `Get tenant details failed for tenant ID ${e}`);
1307
2091
  }
@@ -1313,8 +2097,8 @@ class X {
1313
2097
  */
1314
2098
  async getTenantUserMembership(e) {
1315
2099
  try {
1316
- const t = await this.tenantAPI.getTenantDetails(e);
1317
- return new Y(t);
2100
+ const t = await this.tenantApi.getTenantDetails(e);
2101
+ return new Re(t);
1318
2102
  } catch (t) {
1319
2103
  this.handlePassflowError(t, `Get tenant user membership failed for tenant ID ${e}`);
1320
2104
  }
@@ -1327,7 +2111,7 @@ class X {
1327
2111
  */
1328
2112
  async updateTenant(e, t) {
1329
2113
  try {
1330
- return await this.tenantAPI.updateTenant(e, t);
2114
+ return await this.tenantApi.updateTenant(e, t);
1331
2115
  } catch (s) {
1332
2116
  this.handlePassflowError(s, `Update tenant failed for tenant ID ${e}`);
1333
2117
  }
@@ -1339,7 +2123,7 @@ class X {
1339
2123
  */
1340
2124
  async deleteTenant(e) {
1341
2125
  try {
1342
- return await this.tenantAPI.deleteTenant(e);
2126
+ return await this.tenantApi.deleteTenant(e);
1343
2127
  } catch (t) {
1344
2128
  this.handlePassflowError(t, `Delete tenant failed for tenant ID ${e}`);
1345
2129
  }
@@ -1350,7 +2134,7 @@ class X {
1350
2134
  */
1351
2135
  async getUserTenantMembership() {
1352
2136
  try {
1353
- return await this.tenantAPI.getUserTenantMembership();
2137
+ return await this.tenantApi.getUserTenantMembership();
1354
2138
  } catch (e) {
1355
2139
  this.handlePassflowError(e, "Get user tenant memberships failed");
1356
2140
  }
@@ -1364,7 +2148,7 @@ class X {
1364
2148
  */
1365
2149
  async createGroup(e, t) {
1366
2150
  try {
1367
- return await this.tenantAPI.createGroup(e, t);
2151
+ return await this.tenantApi.createGroup(e, t);
1368
2152
  } catch (s) {
1369
2153
  this.handlePassflowError(s, `Group creation failed for tenant ID ${e}`);
1370
2154
  }
@@ -1377,7 +2161,7 @@ class X {
1377
2161
  */
1378
2162
  async getGroupInfo(e, t) {
1379
2163
  try {
1380
- return await this.tenantAPI.getGroupInfo(e, t);
2164
+ return await this.tenantApi.getGroupInfo(e, t);
1381
2165
  } catch (s) {
1382
2166
  this.handlePassflowError(s, `Get group info failed for tenant ID ${e}, group ID ${t}`);
1383
2167
  }
@@ -1391,7 +2175,7 @@ class X {
1391
2175
  */
1392
2176
  async updateGroup(e, t, s) {
1393
2177
  try {
1394
- return await this.tenantAPI.updateGroup(e, t, s);
2178
+ return await this.tenantApi.updateGroup(e, t, s);
1395
2179
  } catch (r) {
1396
2180
  this.handlePassflowError(r, `Update group failed for tenant ID ${e}, group ID ${t}`);
1397
2181
  }
@@ -1404,7 +2188,7 @@ class X {
1404
2188
  */
1405
2189
  async deleteGroup(e, t) {
1406
2190
  try {
1407
- return await this.tenantAPI.deleteGroup(e, t);
2191
+ return await this.tenantApi.deleteGroup(e, t);
1408
2192
  } catch (s) {
1409
2193
  this.handlePassflowError(s, `Delete group failed for tenant ID ${e}, group ID ${t}`);
1410
2194
  }
@@ -1419,7 +2203,7 @@ class X {
1419
2203
  */
1420
2204
  async addUserToGroup(e, t, s, r) {
1421
2205
  try {
1422
- return await this.tenantAPI.addUserToGroup(e, t, s, r);
2206
+ return await this.tenantApi.addUserToGroup(e, t, s, r);
1423
2207
  } catch (o) {
1424
2208
  this.handlePassflowError(
1425
2209
  o,
@@ -1437,7 +2221,7 @@ class X {
1437
2221
  */
1438
2222
  async removeUserRolesFromGroup(e, t, s, r) {
1439
2223
  try {
1440
- return await this.tenantAPI.removeUserRolesFromGroup(e, t, s, r);
2224
+ return await this.tenantApi.removeUserRolesFromGroup(e, t, s, r);
1441
2225
  } catch (o) {
1442
2226
  this.handlePassflowError(
1443
2227
  o,
@@ -1455,7 +2239,7 @@ class X {
1455
2239
  */
1456
2240
  async changeUserRoles(e, t, s, r) {
1457
2241
  try {
1458
- return await this.tenantAPI.changeUserRoles(e, t, s, r);
2242
+ return await this.tenantApi.changeUserRoles(e, t, s, r);
1459
2243
  } catch (o) {
1460
2244
  this.handlePassflowError(
1461
2245
  o,
@@ -1472,7 +2256,7 @@ class X {
1472
2256
  */
1473
2257
  async deleteUserFromGroup(e, t, s) {
1474
2258
  try {
1475
- return await this.tenantAPI.deleteUserFromGroup(e, t, s);
2259
+ return await this.tenantApi.deleteUserFromGroup(e, t, s);
1476
2260
  } catch (r) {
1477
2261
  this.handlePassflowError(
1478
2262
  r,
@@ -1488,7 +2272,7 @@ class X {
1488
2272
  */
1489
2273
  async getRolesForTenant(e) {
1490
2274
  try {
1491
- return await this.tenantAPI.getRolesForTenant(e);
2275
+ return await this.tenantApi.getRolesForTenant(e);
1492
2276
  } catch (t) {
1493
2277
  this.handlePassflowError(t, `Get roles for tenant failed for tenant ID ${e}`);
1494
2278
  }
@@ -1501,7 +2285,7 @@ class X {
1501
2285
  */
1502
2286
  async createRoleForTenant(e, t) {
1503
2287
  try {
1504
- return await this.tenantAPI.createRoleForTenant(e, t);
2288
+ return await this.tenantApi.createRoleForTenant(e, t);
1505
2289
  } catch (s) {
1506
2290
  this.handlePassflowError(s, `Create role for tenant failed for tenant ID ${e}`);
1507
2291
  }
@@ -1515,7 +2299,7 @@ class X {
1515
2299
  */
1516
2300
  async updateRole(e, t, s) {
1517
2301
  try {
1518
- return await this.tenantAPI.updateRole(e, t, s);
2302
+ return await this.tenantApi.updateRole(e, t, s);
1519
2303
  } catch (r) {
1520
2304
  this.handlePassflowError(r, `Update role failed for tenant ID ${e}, role ID ${t}`);
1521
2305
  }
@@ -1528,7 +2312,7 @@ class X {
1528
2312
  */
1529
2313
  async deleteRole(e, t) {
1530
2314
  try {
1531
- return await this.tenantAPI.deleteRole(e, t);
2315
+ return await this.tenantApi.deleteRole(e, t);
1532
2316
  } catch (s) {
1533
2317
  this.handlePassflowError(s, `Delete role failed for tenant ID ${e}, role ID ${t}`);
1534
2318
  }
@@ -1542,7 +2326,7 @@ class X {
1542
2326
  */
1543
2327
  async deleteUserFromTenant(e, t) {
1544
2328
  try {
1545
- return await this.tenantAPI.deleteUserFromTenant(e, t);
2329
+ return await this.tenantApi.deleteUserFromTenant(e, t);
1546
2330
  } catch (s) {
1547
2331
  this.handlePassflowError(s, `Delete user from tenant failed for tenant ID ${e}, user ID ${t}`);
1548
2332
  }
@@ -1558,7 +2342,7 @@ class X {
1558
2342
  */
1559
2343
  async getGroupInvitations(e, t, s, r) {
1560
2344
  try {
1561
- return await this.tenantAPI.getGroupInvitations(e, t, s, r);
2345
+ return await this.tenantApi.getGroupInvitations(e, t, s, r);
1562
2346
  } catch (o) {
1563
2347
  this.handlePassflowError(o, `Get group invitations failed for tenant ID ${e}, group ID ${t}`);
1564
2348
  }
@@ -1572,7 +2356,7 @@ class X {
1572
2356
  */
1573
2357
  async getTenantInvitations(e, t, s) {
1574
2358
  try {
1575
- return await this.tenantAPI.getTenantInvitations(e, t, s);
2359
+ return await this.tenantApi.getTenantInvitations(e, t, s);
1576
2360
  } catch (r) {
1577
2361
  this.handlePassflowError(r, `Get tenant invitations failed for tenant ID ${e}`);
1578
2362
  }
@@ -1586,7 +2370,7 @@ class X {
1586
2370
  */
1587
2371
  async invalidateInviteById(e, t, s) {
1588
2372
  try {
1589
- return await this.tenantAPI.invalidateInviteById(e, t, s);
2373
+ return await this.tenantApi.invalidateInviteById(e, t, s);
1590
2374
  } catch (r) {
1591
2375
  this.handlePassflowError(
1592
2376
  r,
@@ -1603,7 +2387,7 @@ class X {
1603
2387
  */
1604
2388
  async invalidateInviteByEmail(e, t, s) {
1605
2389
  try {
1606
- return await this.tenantAPI.invalidateInviteByEmail(e, t, s);
2390
+ return await this.tenantApi.invalidateInviteByEmail(e, t, s);
1607
2391
  } catch (r) {
1608
2392
  this.handlePassflowError(
1609
2393
  r,
@@ -1612,83 +2396,37 @@ class X {
1612
2396
  }
1613
2397
  }
1614
2398
  }
1615
- class Q {
1616
- constructor(e, t) {
1617
- this.userAPI = e, this.deviceService = t;
2399
+ class De {
2400
+ constructor(e, t, s) {
2401
+ this.storageManager = e, this.authApi = t, this.subscribeStore = s, this.checkInterval = null, this.CHECK_INTERVAL = 6e4, this.visibilityChangeHandler = null, this.isRefreshing = !1, this.tokenExpiredFlag = !1, this.storageManager = e, this.authApi = t, this.setupPageUnloadHandler();
1618
2402
  }
1619
- /**
1620
- * Get user's registered passkeys
1621
- * @returns Promise with passkeys array
1622
- */
1623
- getUserPasskeys() {
1624
- return this.userAPI.getUserPasskeys();
1625
- }
1626
- /**
1627
- * Rename a user passkey
1628
- * @param name The new name for the passkey
1629
- * @param passkeyId The ID of the passkey to rename
1630
- * @returns Promise with success response
1631
- */
1632
- renameUserPasskey(e, t) {
1633
- return this.userAPI.renameUserPasskey(e, t);
1634
- }
1635
- /**
1636
- * Delete a user passkey
1637
- * @param passkeyId The ID of the passkey to delete
1638
- * @returns Promise with success response
1639
- */
1640
- deleteUserPasskey(e) {
1641
- return this.userAPI.deleteUserPasskey(e);
1642
- }
1643
- /**
1644
- * Add a new passkey for the current user
1645
- * @param options Optional parameters for the passkey
1646
- * @returns Promise that resolves when the passkey is added
1647
- */
1648
- async addUserPasskey({
1649
- relyingPartyId: e,
1650
- passkeyUsername: t,
1651
- passkeyDisplayName: s
1652
- } = {}) {
1653
- const r = this.deviceService.getDeviceId(), o = w.web, { challenge_id: n, publicKey: h } = await this.userAPI.addUserPasskeyStart({
1654
- relyingPartyId: e || window?.location?.hostname,
1655
- deviceId: r,
1656
- os: o,
1657
- passkeyDisplayName: s,
1658
- passkeyUsername: t
1659
- });
1660
- h.user.id = btoa(h.user.id);
1661
- const l = await _({ optionsJSON: h });
1662
- return await this.userAPI.addUserPasskeyComplete(l, r, n);
1663
- }
1664
- }
1665
- class Z {
1666
- constructor(e, t, s) {
1667
- this.storageManager = e, this.authApi = t, this.subscribeStore = s, this.checkInterval = null, this.CHECK_INTERVAL = 10, this.isRefreshing = !1, this.isExpired = !1, this.storageManager = e, this.authApi = t;
1668
- }
1669
- initialize() {
1670
- try {
1671
- const e = this.storageManager.getTokens();
1672
- if (!e || !e.access_token) {
1673
- this.startTokenCheck();
1674
- return;
1675
- }
1676
- const t = d(e.access_token);
1677
- f(t) ? (this.isExpired = !0, this.stopTokenCheck(), this.subscribeStore.notify(a.TokenCacheExpired, { isExpired: !0 })) : (this.setTokensCache(e), this.startTokenCheck());
1678
- } catch (e) {
1679
- const t = {
1680
- message: e instanceof Error ? e.message : "Failed to get tokens",
1681
- originalError: e
1682
- };
1683
- this.subscribeStore.notify(a.Error, t), this.setTokensCache(void 0);
1684
- }
2403
+ initialize() {
2404
+ try {
2405
+ const e = this.storageManager.getTokens();
2406
+ if (!e) {
2407
+ this.startTokenCheck();
2408
+ return;
2409
+ }
2410
+ if (!e.access_token) {
2411
+ this.setTokensCache(e), this.startTokenCheck();
2412
+ return;
2413
+ }
2414
+ const t = y(e.access_token);
2415
+ S(t) ? (this.tokenExpiredFlag = !0, this.stopTokenCheck(), this.subscribeStore.notify(a.TokenCacheExpired, { isExpired: !0 })) : (this.setTokensCache(e), this.startTokenCheck());
2416
+ } catch (e) {
2417
+ const t = {
2418
+ message: e instanceof Error ? e.message : "Failed to get tokens",
2419
+ originalError: e
2420
+ };
2421
+ this.subscribeStore.notify(a.Error, t), this.setTokensCache(void 0);
2422
+ }
1685
2423
  }
1686
2424
  async refreshTokensCache(e) {
1687
2425
  if (!this.isRefreshing)
1688
2426
  try {
1689
2427
  this.isRefreshing = !0, this.subscribeStore.notify(a.RefreshStart, {});
1690
2428
  const t = await this.authApi.refreshToken(e?.refresh_token ?? "", e.scopes ?? [], e.access_token);
1691
- this.setTokensCache(t), this.subscribeStore.notify(a.Refresh, { tokens: t, parsedTokens: this.getParsedTokenCache() }), this.subscribeStore.notify(a.TokenCacheExpired, { isExpired: !1 }), this.isExpired = !1, this.startTokenCheck();
2429
+ this.setTokensCache(t), this.subscribeStore.notify(a.Refresh, { tokens: t, parsedTokens: this.getParsedTokens() }), this.subscribeStore.notify(a.TokenCacheExpired, { isExpired: !1 }), this.tokenExpiredFlag = !1, this.startTokenCheck();
1692
2430
  } catch (t) {
1693
2431
  const s = {
1694
2432
  message: t instanceof Error ? t.message : "Failed to get tokens",
@@ -1700,29 +2438,48 @@ class Z {
1700
2438
  }
1701
2439
  }
1702
2440
  startTokenCheck() {
1703
- this.checkInterval && clearInterval(this.checkInterval), !this.isExpired && (this.checkInterval = setInterval(() => {
1704
- this.isRefreshing || this.isExpired || this.tokensCacheIsExpired() && !this.isExpired && (this.isExpired = !0, this.subscribeStore.notify(a.TokenCacheExpired, { isExpired: !0 }), this.stopTokenCheck());
2441
+ this.checkInterval && clearInterval(this.checkInterval), !this.tokenExpiredFlag && (this.setupVisibilityListener(), this.checkInterval = setInterval(() => {
2442
+ typeof document < "u" && document.hidden || this.isRefreshing || this.tokenExpiredFlag || this.isExpired() && !this.tokenExpiredFlag && (this.tokenExpiredFlag = !0, this.subscribeStore.notify(a.TokenCacheExpired, { isExpired: !0 }), this.stopTokenCheck());
1705
2443
  }, this.CHECK_INTERVAL));
1706
2444
  }
2445
+ setupVisibilityListener() {
2446
+ typeof document > "u" || (this.visibilityChangeHandler && document.removeEventListener("visibilitychange", this.visibilityChangeHandler), this.visibilityChangeHandler = () => {
2447
+ !document.hidden && this.checkInterval && !this.isRefreshing && !this.tokenExpiredFlag && this.isExpired() && (this.tokenExpiredFlag = !0, this.subscribeStore.notify(a.TokenCacheExpired, { isExpired: !0 }), this.stopTokenCheck());
2448
+ }, document.addEventListener("visibilitychange", this.visibilityChangeHandler));
2449
+ }
2450
+ setupPageUnloadHandler() {
2451
+ typeof window > "u" || window.addEventListener("beforeunload", () => {
2452
+ this.destroy();
2453
+ });
2454
+ }
1707
2455
  stopTokenCheck() {
1708
- this.checkInterval && (clearInterval(this.checkInterval), this.checkInterval = null);
2456
+ this.checkInterval && (clearInterval(this.checkInterval), this.checkInterval = null), this.visibilityChangeHandler && typeof document < "u" && (document.removeEventListener("visibilitychange", this.visibilityChangeHandler), this.visibilityChangeHandler = null);
2457
+ }
2458
+ /**
2459
+ * Cleanup method to stop all intervals and remove event listeners.
2460
+ * Should be called when the service is no longer needed.
2461
+ */
2462
+ destroy() {
2463
+ this.stopTokenCheck();
1709
2464
  }
1710
2465
  setTokensCache(e) {
1711
2466
  this.tokensCache = e, e ? this.parsedTokensCache = {
1712
- access_token: d(e.access_token),
1713
- id_token: e.id_token ? d(e.id_token) : void 0,
1714
- refresh_token: e.refresh_token ? d(e.refresh_token) : void 0,
2467
+ access_token: e.access_token ? y(e.access_token) : void 0,
2468
+ id_token: e.id_token ? y(e.id_token) : void 0,
2469
+ refresh_token: e.refresh_token ? y(e.refresh_token) : void 0,
1715
2470
  scopes: e.scopes
1716
2471
  } : this.parsedTokensCache = void 0;
1717
2472
  }
1718
- getTokensCache() {
2473
+ getTokens() {
1719
2474
  return this.tokensCache;
1720
2475
  }
1721
- async getTokensCacheWithRefresh() {
2476
+ async getTokensWithRefresh() {
1722
2477
  try {
1723
2478
  if (!this.tokensCache) return this.tokensCache;
1724
- const e = d(this.tokensCache.access_token);
1725
- return f(e) && !this.isExpired ? (await this.refreshTokensCache(this.tokensCache), this.tokensCache) : this.tokensCache;
2479
+ if (!this.tokensCache.access_token)
2480
+ return this.tokensCache;
2481
+ const e = y(this.tokensCache.access_token);
2482
+ return S(e) && !this.tokenExpiredFlag ? (await this.refreshTokensCache(this.tokensCache), this.tokensCache) : this.tokensCache;
1726
2483
  } catch (e) {
1727
2484
  const t = {
1728
2485
  message: e instanceof Error ? e.message : "Failed to get tokens",
@@ -1732,28 +2489,352 @@ class Z {
1732
2489
  return;
1733
2490
  }
1734
2491
  }
1735
- getParsedTokenCache() {
2492
+ getParsedTokens() {
1736
2493
  return this.parsedTokensCache;
1737
2494
  }
1738
- tokensCacheIsExpired() {
2495
+ isExpired() {
1739
2496
  if (!this.tokensCache) return !0;
1740
- const e = d(this.tokensCache.access_token);
1741
- return f(e);
2497
+ if (!this.tokensCache.access_token)
2498
+ return !1;
2499
+ const e = y(this.tokensCache.access_token);
2500
+ return S(e);
2501
+ }
2502
+ }
2503
+ class Fe {
2504
+ constructor(e, t) {
2505
+ this.twoFactorApi = e, this.subscribeStore = t, this.PARTIAL_AUTH_TIMEOUT_MS = 300 * 1e3, this.SESSION_STORAGE_KEY = "passflow_2fa_challenge", this.totpDigits = 6;
2506
+ const s = {
2507
+ onAuthChange: (r, o) => {
2508
+ if (r === a.TwoFactorRequired) {
2509
+ const n = o;
2510
+ this.setPartialAuthState(n.email, n.challengeId, n.tfaToken);
2511
+ }
2512
+ }
2513
+ };
2514
+ this.subscribeStore.subscribe(s, [a.TwoFactorRequired]);
2515
+ }
2516
+ /**
2517
+ * Emit error event and throw the error
2518
+ * Helper method to ensure errors are properly emitted to subscribers
2519
+ */
2520
+ emitErrorAndThrow(e, t) {
2521
+ const s = {
2522
+ message: e instanceof Error ? e.message : `${t} failed`,
2523
+ originalError: e,
2524
+ code: e?.id || void 0
2525
+ };
2526
+ throw this.subscribeStore.notify(a.Error, s), e;
2527
+ }
2528
+ /**
2529
+ * Get 2FA enrollment status for current user
2530
+ */
2531
+ async getStatus() {
2532
+ try {
2533
+ const e = await this.twoFactorApi.getStatus();
2534
+ return e.totp_digits && (this.totpDigits = e.totp_digits), e;
2535
+ } catch (e) {
2536
+ this.emitErrorAndThrow(e, "Get 2FA status");
2537
+ }
2538
+ }
2539
+ /**
2540
+ * Begin 2FA setup process
2541
+ * Returns secret and QR code for authenticator app
2542
+ */
2543
+ async beginSetup() {
2544
+ try {
2545
+ const e = await this.twoFactorApi.beginSetup();
2546
+ return e.totp_digits && (this.totpDigits = e.totp_digits), this.subscribeStore.notify(a.TwoFactorSetupStarted, { secret: e.secret }), e;
2547
+ } catch (e) {
2548
+ this.emitErrorAndThrow(e, "Begin 2FA setup");
2549
+ }
2550
+ }
2551
+ /**
2552
+ * Confirm 2FA setup with TOTP code
2553
+ * Returns recovery codes that MUST be displayed to user
2554
+ */
2555
+ async confirmSetup(e) {
2556
+ if (!R(e, this.totpDigits))
2557
+ throw new Error(`Invalid TOTP code format. Code must be exactly ${this.totpDigits} digits.`);
2558
+ try {
2559
+ const t = await this.twoFactorApi.confirmSetup({ code: e });
2560
+ return this.subscribeStore.notify(a.TwoFactorEnabled, {
2561
+ recoveryCodes: t.recovery_codes,
2562
+ clearRecoveryCodes: () => {
2563
+ t.recovery_codes.length = 0;
2564
+ }
2565
+ }), t;
2566
+ } catch (t) {
2567
+ this.emitErrorAndThrow(t, "Confirm 2FA setup");
2568
+ }
2569
+ }
2570
+ /**
2571
+ * Verify TOTP code during login
2572
+ * Completes authentication if successful
2573
+ */
2574
+ async verify(e) {
2575
+ if (!R(e, this.totpDigits))
2576
+ throw new Error(`Invalid TOTP code format. Code must be exactly ${this.totpDigits} digits.`);
2577
+ if (this.recoverPartialAuthState(), !this.isVerificationRequired())
2578
+ throw new Error("2FA verification expired or not required. User must sign in first.");
2579
+ if (!this.partialAuthState?.tfaToken)
2580
+ throw new Error("No TFA token found. User must sign in first.");
2581
+ try {
2582
+ const t = await this.twoFactorApi.verify({
2583
+ code: e,
2584
+ tfa_token: this.partialAuthState.tfaToken
2585
+ });
2586
+ return this.clearPartialAuthState(), this.subscribeStore.notify(a.TwoFactorVerified, { tokens: t }), t;
2587
+ } catch (t) {
2588
+ this.emitErrorAndThrow(t, "Verify 2FA code");
2589
+ }
2590
+ }
2591
+ /**
2592
+ * Use recovery code for authentication
2593
+ * Completes authentication if successful
2594
+ */
2595
+ async useRecoveryCode(e) {
2596
+ try {
2597
+ const t = _e(e);
2598
+ if (!t)
2599
+ throw new Error("Invalid recovery code format. Expected format: XXXX-XXXX or XXXXXXXX (alphanumeric).");
2600
+ if (this.recoverPartialAuthState(), !this.isVerificationRequired())
2601
+ throw new Error("2FA verification expired or not required. User must sign in first.");
2602
+ if (!this.partialAuthState?.tfaToken)
2603
+ throw new Error("No TFA token found. User must sign in first.");
2604
+ const s = await this.twoFactorApi.useRecoveryCode({
2605
+ recovery_code: t,
2606
+ tfa_token: this.partialAuthState.tfaToken
2607
+ });
2608
+ return this.clearPartialAuthState(), s.remaining_recovery_codes === 0 ? this.subscribeStore.notify(a.TwoFactorRecoveryCodesExhausted, { tokens: s }) : s.remaining_recovery_codes <= 2 && this.subscribeStore.notify(a.TwoFactorRecoveryCodesLow, {
2609
+ tokens: s,
2610
+ remainingCodes: s.remaining_recovery_codes
2611
+ }), this.subscribeStore.notify(a.TwoFactorRecoveryUsed, {
2612
+ tokens: s,
2613
+ remainingCodes: s.remaining_recovery_codes
2614
+ }), this.subscribeStore.notify(a.TwoFactorVerified, { tokens: s }), s;
2615
+ } catch (t) {
2616
+ this.emitErrorAndThrow(t, "Use recovery code");
2617
+ }
2618
+ }
2619
+ /**
2620
+ * Disable 2FA (requires TOTP verification)
2621
+ */
2622
+ async disable(e) {
2623
+ if (!R(e, this.totpDigits))
2624
+ throw new Error(`Invalid TOTP code format. Code must be exactly ${this.totpDigits} digits.`);
2625
+ try {
2626
+ const t = await this.twoFactorApi.disable({ code: e });
2627
+ return this.subscribeStore.notify(a.TwoFactorDisabled, {}), t;
2628
+ } catch (t) {
2629
+ this.emitErrorAndThrow(t, "Disable 2FA");
2630
+ }
2631
+ }
2632
+ /**
2633
+ * Regenerate recovery codes
2634
+ */
2635
+ async regenerateRecoveryCodes(e) {
2636
+ if (!R(e, this.totpDigits))
2637
+ throw new Error(`Invalid TOTP code format. Code must be exactly ${this.totpDigits} digits.`);
2638
+ try {
2639
+ const t = await this.twoFactorApi.regenerateRecoveryCodes({ code: e }), s = [...t.recovery_codes];
2640
+ return t.recovery_codes = [], t.recovery_codes = s, t;
2641
+ } catch (t) {
2642
+ this.emitErrorAndThrow(t, "Regenerate recovery codes");
2643
+ }
2644
+ }
2645
+ /**
2646
+ * Check if 2FA verification is required (local state check)
2647
+ * Returns true if user has signed in but needs 2FA verification
2648
+ */
2649
+ isVerificationRequired() {
2650
+ return this.recoverPartialAuthState(), this.partialAuthState ? Date.now() > this.partialAuthState.expiresAt ? (this.clearPartialAuthState(), !1) : !0 : !1;
2651
+ }
2652
+ /**
2653
+ * Set partial auth state when login requires 2FA
2654
+ * Called internally via event listener when AuthService emits TwoFactorRequired
2655
+ */
2656
+ setPartialAuthState(e, t, s) {
2657
+ if (this.partialAuthState = {
2658
+ email: e,
2659
+ challengeId: t,
2660
+ tfaToken: s,
2661
+ timestamp: Date.now(),
2662
+ expiresAt: Date.now() + this.PARTIAL_AUTH_TIMEOUT_MS
2663
+ }, typeof sessionStorage < "u")
2664
+ try {
2665
+ sessionStorage.setItem(this.SESSION_STORAGE_KEY, JSON.stringify(this.partialAuthState));
2666
+ } catch {
2667
+ }
2668
+ }
2669
+ /**
2670
+ * Clear partial auth state
2671
+ * Called on logout or successful verification
2672
+ */
2673
+ clearPartialAuthState() {
2674
+ if (this.partialAuthState = void 0, typeof sessionStorage < "u")
2675
+ try {
2676
+ sessionStorage.removeItem(this.SESSION_STORAGE_KEY);
2677
+ } catch {
2678
+ }
2679
+ }
2680
+ /**
2681
+ * Attempt to recover partial auth state from sessionStorage
2682
+ * Called before verification operations to handle page refresh
2683
+ */
2684
+ recoverPartialAuthState() {
2685
+ if (!this.partialAuthState && !(typeof sessionStorage > "u"))
2686
+ try {
2687
+ const e = sessionStorage.getItem(this.SESSION_STORAGE_KEY);
2688
+ if (!e) return;
2689
+ const t = JSON.parse(e);
2690
+ Date.now() < t.expiresAt ? this.partialAuthState = t : sessionStorage.removeItem(this.SESSION_STORAGE_KEY);
2691
+ } catch {
2692
+ try {
2693
+ sessionStorage.removeItem(this.SESSION_STORAGE_KEY);
2694
+ } catch {
2695
+ }
2696
+ }
2697
+ }
2698
+ // ============================================
2699
+ // Magic Link 2FA Setup Methods
2700
+ // ============================================
2701
+ /**
2702
+ * Validate magic link token for 2FA setup
2703
+ *
2704
+ * This method validates an admin-generated magic link token and
2705
+ * creates a scoped session (scope: "2fa_setup") that can ONLY be
2706
+ * used for completing 2FA setup operations.
2707
+ *
2708
+ * Session characteristics:
2709
+ * - Stored in memory only (no persistence across page reloads)
2710
+ * - Short-lived (typically 1 hour expiration)
2711
+ * - Cannot be refreshed
2712
+ * - Cannot be promoted to full authentication
2713
+ * - Only valid for 2FA setup endpoints
2714
+ *
2715
+ * @param token - Magic link token from URL parameter
2716
+ * @returns Validation response with session details
2717
+ */
2718
+ async validateTwoFactorSetupMagicLink(e) {
2719
+ const t = await this.twoFactorApi.validateTwoFactorSetupMagicLink(e);
2720
+ return t.success && t.sessionToken && t.userId ? (this.magicLinkSession = {
2721
+ sessionToken: t.sessionToken,
2722
+ userId: t.userId,
2723
+ appId: t.appId,
2724
+ scope: "2fa_setup",
2725
+ timestamp: Date.now(),
2726
+ expiresAt: Date.now() + (t.expiresIn || 3600) * 1e3
2727
+ }, this.subscribeStore.notify(a.TwoFactorSetupMagicLinkValidated, {
2728
+ userId: t.userId,
2729
+ appId: t.appId,
2730
+ expiresIn: t.expiresIn || 3600,
2731
+ sessionToken: t.sessionToken
2732
+ })) : t.error && this.subscribeStore.notify(a.TwoFactorSetupMagicLinkFailed, {
2733
+ error: t.error
2734
+ }), t;
2735
+ }
2736
+ /**
2737
+ * Get current magic link session (if any)
2738
+ * Used by React SDK to access session token for API calls
2739
+ *
2740
+ * @returns Active magic link session or null if none/expired
2741
+ */
2742
+ getMagicLinkSession() {
2743
+ return this.magicLinkSession ? Date.now() > this.magicLinkSession.expiresAt ? (this.clearMagicLinkSession(), null) : this.magicLinkSession : null;
2744
+ }
2745
+ /**
2746
+ * Clear magic link session
2747
+ * Called after successful setup completion or on error
2748
+ */
2749
+ clearMagicLinkSession() {
2750
+ this.magicLinkSession = void 0;
2751
+ }
2752
+ /**
2753
+ * Check if magic link session is active
2754
+ * Used by React SDK to determine if form can use magic link auth
2755
+ */
2756
+ hasMagicLinkSession() {
2757
+ return this.getMagicLinkSession() !== null;
2758
+ }
2759
+ /**
2760
+ * Get the session token from magic link session (if active)
2761
+ * Used by AxiosClient for injecting auth header on 2FA setup endpoints
2762
+ */
2763
+ getMagicLinkSessionToken() {
2764
+ return this.getMagicLinkSession()?.sessionToken || null;
2765
+ }
2766
+ /**
2767
+ * Get configured TOTP digit count
2768
+ * Returns the number of digits (6 or 8) for TOTP codes
2769
+ * Useful for UI components that need to render the correct number of input fields
2770
+ */
2771
+ getTotpDigits() {
2772
+ return this.totpDigits;
2773
+ }
2774
+ }
2775
+ class Me {
2776
+ constructor(e, t) {
2777
+ this.userAPI = e, this.deviceService = t;
2778
+ }
2779
+ /**
2780
+ * Get user's registered passkeys
2781
+ * @returns Promise with passkeys array
2782
+ */
2783
+ getUserPasskeys() {
2784
+ return this.userAPI.getUserPasskeys();
2785
+ }
2786
+ /**
2787
+ * Rename a user passkey
2788
+ * @param name The new name for the passkey
2789
+ * @param passkeyId The ID of the passkey to rename
2790
+ * @returns Promise with success response
2791
+ */
2792
+ renameUserPasskey(e, t) {
2793
+ return this.userAPI.renameUserPasskey(e, t);
2794
+ }
2795
+ /**
2796
+ * Delete a user passkey
2797
+ * @param passkeyId The ID of the passkey to delete
2798
+ * @returns Promise with success response
2799
+ */
2800
+ deleteUserPasskey(e) {
2801
+ return this.userAPI.deleteUserPasskey(e);
2802
+ }
2803
+ /**
2804
+ * Add a new passkey for the current user
2805
+ * @param options Optional parameters for the passkey
2806
+ * @returns Promise that resolves when the passkey is added
2807
+ */
2808
+ async addUserPasskey({
2809
+ relyingPartyId: e,
2810
+ passkeyUsername: t,
2811
+ passkeyDisplayName: s
2812
+ } = {}) {
2813
+ const r = this.deviceService.getDeviceId(), o = I.web, { challenge_id: n, publicKey: d } = await this.userAPI.addUserPasskeyStart({
2814
+ relyingPartyId: e || window?.location?.hostname,
2815
+ deviceId: r,
2816
+ os: o,
2817
+ passkeyDisplayName: s,
2818
+ passkeyUsername: t
2819
+ });
2820
+ d.user.id = btoa(d.user.id);
2821
+ const c = await K({ optionsJSON: d });
2822
+ return await this.userAPI.addUserPasskeyComplete(c, r, n);
1742
2823
  }
1743
2824
  }
1744
- class ie {
2825
+ const O = class O {
1745
2826
  constructor(e) {
1746
2827
  this.doRefreshTokens = !1, this.origin = window.location.origin, this.session = async ({
1747
2828
  createSession: o,
1748
2829
  expiredSession: n,
1749
- doRefresh: h = !1
2830
+ doRefresh: d = !1
1750
2831
  }) => {
1751
- this.createSessionCallback = o, this.expiredSessionCallback = n, this.doRefreshTokens = h, await this.submitSessionCheck();
2832
+ this.createSessionCallback = o, this.expiredSessionCallback = n, this.doRefreshTokens = d, await this.submitSessionCheck();
1752
2833
  };
1753
2834
  const { url: t, appId: s, scopes: r } = e;
1754
- this.url = t || A, this.appId = s, this.authApi = new O(e), this.appApi = new j(e), this.userApi = new N(e), this.settingApi = new K(e), this.tenantAPI = new B(e), this.invitationAPI = new W(e), this.storageManager = new b({
2835
+ this.url = t || G, this.appId = s, this.storageManager = new $({
1755
2836
  prefix: e.keyStoragePrefix ?? ""
1756
- }), this.deviceService = new $(), this.subscribeStore = new H(), this.tokenCacheService = new Z(this.storageManager, this.authApi, this.subscribeStore), this.scopes = r ?? F, this.createTenantForNewUser = e.createTenantForNewUser ?? !1, this.authService = new q(
2837
+ }), this.deviceService = new Y(this.storageManager), this.authApi = new fe(e, this.storageManager, this.deviceService), this.appApi = new pe(e, this.storageManager, this.deviceService), this.userApi = new me(e, this.storageManager, this.deviceService), this.settingApi = new ye(e, this.storageManager, this.deviceService), this.tenantApi = new ve(e, this.storageManager, this.deviceService), this.invitationApi = new ke(e, this.storageManager, this.deviceService), this.twoFactorApi = new Se(e, this.storageManager, this.deviceService), this.subscribeStore = new we(), this.tokenCacheService = new De(this.storageManager, this.authApi, this.subscribeStore), this.scopes = r ?? Q, this.createTenantForNewUser = e.createTenantForNewUser ?? !1, this.authService = new Ie(
1757
2838
  this.authApi,
1758
2839
  this.deviceService,
1759
2840
  this.storageManager,
@@ -1767,13 +2848,29 @@ class ie {
1767
2848
  createSession: this.createSessionCallback,
1768
2849
  expiredSession: this.expiredSessionCallback
1769
2850
  },
1770
- this.appId ?? ""
1771
- ), this.userService = new Q(this.userApi, this.deviceService), this.tenantService = new X(this.tenantAPI, this.scopes), this.tenant = this.tenantService, this.invitationService = new V(this.invitationAPI), e.parseQueryParams && this.checkAndSetTokens(), this.setTokensToCacheFromLocalStorage();
2851
+ this.appId ?? "",
2852
+ e.tokenExchange
2853
+ ), this.userService = new Me(this.userApi, this.deviceService), this.tenantService = new Pe(this.tenantApi, this.scopes), this.tenant = this.tenantService, this.invitationService = new be(this.invitationApi), this.twoFactorService = new Fe(this.twoFactorApi, this.subscribeStore), this.twoFactor = this.twoFactorService, e.parseQueryParams && this.checkAndSetTokens(), this.setTokensToCacheFromLocalStorage();
2854
+ }
2855
+ /**
2856
+ * Update the appId and propagate it to all API clients.
2857
+ * This ensures that all future API requests use the new appId in their headers.
2858
+ *
2859
+ * @param appId - The new application ID to set
2860
+ *
2861
+ * @example
2862
+ * ```typescript
2863
+ * // Update appId after discovery
2864
+ * passflow.setAppId('discovered-app-id-123');
2865
+ * ```
2866
+ */
2867
+ setAppId(e) {
2868
+ this.appId = e, this.authApi.setAppId(e), this.appApi.setAppId(e), this.userApi.setAppId(e), this.settingApi.setAppId(e), this.tenantApi.setAppId(e), this.invitationApi.setAppId(e), this.twoFactorApi.setAppId(e), this.authService;
1772
2869
  }
1773
2870
  async submitSessionCheck() {
1774
2871
  let e, t;
1775
2872
  try {
1776
- e = await this.authService.getTokens(this.doRefreshTokens), t = this.tokenCacheService.getParsedTokenCache();
2873
+ e = await this.authService.getTokens(this.doRefreshTokens), t = this.tokenCacheService.getParsedTokens();
1777
2874
  } catch (s) {
1778
2875
  const r = {
1779
2876
  message: s instanceof Error || s instanceof u ? s.message : "Session check failed",
@@ -1784,89 +2881,424 @@ class ie {
1784
2881
  e && this.createSessionCallback && await this.createSessionCallback({ tokens: e, parsedTokens: t }), !e && this.expiredSessionCallback && await this.expiredSessionCallback();
1785
2882
  }
1786
2883
  // Event subscription
2884
+ /**
2885
+ * Subscribe to Passflow authentication events.
2886
+ *
2887
+ * @param subscriber - Subscriber function that receives event type and payload
2888
+ * @param events - Optional array of specific events to listen for. If omitted, subscribes to all events.
2889
+ *
2890
+ * @example
2891
+ * ```typescript
2892
+ * // Subscribe to all events
2893
+ * passflow.subscribe((event, payload) => {
2894
+ * console.log('Event:', event, payload);
2895
+ * });
2896
+ *
2897
+ * // Subscribe to specific events only
2898
+ * passflow.subscribe(
2899
+ * (event, payload) => {
2900
+ * if (event === PassflowEvent.SignIn) {
2901
+ * console.log('User signed in', payload.tokens);
2902
+ * }
2903
+ * },
2904
+ * [PassflowEvent.SignIn, PassflowEvent.SignOut]
2905
+ * );
2906
+ * ```
2907
+ */
1787
2908
  subscribe(e, t) {
1788
2909
  this.subscribeStore.subscribe(e, t), this.tokenCacheService.initialize();
1789
2910
  }
2911
+ /**
2912
+ * Unsubscribe from Passflow authentication events.
2913
+ *
2914
+ * @param subscriber - The subscriber function to remove
2915
+ * @param events - Optional array of specific events to unsubscribe from. If omitted, unsubscribes from all events.
2916
+ *
2917
+ * @example
2918
+ * ```typescript
2919
+ * const subscriber = (event, payload) => console.log(event, payload);
2920
+ *
2921
+ * // Subscribe
2922
+ * passflow.subscribe(subscriber);
2923
+ *
2924
+ * // Later, unsubscribe
2925
+ * passflow.unsubscribe(subscriber);
2926
+ * ```
2927
+ */
1790
2928
  unsubscribe(e, t) {
1791
2929
  this.subscribeStore.unsubscribe(e, t);
1792
2930
  }
1793
2931
  // Token handling
2932
+ /**
2933
+ * Handle OAuth redirect callback and extract tokens from URL query parameters.
2934
+ * This method should be called on the redirect page after authentication.
2935
+ *
2936
+ * @returns Tokens object if found in URL, undefined otherwise
2937
+ *
2938
+ * @example
2939
+ * ```typescript
2940
+ * // On your redirect page (e.g., /auth/callback)
2941
+ * const tokens = passflow.handleTokensRedirect();
2942
+ * if (tokens) {
2943
+ * console.log('Authentication successful', tokens.access_token);
2944
+ * // Tokens are automatically saved to storage
2945
+ * }
2946
+ * ```
2947
+ */
1794
2948
  handleTokensRedirect() {
1795
2949
  return this.checkAndSetTokens();
1796
2950
  }
1797
2951
  checkAndSetTokens() {
1798
- const e = new URLSearchParams(window.location.search), t = e.get("access_token"), s = e.get("refresh_token"), r = e.get("id_token"), o = e.get("scopes")?.split(",") ?? this.scopes;
1799
- let n;
1800
- if (t)
1801
- return n = {
1802
- access_token: t,
1803
- refresh_token: s ?? void 0,
1804
- id_token: r ?? void 0,
1805
- scopes: o
1806
- }, this.storageManager.saveTokens(n), this.tokenCacheService.setTokensCache(n), this.subscribeStore.notify(a.SignIn, { tokens: n, parsedTokens: this.getParsedTokenCache() }), this.submitSessionCheck(), e.delete("access_token"), e.delete("refresh_token"), e.delete("id_token"), e.delete("client_challenge"), e.size > 0 ? window.history.replaceState({}, document.title, `${window.location.pathname}?${e.toString()}`) : window.history.replaceState({}, document.title, window.location.pathname), this.error = void 0, n;
1807
- this.error = this.checkErrorsFromURL();
2952
+ let e = new URLSearchParams(window.location.search), t = !1;
2953
+ if (!e.get("access_token") && window.location.hash) {
2954
+ const c = new URLSearchParams(window.location.hash.substring(1));
2955
+ c.get("access_token") && (e = c, t = !0);
2956
+ }
2957
+ const s = e.get("access_token"), r = e.get("refresh_token"), o = e.get("id_token"), n = e.get("scopes")?.split(",") ?? this.scopes;
2958
+ let d;
2959
+ if (s) {
2960
+ if (!F(s)) {
2961
+ const c = {
2962
+ message: "Invalid access token format received",
2963
+ code: "INVALID_TOKEN_FORMAT"
2964
+ };
2965
+ this.subscribeStore.notify(a.Error, c), this.cleanupUrlParams(t);
2966
+ return;
2967
+ }
2968
+ if (r && !F(r)) {
2969
+ const c = {
2970
+ message: "Invalid refresh token format received",
2971
+ code: "INVALID_TOKEN_FORMAT"
2972
+ };
2973
+ this.subscribeStore.notify(a.Error, c), this.cleanupUrlParams(t);
2974
+ return;
2975
+ }
2976
+ if (o && !F(o)) {
2977
+ const c = {
2978
+ message: "Invalid ID token format received",
2979
+ code: "INVALID_TOKEN_FORMAT"
2980
+ };
2981
+ this.subscribeStore.notify(a.Error, c), this.cleanupUrlParams(t);
2982
+ return;
2983
+ }
2984
+ return d = {
2985
+ access_token: s,
2986
+ refresh_token: r ?? void 0,
2987
+ id_token: o ?? void 0,
2988
+ scopes: n
2989
+ }, this.storageManager.saveTokens(d), this.tokenCacheService.setTokensCache(d), this.subscribeStore.notify(a.SignIn, { tokens: d, parsedTokens: this.getParsedTokens() }), this.submitSessionCheck(), this.cleanupUrlParams(t), this.error = void 0, d;
2990
+ } else
2991
+ this.error = this.checkErrorsFromURL();
1808
2992
  }
1809
2993
  checkErrorsFromURL() {
1810
2994
  const t = new URLSearchParams(window.location.search).get("error");
1811
- if (t)
1812
- return new Error(t);
2995
+ if (t) {
2996
+ const s = Te(t);
2997
+ return new Error(s);
2998
+ }
2999
+ }
3000
+ cleanupUrlParams(e = !1) {
3001
+ if (e)
3002
+ window.history.replaceState({}, document.title, window.location.pathname + window.location.search);
3003
+ else {
3004
+ const t = new URLSearchParams(window.location.search);
3005
+ t.delete("access_token"), t.delete("refresh_token"), t.delete("id_token"), t.delete("client_challenge"), t.size > 0 ? window.history.replaceState({}, document.title, `${window.location.pathname}?${t.toString()}`) : window.history.replaceState({}, document.title, window.location.pathname);
3006
+ }
1813
3007
  }
1814
3008
  setTokensToCacheFromLocalStorage() {
1815
3009
  const e = this.storageManager.getTokens();
1816
3010
  e && this.tokenCacheService.setTokensCache(e);
1817
3011
  }
1818
- getTokensCache() {
1819
- return this.tokenCacheService.getTokensCache();
3012
+ /**
3013
+ * Get cached tokens from memory without triggering a refresh.
3014
+ *
3015
+ * @returns Cached tokens or undefined if not cached
3016
+ *
3017
+ * @example
3018
+ * ```typescript
3019
+ * const tokens = passflow.getCachedTokens();
3020
+ * if (tokens) {
3021
+ * console.log('Access token:', tokens.access_token);
3022
+ * }
3023
+ * ```
3024
+ */
3025
+ getCachedTokens() {
3026
+ return this.tokenCacheService.getTokens();
1820
3027
  }
1821
- getTokensCacheWithRefresh() {
1822
- return this.tokenCacheService.getTokensCacheWithRefresh();
3028
+ /**
3029
+ * Get cached tokens from memory and automatically refresh if expired.
3030
+ *
3031
+ * @returns Promise resolving to tokens or undefined
3032
+ *
3033
+ * @example
3034
+ * ```typescript
3035
+ * const tokens = await passflow.getTokensWithRefresh();
3036
+ * // Tokens are guaranteed to be valid or undefined
3037
+ * ```
3038
+ */
3039
+ getTokensWithRefresh() {
3040
+ return this.tokenCacheService.getTokensWithRefresh();
1823
3041
  }
1824
- getParsedTokenCache() {
1825
- return this.tokenCacheService.getParsedTokenCache();
3042
+ /**
3043
+ * Get parsed JWT tokens with decoded claims.
3044
+ *
3045
+ * @returns Parsed token objects with decoded payloads
3046
+ *
3047
+ * @example
3048
+ * ```typescript
3049
+ * const parsed = passflow.getParsedTokens();
3050
+ * if (parsed?.access_token) {
3051
+ * console.log('User ID:', parsed.access_token.sub);
3052
+ * console.log('Expires at:', new Date(parsed.access_token.exp * 1000));
3053
+ * }
3054
+ * ```
3055
+ */
3056
+ getParsedTokens() {
3057
+ return this.tokenCacheService.getParsedTokens();
1826
3058
  }
1827
- tokensCacheIsExpired() {
1828
- return this.tokenCacheService.tokensCacheIsExpired();
3059
+ /**
3060
+ * Check if the cached tokens are expired.
3061
+ *
3062
+ * @returns True if tokens are expired, false otherwise
3063
+ *
3064
+ * @example
3065
+ * ```typescript
3066
+ * if (passflow.areTokensExpired()) {
3067
+ * await passflow.refreshToken();
3068
+ * }
3069
+ * ```
3070
+ */
3071
+ areTokensExpired() {
3072
+ return this.tokenCacheService.isExpired();
1829
3073
  }
1830
3074
  // Auth delegation methods
3075
+ /**
3076
+ * Check if the user is currently authenticated with valid tokens.
3077
+ *
3078
+ * @returns True if user has valid, non-expired tokens
3079
+ *
3080
+ * @example
3081
+ * ```typescript
3082
+ * if (passflow.isAuthenticated()) {
3083
+ * console.log('User is logged in');
3084
+ * } else {
3085
+ * console.log('User needs to sign in');
3086
+ * }
3087
+ * ```
3088
+ */
1831
3089
  isAuthenticated() {
1832
3090
  const e = this.storageManager.getTokens();
1833
3091
  if (!e || !e.access_token) return !1;
1834
- const t = {
1835
- access_token: d(e.access_token),
1836
- refresh_token: e.refresh_token ? d(e.refresh_token) : void 0
1837
- };
1838
- return this.authService.isAuthenticated(t);
3092
+ const t = this.tokenCacheService.getParsedTokens();
3093
+ return t ? this.authService.isAuthenticated(t) : !1;
1839
3094
  }
3095
+ /**
3096
+ * Sign in a user with email/username and password.
3097
+ *
3098
+ * @param payload - Sign-in credentials and options
3099
+ * @param payload.email - User's email or username
3100
+ * @param payload.password - User's password
3101
+ * @param payload.scopes - Optional scopes to request (defaults to SDK scopes)
3102
+ * @returns Promise with authorization response containing tokens
3103
+ * @throws {PassflowError} If authentication fails
3104
+ *
3105
+ * @example
3106
+ * ```typescript
3107
+ * try {
3108
+ * const response = await passflow.signIn({
3109
+ * email: 'user@example.com',
3110
+ * password: 'secure-password'
3111
+ * });
3112
+ * console.log('Signed in successfully', response.access_token);
3113
+ * } catch (error) {
3114
+ * console.error('Sign in failed', error.message);
3115
+ * }
3116
+ * ```
3117
+ */
1840
3118
  async signIn(e) {
1841
3119
  return await this.authService.signIn(e);
1842
3120
  }
3121
+ /**
3122
+ * Register a new user account with email and password.
3123
+ *
3124
+ * @param payload - Registration details
3125
+ * @param payload.email - User's email address
3126
+ * @param payload.password - User's password
3127
+ * @param payload.username - Optional username
3128
+ * @param payload.scopes - Optional scopes to request
3129
+ * @returns Promise with authorization response containing tokens
3130
+ * @throws {PassflowError} If registration fails
3131
+ *
3132
+ * @example
3133
+ * ```typescript
3134
+ * try {
3135
+ * const response = await passflow.signUp({
3136
+ * email: 'newuser@example.com',
3137
+ * password: 'secure-password',
3138
+ * username: 'newuser'
3139
+ * });
3140
+ * console.log('Account created', response.access_token);
3141
+ * } catch (error) {
3142
+ * console.error('Sign up failed', error.message);
3143
+ * }
3144
+ * ```
3145
+ */
1843
3146
  async signUp(e) {
1844
3147
  return await this.authService.signUp(e);
1845
3148
  }
3149
+ /**
3150
+ * Initiate passwordless authentication by sending a magic link or OTP.
3151
+ *
3152
+ * @param payload - Passwordless sign-in configuration
3153
+ * @param payload.email - User's email address
3154
+ * @param payload.method - Delivery method ('email' or 'sms')
3155
+ * @returns Promise with response indicating if the code was sent
3156
+ * @throws {PassflowError} If request fails
3157
+ *
3158
+ * @example
3159
+ * ```typescript
3160
+ * // Send magic link via email
3161
+ * const response = await passflow.passwordlessSignIn({
3162
+ * email: 'user@example.com',
3163
+ * method: 'email'
3164
+ * });
3165
+ * console.log('Magic link sent:', response.success);
3166
+ * ```
3167
+ */
1846
3168
  passwordlessSignIn(e) {
1847
3169
  return this.authService.passwordlessSignIn(e);
1848
3170
  }
3171
+ /**
3172
+ * Complete passwordless authentication by verifying the OTP or token.
3173
+ *
3174
+ * @param payload - Verification payload
3175
+ * @param payload.email - User's email address
3176
+ * @param payload.code - Verification code from email/SMS
3177
+ * @param payload.scopes - Optional scopes to request
3178
+ * @returns Promise with validation response containing tokens
3179
+ * @throws {PassflowError} If verification fails
3180
+ *
3181
+ * @example
3182
+ * ```typescript
3183
+ * try {
3184
+ * const response = await passflow.passwordlessSignInComplete({
3185
+ * email: 'user@example.com',
3186
+ * code: '123456'
3187
+ * });
3188
+ * console.log('Passwordless sign-in complete', response.access_token);
3189
+ * } catch (error) {
3190
+ * console.error('Invalid code', error.message);
3191
+ * }
3192
+ * ```
3193
+ */
1849
3194
  async passwordlessSignInComplete(e) {
1850
3195
  return await this.authService.passwordlessSignInComplete(e);
1851
3196
  }
3197
+ /**
3198
+ * Centralized error handler for all public methods.
3199
+ * Creates proper ErrorPayload, distinguishes PassflowError from generic Error,
3200
+ * notifies error event, and re-throws the error.
3201
+ *
3202
+ * @param error - The error to handle
3203
+ * @param context - Context description for the error
3204
+ * @throws The original error after handling
3205
+ */
3206
+ handleError(e, t) {
3207
+ const s = {
3208
+ message: e instanceof Error ? e.message : `${t} failed`,
3209
+ originalError: e,
3210
+ code: e instanceof u ? e.id : void 0
3211
+ };
3212
+ throw this.subscribeStore.notify(a.Error, s), e;
3213
+ }
3214
+ /**
3215
+ * Sign out the current user and clear all tokens.
3216
+ *
3217
+ * @returns Promise that resolves when sign-out is complete
3218
+ * @throws {PassflowError} If sign-out request fails
3219
+ *
3220
+ * @example
3221
+ * ```typescript
3222
+ * try {
3223
+ * await passflow.logOut();
3224
+ * console.log('User signed out successfully');
3225
+ * // Redirect to login page
3226
+ * } catch (error) {
3227
+ * console.error('Sign out failed', error.message);
3228
+ * }
3229
+ * ```
3230
+ */
1852
3231
  async logOut() {
1853
3232
  try {
1854
- await this.authService.logOut(), this.storageManager.deleteTokens(), await this.submitSessionCheck();
3233
+ await this.authService.logOut(), this.storageManager.deleteTokens(), this.tokenCacheService.setTokensCache(void 0), this.twoFactorService.clearPartialAuthState(), await this.submitSessionCheck(), this.subscribeStore.notify(a.SignOut, {});
1855
3234
  } catch (e) {
1856
- const t = {
1857
- message: e instanceof Error ? e.message : "Failed to log out",
1858
- originalError: e
1859
- };
1860
- this.subscribeStore.notify(a.Error, t);
3235
+ this.handleError(e, "Log out");
1861
3236
  }
1862
- this.tokenCacheService.setTokensCache(void 0), this.subscribeStore.notify(a.SignOut, {});
1863
3237
  }
3238
+ /**
3239
+ * Initiate federated authentication (OAuth) with a popup window.
3240
+ *
3241
+ * @param payload - Federated authentication configuration
3242
+ * @param payload.provider - OAuth provider (e.g., 'google', 'github', 'microsoft')
3243
+ * @param payload.scopes - Optional scopes to request
3244
+ *
3245
+ * @example
3246
+ * ```typescript
3247
+ * // Sign in with Google using a popup
3248
+ * passflow.federatedAuthWithPopup({
3249
+ * provider: 'google'
3250
+ * });
3251
+ *
3252
+ * // Listen for the result via subscribe
3253
+ * passflow.subscribe((event, payload) => {
3254
+ * if (event === PassflowEvent.SignIn) {
3255
+ * console.log('OAuth sign-in successful', payload.tokens);
3256
+ * }
3257
+ * });
3258
+ * ```
3259
+ */
1864
3260
  federatedAuthWithPopup(e) {
1865
3261
  this.authService.federatedAuthWithPopup(e);
1866
3262
  }
3263
+ /**
3264
+ * Initiate federated authentication (OAuth) with a full-page redirect.
3265
+ *
3266
+ * @param payload - Federated authentication configuration
3267
+ * @param payload.provider - OAuth provider (e.g., 'google', 'github', 'microsoft')
3268
+ * @param payload.scopes - Optional scopes to request
3269
+ * @param payload.redirectUrl - URL to redirect to after authentication
3270
+ *
3271
+ * @example
3272
+ * ```typescript
3273
+ * // Sign in with GitHub using redirect
3274
+ * passflow.federatedAuthWithRedirect({
3275
+ * provider: 'github',
3276
+ * redirectUrl: window.location.origin + '/auth/callback'
3277
+ * });
3278
+ * ```
3279
+ */
1867
3280
  federatedAuthWithRedirect(e) {
1868
3281
  this.authService.federatedAuthWithRedirect(e);
1869
3282
  }
3283
+ /**
3284
+ * Reset the SDK state by clearing all tokens and optionally throwing an error.
3285
+ *
3286
+ * @param error - Optional error message to throw after reset
3287
+ * @throws {Error} If error message is provided
3288
+ *
3289
+ * @example
3290
+ * ```typescript
3291
+ * // Clear tokens without error
3292
+ * passflow.reset();
3293
+ *
3294
+ * // Clear tokens and throw error
3295
+ * try {
3296
+ * passflow.reset('Session expired');
3297
+ * } catch (error) {
3298
+ * console.error('Reset error:', error.message);
3299
+ * }
3300
+ * ```
3301
+ */
1870
3302
  reset(e) {
1871
3303
  if (this.storageManager.deleteTokens(), this.tokenCacheService.setTokensCache(void 0), this.subscribeStore.notify(a.SignOut, {}), e) {
1872
3304
  this.error = new Error(e);
@@ -1877,6 +3309,24 @@ class ie {
1877
3309
  throw this.subscribeStore.notify(a.Error, t), this.error;
1878
3310
  }
1879
3311
  }
3312
+ /**
3313
+ * Refresh the access token using the refresh token.
3314
+ *
3315
+ * @returns Promise with new authorization response containing refreshed tokens
3316
+ * @throws {Error} If no refresh token is found
3317
+ * @throws {PassflowError} If refresh request fails
3318
+ *
3319
+ * @example
3320
+ * ```typescript
3321
+ * try {
3322
+ * const response = await passflow.refreshToken();
3323
+ * console.log('Token refreshed', response.access_token);
3324
+ * } catch (error) {
3325
+ * console.error('Failed to refresh token', error.message);
3326
+ * // Redirect to login
3327
+ * }
3328
+ * ```
3329
+ */
1880
3330
  async refreshToken() {
1881
3331
  if (!this.tokenCacheService.parsedTokensCache?.refresh_token)
1882
3332
  throw new Error("No refresh token found");
@@ -1889,122 +3339,337 @@ class ie {
1889
3339
  }), e;
1890
3340
  }
1891
3341
  }
3342
+ /**
3343
+ * Send a password reset email to the user.
3344
+ *
3345
+ * @param payload - Password reset request
3346
+ * @param payload.email - User's email address
3347
+ * @returns Promise with success response
3348
+ * @throws {PassflowError} If request fails
3349
+ *
3350
+ * @example
3351
+ * ```typescript
3352
+ * try {
3353
+ * await passflow.sendPasswordResetEmail({
3354
+ * email: 'user@example.com'
3355
+ * });
3356
+ * console.log('Password reset email sent');
3357
+ * } catch (error) {
3358
+ * console.error('Failed to send reset email', error.message);
3359
+ * }
3360
+ * ```
3361
+ */
1892
3362
  sendPasswordResetEmail(e) {
1893
3363
  return this.authService.sendPasswordResetEmail(e);
1894
3364
  }
3365
+ /**
3366
+ * Reset password using a reset token (typically from URL after clicking email link).
3367
+ *
3368
+ * @param newPassword - The new password to set
3369
+ * @param scopes - Optional scopes to request after reset
3370
+ * @returns Promise with authorization response containing new tokens
3371
+ * @throws {PassflowError} If reset fails
3372
+ *
3373
+ * @example
3374
+ * ```typescript
3375
+ * // On password reset page (e.g., /reset-password?token=xyz)
3376
+ * try {
3377
+ * const response = await passflow.resetPassword('new-secure-password');
3378
+ * console.log('Password reset successful', response.access_token);
3379
+ * } catch (error) {
3380
+ * console.error('Password reset failed', error.message);
3381
+ * }
3382
+ * ```
3383
+ */
1895
3384
  async resetPassword(e, t) {
1896
3385
  return await this.authService.resetPassword(e, t);
1897
3386
  }
1898
3387
  // App settings
3388
+ /**
3389
+ * Get application settings and configuration.
3390
+ *
3391
+ * @returns Promise with app settings including branding, features, and config
3392
+ * @throws {PassflowError} If request fails
3393
+ *
3394
+ * @example
3395
+ * ```typescript
3396
+ * const settings = await passflow.getAppSettings();
3397
+ * console.log('App name:', settings.name);
3398
+ * console.log('Passwordless enabled:', settings.passwordless_enabled);
3399
+ * ```
3400
+ */
1899
3401
  async getAppSettings() {
1900
3402
  try {
1901
3403
  return await this.appApi.getAppSettings();
1902
3404
  } catch (e) {
1903
- const t = {
1904
- message: e instanceof Error ? e.message : "Failed to get app settings",
1905
- originalError: e
1906
- };
1907
- throw this.subscribeStore.notify(a.Error, t), e;
3405
+ this.handleError(e, "Get app settings");
1908
3406
  }
1909
3407
  }
3408
+ /**
3409
+ * Get all Passflow settings including password policy, passkey settings, etc.
3410
+ *
3411
+ * @returns Promise with all settings
3412
+ * @throws {PassflowError} If request fails
3413
+ *
3414
+ * @example
3415
+ * ```typescript
3416
+ * const settings = await passflow.getSettingsAll();
3417
+ * console.log('Password policy:', settings.password_policy);
3418
+ * console.log('Passkey settings:', settings.passkey);
3419
+ * ```
3420
+ */
1910
3421
  async getSettingsAll() {
1911
3422
  try {
1912
3423
  return await this.settingApi.getSettingsAll();
1913
3424
  } catch (e) {
1914
- const t = {
1915
- message: e instanceof Error ? e.message : "Failed to get all settings",
1916
- originalError: e
1917
- };
1918
- throw this.subscribeStore.notify(a.Error, t), e;
3425
+ this.handleError(e, "Get all settings");
1919
3426
  }
1920
3427
  }
3428
+ /**
3429
+ * Get password policy settings (min length, complexity requirements, etc.).
3430
+ *
3431
+ * @returns Promise with password policy configuration
3432
+ * @throws {PassflowError} If request fails
3433
+ *
3434
+ * @example
3435
+ * ```typescript
3436
+ * const policy = await passflow.getPasswordPolicySettings();
3437
+ * console.log('Min length:', policy.min_length);
3438
+ * console.log('Require uppercase:', policy.require_uppercase);
3439
+ * ```
3440
+ */
1921
3441
  async getPasswordPolicySettings() {
1922
3442
  try {
1923
3443
  return await this.settingApi.getPasswordPolicySettings();
1924
3444
  } catch (e) {
1925
- const t = {
1926
- message: e instanceof Error ? e.message : "Failed to get password policy settings",
1927
- originalError: e
1928
- };
1929
- throw this.subscribeStore.notify(a.Error, t), e;
3445
+ this.handleError(e, "Get password policy settings");
1930
3446
  }
1931
3447
  }
3448
+ /**
3449
+ * Get passkey (WebAuthn) configuration settings.
3450
+ *
3451
+ * @returns Promise with passkey settings
3452
+ * @throws {PassflowError} If request fails
3453
+ *
3454
+ * @example
3455
+ * ```typescript
3456
+ * const passkeySettings = await passflow.getPasskeySettings();
3457
+ * console.log('Passkeys enabled:', passkeySettings.enabled);
3458
+ * console.log('User verification:', passkeySettings.user_verification);
3459
+ * ```
3460
+ */
1932
3461
  async getPasskeySettings() {
1933
3462
  try {
1934
3463
  return await this.settingApi.getPasskeySettings();
1935
3464
  } catch (e) {
1936
- const t = {
1937
- message: e instanceof Error ? e.message : "Failed to get passkey settings",
1938
- originalError: e
1939
- };
1940
- throw this.subscribeStore.notify(a.Error, t), e;
3465
+ this.handleError(e, "Get passkey settings");
1941
3466
  }
1942
3467
  }
1943
3468
  // Passkey methods
3469
+ /**
3470
+ * Register a new user with a passkey (WebAuthn).
3471
+ *
3472
+ * @param payload - Passkey registration configuration
3473
+ * @param payload.email - User's email address
3474
+ * @param payload.username - Optional username
3475
+ * @param payload.scopes - Optional scopes to request
3476
+ * @returns Promise with authorization response containing tokens
3477
+ * @throws {PassflowError} If passkey registration fails
3478
+ *
3479
+ * @example
3480
+ * ```typescript
3481
+ * try {
3482
+ * const response = await passflow.passkeyRegister({
3483
+ * email: 'user@example.com',
3484
+ * username: 'myusername'
3485
+ * });
3486
+ * console.log('Passkey registered', response.access_token);
3487
+ * } catch (error) {
3488
+ * console.error('Passkey registration failed', error.message);
3489
+ * }
3490
+ * ```
3491
+ */
1944
3492
  async passkeyRegister(e) {
1945
3493
  return await this.authService.passkeyRegister(e);
1946
3494
  }
3495
+ /**
3496
+ * Authenticate a user with a passkey (WebAuthn).
3497
+ *
3498
+ * @param payload - Passkey authentication configuration
3499
+ * @param payload.email - Optional user email to pre-fill
3500
+ * @param payload.scopes - Optional scopes to request
3501
+ * @returns Promise with authorization response containing tokens
3502
+ * @throws {PassflowError} If passkey authentication fails
3503
+ *
3504
+ * @example
3505
+ * ```typescript
3506
+ * try {
3507
+ * // Let user select from available passkeys
3508
+ * const response = await passflow.passkeyAuthenticate({});
3509
+ * console.log('Passkey sign-in successful', response.access_token);
3510
+ * } catch (error) {
3511
+ * console.error('Passkey authentication failed', error.message);
3512
+ * }
3513
+ * ```
3514
+ */
1947
3515
  async passkeyAuthenticate(e) {
1948
3516
  return await this.authService.passkeyAuthenticate(e);
1949
3517
  }
1950
3518
  // Token management
3519
+ /**
3520
+ * Manually set tokens (useful after custom authentication flows).
3521
+ * This will save tokens to storage, update cache, and trigger SignIn event.
3522
+ *
3523
+ * @param tokensData - Tokens object to set
3524
+ * @param tokensData.access_token - JWT access token
3525
+ * @param tokensData.refresh_token - Optional refresh token
3526
+ * @param tokensData.id_token - Optional ID token
3527
+ * @param tokensData.scopes - Token scopes
3528
+ *
3529
+ * @example
3530
+ * ```typescript
3531
+ * // Set tokens from a custom auth flow
3532
+ * passflow.setTokens({
3533
+ * access_token: 'eyJhbGci...',
3534
+ * refresh_token: 'eyJhbGci...',
3535
+ * id_token: 'eyJhbGci...',
3536
+ * scopes: ['id', 'offline', 'email']
3537
+ * });
3538
+ * ```
3539
+ */
1951
3540
  setTokens(e) {
1952
3541
  this.storageManager.saveTokens(e), this.tokenCacheService.setTokensCache(e), this.subscribeStore.notify(a.SignIn, {
1953
3542
  tokens: e,
1954
- parsedTokens: this.tokenCacheService.getParsedTokenCache()
3543
+ parsedTokens: this.tokenCacheService.getParsedTokens()
1955
3544
  });
1956
3545
  }
1957
- // Add getTokens method
3546
+ /**
3547
+ * Get current tokens from storage, optionally refreshing if expired.
3548
+ *
3549
+ * @param doRefresh - If true, automatically refresh expired tokens (default: false)
3550
+ * @returns Promise with tokens or undefined if not authenticated
3551
+ *
3552
+ * @example
3553
+ * ```typescript
3554
+ * // Get tokens without refresh
3555
+ * const tokens = await passflow.getTokens();
3556
+ *
3557
+ * // Get tokens and auto-refresh if expired
3558
+ * const freshTokens = await passflow.getTokens(true);
3559
+ * ```
3560
+ */
1958
3561
  async getTokens(e = !1) {
1959
3562
  return await this.authService.getTokens(e);
1960
3563
  }
1961
- // Get token from storage by key
3564
+ /**
3565
+ * Get a specific token from storage by type.
3566
+ *
3567
+ * @param tokenType - Type of token to retrieve ('access_token', 'refresh_token', 'id_token')
3568
+ * @returns Token string or undefined if not found
3569
+ *
3570
+ * @example
3571
+ * ```typescript
3572
+ * const accessToken = passflow.getToken('access_token');
3573
+ * if (accessToken) {
3574
+ * // Use token for API calls
3575
+ * fetch('/api/data', {
3576
+ * headers: { Authorization: `Bearer ${accessToken}` }
3577
+ * });
3578
+ * }
3579
+ * ```
3580
+ */
1962
3581
  getToken(e) {
1963
3582
  return this.storageManager.getToken(e);
1964
3583
  }
1965
3584
  // User passkey methods delegated to UserService
3585
+ /**
3586
+ * Get list of passkeys registered for the current user.
3587
+ *
3588
+ * @returns Promise with array of user's passkeys
3589
+ * @throws {PassflowError} If request fails
3590
+ *
3591
+ * @example
3592
+ * ```typescript
3593
+ * const passkeys = await passflow.getUserPasskeys();
3594
+ * passkeys.forEach(passkey => {
3595
+ * console.log('Passkey:', passkey.name, passkey.id);
3596
+ * });
3597
+ * ```
3598
+ */
1966
3599
  async getUserPasskeys() {
1967
3600
  try {
1968
3601
  return await this.userService.getUserPasskeys();
1969
3602
  } catch (e) {
1970
- const t = {
1971
- message: e instanceof Error ? e.message : "Failed to get user passkeys",
1972
- originalError: e
1973
- };
1974
- throw this.subscribeStore.notify(a.Error, t), e;
3603
+ this.handleError(e, "Get user passkeys");
1975
3604
  }
1976
3605
  }
3606
+ /**
3607
+ * Rename a user's passkey to a friendly name.
3608
+ *
3609
+ * @param name - New friendly name for the passkey
3610
+ * @param passkeyId - ID of the passkey to rename
3611
+ * @returns Promise with success response
3612
+ * @throws {PassflowError} If request fails
3613
+ *
3614
+ * @example
3615
+ * ```typescript
3616
+ * await passflow.renameUserPasskey('My MacBook Pro', 'passkey-123');
3617
+ * console.log('Passkey renamed successfully');
3618
+ * ```
3619
+ */
1977
3620
  async renameUserPasskey(e, t) {
1978
3621
  try {
1979
3622
  return await this.userService.renameUserPasskey(e, t);
1980
3623
  } catch (s) {
1981
- const r = {
1982
- message: s instanceof Error ? s.message : "Failed to rename user passkey",
1983
- originalError: s
1984
- };
1985
- throw this.subscribeStore.notify(a.Error, r), s;
3624
+ this.handleError(s, "Rename user passkey");
1986
3625
  }
1987
3626
  }
3627
+ /**
3628
+ * Delete a passkey from the user's account.
3629
+ *
3630
+ * @param passkeyId - ID of the passkey to delete
3631
+ * @returns Promise with success response
3632
+ * @throws {PassflowError} If request fails
3633
+ *
3634
+ * @example
3635
+ * ```typescript
3636
+ * await passflow.deleteUserPasskey('passkey-123');
3637
+ * console.log('Passkey deleted successfully');
3638
+ * ```
3639
+ */
1988
3640
  async deleteUserPasskey(e) {
1989
3641
  try {
1990
3642
  return await this.userService.deleteUserPasskey(e);
1991
3643
  } catch (t) {
1992
- const s = {
1993
- message: t instanceof Error ? t.message : "Failed to delete user passkey",
1994
- originalError: t
1995
- };
1996
- throw this.subscribeStore.notify(a.Error, s), t;
3644
+ this.handleError(t, "Delete user passkey");
1997
3645
  }
1998
3646
  }
3647
+ /**
3648
+ * Add a new passkey to the current user's account (requires active session).
3649
+ *
3650
+ * @param options - Optional passkey configuration
3651
+ * @param options.relyingPartyId - Optional RP ID for the passkey
3652
+ * @param options.passkeyUsername - Optional username to associate with passkey
3653
+ * @param options.passkeyDisplayName - Optional display name for the passkey
3654
+ * @returns Promise that resolves when passkey is added
3655
+ * @throws {PassflowError} If request fails
3656
+ *
3657
+ * @example
3658
+ * ```typescript
3659
+ * // Add passkey with default settings
3660
+ * await passflow.addUserPasskey();
3661
+ *
3662
+ * // Add passkey with custom display name
3663
+ * await passflow.addUserPasskey({
3664
+ * passkeyDisplayName: 'My iPhone'
3665
+ * });
3666
+ * ```
3667
+ */
1999
3668
  async addUserPasskey(e) {
2000
3669
  try {
2001
3670
  return await this.userService.addUserPasskey(e);
2002
3671
  } catch (t) {
2003
- const s = {
2004
- message: t instanceof Error ? t.message : "Failed to add user passkey",
2005
- originalError: t
2006
- };
2007
- throw this.subscribeStore.notify(a.Error, s), t;
3672
+ this.handleError(t, "Add user passkey");
2008
3673
  }
2009
3674
  }
2010
3675
  // Tenant methods delegated to TenantService
@@ -2019,11 +3684,7 @@ class ie {
2019
3684
  const s = await this.tenant.joinInvitation(e, t);
2020
3685
  return s.scopes = t ?? this.scopes, this.storageManager.saveTokens(s), this.tokenCacheService.setTokensCache(s), s;
2021
3686
  } catch (s) {
2022
- const r = {
2023
- message: s instanceof Error ? s.message : "Failed to join invitation",
2024
- originalError: s
2025
- };
2026
- throw this.subscribeStore.notify(a.Error, r), s;
3687
+ this.handleError(s, "Join invitation");
2027
3688
  }
2028
3689
  }
2029
3690
  /**
@@ -2037,23 +3698,35 @@ class ie {
2037
3698
  const s = await this.tenant.createTenant(e);
2038
3699
  return t && await this.refreshToken(), s;
2039
3700
  } catch (s) {
2040
- const r = {
2041
- message: s instanceof Error ? s.message : "Failed to create tenant",
2042
- originalError: s
2043
- };
2044
- throw this.subscribeStore.notify(a.Error, r), s;
3701
+ this.handleError(s, "Create tenant");
2045
3702
  }
2046
3703
  }
2047
3704
  // Invitation methods delegated to InvitationService
3705
+ /**
3706
+ * Request an invitation link for a user to join a tenant.
3707
+ *
3708
+ * @param payload - Invitation request configuration
3709
+ * @param payload.email - Email address to send invitation to
3710
+ * @param payload.tenantID - Tenant ID for the invitation
3711
+ * @param payload.send_to_email - Whether to send email automatically (default: true)
3712
+ * @returns Promise with invitation link response
3713
+ * @throws {PassflowError} If request fails
3714
+ *
3715
+ * @example
3716
+ * ```typescript
3717
+ * const invitation = await passflow.requestInviteLink({
3718
+ * email: 'newuser@example.com',
3719
+ * tenantID: 'tenant-123',
3720
+ * send_to_email: true
3721
+ * });
3722
+ * console.log('Invitation link:', invitation.link);
3723
+ * ```
3724
+ */
2048
3725
  async requestInviteLink(e) {
2049
3726
  try {
2050
3727
  return e.send_to_email === void 0 && (e.send_to_email = !0), await this.invitationService.requestInviteLink(e);
2051
3728
  } catch (t) {
2052
- const s = {
2053
- message: t instanceof Error ? t.message : "Failed to request invite link",
2054
- originalError: t
2055
- };
2056
- throw this.subscribeStore.notify(a.Error, s), t;
3729
+ this.handleError(t, "Request invite link");
2057
3730
  }
2058
3731
  }
2059
3732
  /**
@@ -2065,85 +3738,1012 @@ class ie {
2065
3738
  try {
2066
3739
  return await this.invitationService.getInvitations(e);
2067
3740
  } catch (t) {
2068
- const s = {
2069
- message: t instanceof Error ? t.message : "Failed to get invitations",
2070
- originalError: t
2071
- };
2072
- throw this.subscribeStore.notify(a.Error, s), t;
3741
+ this.handleError(t, "Get invitations");
2073
3742
  }
2074
3743
  }
3744
+ /**
3745
+ * Delete an invitation by its token.
3746
+ *
3747
+ * @param token - Invitation token to delete
3748
+ * @returns Promise with success response
3749
+ * @throws {PassflowError} If request fails
3750
+ *
3751
+ * @example
3752
+ * ```typescript
3753
+ * await passflow.deleteInvitation('invitation-token-123');
3754
+ * console.log('Invitation deleted');
3755
+ * ```
3756
+ */
2075
3757
  async deleteInvitation(e) {
2076
3758
  try {
2077
3759
  return await this.invitationService.deleteInvitation(e);
2078
3760
  } catch (t) {
2079
- const s = {
2080
- message: t instanceof Error ? t.message : "Failed to delete invitation",
2081
- originalError: t
2082
- };
2083
- throw this.subscribeStore.notify(a.Error, s), t;
3761
+ this.handleError(t, "Delete invitation");
2084
3762
  }
2085
3763
  }
3764
+ /**
3765
+ * Resend an invitation email.
3766
+ *
3767
+ * @param token - Invitation token to resend
3768
+ * @returns Promise with success response
3769
+ * @throws {PassflowError} If request fails
3770
+ *
3771
+ * @example
3772
+ * ```typescript
3773
+ * await passflow.resendInvitation('invitation-token-123');
3774
+ * console.log('Invitation email resent');
3775
+ * ```
3776
+ */
2086
3777
  async resendInvitation(e) {
2087
3778
  try {
2088
3779
  return await this.invitationService.resendInvitation(e);
2089
3780
  } catch (t) {
2090
- const s = {
2091
- message: t instanceof Error ? t.message : "Failed to resend invitation",
2092
- originalError: t
2093
- };
2094
- throw this.subscribeStore.notify(a.Error, s), t;
3781
+ this.handleError(t, "Resend invitation");
2095
3782
  }
2096
3783
  }
3784
+ /**
3785
+ * Get invitation details and link by invitation ID.
3786
+ *
3787
+ * @param invitationID - Invitation ID to retrieve
3788
+ * @returns Promise with invitation link response
3789
+ * @throws {PassflowError} If request fails
3790
+ *
3791
+ * @example
3792
+ * ```typescript
3793
+ * const invitation = await passflow.getInvitationLink('invitation-123');
3794
+ * console.log('Invitation link:', invitation.link);
3795
+ * ```
3796
+ */
2097
3797
  async getInvitationLink(e) {
2098
3798
  try {
2099
3799
  return await this.invitationService.getInvitationLink(e);
2100
3800
  } catch (t) {
2101
- const s = {
2102
- message: t instanceof Error ? t.message : "Failed to get invitation link",
2103
- originalError: t
2104
- };
2105
- throw this.subscribeStore.notify(a.Error, s), t;
3801
+ this.handleError(t, "Get invitation link");
2106
3802
  }
2107
3803
  }
2108
3804
  // Auth redirect helpers
3805
+ /**
3806
+ * Generate an authentication redirect URL for hosted login.
3807
+ *
3808
+ * @param options - Redirect URL configuration
3809
+ * @param options.url - Optional custom Passflow server URL
3810
+ * @param options.redirectUrl - URL to redirect to after authentication
3811
+ * @param options.scopes - Optional scopes to request
3812
+ * @param options.appId - Optional app ID to use
3813
+ * @returns Authentication redirect URL
3814
+ *
3815
+ * @example
3816
+ * ```typescript
3817
+ * const authUrl = passflow.authRedirectUrl({
3818
+ * redirectUrl: window.location.origin + '/auth/callback',
3819
+ * scopes: ['id', 'email', 'offline']
3820
+ * });
3821
+ * console.log('Auth URL:', authUrl);
3822
+ * // Use this URL for custom navigation logic
3823
+ * ```
3824
+ */
2109
3825
  authRedirectUrl(e = {}) {
2110
3826
  return this.authService.authRedirectUrl(e);
2111
3827
  }
3828
+ /**
3829
+ * Redirect to the Passflow hosted login page.
3830
+ *
3831
+ * @param options - Redirect configuration
3832
+ * @param options.url - Optional custom Passflow server URL
3833
+ * @param options.redirectUrl - URL to redirect to after authentication
3834
+ * @param options.scopes - Optional scopes to request
3835
+ * @param options.appId - Optional app ID to use
3836
+ *
3837
+ * @example
3838
+ * ```typescript
3839
+ * // Redirect to hosted login page
3840
+ * passflow.authRedirect({
3841
+ * redirectUrl: window.location.origin + '/auth/callback'
3842
+ * });
3843
+ * ```
3844
+ */
2112
3845
  authRedirect(e = {}) {
2113
3846
  this.authService.authRedirect(e);
2114
3847
  }
3848
+ /**
3849
+ * Get the current token delivery mode.
3850
+ * Returns the mode determined by the server (json_body, cookie, or mobile).
3851
+ *
3852
+ * @returns The current token delivery mode
3853
+ *
3854
+ * @example
3855
+ * ```typescript
3856
+ * import { TokenDeliveryMode } from '@passflow/core';
3857
+ *
3858
+ * const mode = passflow.getDeliveryMode();
3859
+ * if (mode === TokenDeliveryMode.Cookie) {
3860
+ * console.log('Using cookie-based authentication');
3861
+ * } else {
3862
+ * console.log('Using JSON body authentication');
3863
+ * }
3864
+ * ```
3865
+ */
3866
+ getDeliveryMode() {
3867
+ return this.authService.tokenDeliveryManager.getMode();
3868
+ }
3869
+ /**
3870
+ * Restore and validate session for cookie mode on page load.
3871
+ * Only applicable when using cookie-based token delivery.
3872
+ * Validates that HttpOnly cookies are still valid with the server.
3873
+ *
3874
+ * This method is automatically called on SDK initialization for cookie mode,
3875
+ * but can be called manually to re-validate the session.
3876
+ *
3877
+ * @returns Promise resolving to true if session is valid, false otherwise
3878
+ *
3879
+ * @example
3880
+ * ```typescript
3881
+ * // Check if session is valid (useful after page reload)
3882
+ * const isValid = await passflow.restoreSession();
3883
+ * if (isValid) {
3884
+ * console.log('Session restored successfully');
3885
+ * } else {
3886
+ * console.log('Session expired, please sign in again');
3887
+ * }
3888
+ * ```
3889
+ */
3890
+ async restoreSession() {
3891
+ return await this.authService.restoreSession();
3892
+ }
3893
+ // Two-Factor Authentication methods
3894
+ /**
3895
+ * Get the current 2FA enrollment status for the authenticated user.
3896
+ *
3897
+ * @returns Promise with 2FA status including enabled state and policy
3898
+ * @throws {PassflowError} If request fails
3899
+ *
3900
+ * @example
3901
+ * ```typescript
3902
+ * const status = await passflow.getTwoFactorStatus();
3903
+ * console.log('2FA enabled:', status.enabled);
3904
+ * console.log('Recovery codes remaining:', status.recovery_codes_remaining);
3905
+ * ```
3906
+ */
3907
+ async getTwoFactorStatus() {
3908
+ try {
3909
+ return await this.twoFactorService.getStatus();
3910
+ } catch (e) {
3911
+ this.handleError(e, "Get 2FA status");
3912
+ }
3913
+ }
3914
+ /**
3915
+ * Begin the 2FA setup process for the authenticated user.
3916
+ * Returns a secret and QR code to scan with an authenticator app.
3917
+ *
3918
+ * @returns Promise with setup response containing secret and QR code
3919
+ * @throws {PassflowError} If request fails
3920
+ *
3921
+ * @example
3922
+ * ```typescript
3923
+ * const setup = await passflow.beginTwoFactorSetup();
3924
+ * console.log('Scan this QR code:', setup.qr_code);
3925
+ * console.log('Or enter this secret:', setup.secret);
3926
+ * ```
3927
+ */
3928
+ async beginTwoFactorSetup() {
3929
+ try {
3930
+ return await this.twoFactorService.beginSetup();
3931
+ } catch (e) {
3932
+ this.handleError(e, "Begin 2FA setup");
3933
+ }
3934
+ }
3935
+ /**
3936
+ * Confirm 2FA setup by verifying a TOTP code from the authenticator app.
3937
+ * Returns recovery codes that MUST be saved by the user.
3938
+ *
3939
+ * @param code - 6-digit TOTP code from authenticator app
3940
+ * @returns Promise with confirmation response including recovery codes
3941
+ * @throws {PassflowError} If verification fails
3942
+ *
3943
+ * @example
3944
+ * ```typescript
3945
+ * const result = await passflow.confirmTwoFactorSetup('123456');
3946
+ * console.log('SAVE THESE RECOVERY CODES:', result.recovery_codes);
3947
+ * // Display recovery codes to user for safekeeping
3948
+ * ```
3949
+ */
3950
+ async confirmTwoFactorSetup(e) {
3951
+ try {
3952
+ return await this.twoFactorService.confirmSetup(e);
3953
+ } catch (t) {
3954
+ this.handleError(t, "Confirm 2FA setup");
3955
+ }
3956
+ }
3957
+ /**
3958
+ * Verify a TOTP code during login when 2FA is required.
3959
+ * Completes the authentication process and saves tokens.
3960
+ *
3961
+ * @param code - 6-digit TOTP code from authenticator app
3962
+ * @returns Promise with verification response containing tokens
3963
+ * @throws {PassflowError} If verification fails
3964
+ *
3965
+ * @example
3966
+ * ```typescript
3967
+ * // After signIn returns requires_2fa: true
3968
+ * if (passflow.isTwoFactorVerificationRequired()) {
3969
+ * const response = await passflow.verifyTwoFactor('123456');
3970
+ * console.log('Authentication complete', response.access_token);
3971
+ * }
3972
+ * ```
3973
+ */
3974
+ async verifyTwoFactor(e) {
3975
+ try {
3976
+ const t = await this.twoFactorService.verify(e);
3977
+ return this.storageManager.saveTokens(t), this.tokenCacheService.setTokensCache(t), this.subscribeStore.notify(a.SignIn, {
3978
+ tokens: t,
3979
+ parsedTokens: this.tokenCacheService.getParsedTokens()
3980
+ }), await this.submitSessionCheck(), t;
3981
+ } catch (t) {
3982
+ this.handleError(t, "Verify 2FA");
3983
+ }
3984
+ }
3985
+ /**
3986
+ * Use a recovery code for authentication when TOTP is unavailable.
3987
+ * Completes the authentication process and saves tokens.
3988
+ *
3989
+ * @param code - Recovery code from the list provided during setup
3990
+ * @returns Promise with recovery response containing tokens and remaining codes count
3991
+ * @throws {PassflowError} If recovery code is invalid
3992
+ *
3993
+ * @example
3994
+ * ```typescript
3995
+ * // After signIn returns requires_2fa: true
3996
+ * const result = await passflow.useTwoFactorRecoveryCode('ABCD-1234');
3997
+ * console.log('Authenticated with recovery code');
3998
+ * console.log(`${result.remaining_recovery_codes} recovery codes remaining`);
3999
+ * ```
4000
+ */
4001
+ async useTwoFactorRecoveryCode(e) {
4002
+ try {
4003
+ const t = await this.twoFactorService.useRecoveryCode(e);
4004
+ return this.storageManager.saveTokens(t), this.tokenCacheService.setTokensCache(t), this.subscribeStore.notify(a.SignIn, {
4005
+ tokens: t,
4006
+ parsedTokens: this.tokenCacheService.getParsedTokens()
4007
+ }), await this.submitSessionCheck(), t;
4008
+ } catch (t) {
4009
+ this.handleError(t, "Use 2FA recovery code");
4010
+ }
4011
+ }
4012
+ /**
4013
+ * Disable 2FA for the authenticated user.
4014
+ * Requires verification with a current TOTP code.
4015
+ *
4016
+ * @param code - Current 6-digit TOTP code for verification
4017
+ * @returns Promise with success response
4018
+ * @throws {PassflowError} If verification fails
4019
+ *
4020
+ * @example
4021
+ * ```typescript
4022
+ * await passflow.disableTwoFactor('123456');
4023
+ * console.log('2FA disabled successfully');
4024
+ * ```
4025
+ */
4026
+ async disableTwoFactor(e) {
4027
+ try {
4028
+ return await this.twoFactorService.disable(e);
4029
+ } catch (t) {
4030
+ this.handleError(t, "Disable 2FA");
4031
+ }
4032
+ }
4033
+ /**
4034
+ * Regenerate recovery codes for the authenticated user.
4035
+ * Old recovery codes will be invalidated. Requires verification with a current TOTP code.
4036
+ *
4037
+ * @param code - Current 6-digit TOTP code for verification
4038
+ * @returns Promise with response containing new recovery codes
4039
+ * @throws {PassflowError} If verification fails
4040
+ *
4041
+ * @example
4042
+ * ```typescript
4043
+ * const result = await passflow.regenerateTwoFactorRecoveryCodes('123456');
4044
+ * console.log('New recovery codes:', result.recovery_codes);
4045
+ * // Display new recovery codes to user for safekeeping
4046
+ * ```
4047
+ */
4048
+ async regenerateTwoFactorRecoveryCodes(e) {
4049
+ try {
4050
+ return await this.twoFactorService.regenerateRecoveryCodes(e);
4051
+ } catch (t) {
4052
+ this.handleError(t, "Regenerate 2FA recovery codes");
4053
+ }
4054
+ }
4055
+ /**
4056
+ * Check if 2FA verification is currently required (local state check).
4057
+ * Returns true if user has signed in but needs to complete 2FA verification.
4058
+ *
4059
+ * @returns True if 2FA verification is pending, false otherwise
4060
+ *
4061
+ * @example
4062
+ * ```typescript
4063
+ * if (passflow.isTwoFactorVerificationRequired()) {
4064
+ * // Show 2FA code input UI
4065
+ * console.log('Please enter your 2FA code');
4066
+ * }
4067
+ * ```
4068
+ */
4069
+ isTwoFactorVerificationRequired() {
4070
+ return this.twoFactorService.isVerificationRequired();
4071
+ }
4072
+ /**
4073
+ * Get configured TOTP digit count for the current app
4074
+ * @returns Number of digits (6 or 8) configured for TOTP codes
4075
+ *
4076
+ * @example
4077
+ * ```typescript
4078
+ * const digits = passflow.getTotpDigits();
4079
+ * console.log(`TOTP codes should be ${digits} digits`);
4080
+ * ```
4081
+ */
4082
+ getTotpDigits() {
4083
+ return this.twoFactorService.getTotpDigits();
4084
+ }
4085
+ // Magic Link 2FA Setup Methods
4086
+ /**
4087
+ * Validate a magic link token for 2FA setup.
4088
+ * Used when a user clicks on an admin-generated magic link to set up 2FA.
4089
+ *
4090
+ * The magic link creates a scoped session that can ONLY be used for 2FA setup,
4091
+ * not for regular authentication.
4092
+ *
4093
+ * @param token - Magic link token from URL parameter
4094
+ * @returns Promise with validation response containing scoped session or error
4095
+ *
4096
+ * @example
4097
+ * ```typescript
4098
+ * // On the magic link landing page (e.g., /2fa-setup/:token)
4099
+ * const urlToken = extractTokenFromUrl();
4100
+ * const result = await passflow.validateTwoFactorSetupMagicLink(urlToken);
4101
+ *
4102
+ * if (result.success) {
4103
+ * console.log('Magic link validated');
4104
+ * console.log('User ID:', result.userId);
4105
+ * console.log('Session expires in:', result.expiresIn, 'seconds');
4106
+ * // Show 2FA setup form
4107
+ * } else {
4108
+ * console.error('Validation failed:', result.error?.message);
4109
+ * // Show error UI
4110
+ * }
4111
+ * ```
4112
+ */
4113
+ async validateTwoFactorSetupMagicLink(e) {
4114
+ return await this.twoFactorService.validateTwoFactorSetupMagicLink(e);
4115
+ }
4116
+ /**
4117
+ * Get the current magic link session (if any).
4118
+ * Used by React SDK components to access session data.
4119
+ *
4120
+ * @returns Active magic link session or null if none/expired
4121
+ *
4122
+ * @example
4123
+ * ```typescript
4124
+ * const session = passflow.getMagicLinkSession();
4125
+ * if (session) {
4126
+ * console.log('Active session for user:', session.userId);
4127
+ * console.log('Expires at:', new Date(session.expiresAt));
4128
+ * }
4129
+ * ```
4130
+ */
4131
+ getMagicLinkSession() {
4132
+ return this.twoFactorService.getMagicLinkSession();
4133
+ }
4134
+ /**
4135
+ * Check if a magic link session is currently active.
4136
+ *
4137
+ * @returns True if magic link session is active and not expired
4138
+ *
4139
+ * @example
4140
+ * ```typescript
4141
+ * if (passflow.hasMagicLinkSession()) {
4142
+ * // User can proceed with 2FA setup
4143
+ * } else {
4144
+ * // Redirect to error page or request new link
4145
+ * }
4146
+ * ```
4147
+ */
4148
+ hasMagicLinkSession() {
4149
+ return this.twoFactorService.hasMagicLinkSession();
4150
+ }
4151
+ /**
4152
+ * Clear the magic link session.
4153
+ * Called after successful 2FA setup or on error.
4154
+ *
4155
+ * @example
4156
+ * ```typescript
4157
+ * // After 2FA setup is complete
4158
+ * passflow.clearMagicLinkSession();
4159
+ * // Redirect to sign-in
4160
+ * ```
4161
+ */
4162
+ clearMagicLinkSession() {
4163
+ this.twoFactorService.clearMagicLinkSession();
4164
+ }
4165
+ };
4166
+ O.version = Z;
4167
+ let L = O;
4168
+ class l extends Error {
4169
+ constructor(e) {
4170
+ super(e.message), this.name = "M2MError", this.code = e.code, this.status = e.status ?? 400, this.errorUri = e.errorUri, this.rateLimitInfo = e.rateLimitInfo, this.headers = e.headers, this.cause = e.cause, this.timestamp = (/* @__PURE__ */ new Date()).toISOString(), Error.captureStackTrace && Error.captureStackTrace(this, l);
4171
+ }
4172
+ /**
4173
+ * Create an M2MError from an OAuth 2.0 error response
4174
+ */
4175
+ static fromOAuthError(e, t, s) {
4176
+ const r = s ? l.parseRateLimitHeaders(s) : void 0;
4177
+ return new l({
4178
+ code: e.error,
4179
+ message: e.error_description ?? l.getDefaultMessage(e.error),
4180
+ status: t,
4181
+ errorUri: e.error_uri,
4182
+ rateLimitInfo: r,
4183
+ headers: s
4184
+ });
4185
+ }
4186
+ /**
4187
+ * Create an M2MError from a network or other error
4188
+ */
4189
+ static fromError(e, t = "server_error") {
4190
+ return new l({
4191
+ code: t,
4192
+ message: e.message || "An unexpected error occurred",
4193
+ status: 500,
4194
+ cause: e
4195
+ });
4196
+ }
4197
+ /**
4198
+ * Parse rate limit headers from response
4199
+ */
4200
+ static parseRateLimitHeaders(e) {
4201
+ const t = e["x-ratelimit-limit"], s = e["x-ratelimit-remaining"], r = e["x-ratelimit-reset"] || e["retry-after"];
4202
+ if (t && s && r)
4203
+ return {
4204
+ limit: parseInt(t, 10),
4205
+ remaining: parseInt(s, 10),
4206
+ reset: parseInt(r, 10)
4207
+ };
4208
+ }
4209
+ /**
4210
+ * Get default error message for an error code
4211
+ */
4212
+ static getDefaultMessage(e) {
4213
+ return {
4214
+ invalid_request: "The request is missing a required parameter or is otherwise malformed.",
4215
+ invalid_client: "Client authentication failed. Verify your client credentials.",
4216
+ invalid_grant: "The provided authorization grant is invalid or expired.",
4217
+ invalid_scope: "The requested scope is invalid, unknown, or exceeds the allowed scopes.",
4218
+ unauthorized_client: "The client is not authorized to use this grant type.",
4219
+ unsupported_grant_type: "The authorization grant type is not supported.",
4220
+ rate_limit_exceeded: "Too many requests. Please retry after the rate limit window resets.",
4221
+ server_error: "The authorization server encountered an unexpected error.",
4222
+ temporarily_unavailable: "The authorization server is temporarily unavailable. Please try again later."
4223
+ }[e] || "An unknown error occurred.";
4224
+ }
4225
+ /**
4226
+ * Check if the error is retryable
4227
+ */
4228
+ isRetryable() {
4229
+ return this.code === "server_error" || this.code === "temporarily_unavailable" || this.code === "rate_limit_exceeded" || this.status >= 500;
4230
+ }
4231
+ /**
4232
+ * Get suggested wait time before retry (in milliseconds)
4233
+ */
4234
+ getRetryAfter() {
4235
+ if (this.rateLimitInfo?.reset) {
4236
+ const e = Math.floor(Date.now() / 1e3), t = this.rateLimitInfo.reset - e;
4237
+ return Math.max(t * 1e3, 1e3);
4238
+ }
4239
+ return 1e3;
4240
+ }
4241
+ /**
4242
+ * Convert to JSON-serializable object
4243
+ */
4244
+ toJSON() {
4245
+ return {
4246
+ name: this.name,
4247
+ code: this.code,
4248
+ message: this.message,
4249
+ status: this.status,
4250
+ errorUri: this.errorUri,
4251
+ rateLimitInfo: this.rateLimitInfo,
4252
+ timestamp: this.timestamp
4253
+ };
4254
+ }
4255
+ /**
4256
+ * Create a human-readable string representation
4257
+ */
4258
+ toString() {
4259
+ let e = `M2MError [${this.code}]: ${this.message}`;
4260
+ return this.status && (e += ` (HTTP ${this.status})`), e;
4261
+ }
4262
+ }
4263
+ class N extends l {
4264
+ constructor(e, t) {
4265
+ super({
4266
+ code: "temporarily_unavailable",
4267
+ message: e,
4268
+ status: 0,
4269
+ cause: t
4270
+ }), this.name = "M2MNetworkError";
4271
+ }
4272
+ }
4273
+ class P extends l {
4274
+ constructor(e, t) {
4275
+ super({
4276
+ code: "invalid_request",
4277
+ message: e,
4278
+ status: 400,
4279
+ cause: t
4280
+ }), this.name = "M2MTokenParseError";
4281
+ }
4282
+ }
4283
+ class U extends l {
4284
+ constructor(e) {
4285
+ super({
4286
+ code: "invalid_request",
4287
+ message: e,
4288
+ status: 400
4289
+ }), this.name = "M2MConfigError";
4290
+ }
4291
+ }
4292
+ const Ge = {
4293
+ InvalidRequest: "invalid_request",
4294
+ InvalidClient: "invalid_client",
4295
+ InvalidGrant: "invalid_grant",
4296
+ InvalidScope: "invalid_scope",
4297
+ UnauthorizedClient: "unauthorized_client",
4298
+ UnsupportedGrantType: "unsupported_grant_type",
4299
+ RateLimitExceeded: "rate_limit_exceeded",
4300
+ ServerError: "server_error",
4301
+ TemporarilyUnavailable: "temporarily_unavailable"
4302
+ }, m = {
4303
+ /** Default token endpoint path */
4304
+ TOKEN_ENDPOINT: "/oauth2/token",
4305
+ /** Default request timeout in milliseconds */
4306
+ TIMEOUT: 1e4,
4307
+ /** Default number of retry attempts */
4308
+ RETRIES: 3,
4309
+ /** Default delay between retries in milliseconds */
4310
+ RETRY_DELAY: 1e3,
4311
+ /** Default refresh threshold in seconds */
4312
+ REFRESH_THRESHOLD: 30,
4313
+ /** Content-Type for token requests */
4314
+ CONTENT_TYPE: "application/x-www-form-urlencoded"
4315
+ };
4316
+ class xe {
4317
+ constructor() {
4318
+ this.cache = /* @__PURE__ */ new Map();
4319
+ }
4320
+ async get(e) {
4321
+ const t = this.cache.get(e);
4322
+ return t ? Date.now() >= t.expiresAt ? (this.cache.delete(e), null) : t.token : null;
4323
+ }
4324
+ async set(e, t, s) {
4325
+ this.cache.set(e, {
4326
+ token: t,
4327
+ expiresAt: Date.now() + s * 1e3
4328
+ });
4329
+ }
4330
+ async delete(e) {
4331
+ this.cache.delete(e);
4332
+ }
4333
+ }
4334
+ const Ue = {
4335
+ shouldRetry(i, e) {
4336
+ return e >= 3 ? !1 : i.code === "server_error" || i.code === "temporarily_unavailable" || i.code === "rate_limit_exceeded" || i.status !== void 0 && i.status >= 500;
4337
+ },
4338
+ getDelay(i) {
4339
+ return Math.pow(2, i - 1) * 1e3;
4340
+ }
4341
+ };
4342
+ class Ve {
4343
+ /**
4344
+ * Create a new M2M client
4345
+ *
4346
+ * @param config - Client configuration
4347
+ * @throws {M2MConfigError} If required configuration is missing
4348
+ *
4349
+ * @example
4350
+ * ```typescript
4351
+ * const m2m = new M2MClient({
4352
+ * url: 'https://auth.yourapp.com',
4353
+ * clientId: 'your-client-id',
4354
+ * clientSecret: 'your-client-secret',
4355
+ * });
4356
+ * ```
4357
+ */
4358
+ constructor(e) {
4359
+ if (!e.url)
4360
+ throw new U("M2M client requires a URL");
4361
+ if (!e.clientId)
4362
+ throw new U("M2M client requires a clientId");
4363
+ if (!e.clientSecret)
4364
+ throw new U("M2M client requires a clientSecret");
4365
+ const t = e.url.replace(/\/$/, "");
4366
+ this.config = {
4367
+ url: t,
4368
+ clientId: e.clientId,
4369
+ clientSecret: e.clientSecret,
4370
+ scopes: e.scopes,
4371
+ audience: e.audience,
4372
+ autoRefresh: e.autoRefresh ?? !1,
4373
+ refreshThreshold: e.refreshThreshold ?? m.REFRESH_THRESHOLD,
4374
+ timeout: e.timeout ?? m.TIMEOUT,
4375
+ retries: e.retries ?? m.RETRIES,
4376
+ retryDelay: e.retryDelay ?? m.RETRY_DELAY,
4377
+ retryStrategy: e.retryStrategy,
4378
+ cache: e.cache,
4379
+ onTokenRequest: e.onTokenRequest,
4380
+ onTokenResponse: e.onTokenResponse,
4381
+ onError: e.onError
4382
+ }, this.cache = e.cache ?? new xe(), this.retryStrategy = e.retryStrategy ?? Ue, this.tokenEndpoint = `${t}${m.TOKEN_ENDPOINT}`;
4383
+ }
4384
+ /**
4385
+ * Get the cache key for this client
4386
+ */
4387
+ getCacheKey(e, t) {
4388
+ const s = e?.sort().join(",") || "", r = t?.sort().join(",") || "";
4389
+ return `m2m:${this.config.clientId}:${s}:${r}`;
4390
+ }
4391
+ /**
4392
+ * Request an access token from the authorization server
4393
+ *
4394
+ * @param options - Optional request overrides
4395
+ * @returns Token response
4396
+ * @throws {M2MError} On authentication failure
4397
+ *
4398
+ * @example
4399
+ * ```typescript
4400
+ * // Basic usage
4401
+ * const token = await m2m.getToken();
4402
+ *
4403
+ * // With options
4404
+ * const token = await m2m.getToken({
4405
+ * scopes: ['users:read'],
4406
+ * forceRefresh: true,
4407
+ * });
4408
+ * ```
4409
+ */
4410
+ async getToken(e) {
4411
+ const t = e?.scopes ?? this.config.scopes, s = e?.audience ?? this.config.audience, r = this.getCacheKey(t, s);
4412
+ if (!e?.forceRefresh) {
4413
+ const o = await this.cache.get(r);
4414
+ if (o && !this.isTokenExpired(o))
4415
+ return o;
4416
+ }
4417
+ return this.requestToken(t, s, r);
4418
+ }
4419
+ /**
4420
+ * Get a valid token, automatically refreshing if needed
4421
+ *
4422
+ * When autoRefresh is enabled, this will proactively refresh tokens
4423
+ * that are about to expire (within refreshThreshold seconds).
4424
+ *
4425
+ * @returns Valid token response
4426
+ * @throws {M2MError} On authentication failure
4427
+ *
4428
+ * @example
4429
+ * ```typescript
4430
+ * // Always returns a valid, non-expired token
4431
+ * const token = await m2m.getValidToken();
4432
+ * ```
4433
+ */
4434
+ async getValidToken() {
4435
+ const e = this.config.scopes, t = this.config.audience, s = this.getCacheKey(e, t), r = await this.cache.get(s);
4436
+ if (r) {
4437
+ if (this.config.autoRefresh && this.isTokenExpired(r, this.config.refreshThreshold))
4438
+ return this.requestToken(e, t, s);
4439
+ if (!this.isTokenExpired(r))
4440
+ return r;
4441
+ }
4442
+ return this.requestToken(e, t, s);
4443
+ }
4444
+ /**
4445
+ * Request a new token from the authorization server
4446
+ */
4447
+ async requestToken(e, t, s) {
4448
+ const r = {
4449
+ grant_type: "client_credentials",
4450
+ client_id: this.config.clientId,
4451
+ client_secret: this.config.clientSecret
4452
+ };
4453
+ e && e.length > 0 && (r.scope = e.join(" ")), t && t.length > 0 && (r.audience = t.join(" ")), this.config.onTokenRequest && this.config.onTokenRequest({
4454
+ clientId: this.config.clientId,
4455
+ scopes: e ?? [],
4456
+ audience: t ?? [],
4457
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
4458
+ });
4459
+ const o = await this.executeWithRetry(() => this.doTokenRequest(r));
4460
+ return o.issued_at = Math.floor(Date.now() / 1e3), s && await this.cache.set(s, o, o.expires_in), this.config.onTokenResponse && this.config.onTokenResponse(o), o;
4461
+ }
4462
+ /**
4463
+ * Execute the actual HTTP request to the token endpoint
4464
+ */
4465
+ async doTokenRequest(e) {
4466
+ const t = new URLSearchParams();
4467
+ t.append("grant_type", e.grant_type), t.append("client_id", e.client_id), t.append("client_secret", e.client_secret), e.scope && t.append("scope", e.scope), e.audience && t.append("audience", e.audience);
4468
+ const s = new AbortController(), r = setTimeout(() => s.abort(), this.config.timeout);
4469
+ try {
4470
+ const o = await fetch(this.tokenEndpoint, {
4471
+ method: "POST",
4472
+ headers: {
4473
+ "Content-Type": m.CONTENT_TYPE,
4474
+ Accept: "application/json"
4475
+ },
4476
+ body: t.toString(),
4477
+ signal: s.signal
4478
+ });
4479
+ clearTimeout(r);
4480
+ const n = {};
4481
+ o.headers.forEach((c, g) => {
4482
+ n[g.toLowerCase()] = c;
4483
+ });
4484
+ const d = await o.json();
4485
+ if (!o.ok) {
4486
+ const c = l.fromOAuthError(
4487
+ {
4488
+ error: d.error || "server_error",
4489
+ error_description: d.error_description || d.message,
4490
+ error_uri: d.error_uri
4491
+ },
4492
+ o.status,
4493
+ n
4494
+ );
4495
+ throw this.config.onError && this.config.onError({
4496
+ error: c.code,
4497
+ error_description: c.message
4498
+ }), c;
4499
+ }
4500
+ return d;
4501
+ } catch (o) {
4502
+ throw clearTimeout(r), o instanceof Error && o.name === "AbortError" ? new N(`Request timed out after ${this.config.timeout}ms`) : o instanceof TypeError && o.message.includes("fetch") ? new N(`Network error: ${o.message}`, o) : o instanceof l ? o : l.fromError(o instanceof Error ? o : new Error(String(o)));
4503
+ }
4504
+ }
4505
+ /**
4506
+ * Execute a request with retry logic
4507
+ */
4508
+ async executeWithRetry(e) {
4509
+ let t;
4510
+ for (let s = 1; s <= this.config.retries; s++)
4511
+ try {
4512
+ return await e();
4513
+ } catch (r) {
4514
+ if (!(r instanceof l))
4515
+ throw r;
4516
+ if (t = r, s < this.config.retries && this.retryStrategy.shouldRetry({ code: r.code, status: r.status }, s)) {
4517
+ const o = this.retryStrategy.getDelay(s);
4518
+ await this.sleep(o);
4519
+ continue;
4520
+ }
4521
+ throw r;
4522
+ }
4523
+ throw t ?? new l({ code: "server_error", message: "Request failed after retries" });
4524
+ }
4525
+ /**
4526
+ * Sleep for a given duration
4527
+ */
4528
+ sleep(e) {
4529
+ return new Promise((t) => setTimeout(t, e));
4530
+ }
4531
+ /**
4532
+ * Get the currently cached token without making a request
4533
+ *
4534
+ * @returns Cached token or null if not cached
4535
+ *
4536
+ * @example
4537
+ * ```typescript
4538
+ * const token = m2m.getCachedToken();
4539
+ * if (token && !m2m.isTokenExpired(token)) {
4540
+ * console.log('Using cached token');
4541
+ * }
4542
+ * ```
4543
+ */
4544
+ getCachedToken() {
4545
+ const e = this.cache;
4546
+ if ("cache" in e) {
4547
+ const t = this.getCacheKey(this.config.scopes, this.config.audience);
4548
+ return e.cache.get(
4549
+ t
4550
+ )?.token ?? null;
4551
+ }
4552
+ return null;
4553
+ }
4554
+ /**
4555
+ * Check if a token is expired or about to expire
4556
+ *
4557
+ * @param token - Token to check (uses issued_at + expires_in if available)
4558
+ * @param threshold - Seconds before actual expiry to consider expired (default: 0)
4559
+ * @returns true if expired or about to expire
4560
+ *
4561
+ * @example
4562
+ * ```typescript
4563
+ * if (m2m.isTokenExpired(token)) {
4564
+ * console.log('Token is expired');
4565
+ * }
4566
+ *
4567
+ * // Check if expiring within 5 minutes
4568
+ * if (m2m.isTokenExpired(token, 300)) {
4569
+ * console.log('Token expires soon');
4570
+ * }
4571
+ * ```
4572
+ */
4573
+ isTokenExpired(e, t = 0) {
4574
+ if (!e) return !0;
4575
+ const s = Math.floor(Date.now() / 1e3), o = (e.issued_at ?? s - e.expires_in) + e.expires_in;
4576
+ return s >= o - t;
4577
+ }
4578
+ /**
4579
+ * Parse token claims from a JWT access token
4580
+ *
4581
+ * @param token - JWT access token string
4582
+ * @returns Decoded token claims
4583
+ * @throws {M2MTokenParseError} If token format is invalid
4584
+ *
4585
+ * @example
4586
+ * ```typescript
4587
+ * const token = await m2m.getToken();
4588
+ * const claims = m2m.parseToken(token.access_token);
4589
+ * console.log('Client ID:', claims.client_id);
4590
+ * console.log('Scopes:', claims.scopes);
4591
+ * ```
4592
+ */
4593
+ parseToken(e) {
4594
+ try {
4595
+ const t = e.split(".");
4596
+ if (t.length !== 3)
4597
+ throw new P("Invalid JWT format: expected 3 parts");
4598
+ const s = t[1];
4599
+ if (!s)
4600
+ throw new P("Invalid JWT format: missing payload");
4601
+ const r = atob(s.replace(/-/g, "+").replace(/_/g, "/")), o = JSON.parse(r);
4602
+ return o.scopes && typeof o.scopes == "string" ? o.scopes = o.scopes.split(" ") : o.scopes || (o.scopes = []), o;
4603
+ } catch (t) {
4604
+ throw t instanceof P ? t : new P(`Failed to parse token: ${t instanceof Error ? t.message : "Unknown error"}`);
4605
+ }
4606
+ }
4607
+ /**
4608
+ * Clear the token cache, forcing a new request on next getToken()
4609
+ *
4610
+ * @example
4611
+ * ```typescript
4612
+ * m2m.clearCache();
4613
+ * // Next getToken() will request a new token
4614
+ * const token = await m2m.getToken();
4615
+ * ```
4616
+ */
4617
+ clearCache() {
4618
+ const e = this.getCacheKey(this.config.scopes, this.config.audience);
4619
+ this.cache.delete(e);
4620
+ }
4621
+ /**
4622
+ * Revoke the current token
4623
+ *
4624
+ * Note: Requires the server to support token revocation (RFC 7009).
4625
+ * Not all Passflow deployments may support this endpoint.
4626
+ *
4627
+ * @throws {M2MError} If revocation fails
4628
+ *
4629
+ * @example
4630
+ * ```typescript
4631
+ * await m2m.revokeToken();
4632
+ * console.log('Token revoked');
4633
+ * ```
4634
+ */
4635
+ async revokeToken() {
4636
+ const e = this.getCachedToken();
4637
+ if (!e)
4638
+ return;
4639
+ const t = `${this.config.url}/oauth2/revoke`, s = new URLSearchParams();
4640
+ s.append("token", e.access_token), s.append("client_id", this.config.clientId), s.append("client_secret", this.config.clientSecret);
4641
+ try {
4642
+ const r = await fetch(t, {
4643
+ method: "POST",
4644
+ headers: {
4645
+ "Content-Type": m.CONTENT_TYPE
4646
+ },
4647
+ body: s.toString()
4648
+ });
4649
+ if (!r.ok && r.status !== 200) {
4650
+ const o = await r.json().catch(() => ({}));
4651
+ throw l.fromOAuthError(
4652
+ {
4653
+ error: o.error || "server_error",
4654
+ error_description: o.error_description || "Token revocation failed"
4655
+ },
4656
+ r.status
4657
+ );
4658
+ }
4659
+ this.clearCache();
4660
+ } catch (r) {
4661
+ throw r instanceof l ? r : l.fromError(r instanceof Error ? r : new Error(String(r)));
4662
+ }
4663
+ }
4664
+ /**
4665
+ * Get the configured URL
4666
+ */
4667
+ get url() {
4668
+ return this.config.url;
4669
+ }
4670
+ /**
4671
+ * Get the configured client ID
4672
+ */
4673
+ get clientId() {
4674
+ return this.config.clientId;
4675
+ }
4676
+ /**
4677
+ * Get the configured scopes
4678
+ */
4679
+ get scopes() {
4680
+ return this.config.scopes;
4681
+ }
4682
+ /**
4683
+ * Get the configured audience
4684
+ */
4685
+ get audience() {
4686
+ return this.config.audience;
4687
+ }
2115
4688
  }
2116
4689
  export {
2117
- P as APP_ID_HEADER_KEY,
2118
- m as AUTHORIZATION_HEADER_KEY,
2119
- j as AppAPI,
2120
- O as AuthAPI,
2121
- q as AuthService,
2122
- re as DEFAULT_GROUP_NAME,
2123
- F as DEFAULT_SCOPES,
2124
- D as DEVICE_ID_HEADER_KEY,
2125
- x as DEVICE_TYPE_HEADER_KEY,
2126
- W as InvitationAPI,
2127
- V as InvitationService,
2128
- w as OS,
2129
- A as PASSFLOW_CLOUD_URL,
2130
- ie as Passflow,
2131
- y as PassflowAdminEndpointPaths,
2132
- c as PassflowEndpointPaths,
4690
+ C as APP_ID_HEADER_KEY,
4691
+ _ as AUTHORIZATION_HEADER_KEY,
4692
+ pe as AppAPI,
4693
+ fe as AuthAPI,
4694
+ Ie as AuthService,
4695
+ Ke as DEFAULT_GROUP_NAME,
4696
+ Q as DEFAULT_SCOPES,
4697
+ W as DEVICE_ID_HEADER_KEY,
4698
+ J as DEVICE_TYPE_HEADER_KEY,
4699
+ ne as ERROR_MESSAGE_MAX_LENGTH,
4700
+ ke as InvitationAPI,
4701
+ be as InvitationService,
4702
+ Ve as M2MClient,
4703
+ U as M2MConfigError,
4704
+ l as M2MError,
4705
+ Ge as M2MErrorCodes,
4706
+ N as M2MNetworkError,
4707
+ P as M2MTokenParseError,
4708
+ m as M2M_DEFAULTS,
4709
+ Ne as MINIMAL_DEFAULT_SCOPES,
4710
+ I as OS,
4711
+ G as PASSFLOW_CLOUD_URL,
4712
+ te as POPUP_HEIGHT,
4713
+ se as POPUP_POLL_INTERVAL_MS,
4714
+ re as POPUP_TIMEOUT_MS,
4715
+ ee as POPUP_WIDTH,
4716
+ L as Passflow,
4717
+ w as PassflowAdminEndpointPaths,
4718
+ h as PassflowEndpointPaths,
2133
4719
  u as PassflowError,
2134
4720
  a as PassflowEvent,
2135
- L as Providers,
2136
- S as RequestMethod,
2137
- K as SettingAPI,
2138
- B as TenantAPI,
2139
- X as TenantService,
2140
- Y as TenantUserMembership,
2141
- Z as TokenCacheService,
2142
- g as TokenType,
2143
- N as UserAPI,
2144
- Q as UserService,
2145
- f as isTokenExpired,
2146
- d as parseToken,
2147
- C as pathWithParams
4721
+ de as Providers,
4722
+ E as RequestMethod,
4723
+ Z as SDK_VERSION,
4724
+ q as SessionState,
4725
+ ye as SettingAPI,
4726
+ V as TOKEN_EXPIRY_BUFFER_SECONDS,
4727
+ ve as TenantAPI,
4728
+ Pe as TenantService,
4729
+ Re as TenantUserMembership,
4730
+ De as TokenCacheService,
4731
+ v as TokenDeliveryMode,
4732
+ k as TokenType,
4733
+ Se as TwoFactorApiClient,
4734
+ ue as TwoFactorPolicy,
4735
+ Fe as TwoFactorService,
4736
+ oe as USERNAME_MAX_LENGTH,
4737
+ ie as USERNAME_MIN_LENGTH,
4738
+ me as UserAPI,
4739
+ Me as UserService,
4740
+ S as isTokenExpired,
4741
+ M as isValidEmail,
4742
+ F as isValidJWTFormat,
4743
+ x as isValidPhoneNumber,
4744
+ Ee as isValidUsername,
4745
+ y as parseToken,
4746
+ A as pathWithParams,
4747
+ Te as sanitizeErrorMessage
2148
4748
  };
2149
4749
  //# sourceMappingURL=index.mjs.map