@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.
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3174 -574
- package/dist/index.mjs.map +1 -1
- package/dist/lib/api/app.d.ts +4 -1
- package/dist/lib/api/app.d.ts.map +1 -1
- package/dist/lib/api/auth.d.ts +6 -4
- package/dist/lib/api/auth.d.ts.map +1 -1
- package/dist/lib/api/axios-client.d.ts +26 -4
- package/dist/lib/api/axios-client.d.ts.map +1 -1
- package/dist/lib/api/index.d.ts +4 -3
- package/dist/lib/api/index.d.ts.map +1 -1
- package/dist/lib/api/invitation.d.ts +4 -1
- package/dist/lib/api/invitation.d.ts.map +1 -1
- package/dist/lib/api/model.d.ts +180 -11
- package/dist/lib/api/model.d.ts.map +1 -1
- package/dist/lib/api/setting.d.ts +4 -1
- package/dist/lib/api/setting.d.ts.map +1 -1
- package/dist/lib/api/tenant.d.ts +4 -1
- package/dist/lib/api/tenant.d.ts.map +1 -1
- package/dist/lib/api/two-factor.d.ts +75 -0
- package/dist/lib/api/two-factor.d.ts.map +1 -0
- package/dist/lib/api/user.d.ts +4 -1
- package/dist/lib/api/user.d.ts.map +1 -1
- package/dist/lib/constants/index.d.ts +25 -0
- package/dist/lib/constants/index.d.ts.map +1 -1
- package/dist/lib/device/index.d.ts +17 -0
- package/dist/lib/device/index.d.ts.map +1 -0
- package/dist/lib/index.d.ts +10 -3
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/m2m/client.d.ts +196 -0
- package/dist/lib/m2m/client.d.ts.map +1 -0
- package/dist/lib/m2m/errors.d.ts +107 -0
- package/dist/lib/m2m/errors.d.ts.map +1 -0
- package/dist/lib/m2m/index.d.ts +27 -0
- package/dist/lib/m2m/index.d.ts.map +1 -0
- package/dist/lib/m2m/types.d.ts +217 -0
- package/dist/lib/m2m/types.d.ts.map +1 -0
- package/dist/lib/passflow.d.ts +972 -11
- package/dist/lib/passflow.d.ts.map +1 -1
- package/dist/lib/services/auth-service.d.ts +29 -4
- package/dist/lib/services/auth-service.d.ts.map +1 -1
- package/dist/lib/services/index.d.ts +2 -1
- package/dist/lib/services/index.d.ts.map +1 -1
- package/dist/lib/services/invitation-service.d.ts +2 -2
- package/dist/lib/services/tenant-service.d.ts +2 -2
- package/dist/lib/services/token-cache-service.d.ts +14 -6
- package/dist/lib/services/token-cache-service.d.ts.map +1 -1
- package/dist/lib/services/two-factor-service.d.ts +120 -0
- package/dist/lib/services/two-factor-service.d.ts.map +1 -0
- package/dist/lib/services/user-service.d.ts +1 -1
- package/dist/lib/services/user-service.d.ts.map +1 -1
- package/dist/lib/storage/index.d.ts +96 -0
- package/dist/lib/storage/index.d.ts.map +1 -0
- package/dist/lib/store.d.ts +67 -1
- package/dist/lib/store.d.ts.map +1 -1
- package/dist/lib/token/delivery-manager.d.ts +121 -0
- package/dist/lib/token/delivery-manager.d.ts.map +1 -0
- package/dist/lib/{token-service → token}/index.d.ts +1 -1
- package/dist/lib/token/index.d.ts.map +1 -0
- package/dist/lib/token/membership.d.ts.map +1 -0
- package/dist/lib/{token-service → token}/service.d.ts +15 -3
- package/dist/lib/token/service.d.ts.map +1 -0
- package/dist/lib/token/token.d.ts +55 -0
- package/dist/lib/token/token.d.ts.map +1 -0
- package/dist/lib/types/index.d.ts +5 -3
- package/dist/lib/types/index.d.ts.map +1 -1
- package/dist/lib/utils/validation.d.ts +54 -0
- package/dist/lib/utils/validation.d.ts.map +1 -0
- package/dist/tests/storage/fake-storage.d.ts.map +1 -0
- package/dist/tests/storage/storage-manager.test.d.ts.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -15
- package/dist/lib/device-service/index.d.ts +0 -7
- package/dist/lib/device-service/index.d.ts.map +0 -1
- package/dist/lib/storage-manager/index.d.ts +0 -37
- package/dist/lib/storage-manager/index.d.ts.map +0 -1
- package/dist/lib/token-service/index.d.ts.map +0 -1
- package/dist/lib/token-service/membership.d.ts.map +0 -1
- package/dist/lib/token-service/service.d.ts.map +0 -1
- package/dist/lib/token-service/token.d.ts +0 -34
- package/dist/lib/token-service/token.d.ts.map +0 -1
- package/dist/tests/storage-manager/fake-storage.d.ts.map +0 -1
- package/dist/tests/storage-manager/storage-manager.test.d.ts.map +0 -1
- /package/dist/lib/{token-service → token}/membership.d.ts +0 -0
- /package/dist/tests/{storage-manager → storage}/fake-storage.d.ts +0 -0
- /package/dist/tests/{storage-manager → storage}/storage-manager.test.d.ts +0 -0
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { v4 as
|
|
3
|
-
import { startRegistration as
|
|
4
|
-
const
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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 =
|
|
33
|
-
return s ?
|
|
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
|
|
54
|
+
return y(t);
|
|
46
55
|
}
|
|
47
56
|
}
|
|
48
|
-
function
|
|
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
|
|
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
|
-
|
|
56
|
-
),
|
|
57
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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.
|
|
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:
|
|
77
|
-
id_token: this.storage.getItem(this.getKeyForTokenType(
|
|
78
|
-
refresh_token: this.storage.getItem(this.getKeyForTokenType(
|
|
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(
|
|
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
|
|
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
|
|
402
|
+
return H();
|
|
137
403
|
}
|
|
138
404
|
}
|
|
139
|
-
var
|
|
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
|
|
146
|
-
function
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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:
|
|
159
|
-
this.url =
|
|
160
|
-
prefix:
|
|
161
|
-
}), this.deviceService = new
|
|
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
|
-
[
|
|
431
|
+
[C]: o
|
|
164
432
|
});
|
|
165
|
-
const
|
|
433
|
+
const d = this.deviceService.getDeviceId();
|
|
166
434
|
this.defaultHeaders = {
|
|
167
435
|
...this.defaultHeaders,
|
|
168
|
-
[
|
|
169
|
-
[
|
|
170
|
-
}, this.instance =
|
|
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 (
|
|
174
|
-
if (this.isNonAuthEndpoint(
|
|
175
|
-
return
|
|
176
|
-
if (
|
|
177
|
-
|
|
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(),
|
|
452
|
+
return p.abort(), c.signal = p.signal, c;
|
|
180
453
|
}
|
|
181
|
-
return
|
|
454
|
+
return c;
|
|
182
455
|
}
|
|
183
|
-
const
|
|
184
|
-
if (
|
|
185
|
-
const p =
|
|
186
|
-
if (
|
|
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
|
|
190
|
-
return
|
|
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
|
-
|
|
193
|
-
|
|
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
|
|
475
|
+
return c.headers[_] = `Bearer ${g.access_token}`, c;
|
|
209
476
|
}
|
|
210
|
-
return
|
|
477
|
+
return c;
|
|
211
478
|
}), this.instance.interceptors.response.use(
|
|
212
|
-
(
|
|
213
|
-
(
|
|
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(
|
|
582
|
+
return this.send(E.GET, e, t);
|
|
251
583
|
}
|
|
252
584
|
post(e, t, s) {
|
|
253
|
-
return this.send(
|
|
585
|
+
return this.send(E.POST, e, { data: t, ...s });
|
|
254
586
|
}
|
|
255
587
|
put(e, t, s) {
|
|
256
|
-
return this.send(
|
|
588
|
+
return this.send(E.PUT, e, { data: t, ...s });
|
|
257
589
|
}
|
|
258
590
|
patch(e, t, s) {
|
|
259
|
-
return this.send(
|
|
591
|
+
return this.send(E.PATCH, e, { data: t, ...s });
|
|
260
592
|
}
|
|
261
593
|
delete(e, t) {
|
|
262
|
-
return this.send(
|
|
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
|
|
266
|
-
constructor(e) {
|
|
267
|
-
this.
|
|
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(
|
|
632
|
+
return this.axiosClient.post(h.refresh, r, {
|
|
275
633
|
headers: {
|
|
276
|
-
[
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
668
|
+
h.passwordless,
|
|
311
669
|
o
|
|
312
670
|
);
|
|
313
671
|
}
|
|
314
672
|
passwordlessSignInComplete(e) {
|
|
315
673
|
return this.axiosClient.post(
|
|
316
|
-
|
|
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 ?
|
|
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
|
-
|
|
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(
|
|
696
|
+
return this.axiosClient.post(h.resetPassword, r, {
|
|
336
697
|
headers: {
|
|
337
|
-
[
|
|
338
|
-
[
|
|
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
|
-
},
|
|
349
|
-
return this.axiosClient.post(
|
|
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 ?
|
|
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 ?
|
|
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 ?
|
|
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
|
|
386
|
-
!o && r && (
|
|
387
|
-
const
|
|
388
|
-
return this.axiosClient.post(
|
|
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
|
|
392
|
-
constructor(e) {
|
|
393
|
-
this.axiosClient = new
|
|
752
|
+
class ke {
|
|
753
|
+
constructor(e, t, s) {
|
|
754
|
+
this.axiosClient = new T(e, t, s);
|
|
394
755
|
}
|
|
395
|
-
|
|
396
|
-
|
|
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
|
|
400
|
-
constructor(e) {
|
|
401
|
-
this.axiosClient = new
|
|
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(
|
|
828
|
+
return this.axiosClient.get(h.settingsAll);
|
|
405
829
|
}
|
|
406
830
|
getPasswordPolicySettings() {
|
|
407
|
-
return this.axiosClient.get(
|
|
831
|
+
return this.axiosClient.get(h.settingsPasswordPolicy);
|
|
408
832
|
}
|
|
409
833
|
getPasskeySettings() {
|
|
410
|
-
return this.axiosClient.get(
|
|
834
|
+
return this.axiosClient.get(h.settingsPasskey);
|
|
411
835
|
}
|
|
412
836
|
}
|
|
413
|
-
class
|
|
414
|
-
constructor(e) {
|
|
415
|
-
this.axiosClient = new
|
|
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
|
-
|
|
429
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 = `${
|
|
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 = `${
|
|
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 = `${
|
|
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(
|
|
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 = `${
|
|
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 = `${
|
|
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 = `${
|
|
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 = `${
|
|
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 = `${
|
|
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 = `${
|
|
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 = `${
|
|
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 = `${
|
|
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 = `${
|
|
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 = `${
|
|
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 = `${
|
|
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 = `${
|
|
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 = `${
|
|
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 = `${
|
|
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 = `${
|
|
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 = `${
|
|
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 = `${
|
|
1066
|
+
const r = `${h.tenantPath}/${e}/group/${t}/invite/email/${s}`;
|
|
682
1067
|
return this.axiosClient.delete(r);
|
|
683
1068
|
}
|
|
684
1069
|
}
|
|
685
|
-
class
|
|
686
|
-
constructor(e) {
|
|
687
|
-
this.axiosClient = new
|
|
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
|
-
*
|
|
691
|
-
*
|
|
692
|
-
* @returns Promise with invitation link and token
|
|
1078
|
+
* Get current 2FA enrollment status
|
|
1079
|
+
* GET /user/2fa/status
|
|
693
1080
|
*/
|
|
694
|
-
|
|
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
|
-
|
|
1099
|
+
h.twoFactorSetupConfirm,
|
|
697
1100
|
e
|
|
698
1101
|
);
|
|
699
1102
|
}
|
|
700
1103
|
/**
|
|
701
|
-
*
|
|
702
|
-
*
|
|
703
|
-
*
|
|
1104
|
+
* Verify TOTP code during login
|
|
1105
|
+
* POST /auth/2fa/verify
|
|
1106
|
+
* Uses tfa_token as Bearer token for authentication
|
|
704
1107
|
*/
|
|
705
|
-
|
|
706
|
-
const t =
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
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
|
-
*
|
|
718
|
-
*
|
|
719
|
-
*
|
|
1121
|
+
* Use recovery code for authentication
|
|
1122
|
+
* POST /auth/2fa/recovery
|
|
1123
|
+
* Uses tfa_token as Bearer token for authentication
|
|
720
1124
|
*/
|
|
721
|
-
|
|
722
|
-
const t =
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
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
|
-
*
|
|
729
|
-
*
|
|
730
|
-
* @returns Promise with success response
|
|
1138
|
+
* Disable 2FA (requires TOTP verification)
|
|
1139
|
+
* DELETE /user/2fa
|
|
731
1140
|
*/
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
744
|
-
|
|
745
|
-
|
|
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
|
|
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
|
-
|
|
792
|
-
|
|
793
|
-
|
|
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 =
|
|
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
|
|
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.
|
|
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
|
|
1502
|
+
return await this.processAuthResponse(t, e.scopes), this.subscribeStore.notify(a.Register, {
|
|
819
1503
|
tokens: t,
|
|
820
|
-
parsedTokens: this.tokenCacheService.
|
|
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 =
|
|
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
|
|
1549
|
+
return await this.processAuthResponse(t, e.scopes), this.subscribeStore.notify(a.SignIn, {
|
|
850
1550
|
tokens: t,
|
|
851
|
-
parsedTokens: this.tokenCacheService.
|
|
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
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
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.
|
|
896
|
-
}), this.subscribeStore.notify(a.TokenCacheExpired, { isExpired: !1 }), this.tokenCacheService.isRefreshing = !1, this.tokenCacheService.
|
|
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:
|
|
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 :
|
|
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
|
|
1677
|
+
return await this.processAuthResponse(n, o), this.subscribeStore.notify(a.SignIn, {
|
|
928
1678
|
tokens: n,
|
|
929
|
-
parsedTokens: this.tokenCacheService.
|
|
1679
|
+
parsedTokens: this.tokenCacheService.getParsedTokens()
|
|
930
1680
|
}), await this.submitSessionCheck(), n;
|
|
931
1681
|
} catch (n) {
|
|
932
|
-
const
|
|
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,
|
|
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 =
|
|
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
|
-
}),
|
|
1699
|
+
}), d = await this.authApi.passkeyRegisterComplete(
|
|
950
1700
|
n,
|
|
951
1701
|
t,
|
|
952
1702
|
r,
|
|
953
1703
|
!this.appId
|
|
954
1704
|
);
|
|
955
|
-
return
|
|
956
|
-
tokens:
|
|
957
|
-
parsedTokens: this.tokenCacheService.
|
|
958
|
-
}), await this.submitSessionCheck(),
|
|
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 =
|
|
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
|
|
1723
|
+
const { challenge_id: r, publicKey: o } = await this.authApi.passkeyAuthenticateStart(e, t, s, !this.appId), n = await j({
|
|
974
1724
|
optionsJSON: o
|
|
975
|
-
}),
|
|
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
|
|
982
|
-
tokens:
|
|
983
|
-
parsedTokens: this.tokenCacheService.
|
|
984
|
-
}), await this.submitSessionCheck()),
|
|
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",
|
|
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
|
-
|
|
1013
|
-
|
|
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
|
|
1016
|
-
access_token:
|
|
1017
|
-
refresh_token: p,
|
|
1018
|
-
id_token:
|
|
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.
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
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
|
-
}
|
|
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
|
|
1812
|
+
const d = r ?? this.scopes, c = {
|
|
1040
1813
|
appId: o ?? this.appId ?? "",
|
|
1041
1814
|
redirectto: s ?? window.location.href,
|
|
1042
|
-
scopes:
|
|
1043
|
-
},
|
|
1044
|
-
return n.search =
|
|
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
|
-
|
|
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.
|
|
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 =
|
|
1102
|
-
return
|
|
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
|
|
1897
|
+
class be {
|
|
1114
1898
|
constructor(e) {
|
|
1115
|
-
this.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
1939
|
+
return this.invitationApi.getInvitationLink(e);
|
|
1156
1940
|
}
|
|
1157
1941
|
}
|
|
1158
|
-
class
|
|
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
|
|
1173
|
-
return new
|
|
1956
|
+
function Ae() {
|
|
1957
|
+
return new Ce();
|
|
1174
1958
|
}
|
|
1175
|
-
class
|
|
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
|
|
1197
|
-
|
|
1198
|
-
id:
|
|
1199
|
-
name:
|
|
1200
|
-
email:
|
|
1201
|
-
phone:
|
|
1202
|
-
}),
|
|
1203
|
-
userId:
|
|
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((
|
|
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
|
|
2029
|
+
class Pe {
|
|
1246
2030
|
constructor(e, t, s) {
|
|
1247
|
-
this.
|
|
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 (
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
1317
|
-
return new
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
1616
|
-
constructor(e, t) {
|
|
1617
|
-
this.
|
|
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
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
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.
|
|
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.
|
|
1704
|
-
this.isRefreshing || this.
|
|
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:
|
|
1713
|
-
id_token: e.id_token ?
|
|
1714
|
-
refresh_token: e.refresh_token ?
|
|
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
|
-
|
|
2473
|
+
getTokens() {
|
|
1719
2474
|
return this.tokensCache;
|
|
1720
2475
|
}
|
|
1721
|
-
async
|
|
2476
|
+
async getTokensWithRefresh() {
|
|
1722
2477
|
try {
|
|
1723
2478
|
if (!this.tokensCache) return this.tokensCache;
|
|
1724
|
-
|
|
1725
|
-
|
|
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
|
-
|
|
2492
|
+
getParsedTokens() {
|
|
1736
2493
|
return this.parsedTokensCache;
|
|
1737
2494
|
}
|
|
1738
|
-
|
|
2495
|
+
isExpired() {
|
|
1739
2496
|
if (!this.tokensCache) return !0;
|
|
1740
|
-
|
|
1741
|
-
|
|
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
|
|
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:
|
|
2830
|
+
doRefresh: d = !1
|
|
1750
2831
|
}) => {
|
|
1751
|
-
this.createSessionCallback = o, this.expiredSessionCallback = n, this.doRefreshTokens =
|
|
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 ||
|
|
2835
|
+
this.url = t || G, this.appId = s, this.storageManager = new $({
|
|
1755
2836
|
prefix: e.keyStoragePrefix ?? ""
|
|
1756
|
-
}), this.deviceService = new
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1819
|
-
|
|
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
|
-
|
|
1822
|
-
|
|
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
|
-
|
|
1825
|
-
|
|
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
|
-
|
|
1828
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
3543
|
+
parsedTokens: this.tokenCacheService.getParsedTokens()
|
|
1955
3544
|
});
|
|
1956
3545
|
}
|
|
1957
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
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
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
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
|