@classic-homes/auth 1.28.0 → 1.41.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 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-S3DNV7VV.js';
2
- import './chunk-GBYHYVXX.js';
1
+ export { authActions, authStore, currentUser, isAuthenticated } from './chunk-HMZRBRIU.js';
2
+ import './chunk-QLEEAHJJ.js';
3
3
  import './chunk-K6TV3ZVS.js';
@@ -1,5 +1,5 @@
1
- import { authService } from './chunk-TUWTOMZZ.js';
2
- import { authStore, currentUser, isAuthenticated, authActions } from './chunk-S3DNV7VV.js';
1
+ import { authService } from './chunk-BMCGFHVM.js';
2
+ import { authStore, currentUser, isAuthenticated, authActions } from './chunk-HMZRBRIU.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-GBYHYVXX.js';
1
+ import { authApi, InvalidAuthResponseError, RoleDeniedError } from './chunk-QLEEAHJJ.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-JLSRQJAC.js')).authStore;
22
+ store = (await import('./auth.svelte-DSE2NZMP.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-GBYHYVXX.js';
1
+ import { InvalidAuthResponseError, authApi } from './chunk-QLEEAHJJ.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
@@ -96,6 +96,17 @@ var InvalidAuthResponseError = class _InvalidAuthResponseError extends Error {
96
96
  }
97
97
  }
98
98
  };
99
+ var MFAChallengeEmailError = class _MFAChallengeEmailError extends Error {
100
+ constructor(message, status, retryAfterSeconds) {
101
+ super(message);
102
+ this.name = "MFAChallengeEmailError";
103
+ this.status = status;
104
+ this.retryAfterSeconds = retryAfterSeconds;
105
+ if (Error.captureStackTrace) {
106
+ Error.captureStackTrace(this, _MFAChallengeEmailError);
107
+ }
108
+ }
109
+ };
99
110
 
100
111
  // src/core/client.ts
101
112
  var DEFAULT_REFRESH_RETRY_DELAYS = [1e3, 3e3];
@@ -155,7 +166,7 @@ function writeStoredTokens(accessToken, refreshToken) {
155
166
  }
