@absolutejs/auth 0.57.9 → 0.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +38 -0
  2. package/dist/cli/migrate.js +31 -12
  3. package/dist/cli/migrate.js.map +4 -4
  4. package/dist/index.js +522 -95
  5. package/dist/index.js.map +17 -15
  6. package/dist/manifest.js +8 -1
  7. package/dist/manifest.js.map +3 -3
  8. package/dist/mfa/challenge.d.ts +5 -3
  9. package/dist/mfa/config.d.ts +9 -0
  10. package/dist/mfa/management.d.ts +3 -3
  11. package/dist/mfa/postgresMfaStore.d.ts +60 -0
  12. package/dist/mfa/recentAuth.d.ts +2 -0
  13. package/dist/mfa/routes.d.ts +15 -10
  14. package/dist/mfa/sms.d.ts +45 -7
  15. package/dist/mfa/totp.d.ts +3 -3
  16. package/dist/mfa/types.d.ts +35 -0
  17. package/dist/server.js +521 -96
  18. package/dist/server.js.map +18 -16
  19. package/dist/types.d.ts +5 -1
  20. package/dist/verification/types.d.ts +58 -0
  21. package/package.json +1 -1
  22. package/dist/agents/index.d.ts +0 -13
  23. package/dist/cli/import/auth0.d.ts +0 -2
  24. package/dist/cli/import/clerk.d.ts +0 -2
  25. package/dist/cli/import/index.d.ts +0 -10
  26. package/dist/cli/import/lucia.d.ts +0 -2
  27. package/dist/cli/import/nextauth.d.ts +0 -2
  28. package/dist/cli/import/supabase.d.ts +0 -2
  29. package/dist/cli/import/types.d.ts +0 -27
  30. package/dist/cli/migrate.d.ts +0 -2
  31. package/dist/client/components/react/SignIn.d.ts +0 -32
  32. package/dist/client/components/react/SignUp.d.ts +0 -29
  33. package/dist/client/components/react/UserButton.d.ts +0 -36
  34. package/dist/client/createAuthClient.d.ts +0 -285
  35. package/dist/client/index.d.ts +0 -3
  36. package/dist/client/passkeyHelpers.d.ts +0 -19
  37. package/dist/client/react.d.ts +0 -87
  38. package/dist/client/sessionExpiry.d.ts +0 -37
  39. package/dist/client/solid.d.ts +0 -86
  40. package/dist/client/svelte.d.ts +0 -86
  41. package/dist/client/vue.d.ts +0 -88
  42. package/dist/fingerprint-client/index.d.ts +0 -26
  43. package/dist/htmx/index.d.ts +0 -2
  44. package/dist/index.d.ts +0 -3637
  45. package/dist/linkedProviders/index.d.ts +0 -3
  46. package/dist/manifest.d.ts +0 -18
  47. package/dist/manifest.json +0 -364
  48. package/dist/oidc/index.d.ts +0 -12
  49. package/dist/oidc/operator.d.ts +0 -14
  50. package/dist/plugins/denyDisposableEmail.d.ts +0 -7
  51. package/dist/plugins/discordAlert.d.ts +0 -7
  52. package/dist/plugins/geoBlock.d.ts +0 -8
  53. package/dist/plugins/index.d.ts +0 -12
  54. package/dist/plugins/pagerdutyAlert.d.ts +0 -9
  55. package/dist/plugins/posthogIdentify.d.ts +0 -7
  56. package/dist/plugins/slackAlert.d.ts +0 -7
  57. package/dist/providers/index.d.ts +0 -2
  58. package/dist/saml.d.ts +0 -1
  59. package/dist/server.d.ts +0 -31
  60. package/dist/sso/nodeSamlAdapter.d.ts +0 -8
  61. package/dist/vault/index.d.ts +0 -5
  62. package/dist/webauthn/simpleWebAuthnAdapter.d.ts +0 -3
  63. package/dist/webauthn.d.ts +0 -1
@@ -1,6 +1,6 @@
1
1
  import { Elysia } from 'elysia';
