@classic-homes/auth 1.28.0 → 1.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/dist/{auth.svelte-JLSRQJAC.js → auth.svelte-XY4FQFZQ.js} +2 -2
- package/dist/{chunk-GBYHYVXX.js → chunk-7AQYLLTA.js} +109 -6
- package/dist/{chunk-XUOSWSDO.js → chunk-7JLCFRUU.js} +2 -2
- package/dist/{chunk-TUWTOMZZ.js → chunk-LPQKZRPC.js} +11 -2
- package/dist/{chunk-S3DNV7VV.js → chunk-MBVOLE5U.js} +5 -1
- package/dist/core/index.d.ts +11 -4
- package/dist/core/index.js +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -4
- package/dist/svelte/index.d.ts +4 -4
- package/dist/svelte/index.js +4 -4
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +5 -0
- package/dist/{types-B70Kgcc_.d.ts → types-DF5Ny2IA.d.ts} +7 -1
- package/dist/{user-utils-DWEU-Ovm.d.ts → user-utils-Bo3lMsuT.d.ts} +39 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -96,6 +96,24 @@ async function handleLogin(email: string, password: string) {
|
|
|
96
96
|
}
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
+
Email-MFA accounts get their code by email: send it with the challenge token,
|
|
100
|
+
then verify with `method: 'email'`. A cooldown (one code a minute, five an
|
|
101
|
+
hour) throws `MFAChallengeEmailError` with `retryAfterSeconds`.
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import { authService, MFAChallengeEmailError } from '@classic-homes/auth';
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const { email } = await authService.sendMFAChallengeEmail(mfaToken); // "jo***@example.com"
|
|
108
|
+
} catch (error) {
|
|
109
|
+
if (error instanceof MFAChallengeEmailError && error.retryAfterSeconds) {
|
|
110
|
+
// Show "Resend in Ns"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
await authService.verifyMFAChallenge({ mfaToken, code, method: 'email' });
|
|
115
|
+
```
|
|
116
|
+
|
|
99
117
|
To disable auto-set auth (for manual control):
|
|
100
118
|
|
|
101
119
|
```typescript
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { authActions, authStore, currentUser, isAuthenticated } from './chunk-
|
|
2
|
-
import './chunk-
|
|
1
|
+
export { authActions, authStore, currentUser, isAuthenticated } from './chunk-MBVOLE5U.js';
|
|
2
|
+
import './chunk-7AQYLLTA.js';
|
|
3
3
|
import './chunk-K6TV3ZVS.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isInitialized, getConfig, getUserRoles, getStorage, isTokenExpired, getFetch, isValidRedirectUrl } from './chunk-K6TV3ZVS.js';
|
|
1
|
+
import { isInitialized, getConfig, getUserRoles, getStorage, isTokenExpired, getFetch, isValidRedirectUrl, decodeJWT } from './chunk-K6TV3ZVS.js';
|
|
2
2
|
|
|
3
3
|
// src/core/sso-state.ts
|
|
4
4
|
var SSO_CLIENT_STATE_PARAM = "client_state";
|
|
@@ -96,6 +96,69 @@ var InvalidAuthResponseError = class _InvalidAuthResponseError extends Error {
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
};
|
|
99
|
+
var SessionChangedError = class _SessionChangedError extends Error {
|
|
100
|
+
constructor(message = "The session changed while this request was in flight; it was not retried.") {
|
|
101
|
+
super(message);
|
|
102
|
+
this.name = "SessionChangedError";
|
|
103
|
+
if (Error.captureStackTrace) {
|
|
104
|
+
Error.captureStackTrace(this, _SessionChangedError);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
function isSessionChangedError(error) {
|
|
109
|
+
return error instanceof SessionChangedError || error?.name === "SessionChangedError";
|
|
110
|
+
}
|
|
111
|
+
var MFAChallengeEmailError = class _MFAChallengeEmailError extends Error {
|
|
112
|
+
constructor(message, status, retryAfterSeconds) {
|
|
113
|
+
super(message);
|
|
114
|
+
this.name = "MFAChallengeEmailError";
|
|
115
|
+
this.status = status;
|
|
116
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
117
|
+
if (Error.captureStackTrace) {
|
|
118
|
+
Error.captureStackTrace(this, _MFAChallengeEmailError);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// src/core/session.ts
|
|
124
|
+
var generation = 0;
|
|
125
|
+
function bumpSessionGeneration() {
|
|
126
|
+
generation += 1;
|
|
127
|
+
}
|
|
128
|
+
function readIdentity() {
|
|
129
|
+
let parsed = {};
|
|
130
|
+
try {
|
|
131
|
+
const raw = getStorage().getItem(getConfig().storageKey ?? "classic_auth");
|
|
132
|
+
if (raw) parsed = JSON.parse(raw) ?? {};
|
|
133
|
+
} catch {
|
|
134
|
+
}
|
|
135
|
+
const accessToken = typeof parsed.accessToken === "string" ? parsed.accessToken : "";
|
|
136
|
+
const refreshToken = typeof parsed.refreshToken === "string" ? parsed.refreshToken : "";
|
|
137
|
+
const sub = accessToken ? decodeJWT(accessToken)?.sub : void 0;
|
|
138
|
+
const userId = parsed.user?.id;
|
|
139
|
+
return {
|
|
140
|
+
signedIn: accessToken !== "" || refreshToken !== "",
|
|
141
|
+
sub: typeof sub === "string" || typeof sub === "number" ? String(sub) : void 0,
|
|
142
|
+
userId: typeof userId === "string" || typeof userId === "number" ? String(userId) : void 0
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function captureSession() {
|
|
146
|
+
return { generation, identity: readIdentity() };
|
|
147
|
+
}
|
|
148
|
+
function getSessionGeneration() {
|
|
149
|
+
return generation;
|
|
150
|
+
}
|
|
151
|
+
function isCurrentSession(snapshot) {
|
|
152
|
+
if (snapshot.generation !== generation) return false;
|
|
153
|
+
const then = snapshot.identity;
|
|
154
|
+
const now = readIdentity();
|
|
155
|
+
if (then.signedIn !== now.signedIn) return false;
|
|
156
|
+
if (then.sub !== void 0 && now.sub !== void 0 && then.sub !== now.sub) return false;
|
|
157
|
+
if (then.userId !== void 0 && now.userId !== void 0 && then.userId !== now.userId) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
99
162
|
|
|
100
163
|
// src/core/client.ts
|
|
101
164
|
var DEFAULT_REFRESH_RETRY_DELAYS = [1e3, 3e3];
|
|
@@ -155,7 +218,7 @@ function writeStoredTokens(accessToken, refreshToken) {
|
|
|
155
218
|
}
|
|
156
219
|
async function syncSvelteStore(apply) {
|
|
157
220
|
try {
|
|
158
|
-
const { authStore } = await import('./auth.svelte-
|
|
221
|
+
const { authStore } = await import('./auth.svelte-XY4FQFZQ.js');
|
|
159
222
|
apply(authStore);
|
|
160
223
|
} catch {
|
|
161
224
|
}
|
|
@@ -165,6 +228,7 @@ function clearStoredAuth() {
|
|
|
165
228
|
const storage = getStorage();
|
|
166
229
|
storage.removeItem(config.storageKey ?? "classic_auth");
|
|
167
230
|
storage.removeItem(getSessionTokenKey());
|
|
231
|
+
bumpSessionGeneration();
|
|
168
232
|
}
|
|
169
233
|
async function expireSession() {
|
|
170
234
|
clearStoredAuth();
|
|
@@ -234,6 +298,8 @@ function refreshSession(options = {}) {
|
|
|
234
298
|
return refreshInflight;
|
|
235
299
|
}
|
|
236
300
|
async function runRefresh(options) {
|
|
301
|
+
const generation2 = getSessionGeneration();
|
|
302
|
+
const sameGeneration = () => generation2 === getSessionGeneration();
|
|
237
303
|
const presented = getRefreshToken();
|
|
238
304
|
if (!presented) {
|
|
239
305
|
await expireSession();
|
|
@@ -243,6 +309,7 @@ async function runRefresh(options) {
|
|
|
243
309
|
const current = getRefreshToken();
|
|
244
310
|
if (current !== presented) {
|
|
245
311
|
if (!current) {
|
|
312
|
+
if (!sameGeneration()) return "rejected";
|
|
246
313
|
await expireSession();
|
|
247
314
|
return "rejected";
|
|
248
315
|
}
|
|
@@ -267,6 +334,7 @@ async function runRefresh(options) {
|
|
|
267
334
|
await syncSvelteStore((authStore) => authStore.rehydrate());
|
|
268
335
|
return "refreshed";
|
|
269
336
|
}
|
|
337
|
+
if (!sameGeneration()) return "rejected";
|
|
270
338
|
await writeStoredTokens(attempt.accessToken, attempt.refreshToken);
|
|
271
339
|
return "refreshed";
|
|
272
340
|
}
|
|
@@ -276,6 +344,7 @@ async function runRefresh(options) {
|
|
|
276
344
|
await syncSvelteStore((authStore) => authStore.rehydrate());
|
|
277
345
|
return "refreshed";
|
|
278
346
|
}
|
|
347
|
+
if (!sameGeneration()) return "rejected";
|
|
279
348
|
await expireSession();
|
|
280
349
|
return "rejected";
|
|
281
350
|
}
|
|
@@ -305,7 +374,7 @@ function isApiOrigin(url, baseUrl) {
|
|
|
305
374
|
function apiRequest(endpoint, options = {}) {
|
|
306
375
|
return apiRequestInternal(endpoint, options, false);
|
|
307
376
|
}
|
|
308
|
-
async function apiRequestInternal(endpoint, options, isRetry) {
|
|
377
|
+
async function apiRequestInternal(endpoint, options, isRetry, startedIn) {
|
|
309
378
|
const {
|
|
310
379
|
authenticated: wantsAuth = false,
|
|
311
380
|
customFetch,
|
|
@@ -320,6 +389,14 @@ async function apiRequestInternal(endpoint, options, isRetry) {
|
|
|
320
389
|
const isAbsolute = /^https?:\/\//i.test(endpoint);
|
|
321
390
|
const url = isAbsolute ? endpoint : `${config.baseUrl}${endpoint}`;
|
|
322
391
|
const authenticated = wantsAuth && (!isAbsolute || isApiOrigin(url, config.baseUrl));
|
|
392
|
+
const session = startedIn ?? captureSession();
|
|
393
|
+
const assertSameSession = async () => {
|
|
394
|
+
if (isCurrentSession(session)) return;
|
|
395
|
+
if (session.generation === getSessionGeneration()) {
|
|
396
|
+
await syncSvelteStore((authStore) => authStore.rehydrate());
|
|
397
|
+
}
|
|
398
|
+
throw new SessionChangedError();
|
|
399
|
+
};
|
|
323
400
|
if (authenticated && noAuthRetry && !isRetry && typeof window !== "undefined") {
|
|
324
401
|
const accessToken = getAccessToken();
|
|
325
402
|
if (accessToken && isTokenExpired(accessToken)) {
|
|
@@ -327,6 +404,7 @@ async function apiRequestInternal(endpoint, options, isRetry) {
|
|
|
327
404
|
if (outcome === "rejected") {
|
|
328
405
|
throw new Error("Authentication failed. Please sign in again.");
|
|
329
406
|
}
|
|
407
|
+
if (authenticated) await assertSameSession();
|
|
330
408
|
}
|
|
331
409
|
}
|
|
332
410
|
const timeoutMs = timeout ?? config.requestTimeout ?? 3e4;
|
|
@@ -363,13 +441,15 @@ async function apiRequestInternal(endpoint, options, isRetry) {
|
|
|
363
441
|
});
|
|
364
442
|
clearTimeout(timeoutId);
|
|
365
443
|
if (response.status === 401 && authenticated && !noAuthRetry && typeof window !== "undefined") {
|
|
444
|
+
await assertSameSession();
|
|
366
445
|
if (isRetry) {
|
|
367
446
|
await expireSession();
|
|
368
447
|
throw new Error("Authentication failed. Please sign in again.");
|
|
369
448
|
}
|
|
370
449
|
const outcome = await refreshSession();
|
|
371
450
|
if (outcome === "refreshed") {
|
|
372
|
-
|
|
451
|
+
await assertSameSession();
|
|
452
|
+
return apiRequestInternal(endpoint, options, true, session);
|
|
373
453
|
}
|
|
374
454
|
if (outcome === "rejected") {
|
|
375
455
|
throw new Error("Authentication failed. Please sign in again.");
|
|
@@ -729,7 +809,9 @@ var authApi = {
|
|
|
729
809
|
const config = getConfig();
|
|
730
810
|
const requestBody = {
|
|
731
811
|
code: data.code,
|
|
732
|
-
|
|
812
|
+
// CHAPI answers the challenge per method: an authenticator code, an
|
|
813
|
+
// emailed sign-in code or a backup code (no method → authenticator).
|
|
814
|
+
type: data.method === "backup" || data.method === "email" ? data.method : "totp",
|
|
733
815
|
trustDevice: data.trustDevice
|
|
734
816
|
};
|
|
735
817
|
const response = await fetchWithTimeout(`${config.baseUrl}/auth/mfa/challenge`, {
|
|
@@ -747,6 +829,27 @@ var authApi = {
|
|
|
747
829
|
const result = await response.json();
|
|
748
830
|
return extractData(result);
|
|
749
831
|
},
|
|
832
|
+
/**
|
|
833
|
+
* Email a sign-in code for an MFA challenge (accounts with email MFA).
|
|
834
|
+
* Authenticated by the challenge token from login / the SSO callback, not
|
|
835
|
+
* a session. Answer it with verifyMFAChallenge({ method: 'email' }).
|
|
836
|
+
* Throws MFAChallengeEmailError (with `retryAfterSeconds` on a cooldown).
|
|
837
|
+
*/
|
|
838
|
+
async sendMFAChallengeEmail(mfaToken) {
|
|
839
|
+
const config = getConfig();
|
|
840
|
+
const response = await fetchWithTimeout(`${config.baseUrl}/auth/mfa/challenge/email/send`, {
|
|
841
|
+
method: "POST",
|
|
842
|
+
headers: { Authorization: `Bearer ${mfaToken}` }
|
|
843
|
+
});
|
|
844
|
+
const body = await response.json().catch(() => ({}));
|
|
845
|
+
if (!response.ok) {
|
|
846
|
+
const details = body?.error?.details;
|
|
847
|
+
const retryAfter = typeof details?.retryAfterSeconds === "number" ? details.retryAfterSeconds : typeof body?.retryAfterSeconds === "number" ? body.retryAfterSeconds : void 0;
|
|
848
|
+
const message = (typeof body?.error === "string" ? body.error : body?.error?.message) || body?.message || "Could not send the verification code";
|
|
849
|
+
throw new MFAChallengeEmailError(message, response.status, retryAfter);
|
|
850
|
+
}
|
|
851
|
+
return extractData(body);
|
|
852
|
+
},
|
|
750
853
|
// ============================================================================
|
|
751
854
|
// Device Management
|
|
752
855
|
// ============================================================================
|
|
@@ -978,4 +1081,4 @@ var authApi = {
|
|
|
978
1081
|
}
|
|
979
1082
|
};
|
|
980
1083
|
|
|
981
|
-
export { InvalidAuthResponseError, RoleDeniedError, SSO_CLIENT_STATE_PARAM, SSO_CLIENT_STATE_TTL_MS, api, apiRequest, authApi, clearStoredAuth, consumeSsoClientState, createSsoClientState, extractData, getAccessToken, getRefreshToken, getSessionToken, initCrossTabSync, isRoleDeniedError, refreshSession, updateStoredTokens, verifySsoClientState };
|
|
1084
|
+
export { InvalidAuthResponseError, MFAChallengeEmailError, RoleDeniedError, SSO_CLIENT_STATE_PARAM, SSO_CLIENT_STATE_TTL_MS, SessionChangedError, api, apiRequest, authApi, bumpSessionGeneration, clearStoredAuth, consumeSsoClientState, createSsoClientState, extractData, getAccessToken, getRefreshToken, getSessionToken, initCrossTabSync, isRoleDeniedError, isSessionChangedError, refreshSession, updateStoredTokens, verifySsoClientState };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { authService } from './chunk-
|
|
2
|
-
import { authStore, currentUser, isAuthenticated, authActions } from './chunk-
|
|
1
|
+
import { authService } from './chunk-LPQKZRPC.js';
|
|
2
|
+
import { authStore, currentUser, isAuthenticated, authActions } from './chunk-MBVOLE5U.js';
|
|
3
3
|
import { isInitialized, initAuth, getUserRoles } from './chunk-K6TV3ZVS.js';
|
|
4
4
|
|
|
5
5
|
// src/svelte/guards/auth-guard.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { authApi, InvalidAuthResponseError, RoleDeniedError } from './chunk-
|
|
1
|
+
import { authApi, InvalidAuthResponseError, RoleDeniedError } from './chunk-7AQYLLTA.js';
|
|
2
2
|
import { hasAnyRole, getUserRoles } from './chunk-K6TV3ZVS.js';
|
|
3
3
|
|
|
4
4
|
// src/core/guards.ts
|
|
@@ -19,7 +19,7 @@ function getAvailableMethods(response) {
|
|
|
19
19
|
async function setStoreAuth(response) {
|
|
20
20
|
let store;
|
|
21
21
|
try {
|
|
22
|
-
store = (await import('./auth.svelte-
|
|
22
|
+
store = (await import('./auth.svelte-XY4FQFZQ.js')).authStore;
|
|
23
23
|
} catch {
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
@@ -237,6 +237,15 @@ var AuthService = class {
|
|
|
237
237
|
}
|
|
238
238
|
return response;
|
|
239
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* Email a sign-in code for an MFA challenge (accounts with email MFA).
|
|
242
|
+
* Pass the `mfaToken` from the login / SSO challenge; answer with
|
|
243
|
+
* `verifyMFAChallenge({ mfaToken, code, method: 'email' })`.
|
|
244
|
+
* Throws MFAChallengeEmailError — `retryAfterSeconds` is set on a cooldown.
|
|
245
|
+
*/
|
|
246
|
+
async sendMFAChallengeEmail(mfaToken) {
|
|
247
|
+
return await authApi.sendMFAChallengeEmail(mfaToken);
|
|
248
|
+
}
|
|
240
249
|
// ============================================================================
|
|
241
250
|
// Device Management
|
|
242
251
|
// ============================================================================
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { InvalidAuthResponseError, authApi } from './chunk-
|
|
1
|
+
import { InvalidAuthResponseError, bumpSessionGeneration, authApi } from './chunk-7AQYLLTA.js';
|
|
2
2
|
import { isInitialized, getConfig, isValidSSOLogoutUrl, hasRole, hasAnyRole, hasAllRoles, decodeJWT, getStorage, getDefaultStorage } from './chunk-K6TV3ZVS.js';
|
|
3
3
|
|
|
4
4
|
// src/svelte/stores/auth.svelte.ts
|
|
@@ -111,6 +111,7 @@ var AuthStore = class {
|
|
|
111
111
|
throw new InvalidAuthResponseError("setAuth requires an access token and a user");
|
|
112
112
|
}
|
|
113
113
|
const userWithPermissions = normalizeUser(user, accessToken);
|
|
114
|
+
bumpSessionGeneration();
|
|
114
115
|
this.state = {
|
|
115
116
|
accessToken,
|
|
116
117
|
refreshToken,
|
|
@@ -174,6 +175,7 @@ var AuthStore = class {
|
|
|
174
175
|
* Clear auth state (logout).
|
|
175
176
|
*/
|
|
176
177
|
logout() {
|
|
178
|
+
bumpSessionGeneration();
|
|
177
179
|
this.state = {
|
|
178
180
|
accessToken: null,
|
|
179
181
|
refreshToken: null,
|
|
@@ -222,6 +224,7 @@ var AuthStore = class {
|
|
|
222
224
|
*/
|
|
223
225
|
handleSessionExpired() {
|
|
224
226
|
const currentPath = typeof window !== "undefined" ? window.location.pathname : "/";
|
|
227
|
+
bumpSessionGeneration();
|
|
225
228
|
this.state = {
|
|
226
229
|
accessToken: null,
|
|
227
230
|
refreshToken: null,
|
|
@@ -303,6 +306,7 @@ var AuthStore = class {
|
|
|
303
306
|
* ```
|
|
304
307
|
*/
|
|
305
308
|
reset() {
|
|
309
|
+
bumpSessionGeneration();
|
|
306
310
|
this.state = {
|
|
307
311
|
accessToken: null,
|
|
308
312
|
refreshToken: null,
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { e as LoginCredentials, f as LoginResponse, g as LogoutResponse, R as RegisterData,
|
|
2
|
-
export { a as AuthConfig, b as AuthState,
|
|
3
|
-
export { A as AuthService, I as InvalidAuthResponseError, L as LoginOptions, M as MFAVerifyOptions, R as RoleDeniedError,
|
|
1
|
+
import { e as LoginCredentials, f as LoginResponse, g as LogoutResponse, R as RegisterData, l as RegisterResponse, q as User, k as ProfileUpdateData, C as ChangePasswordData, o as Session, A as ApiKey, c as CreateApiKeyRequest, d as CreateApiKeyResponse, U as UpdateApiKeyRequest, j as MFAStatus, i as MFASetupResponse, M as MFAChallengeData, h as MFAChallengeEmailResponse, D as Device, r as UserPreferences, L as LinkedAccount, n as SecurityEvent, P as Pagination, s as UserRolesResponse } from '../types-DF5Ny2IA.js';
|
|
2
|
+
export { a as AuthConfig, b as AuthState, m as ResetPasswordData, S as SSOConfig, p as StorageAdapter, t as createMemoryStorage, u as createSessionStorage, v as getConfig, w as getDefaultStorage, x as getFetch, y as getStorage, z as initAuth, B as isInitialized, E as resetConfig } from '../types-DF5Ny2IA.js';
|
|
3
|
+
export { A as AuthService, I as InvalidAuthResponseError, L as LoginOptions, M as MFAChallengeEmailError, a as MFAVerifyOptions, R as RoleDeniedError, S as SessionChangedError, b as authService, f as formatUserRoles, g as getAvatarFallback, c as getDisplayName, d as getGreeting, e as getUserEmail, h as getUserInitials, i as isRoleDeniedError, j as isSessionChangedError } from '../user-utils-Bo3lMsuT.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* SSO client state
|
|
@@ -347,6 +347,13 @@ declare const authApi: {
|
|
|
347
347
|
* Verify MFA challenge during login.
|
|
348
348
|
*/
|
|
349
349
|
verifyMFAChallenge(data: MFAChallengeData): Promise<LoginResponse>;
|
|
350
|
+
/**
|
|
351
|
+
* Email a sign-in code for an MFA challenge (accounts with email MFA).
|
|
352
|
+
* Authenticated by the challenge token from login / the SSO callback, not
|
|
353
|
+
* a session. Answer it with verifyMFAChallenge({ method: 'email' }).
|
|
354
|
+
* Throws MFAChallengeEmailError (with `retryAfterSeconds` on a cooldown).
|
|
355
|
+
*/
|
|
356
|
+
sendMFAChallengeEmail(mfaToken: string): Promise<MFAChallengeEmailResponse>;
|
|
350
357
|
/**
|
|
351
358
|
* Get all devices.
|
|
352
359
|
*/
|
|
@@ -696,4 +703,4 @@ declare function hasAnyRole(user: Pick<User, 'roles'> | null | undefined, roles:
|
|
|
696
703
|
*/
|
|
697
704
|
declare function hasAllRoles(user: Pick<User, 'roles'> | null | undefined, roles: readonly string[]): boolean;
|
|
698
705
|
|
|
699
|
-
export { ApiKey, type ApiRequestOptions, type ApiResponse, ChangePasswordData, CreateApiKeyRequest, CreateApiKeyResponse, Device, type JWTPayload, LinkedAccount, LoginCredentials, LoginResponse, LogoutResponse, MFAChallengeData, MFASetupResponse, MFAStatus, Pagination, ProfileUpdateData, ROLE_HIERARCHY, ROLE_NAMES, type RefreshOutcome, type RefreshSessionOptions, RegisterData, RegisterResponse, type RoleName, SSO_CLIENT_STATE_PARAM, SSO_CLIENT_STATE_TTL_MS, SecurityEvent, Session, type SsoClientStateResult, type SsoClientStateVerification, UpdateApiKeyRequest, User, UserPreferences, UserRolesResponse, api, apiRequest, authApi, clearStoredAuth, computePrimaryRole, consumeSsoClientState, createSsoClientState, decodeJWT, extractClaims, extractData, getAccessToken, getAvailableMethods, getEffectiveLevel, getMfaToken, getRefreshToken, getSessionToken, getTokenExpiration, getTokenRemainingTime, getUserRoles, hasAllRoles, hasAnyRole, hasRole, initCrossTabSync, isLoginSuccessResponse, isMfaChallengeResponse, isTokenExpired, refreshSession, updateStoredTokens, verifySsoClientState };
|
|
706
|
+
export { ApiKey, type ApiRequestOptions, type ApiResponse, ChangePasswordData, CreateApiKeyRequest, CreateApiKeyResponse, Device, type JWTPayload, LinkedAccount, LoginCredentials, LoginResponse, LogoutResponse, MFAChallengeData, MFAChallengeEmailResponse, MFASetupResponse, MFAStatus, Pagination, ProfileUpdateData, ROLE_HIERARCHY, ROLE_NAMES, type RefreshOutcome, type RefreshSessionOptions, RegisterData, RegisterResponse, type RoleName, SSO_CLIENT_STATE_PARAM, SSO_CLIENT_STATE_TTL_MS, SecurityEvent, Session, type SsoClientStateResult, type SsoClientStateVerification, UpdateApiKeyRequest, User, UserPreferences, UserRolesResponse, api, apiRequest, authApi, clearStoredAuth, computePrimaryRole, consumeSsoClientState, createSsoClientState, decodeJWT, extractClaims, extractData, getAccessToken, getAvailableMethods, getEffectiveLevel, getMfaToken, getRefreshToken, getSessionToken, getTokenExpiration, getTokenRemainingTime, getUserRoles, hasAllRoles, hasAnyRole, hasRole, initCrossTabSync, isLoginSuccessResponse, isMfaChallengeResponse, isTokenExpired, refreshSession, updateStoredTokens, verifySsoClientState };
|
package/dist/core/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { AuthService, authService, formatUserRoles, getAvailableMethods, getAvatarFallback, getDisplayName, getGreeting, getMfaToken, getUserEmail, getUserInitials, isLoginSuccessResponse, isMfaChallengeResponse } from '../chunk-
|
|
2
|
-
export { InvalidAuthResponseError, RoleDeniedError, SSO_CLIENT_STATE_PARAM, SSO_CLIENT_STATE_TTL_MS, api, apiRequest, authApi, clearStoredAuth, consumeSsoClientState, createSsoClientState, extractData, getAccessToken, getRefreshToken, getSessionToken, initCrossTabSync, isRoleDeniedError, refreshSession, updateStoredTokens, verifySsoClientState } from '../chunk-
|
|
1
|
+
export { AuthService, authService, formatUserRoles, getAvailableMethods, getAvatarFallback, getDisplayName, getGreeting, getMfaToken, getUserEmail, getUserInitials, isLoginSuccessResponse, isMfaChallengeResponse } from '../chunk-LPQKZRPC.js';
|
|
2
|
+
export { InvalidAuthResponseError, MFAChallengeEmailError, RoleDeniedError, SSO_CLIENT_STATE_PARAM, SSO_CLIENT_STATE_TTL_MS, SessionChangedError, api, apiRequest, authApi, clearStoredAuth, consumeSsoClientState, createSsoClientState, extractData, getAccessToken, getRefreshToken, getSessionToken, initCrossTabSync, isRoleDeniedError, isSessionChangedError, refreshSession, updateStoredTokens, verifySsoClientState } from '../chunk-7AQYLLTA.js';
|
|
3
3
|
export { ROLE_HIERARCHY, ROLE_NAMES, computePrimaryRole, createMemoryStorage, createSessionStorage, decodeJWT, extractClaims, getConfig, getDefaultStorage, getEffectiveLevel, getFetch, getStorage, getTokenExpiration, getTokenRemainingTime, getUserRoles, hasAllRoles, hasAnyRole, hasRole, initAuth, isInitialized, isTokenExpired, resetConfig } from '../chunk-K6TV3ZVS.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as ApiKey, a as AuthConfig, b as AuthState, C as ChangePasswordData, c as CreateApiKeyRequest, d as CreateApiKeyResponse, D as Device, L as LinkedAccount, e as LoginCredentials, f as LoginResponse, g as LogoutResponse, M as MFAChallengeData, h as
|
|
1
|
+
export { A as ApiKey, a as AuthConfig, b as AuthState, C as ChangePasswordData, c as CreateApiKeyRequest, d as CreateApiKeyResponse, D as Device, L as LinkedAccount, e as LoginCredentials, f as LoginResponse, g as LogoutResponse, M as MFAChallengeData, h as MFAChallengeEmailResponse, i as MFASetupResponse, j as MFAStatus, P as Pagination, k as ProfileUpdateData, R as RegisterData, l as RegisterResponse, m as ResetPasswordData, S as SSOConfig, n as SecurityEvent, o as Session, p as StorageAdapter, U as UpdateApiKeyRequest, q as User, r as UserPreferences, s as UserRolesResponse, t as createMemoryStorage, u as createSessionStorage, v as getConfig, w as getDefaultStorage, x as getFetch, y as getStorage, z as initAuth, B as isInitialized, E as resetConfig } from './types-DF5Ny2IA.js';
|
|
2
2
|
export { ApiRequestOptions, ApiResponse, JWTPayload, ROLE_HIERARCHY, ROLE_NAMES, RefreshOutcome, RefreshSessionOptions, RoleName, SSO_CLIENT_STATE_PARAM, SSO_CLIENT_STATE_TTL_MS, SsoClientStateResult, SsoClientStateVerification, api, apiRequest, authApi, clearStoredAuth, computePrimaryRole, consumeSsoClientState, createSsoClientState, decodeJWT, extractClaims, extractData, getAccessToken, getAvailableMethods, getEffectiveLevel, getMfaToken, getRefreshToken, getSessionToken, getTokenExpiration, getTokenRemainingTime, getUserRoles, hasAllRoles, hasAnyRole, hasRole, initCrossTabSync, isLoginSuccessResponse, isMfaChallengeResponse, isTokenExpired, refreshSession, updateStoredTokens, verifySsoClientState } from './core/index.js';
|
|
3
|
-
export { A as AuthService, I as InvalidAuthResponseError, L as LoginOptions, M as MFAVerifyOptions, R as RoleDeniedError,
|
|
3
|
+
export { A as AuthService, I as InvalidAuthResponseError, L as LoginOptions, M as MFAChallengeEmailError, a as MFAVerifyOptions, R as RoleDeniedError, S as SessionChangedError, b as authService, f as formatUserRoles, g as getAvatarFallback, c as getDisplayName, d as getGreeting, e as getUserEmail, h as getUserInitials, i as isRoleDeniedError, j as isSessionChangedError } from './user-utils-Bo3lMsuT.js';
|
|
4
4
|
export { AuthClient, AuthGuardOptions, AuthGuardResult, AuthHookOptions, CreateAuthClientOptions, NavFilterOptions, RoleRestrictedItem, authActions, authStore, canAccess, checkAuth, createAuthClient, createAuthGuard, createAuthHook, createNavFilter, currentUser, filterByAccess, filterNavSections, isAuthenticated, matchesRoute, protectedLoad, requireAuth, requirePermission, requireRole, routePatterns } from './svelte/index.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { canAccess, checkAuth, createAuthClient, createAuthGuard, createAuthHook, createNavFilter, filterByAccess, filterNavSections, matchesRoute, protectedLoad, requireAuth, requirePermission, requireRole, routePatterns } from './chunk-
|
|
2
|
-
export { AuthService, authService, formatUserRoles, getAvailableMethods, getAvatarFallback, getDisplayName, getGreeting, getMfaToken, getUserEmail, getUserInitials, isLoginSuccessResponse, isMfaChallengeResponse } from './chunk-
|
|
3
|
-
export { authActions, authStore, currentUser, isAuthenticated } from './chunk-
|
|
4
|
-
export { InvalidAuthResponseError, RoleDeniedError, SSO_CLIENT_STATE_PARAM, SSO_CLIENT_STATE_TTL_MS, api, apiRequest, authApi, clearStoredAuth, consumeSsoClientState, createSsoClientState, extractData, getAccessToken, getRefreshToken, getSessionToken, initCrossTabSync, isRoleDeniedError, refreshSession, updateStoredTokens, verifySsoClientState } from './chunk-
|
|
1
|
+
export { canAccess, checkAuth, createAuthClient, createAuthGuard, createAuthHook, createNavFilter, filterByAccess, filterNavSections, matchesRoute, protectedLoad, requireAuth, requirePermission, requireRole, routePatterns } from './chunk-7JLCFRUU.js';
|
|
2
|
+
export { AuthService, authService, formatUserRoles, getAvailableMethods, getAvatarFallback, getDisplayName, getGreeting, getMfaToken, getUserEmail, getUserInitials, isLoginSuccessResponse, isMfaChallengeResponse } from './chunk-LPQKZRPC.js';
|
|
3
|
+
export { authActions, authStore, currentUser, isAuthenticated } from './chunk-MBVOLE5U.js';
|
|
4
|
+
export { InvalidAuthResponseError, MFAChallengeEmailError, RoleDeniedError, SSO_CLIENT_STATE_PARAM, SSO_CLIENT_STATE_TTL_MS, SessionChangedError, api, apiRequest, authApi, clearStoredAuth, consumeSsoClientState, createSsoClientState, extractData, getAccessToken, getRefreshToken, getSessionToken, initCrossTabSync, isRoleDeniedError, isSessionChangedError, refreshSession, updateStoredTokens, verifySsoClientState } from './chunk-7AQYLLTA.js';
|
|
5
5
|
export { ROLE_HIERARCHY, ROLE_NAMES, computePrimaryRole, createMemoryStorage, createSessionStorage, decodeJWT, extractClaims, getConfig, getDefaultStorage, getEffectiveLevel, getFetch, getStorage, getTokenExpiration, getTokenRemainingTime, getUserRoles, hasAllRoles, hasAnyRole, hasRole, initAuth, isInitialized, isTokenExpired, resetConfig } from './chunk-K6TV3ZVS.js';
|
package/dist/svelte/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { b as AuthState,
|
|
2
|
-
export {
|
|
3
|
-
import {
|
|
4
|
-
export { I as InvalidAuthResponseError, R as RoleDeniedError, f as formatUserRoles, g as getAvatarFallback,
|
|
1
|
+
import { b as AuthState, q as User, a as AuthConfig } from '../types-DF5Ny2IA.js';
|
|
2
|
+
export { z as initAuth, B as isInitialized } from '../types-DF5Ny2IA.js';
|
|
3
|
+
import { b as authService } from '../user-utils-Bo3lMsuT.js';
|
|
4
|
+
export { I as InvalidAuthResponseError, R as RoleDeniedError, S as SessionChangedError, f as formatUserRoles, g as getAvatarFallback, c as getDisplayName, d as getGreeting, e as getUserEmail, h as getUserInitials, i as isRoleDeniedError, j as isSessionChangedError } from '../user-utils-Bo3lMsuT.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Auth Store
|
package/dist/svelte/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { canAccess, checkAuth, createAuthClient, createAuthGuard, createAuthHook, createNavFilter, filterByAccess, filterNavSections, matchesRoute, protectedLoad, requireAuth, requirePermission, requireRole, routePatterns } from '../chunk-
|
|
2
|
-
export { formatUserRoles, getAvatarFallback, getDisplayName, getGreeting, getUserEmail, getUserInitials } from '../chunk-
|
|
3
|
-
export { authActions, authStore, currentUser, isAuthenticated } from '../chunk-
|
|
4
|
-
export { InvalidAuthResponseError, RoleDeniedError, isRoleDeniedError } from '../chunk-
|
|
1
|
+
export { canAccess, checkAuth, createAuthClient, createAuthGuard, createAuthHook, createNavFilter, filterByAccess, filterNavSections, matchesRoute, protectedLoad, requireAuth, requirePermission, requireRole, routePatterns } from '../chunk-7JLCFRUU.js';
|
|
2
|
+
export { formatUserRoles, getAvatarFallback, getDisplayName, getGreeting, getUserEmail, getUserInitials } from '../chunk-LPQKZRPC.js';
|
|
3
|
+
export { authActions, authStore, currentUser, isAuthenticated } from '../chunk-MBVOLE5U.js';
|
|
4
|
+
export { InvalidAuthResponseError, RoleDeniedError, SessionChangedError, isRoleDeniedError, isSessionChangedError } from '../chunk-7AQYLLTA.js';
|
|
5
5
|
export { initAuth, isInitialized } from '../chunk-K6TV3ZVS.js';
|
package/dist/testing/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { q as User, D as Device, f as LoginResponse, n as SecurityEvent, o as Session, A as ApiKey, L as LinkedAccount, g as LogoutResponse, i as MFASetupResponse, j as MFAStatus, l as RegisterResponse, r as UserPreferences, p as StorageAdapter, b as AuthState, a as AuthConfig } from '../types-DF5Ny2IA.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* User Fixtures
|
package/dist/testing/index.js
CHANGED
|
@@ -1065,6 +1065,11 @@ var MockFetchInstance = class {
|
|
|
1065
1065
|
path: "/auth/mfa/challenge",
|
|
1066
1066
|
response: { data: mockLoginSuccess }
|
|
1067
1067
|
});
|
|
1068
|
+
this.addRoute({
|
|
1069
|
+
method: "POST",
|
|
1070
|
+
path: "/auth/mfa/challenge/email/send",
|
|
1071
|
+
response: { success: true, email: "te***@example.com" }
|
|
1072
|
+
});
|
|
1068
1073
|
this.addRoute({
|
|
1069
1074
|
method: "DELETE",
|
|
1070
1075
|
path: "/auth/mfa",
|
|
@@ -326,6 +326,12 @@ interface MFAChallengeData {
|
|
|
326
326
|
method?: 'totp' | 'email' | 'backup';
|
|
327
327
|
trustDevice?: boolean;
|
|
328
328
|
}
|
|
329
|
+
/** CHAPI's answer to sending an MFA challenge email code. */
|
|
330
|
+
interface MFAChallengeEmailResponse {
|
|
331
|
+
success: boolean;
|
|
332
|
+
/** The address the code went to, masked (`jo***@example.com`). */
|
|
333
|
+
email: string;
|
|
334
|
+
}
|
|
329
335
|
interface Device {
|
|
330
336
|
id: string;
|
|
331
337
|
deviceFingerprint: string;
|
|
@@ -407,4 +413,4 @@ interface ResetPasswordData {
|
|
|
407
413
|
newPassword: string;
|
|
408
414
|
}
|
|
409
415
|
|
|
410
|
-
export { type ApiKey as A,
|
|
416
|
+
export { type ApiKey as A, isInitialized as B, type ChangePasswordData as C, type Device as D, resetConfig as E, type LinkedAccount as L, type MFAChallengeData as M, type Pagination as P, type RegisterData as R, type SSOConfig as S, type UpdateApiKeyRequest as U, type AuthConfig as a, type AuthState as b, type CreateApiKeyRequest as c, type CreateApiKeyResponse as d, type LoginCredentials as e, type LoginResponse as f, type LogoutResponse as g, type MFAChallengeEmailResponse as h, type MFASetupResponse as i, type MFAStatus as j, type ProfileUpdateData as k, type RegisterResponse as l, type ResetPasswordData as m, type SecurityEvent as n, type Session as o, type StorageAdapter as p, type User as q, type UserPreferences as r, type UserRolesResponse as s, createMemoryStorage as t, createSessionStorage as u, getConfig as v, getDefaultStorage as w, getFetch as x, getStorage as y, initAuth as z };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { e as LoginCredentials, f as LoginResponse, g as LogoutResponse, R as RegisterData,
|
|
1
|
+
import { e as LoginCredentials, f as LoginResponse, g as LogoutResponse, R as RegisterData, l as RegisterResponse, q as User, k as ProfileUpdateData, o as Session, A as ApiKey, c as CreateApiKeyRequest, d as CreateApiKeyResponse, U as UpdateApiKeyRequest, j as MFAStatus, i as MFASetupResponse, M as MFAChallengeData, h as MFAChallengeEmailResponse, D as Device, r as UserPreferences, L as LinkedAccount, n as SecurityEvent, P as Pagination } from './types-DF5Ny2IA.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Auth Service
|
|
@@ -192,6 +192,13 @@ declare class AuthService {
|
|
|
192
192
|
* @param options - Optional settings for verification behavior
|
|
193
193
|
*/
|
|
194
194
|
verifyMFAChallenge(data: MFAChallengeData, options?: MFAVerifyOptions): Promise<LoginResponse>;
|
|
195
|
+
/**
|
|
196
|
+
* Email a sign-in code for an MFA challenge (accounts with email MFA).
|
|
197
|
+
* Pass the `mfaToken` from the login / SSO challenge; answer with
|
|
198
|
+
* `verifyMFAChallenge({ mfaToken, code, method: 'email' })`.
|
|
199
|
+
* Throws MFAChallengeEmailError — `retryAfterSeconds` is set on a cooldown.
|
|
200
|
+
*/
|
|
201
|
+
sendMFAChallengeEmail(mfaToken: string): Promise<MFAChallengeEmailResponse>;
|
|
195
202
|
/**
|
|
196
203
|
* Get all devices.
|
|
197
204
|
*/
|
|
@@ -319,6 +326,36 @@ declare class InvalidAuthResponseError extends Error {
|
|
|
319
326
|
readonly name = "InvalidAuthResponseError";
|
|
320
327
|
constructor(message?: string);
|
|
321
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* Error thrown when the session changed (logout, login as another account,
|
|
331
|
+
* expiry) while a request was in flight, and the request needed the session
|
|
332
|
+
* to continue (a 401 that would have been refreshed and retried). The request
|
|
333
|
+
* is not refreshed, retried or allowed to expire the new session: replaying it
|
|
334
|
+
* would send the previous account's request with the next account's
|
|
335
|
+
* credentials.
|
|
336
|
+
*/
|
|
337
|
+
declare class SessionChangedError extends Error {
|
|
338
|
+
readonly name = "SessionChangedError";
|
|
339
|
+
constructor(message?: string);
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Type guard to check if an error is a SessionChangedError.
|
|
343
|
+
*/
|
|
344
|
+
declare function isSessionChangedError(error: unknown): error is SessionChangedError;
|
|
345
|
+
/**
|
|
346
|
+
* Error thrown when CHAPI won't send an MFA challenge email code: the
|
|
347
|
+
* challenge token is invalid or expired (401), the account has no email MFA
|
|
348
|
+
* (400), or a code was requested too soon / too often (429). On a 429,
|
|
349
|
+
* `retryAfterSeconds` says how long to wait when the server gave one.
|
|
350
|
+
*/
|
|
351
|
+
declare class MFAChallengeEmailError extends Error {
|
|
352
|
+
readonly name = "MFAChallengeEmailError";
|
|
353
|
+
/** HTTP status CHAPI answered with. */
|
|
354
|
+
readonly status: number;
|
|
355
|
+
/** Seconds until another code may be requested (cooldown 429s only). */
|
|
356
|
+
readonly retryAfterSeconds?: number;
|
|
357
|
+
constructor(message: string, status: number, retryAfterSeconds?: number);
|
|
358
|
+
}
|
|
322
359
|
|
|
323
360
|
/**
|
|
324
361
|
* User Display Utilities
|
|
@@ -423,4 +460,4 @@ declare function formatUserRoles(user: User | null | undefined, options?: {
|
|
|
423
460
|
capitalize?: boolean;
|
|
424
461
|
}): string;
|
|
425
462
|
|
|
426
|
-
export { AuthService as A, InvalidAuthResponseError as I, type LoginOptions as L,
|
|
463
|
+
export { AuthService as A, InvalidAuthResponseError as I, type LoginOptions as L, MFAChallengeEmailError as M, RoleDeniedError as R, SessionChangedError as S, type MFAVerifyOptions as a, authService as b, getDisplayName as c, getGreeting as d, getUserEmail as e, formatUserRoles as f, getAvatarFallback as g, getUserInitials as h, isRoleDeniedError as i, isSessionChangedError as j };
|