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