2
2
  import { type MfaRouteProps } from './config';
3
- export declare const mfaChallenge: <UserType>({ authSessionStore, challengeRoute, cookieSecure, encryptionKey, getChallengeUser, getUserId, mfaStore, onMfaChallengeError, onMfaChallengeSuccess, onSendSmsCode, sessionDurationMs, smsCodeLength, smsCodeTtlMs, smsMaxAttempts, totpMaxAttempts }: MfaRouteProps<UserType>) => Elysia<"", {
3
+ export declare const mfaChallenge: <UserType>({ authSessionStore, challengeRoute, cookieSecure, encryptionKey, getChallengeUser, getUserId, mfaStore, onMfaChallengeError, onMfaChallengeSuccess, onSendSmsCode, verificationProvider, sessionDurationMs, smsCodeLength, smsCodeTtlMs, smsMaxAttempts, smsResendCooldownMs, totpMaxAttempts }: MfaRouteProps<UserType>) => Elysia<"", {
4
4
  decorator: {};
5
5
  store: {
6
6
  session: import("..").SessionRecord<UserType>;
@@ -30,8 +30,8 @@ export declare const mfaChallenge: <UserType>({ authSessionStore, challengeRoute
30
30
  query: unknown;
31
31
  headers: unknown;
32
32
  response: {
33
- 400: "SMS code required" | "No SMS code in progress";
34
- 401: "SMS code expired" | "Too many attempts" | "No MFA challenge in progress" | "Invalid MFA code";
33
+ 400: string;
34
+ 401: "SMS code expired" | "Too many attempts" | "SMS challenge is no longer active" | "No MFA challenge in progress" | "Invalid MFA code";
35
35
  200: {
36
36
  readonly status: "sent";
37
37
  } | {
@@ -46,6 +46,8 @@ export declare const mfaChallenge: <UserType>({ authSessionStore, challengeRoute
46
46
  property?: string;
47
47
  expected?: string;
48
48
  };
49
+ 429: string;
50
+ 503: string;
49
51
  };
50
52
  };
51
53
  };
@@ -3,15 +3,19 @@ import type { RouteString, UserSessionId } from '../types';
3
3
  import type { MFAStore } from './types';
4
4
  export declare const DEFAULT_BACKUP_CODE_COUNT = 10;
5
5
  export declare const DEFAULT_MFA_ISSUER = "AbsoluteAuth";
6
+ export declare const DEFAULT_MFA_MANAGEMENT_AUTH_MAX_AGE_MS: number;
6
7
  export declare const DEFAULT_MFA_SESSION_TTL_MS: number;
7
8
  export declare const DEFAULT_SMS_CODE_LENGTH = 6;
8
9
  export declare const DEFAULT_SMS_CODE_TTL_MS: number;
9
10
  export declare const DEFAULT_SMS_MAX_ATTEMPTS = 3;
11
+ export declare const DEFAULT_SMS_RESEND_COOLDOWN_MS: number;
10
12
  export declare const DEFAULT_TOTP_MAX_ATTEMPTS = 5;
11
13
  export type SmsCodeMessage = {
12
14
  code: string;
13
15
  expiresAt: number;
14
16
  phone: string;
17
+ purpose: 'mfa_challenge' | 'mfa_enrollment';
18
+ userId: string;
15
19
  };
16
20
  export type MfaConfig<UserType> = {
17
21
  mfaStore: MFAStore;
@@ -22,6 +26,8 @@ export type MfaConfig<UserType> = {
22
26
  encryptionKey?: string;
23
27
  issuer?: string;
24
28
  managementRoute?: RouteString;
29
+ /** Maximum age of the authentication ceremony used to enroll, replace, or remove MFA. */
30
+ managementAuthMaxAgeMs?: number;
25
31
  onMfaChallengeError?: (context: {
26
32
  error: unknown;
27
33
  userId?: string;
@@ -38,6 +44,8 @@ export type MfaConfig<UserType> = {
38
44
  smsCodeLength?: number;
39
45
  smsCodeTtlMs?: number;
40
46
  smsMaxAttempts?: number;
47
+ /** Minimum delay between code sends for the same enrollment. */
48
+ smsResendCooldownMs?: number;
41
49
  smsSetupRoute?: RouteString;
42
50
  smsVerifyRoute?: RouteString;
43
51
  totpMaxAttempts?: number;
@@ -47,4 +55,5 @@ export type MfaConfig<UserType> = {
47
55
  export type MfaRouteProps<UserType> = MfaConfig<UserType> & {
48
56
  authSessionStore?: AuthSessionStore<UserType>;
49
57
  cookieSecure?: boolean;
58
+ verificationProvider?: import('../verification/types').VerificationProvider;
50
59
  };
@@ -1,5 +1,5 @@
1
1
  import { Elysia } from 'elysia';
2
- import type { MfaRouteProps } from './config';
2
+ import { type MfaRouteProps } from './config';
3
3
  export type MfaStatus = {
4
4
  backupCodesRemaining: number;
5
5
  enabled: boolean;
@@ -11,7 +11,7 @@ export type MfaStatus = {
11
11
  enabled: boolean;
12
12
  };
13
13
  };
14
- export declare const mfaManagementRoutes: <UserType>({ authSessionStore, getUserId, managementRoute, mfaStore }: MfaRouteProps<UserType>) => Elysia<"", {
14
+ export declare const mfaManagementRoutes: <UserType>({ authSessionStore, getUserId, managementRoute, managementAuthMaxAgeMs, mfaStore }: MfaRouteProps<UserType>) => Elysia<"", {
15
15
  decorator: {};
16
16
  store: {
17
17
  session: import("..").SessionRecord<UserType>;
@@ -69,7 +69,7 @@ export declare const mfaManagementRoutes: <UserType>({ authSessionStore, getUser
69
69
  query: unknown;
70
70
  headers: unknown;
71
71
  response: {
72
- 401: "Authentication required";
72
+ 401: "Recent authentication required" | "Authentication required";
73
73
  200: {
74
74
  readonly status: "disabled";
75
75
  };
@@ -49,6 +49,36 @@ export declare const mfaEnrollmentsTable: import("drizzle-orm/pg-core").PgTableW
49
49
  identity: undefined;
50
50
  generated: undefined;
51
51
  }>;
52
+ sms_challenge_id: import("drizzle-orm/pg-core").PgBuildColumn<"auth_mfa_enrollments", import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>, {
53
+ name: string;
54
+ tableName: "auth_mfa_enrollments";
55
+ dataType: "string";
56
+ data: string;
57
+ driverParam: string;
58
+ notNull: false;
59
+ hasDefault: false;
60
+ isPrimaryKey: false;
61
+ isAutoincrement: false;
62
+ hasRuntimeDefault: false;
63
+ enumValues: undefined;
64
+ identity: undefined;
65
+ generated: undefined;
66
+ }>;
67
+ sms_code_sent_at_ms: import("drizzle-orm/pg-core").PgBuildColumn<"auth_mfa_enrollments", import("drizzle-orm/pg-core").PgBigInt53Builder, {
68
+ name: string;
69
+ tableName: "auth_mfa_enrollments";
70
+ dataType: "number int53";
71
+ data: number;
72
+ driverParam: string | number;
73
+ notNull: false;
74
+ hasDefault: false;
75
+ isPrimaryKey: false;
76
+ isAutoincrement: false;
77
+ hasRuntimeDefault: false;
78
+ enumValues: undefined;
79
+ identity: undefined;
80
+ generated: undefined;
81
+ }>;
52
82
  sms_failed_attempts: import("drizzle-orm/pg-core").PgBuildColumn<"auth_mfa_enrollments", import("drizzle-orm/pg-core").SetHasDefault<import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgSmallIntBuilder>>, {
53
83
  name: string;
54
84
  tableName: "auth_mfa_enrollments";
@@ -94,6 +124,21 @@ export declare const mfaEnrollmentsTable: import("drizzle-orm/pg-core").PgTableW
94
124
  identity: undefined;
95
125
  generated: undefined;
96
126
  }>;
127
+ sms_pending_purpose: import("drizzle-orm/pg-core").PgBuildColumn<"auth_mfa_enrollments", import("drizzle-orm/pg-core").Set$Type<import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>, import("..").VerificationPurpose | undefined>, {
128
+ name: string;
129
+ tableName: "auth_mfa_enrollments";
130
+ dataType: "string";
131
+ data: import("..").VerificationPurpose | undefined;
132
+ driverParam: string;
133
+ notNull: false;
134
+ hasDefault: false;
135
+ isPrimaryKey: false;
136
+ isAutoincrement: false;
137
+ hasRuntimeDefault: false;
138
+ enumValues: undefined;
139
+ identity: undefined;
140
+ generated: undefined;
141
+ }>;
97
142
  sms_phone: import("drizzle-orm/pg-core").PgBuildColumn<"auth_mfa_enrollments", import("drizzle-orm/pg-core").PgVarcharBuilder<[string, ...string[]]>, {
98
143
  name: string;
99
144
  tableName: "auth_mfa_enrollments";
@@ -109,6 +154,21 @@ export declare const mfaEnrollmentsTable: import("drizzle-orm/pg-core").PgTableW
109
154
  identity: undefined;
110
155
  generated: undefined;
111
156
  }>;
157
+ sms_provider_reference: import("drizzle-orm/pg-core").PgBuildColumn<"auth_mfa_enrollments", import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>, {
158
+ name: string;
159
+ tableName: "auth_mfa_enrollments";
160
+ dataType: "string";
161
+ data: string;
162
+ driverParam: string;
163
+ notNull: false;
164
+ hasDefault: false;
165
+ isPrimaryKey: false;
166
+ isAutoincrement: false;
167
+ hasRuntimeDefault: false;
168
+ enumValues: undefined;
169
+ identity: undefined;
170
+ generated: undefined;
171
+ }>;
112
172
  sms_verified: import("drizzle-orm/pg-core").PgBuildColumn<"auth_mfa_enrollments", import("drizzle-orm/pg-core").SetHasDefault<import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgBooleanBuilder>>, {
113
173
  name: string;
114
174
  tableName: "auth_mfa_enrollments";
@@ -0,0 +1,2 @@
1
+ import type { SessionData } from '../types';
2
+ export declare const hasRecentAuthentication: <UserType>(session: SessionData<UserType>, maxAgeMs: number, now?: number) => boolean;
@@ -58,7 +58,7 @@ export declare const mfaRoutes: <UserType>(config: MfaRouteProps<UserType>) => E
58
58
  query: unknown;
59
59
  headers: unknown;
60
60
  response: {
61
- 401: "Authentication required";
61
+ 401: "Recent authentication required" | "Authentication required";
62
62
  200: {
63
63
  readonly status: "disabled";
64
64
  };
@@ -82,7 +82,7 @@ export declare const mfaRoutes: <UserType>(config: MfaRouteProps<UserType>) => E
82
82
  query: unknown;
83
83
  headers: unknown;
84
84
  response: {
85
- 401: "Authentication required";
85
+ 401: "Recent authentication required" | "Authentication required";
86
86
  200: {
87
87
  readonly secret: string;
88
88
  readonly uri: string;
@@ -110,7 +110,7 @@ export declare const mfaRoutes: <UserType>(config: MfaRouteProps<UserType>) => E
110
110
  headers: unknown;
111
111
  response: {
112
112
  400: "No TOTP enrollment in progress" | "Invalid TOTP code";
113
- 401: "Authentication required";
113
+ 401: "Recent authentication required" | "Authentication required";
114
114
  200: {
115
115
  readonly backupCodes: string[];
116
116
  };
@@ -136,8 +136,8 @@ export declare const mfaRoutes: <UserType>(config: MfaRouteProps<UserType>) => E
136
136
  query: unknown;
137
137
  headers: unknown;
138
138
  response: {
139
- 400: "Phone must be in E.164 format";
140
- 401: "Authentication required";
139
+ 400: string;
140
+ 401: "Recent authentication required" | "Authentication required";
141
141
  501: "SMS MFA is not configured";
142
142
  200: {
143
143
  readonly phone: string;
@@ -151,6 +151,8 @@ export declare const mfaRoutes: <UserType>(config: MfaRouteProps<UserType>) => E
151
151
  property?: string;
152
152
  expected?: string;
153
153
  };
154
+ 429: string;
155
+ 503: string;
154
156
  };
155
157
  };
156
158
  };
@@ -164,8 +166,8 @@ export declare const mfaRoutes: <UserType>(config: MfaRouteProps<UserType>) => E
164
166
  query: unknown;
165
167
  headers: unknown;
166
168
  response: {
167
- 400: "No SMS enrollment in progress" | "SMS code expired" | "Invalid SMS code";
168
- 401: "Authentication required";
169
+ 400: string;
170
+ 401: "Recent authentication required" | "Authentication required";
169
171
  200: {
170
172
  readonly status: "enrolled";
171
173
  };
@@ -178,7 +180,8 @@ export declare const mfaRoutes: <UserType>(config: MfaRouteProps<UserType>) => E
178
180
  property?: string;
179
181
  expected?: string;
180
182
  };
181
- 429: "Too many attempts";
183
+ 429: string;
184
+ 503: string;
182
185
  };
183
186
  };
184
187
  };
@@ -194,8 +197,8 @@ export declare const mfaRoutes: <UserType>(config: MfaRouteProps<UserType>) => E
194
197
  query: unknown;
195
198
  headers: unknown;
196
199
  response: {
197
- 400: "SMS code required" | "No SMS code in progress";
198
- 401: "SMS code expired" | "Too many attempts" | "No MFA challenge in progress" | "Invalid MFA code";
200
+ 400: string;
201
+ 401: "SMS code expired" | "Too many attempts" | "SMS challenge is no longer active" | "No MFA challenge in progress" | "Invalid MFA code";
199
202
  200: {
200
203
  readonly status: "sent";
201
204
  } | {
@@ -210,6 +213,8 @@ export declare const mfaRoutes: <UserType>(config: MfaRouteProps<UserType>) => E
210
213
  property?: string;
211
214
  expected?: string;
212
215
  };
216
+ 429: string;
217
+ 503: string;
213
218
  };
214
219
  };
215
220
  };
package/dist/mfa/sms.d.ts CHANGED
@@ -1,16 +1,51 @@
1
1
  import { Elysia } from 'elysia';
2
+ import { type VerificationCheckInput, type VerificationProvider } from '../verification/types';
2
3
  import { type MfaRouteProps, type SmsCodeMessage } from './config';
3
4
  import type { MfaEnrollment, MFAStore } from './types';
5
+ export declare class SmsChallengeConflictError extends Error {
6
+ name: string;
7
+ }
8
+ export declare const checkWithVerificationProvider: (provider: VerificationProvider, input: VerificationCheckInput) => Promise<{
9
+ result: import("..").VerificationCheckResult;
10
+ readonly error?: undefined;
11
+ } | {
12
+ error: {
13
+ message: string;
14
+ status: "Too Many Requests";
15
+ } | {
16
+ message: string;
17
+ status: "Bad Request";
18
+ } | {
19
+ message: string;
20
+ status: "Service Unavailable";
21
+ };
22
+ result?: undefined;
23
+ }>;
4
24
  export declare const isE164Phone: (phone: string) => boolean;
5
- export declare const issueAndStoreSmsCode: ({ codeLength, enrollment, mfaStore, onSendSmsCode, ttlMs }: {
25
+ export declare const issueAndStoreSmsCode: ({ codeLength, enrollment, mfaStore, onSendSmsCode, previousEnrollment, purpose, resendCooldownMs, verificationProvider, ttlMs, userId }: {
6
26
  codeLength: number;
7
27
  enrollment: MfaEnrollment;
8
28
  mfaStore: MFAStore;
9
29
  onSendSmsCode?: (message: SmsCodeMessage) => void | Promise<void>;
30
+ previousEnrollment?: MfaEnrollment;
31
+ verificationProvider?: MfaRouteProps<unknown>["verificationProvider"];
32
+ userId: string;
33
+ purpose: "mfa_challenge" | "mfa_enrollment";
34
+ resendCooldownMs: number;
10
35
  ttlMs: number;
11
36
  }) => Promise<number | undefined>;
37
+ export declare const mapVerificationProviderError: (error: unknown) => {
38
+ message: string;
39
+ status: "Too Many Requests";
40
+ } | {
41
+ message: string;
42
+ status: "Bad Request";
43
+ } | {
44
+ message: string;
45
+ status: "Service Unavailable";
46
+ } | undefined;
12
47
  export declare const maskPhone: (phone: string) => string;
13
- export declare const mfaSmsRoutes: <UserType>({ authSessionStore, getUserId, mfaStore, onMfaEnrolled, onSendSmsCode, smsCodeLength, smsCodeTtlMs, smsMaxAttempts, smsSetupRoute, smsVerifyRoute }: MfaRouteProps<UserType>) => Elysia<"", {
48
+ export declare const mfaSmsRoutes: <UserType>({ authSessionStore, getUserId, mfaStore, managementAuthMaxAgeMs, onMfaEnrolled, onSendSmsCode, verificationProvider, smsCodeLength, smsCodeTtlMs, smsMaxAttempts, smsResendCooldownMs, smsSetupRoute, smsVerifyRoute }: MfaRouteProps<UserType>) => Elysia<"", {
14
49
  decorator: {};
15
50
  store: {
16
51
  session: import("..").SessionRecord<UserType>;
@@ -38,8 +73,8 @@ export declare const mfaSmsRoutes: <UserType>({ authSessionStore, getUserId, mfa
38
73
  query: unknown;
39
74
  headers: unknown;
40
75
  response: {
41
- 400: "Phone must be in E.164 format";
42
- 401: "Authentication required";
76
+ 400: string;
77
+ 401: "Recent authentication required" | "Authentication required";
43
78
  501: "SMS MFA is not configured";
44
79
  200: {
45
80
  readonly phone: string;
@@ -53,6 +88,8 @@ export declare const mfaSmsRoutes: <UserType>({ authSessionStore, getUserId, mfa
53
88
  property?: string;
54
89
  expected?: string;
55
90
  };
91
+ 429: string;
92
+ 503: string;
56
93
  };
57
94
  };
58
95
  };
@@ -66,8 +103,8 @@ export declare const mfaSmsRoutes: <UserType>({ authSessionStore, getUserId, mfa
66
103
  query: unknown;
67
104
  headers: unknown;
68
105
  response: {
69
- 400: "No SMS enrollment in progress" | "SMS code expired" | "Invalid SMS code";
70
- 401: "Authentication required";
106
+ 400: string;
107
+ 401: "Recent authentication required" | "Authentication required";
71
108
  200: {
72
109
  readonly status: "enrolled";
73
110
  };
@@ -80,7 +117,8 @@ export declare const mfaSmsRoutes: <UserType>({ authSessionStore, getUserId, mfa
80
117
  property?: string;
81
118
  expected?: string;
82
119
  };
83
- 429: "Too many attempts";
120
+ 429: string;
121
+ 503: string;
84
122
  };
85
123
  };
86
124
  };
@@ -1,6 +1,6 @@
1
1
  import { Elysia } from 'elysia';
2
2
  import { type MfaRouteProps } from './config';
3
- export declare const mfaTotpRoutes: <UserType>({ authSessionStore, backupCodeCount, encryptionKey, getUserId, issuer, mfaStore, onMfaEnrolled, totpSetupRoute, totpVerifyRoute }: MfaRouteProps<UserType>) => Elysia<"", {
3
+ export declare const mfaTotpRoutes: <UserType>({ authSessionStore, backupCodeCount, encryptionKey, getUserId, issuer, managementAuthMaxAgeMs, mfaStore, onMfaEnrolled, totpSetupRoute, totpVerifyRoute }: MfaRouteProps<UserType>) => Elysia<"", {
4
4
  decorator: {};
5
5
  store: {
6
6
  session: import("..").SessionRecord<UserType>;
@@ -26,7 +26,7 @@ export declare const mfaTotpRoutes: <UserType>({ authSessionStore, backupCodeCou
26
26
  query: unknown;
27
27
  headers: unknown;
28
28
  response: {
29
- 401: "Authentication required";
29
+ 401: "Recent authentication required" | "Authentication required";
30
30
  200: {
31
31
  readonly secret: string;
32
32
  readonly uri: string;
@@ -54,7 +54,7 @@ export declare const mfaTotpRoutes: <UserType>({ authSessionStore, backupCodeCou
54
54
  headers: unknown;
55
55
  response: {
56
56
  400: "No TOTP enrollment in progress" | "Invalid TOTP code";
57
- 401: "Authentication required";
57
+ 401: "Recent authentication required" | "Authentication required";
58
58
  200: {
59
59
  readonly backupCodes: string[];
60
60
  };
@@ -1,3 +1,4 @@
1
+ import type { VerificationPurpose } from '../verification/types';
1
2
  export type MfaFactorType = 'backup_codes' | 'sms' | 'totp';
2
3
  export type MfaEnrollment = {
3
4
  backupCodeHashes: string[];
@@ -6,6 +7,11 @@ export type MfaEnrollment = {
6
7
  smsFailedAttempts?: number;
7
8
  smsPendingCodeHash?: string;
8
9
  smsPendingCodeExpiresAt?: number;
10
+ smsPendingPurpose?: VerificationPurpose;
11
+ smsProviderReference?: string;
12
+ smsCodeSentAt?: number;
13
+ /** Opaque generation binding every update to the currently active challenge. */
14
+ smsChallengeId?: string;
9
15
  smsPhone?: string;
10
16
  smsVerified: boolean;
11
17
  totpFailedAttempts?: number;
@@ -15,9 +21,38 @@ export type MfaEnrollment = {
15
21
  userId: string;
16
22
  };
17
23
  export type MFAStore = {
24
+ /** Atomically reserves the SMS slot only when the resend cooldown has elapsed. */
25
+ claimSmsChallenge: (input: {
26
+ challengeId: string;
27
+ cooldownCutoff: number;
28
+ enrollment: MfaEnrollment;
29
+ }) => Promise<boolean>;
30
+ completeSmsChallenge: (input: {
31
+ challengeId: string;
32
+ lastUsedAt?: number;
33
+ smsVerified: boolean;
34
+ userId: string;
35
+ }) => Promise<boolean>;
36
+ finalizeSmsChallenge: (input: {
37
+ challengeId: string;
38
+ expiresAt: number;
39
+ hash?: string;
40
+ providerReference?: string;
41
+ userId: string;
42
+ }) => Promise<boolean>;
18
43
  getEnrollment: (userId: string) => Promise<MfaEnrollment | undefined>;
19
44
  listEnrollments: () => Promise<MfaEnrollment[]>;
20
45
  removeEnrollment: (userId: string) => Promise<void>;
46
+ recordSmsFailure: (input: {
47
+ challengeId: string;
48
+ maxAttempts: number;
49
+ userId: string;
50
+ }) => Promise<number | undefined>;
51
+ rollbackSmsChallenge: (input: {
52
+ challengeId: string;
53
+ previous?: MfaEnrollment;
54
+ userId: string;
55
+ }) => Promise<void>;
21
56
  saveEnrollment: (enrollment: MfaEnrollment) => Promise<void>;
22
57
  };
23
58
  export declare const isMfaEnrolled: (enrollment: MfaEnrollment | undefined) => boolean;