156
167
  async function syncSvelteStore(apply) {
157
168
  try {
158
- const { authStore } = await import('./auth.svelte-JLSRQJAC.js');
169
+ const { authStore } = await import('./auth.svelte-DSE2NZMP.js');
159
170
  apply(authStore);
160
171
  } catch {
161
172
  }
@@ -729,7 +740,9 @@ var authApi = {
729
740
  const config = getConfig();
730
741
  const requestBody = {
731
742
  code: data.code,
732
- type: data.method === "backup" ? "backup" : "totp",
743
+ // CHAPI answers the challenge per method: an authenticator code, an
744
+ // emailed sign-in code or a backup code (no method → authenticator).
745
+ type: data.method === "backup" || data.method === "email" ? data.method : "totp",
733
746
  trustDevice: data.trustDevice
734
747
  };
735
748
  const response = await fetchWithTimeout(`${config.baseUrl}/auth/mfa/challenge`, {
@@ -747,6 +760,27 @@ var authApi = {
747
760
  const result = await response.json();
748
761
  return extractData(result);
749
762
  },
763
+ /**
764
+ * Email a sign-in code for an MFA challenge (accounts with email MFA).
765
+ * Authenticated by the challenge token from login / the SSO callback, not
766
+ * a session. Answer it with verifyMFAChallenge({ method: 'email' }).
767
+ * Throws MFAChallengeEmailError (with `retryAfterSeconds` on a cooldown).
768
+ */
769
+ async sendMFAChallengeEmail(mfaToken) {
770
+ const config = getConfig();
771
+ const response = await fetchWithTimeout(`${config.baseUrl}/auth/mfa/challenge/email/send`, {
772
+ method: "POST",
773
+ headers: { Authorization: `Bearer ${mfaToken}` }
774
+ });
775
+ const body = await response.json().catch(() => ({}));
776
+ if (!response.ok) {
777
+ const details = body?.error?.details;
778
+ const retryAfter = typeof details?.retryAfterSeconds === "number" ? details.retryAfterSeconds : typeof body?.retryAfterSeconds === "number" ? body.retryAfterSeconds : void 0;
779
+ const message = (typeof body?.error === "string" ? body.error : body?.error?.message) || body?.message || "Could not send the verification code";
780
+ throw new MFAChallengeEmailError(message, response.status, retryAfter);
781
+ }
782
+ return extractData(body);
783
+ },
750
784
  // ============================================================================
751
785
  // Device Management
752
786
  // ============================================================================
@@ -978,4 +1012,4 @@ var authApi = {
978
1012
  }
979
1013
  };
980
1014
 
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 };
1015
+ export { InvalidAuthResponseError, MFAChallengeEmailError, 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 };
@@ -1,6 +1,6 @@
1
- import { e as LoginCredentials, f as LoginResponse, g as LogoutResponse, R as RegisterData, k as RegisterResponse, p as User, j as ProfileUpdateData, C as ChangePasswordData, n as Session, A as ApiKey, c as CreateApiKeyRequest, d as CreateApiKeyResponse, U as UpdateApiKeyRequest, i as MFAStatus, h as MFASetupResponse, M as MFAChallengeData, D as Device, q as UserPreferences, L as LinkedAccount, m as SecurityEvent, P as Pagination, r as UserRolesResponse } from '../types-B70Kgcc_.js';
2
- export { a as AuthConfig, b as AuthState, l as ResetPasswordData, S as SSOConfig, o as StorageAdapter, s as createMemoryStorage, t as createSessionStorage, u as getConfig, v as getDefaultStorage, w as getFetch, x as getStorage, y as initAuth, z as isInitialized, B as resetConfig } from '../types-B70Kgcc_.js';
3
- export { A as AuthService, I as InvalidAuthResponseError, L as LoginOptions, M as MFAVerifyOptions, R as RoleDeniedError, a as authService, f as formatUserRoles, g as getAvatarFallback, b as getDisplayName, c as getGreeting, d as getUserEmail, e as getUserInitials, i as isRoleDeniedError } from '../user-utils-DWEU-Ovm.js';
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, b as authService, f as formatUserRoles, g as getAvatarFallback, c as getDisplayName, d as getGreeting, e as getUserEmail, h as getUserInitials, i as isRoleDeniedError } from '../user-utils-B6BNJzF2.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 };
@@ -1,3 +1,3 @@
1
- export { AuthService, authService, formatUserRoles, getAvailableMethods, getAvatarFallback, getDisplayName, getGreeting, getMfaToken, getUserEmail, getUserInitials, isLoginSuccessResponse, isMfaChallengeResponse } from '../chunk-TUWTOMZZ.js';
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-GBYHYVXX.js';
1
+ export { AuthService, authService, formatUserRoles, getAvailableMethods, getAvatarFallback, getDisplayName, getGreeting, getMfaToken, getUserEmail, getUserInitials, isLoginSuccessResponse, isMfaChallengeResponse } from '../chunk-BMCGFHVM.js';
2
+ export { InvalidAuthResponseError, MFAChallengeEmailError, 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-QLEEAHJJ.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 MFASetupResponse, i as MFAStatus, P as Pagination, j as ProfileUpdateData, R as RegisterData, k as RegisterResponse, l as ResetPasswordData, S as SSOConfig, m as SecurityEvent, n as Session, o as StorageAdapter, U as UpdateApiKeyRequest, p as User, q as UserPreferences, r as UserRolesResponse, s as createMemoryStorage, t as createSessionStorage, u as getConfig, v as getDefaultStorage, w as getFetch, x as getStorage, y as initAuth, z as isInitialized, B as resetConfig } from './types-B70Kgcc_.js';
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, a as authService, f as formatUserRoles, g as getAvatarFallback, b as getDisplayName, c as getGreeting, d as getUserEmail, e as getUserInitials, i as isRoleDeniedError } from './user-utils-DWEU-Ovm.js';
3
+ export { A as AuthService, I as InvalidAuthResponseError, L as LoginOptions, M as MFAChallengeEmailError, a as MFAVerifyOptions, R as RoleDeniedError, b as authService, f as formatUserRoles, g as getAvatarFallback, c as getDisplayName, d as getGreeting, e as getUserEmail, h as getUserInitials, i as isRoleDeniedError } from './user-utils-B6BNJzF2.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-XUOSWSDO.js';
2
- export { AuthService, authService, formatUserRoles, getAvailableMethods, getAvatarFallback, getDisplayName, getGreeting, getMfaToken, getUserEmail, getUserInitials, isLoginSuccessResponse, isMfaChallengeResponse } from './chunk-TUWTOMZZ.js';
3
- export { authActions, authStore, currentUser, isAuthenticated } from './chunk-S3DNV7VV.js';
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-GBYHYVXX.js';
1
+ export { canAccess, checkAuth, createAuthClient, createAuthGuard, createAuthHook, createNavFilter, filterByAccess, filterNavSections, matchesRoute, protectedLoad, requireAuth, requirePermission, requireRole, routePatterns } from './chunk-5SZT7GKG.js';
2
+ export { AuthService, authService, formatUserRoles, getAvailableMethods, getAvatarFallback, getDisplayName, getGreeting, getMfaToken, getUserEmail, getUserInitials, isLoginSuccessResponse, isMfaChallengeResponse } from './chunk-BMCGFHVM.js';
3
+ export { authActions, authStore, currentUser, isAuthenticated } from './chunk-HMZRBRIU.js';
4
+ export { InvalidAuthResponseError, MFAChallengeEmailError, 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-QLEEAHJJ.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';
@@ -1,7 +1,7 @@
1
- import { b as AuthState, p as User, a as AuthConfig } from '../types-B70Kgcc_.js';
2
- export { y as initAuth, z as isInitialized } from '../types-B70Kgcc_.js';
3
- import { a as authService } from '../user-utils-DWEU-Ovm.js';
4
- export { I as InvalidAuthResponseError, R as RoleDeniedError, f as formatUserRoles, g as getAvatarFallback, b as getDisplayName, c as getGreeting, d as getUserEmail, e as getUserInitials, i as isRoleDeniedError } from '../user-utils-DWEU-Ovm.js';
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-B6BNJzF2.js';
4
+ export { I as InvalidAuthResponseError, R as RoleDeniedError, f as formatUserRoles, g as getAvatarFallback, c as getDisplayName, d as getGreeting, e as getUserEmail, h as getUserInitials, i as isRoleDeniedError } from '../user-utils-B6BNJzF2.js';
5
5
 
6
6
  /**
7
7
  * Auth Store
@@ -1,5 +1,5 @@
1
- export { canAccess, checkAuth, createAuthClient, createAuthGuard, createAuthHook, createNavFilter, filterByAccess, filterNavSections, matchesRoute, protectedLoad, requireAuth, requirePermission, requireRole, routePatterns } from '../chunk-XUOSWSDO.js';
2
- export { formatUserRoles, getAvatarFallback, getDisplayName, getGreeting, getUserEmail, getUserInitials } from '../chunk-TUWTOMZZ.js';
3
- export { authActions, authStore, currentUser, isAuthenticated } from '../chunk-S3DNV7VV.js';
4
- export { InvalidAuthResponseError, RoleDeniedError, isRoleDeniedError } from '../chunk-GBYHYVXX.js';
1
+ export { canAccess, checkAuth, createAuthClient, createAuthGuard, createAuthHook, createNavFilter, filterByAccess, filterNavSections, matchesRoute, protectedLoad, requireAuth, requirePermission, requireRole, routePatterns } from '../chunk-5SZT7GKG.js';
2
+ export { formatUserRoles, getAvatarFallback, getDisplayName, getGreeting, getUserEmail, getUserInitials } from '../chunk-BMCGFHVM.js';
3
+ export { authActions, authStore, currentUser, isAuthenticated } from '../chunk-HMZRBRIU.js';
4
+ export { InvalidAuthResponseError, RoleDeniedError, isRoleDeniedError } from '../chunk-QLEEAHJJ.js';
5
5
  export { initAuth, isInitialized } from '../chunk-K6TV3ZVS.js';
@@ -1,4 +1,4 @@
1
- import { p as User, D as Device, f as LoginResponse, m as SecurityEvent, n as Session, A as ApiKey, L as LinkedAccount, g as LogoutResponse, h as MFASetupResponse, i as MFAStatus, k as RegisterResponse, q as UserPreferences, o as StorageAdapter, b as AuthState, a as AuthConfig } from '../types-B70Kgcc_.js';
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
@@ -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, resetConfig as B, type ChangePasswordData as C, type Device as D, 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 MFASetupResponse as h, type MFAStatus as i, type ProfileUpdateData as j, type RegisterResponse as k, type ResetPasswordData as l, type SecurityEvent as m, type Session as n, type StorageAdapter as o, type User as p, type UserPreferences as q, type UserRolesResponse as r, createMemoryStorage as s, createSessionStorage as t, getConfig as u, getDefaultStorage as v, getFetch as w, getStorage as x, initAuth as y, isInitialized as z };
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, k as RegisterResponse, p as User, j as ProfileUpdateData, n as Session, A as ApiKey, c as CreateApiKeyRequest, d as CreateApiKeyResponse, U as UpdateApiKeyRequest, i as MFAStatus, h as MFASetupResponse, M as MFAChallengeData, D as Device, q as UserPreferences, L as LinkedAccount, m as SecurityEvent, P as Pagination } from './types-B70Kgcc_.js';
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,20 @@ declare class InvalidAuthResponseError extends Error {
319
326
  readonly name = "InvalidAuthResponseError";
320
327
  constructor(message?: string);
321
328
  }
329
+ /**
330
+ * Error thrown when CHAPI won't send an MFA challenge email code: the
331
+ * challenge token is invalid or expired (401), the account has no email MFA
332
+ * (400), or a code was requested too soon / too often (429). On a 429,
333
+ * `retryAfterSeconds` says how long to wait when the server gave one.
334
+ */
335
+ declare class MFAChallengeEmailError extends Error {
336
+ readonly name = "MFAChallengeEmailError";
337
+ /** HTTP status CHAPI answered with. */
338
+ readonly status: number;
339
+ /** Seconds until another code may be requested (cooldown 429s only). */
340
+ readonly retryAfterSeconds?: number;
341
+ constructor(message: string, status: number, retryAfterSeconds?: number);
342
+ }
322
343
 
323
344
  /**
324
345
  * User Display Utilities
@@ -423,4 +444,4 @@ declare function formatUserRoles(user: User | null | undefined, options?: {
423
444
  capitalize?: boolean;
424
445
  }): string;
425
446
 
426
- export { AuthService as A, InvalidAuthResponseError as I, type LoginOptions as L, type MFAVerifyOptions as M, RoleDeniedError as R, authService as a, getDisplayName as b, getGreeting as c, getUserEmail as d, getUserInitials as e, formatUserRoles as f, getAvatarFallback as g, isRoleDeniedError as i };
447
+ export { AuthService as A, InvalidAuthResponseError as I, type LoginOptions as L, MFAChallengeEmailError as M, RoleDeniedError as R, 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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classic-homes/auth",
3
- "version": "1.28.0",
3
+ "version": "1.41.0",
4
4
  "description": "Authentication services and Svelte bindings for Classic Theme apps",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",