@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
package/dist/index.js CHANGED
@@ -5848,11 +5848,13 @@ var generateBackupCodes = async (count) => {
5848
5848
  init_constants();
5849
5849
  var DEFAULT_BACKUP_CODE_COUNT = 10;
5850
5850
  var DEFAULT_MFA_ISSUER = "AbsoluteAuth";
5851
+ var DEFAULT_MFA_MANAGEMENT_AUTH_MAX_AGE_MS = 5 * SECONDS_IN_A_MINUTE * MILLISECONDS_IN_A_SECOND;
5851
5852
  var DEFAULT_MFA_SESSION_TTL_MS = MILLISECONDS_IN_A_DAY;
5852
5853
  var DEFAULT_SMS_CODE_LENGTH = 6;
5853
5854
  var SMS_CODE_TTL_MINUTES = 5;
5854
5855
  var DEFAULT_SMS_CODE_TTL_MS = SMS_CODE_TTL_MINUTES * SECONDS_IN_A_MINUTE * MILLISECONDS_IN_A_SECOND;
5855
5856
  var DEFAULT_SMS_MAX_ATTEMPTS = 3;
5857
+ var DEFAULT_SMS_RESEND_COOLDOWN_MS = 30 * MILLISECONDS_IN_A_SECOND;
5856
5858
  var DEFAULT_TOTP_MAX_ATTEMPTS = 5;
5857
5859
 
5858
5860
  // src/mfa/secret.ts
@@ -5863,6 +5865,23 @@ var encryptTotpSecret = (secret, encryptionKey) => encryptionKey ? encryptSecret
5863
5865
  // src/mfa/sms.ts
5864
5866
  init_crypto();
5865
5867
  import { Elysia as Elysia17, t as t11 } from "elysia";
5868
+
5869
+ // src/verification/types.ts
5870
+ class VerificationProviderError extends Error {
5871
+ kind;
5872
+ provider;
5873
+ constructor(input) {
5874
+ super(input.message, { cause: input.cause });
5875
+ this.name = "VerificationProviderError";
5876
+ this.kind = input.kind;
5877
+ this.provider = input.provider;
5878
+ }
5879
+ }
5880
+
5881
+ // src/mfa/recentAuth.ts
5882
+ var hasRecentAuthentication = (session, maxAgeMs, now = Date.now()) => session.authenticatedAt !== undefined && now >= session.authenticatedAt && now - session.authenticatedAt <= maxAgeMs;
5883
+
5884
+ // src/mfa/sms.ts
5866
5885
  var DECIMAL_RADIX2 = 10;
5867
5886
  var MASK_VISIBLE_DIGITS = 4;
5868
5887
  var E164_PATTERN = /^\+[1-9]\d{7,14}$/u;
@@ -5877,28 +5896,147 @@ var issueSmsCode = async (codeLength, ttlMs) => {
5877
5896
  const expiresAt = Date.now() + ttlMs;
5878
5897
  return { code, expiresAt, hash };
5879
5898
  };
5899
+ var startProviderSmsChallenge = async (input) => {
5900
+ let started;
5901
+ try {
5902
+ started = await input.verificationProvider.start({
5903
+ channel: "sms",
5904
+ purpose: input.purpose,
5905
+ subject: input.userId,
5906
+ to: input.phone
5907
+ });
5908
+ } catch (error) {
5909
+ await input.mfaStore.rollbackSmsChallenge({
5910
+ challengeId: input.challengeId,
5911
+ previous: input.previousEnrollment,
5912
+ userId: input.userId
5913
+ });
5914
+ throw error;
5915
+ }
5916
+ const finalized = await input.mfaStore.finalizeSmsChallenge({
5917
+ challengeId: input.challengeId,
5918
+ expiresAt: started.expiresAt,
5919
+ providerReference: started.reference,
5920
+ userId: input.userId
5921
+ });
5922
+ if (finalized)
5923
+ return started;
5924
+ await input.verificationProvider.cancel({
5925
+ channel: "sms",
5926
+ purpose: input.purpose,
5927
+ reference: started.reference,
5928
+ subject: input.userId,
5929
+ to: input.phone
5930
+ }).catch(() => {
5931
+ return;
5932
+ });
5933
+ throw new SmsChallengeConflictError("SMS challenge claim was lost");
5934
+ };
5935
+
5936
+ class SmsChallengeConflictError extends Error {
5937
+ name = "SmsChallengeConflictError";
5938
+ }
5939
+ var checkWithVerificationProvider = async (provider, input) => {
5940
+ try {
5941
+ return { result: await provider.check(input) };
5942
+ } catch (error) {
5943
+ const mapped = mapVerificationProviderError(error);
5944
+ if (mapped === undefined)
5945
+ throw error;
5946
+ return { error: mapped };
5947
+ }
5948
+ };
5880
5949
  var isE164Phone = (phone) => E164_PATTERN.test(phone);
5881
5950
  var issueAndStoreSmsCode = async ({
5882
5951
  codeLength,
5883
5952
  enrollment,
5884
5953
  mfaStore,
5885
5954
  onSendSmsCode,
5886
- ttlMs
5955
+ previousEnrollment,
5956
+ purpose,
5957
+ resendCooldownMs,
5958
+ verificationProvider,
5959
+ ttlMs,
5960
+ userId
5887
5961
  }) => {
5888
5962
  const phone = enrollment.smsPhone;
5889
5963
  if (phone === undefined)
5890
5964
  return;
5965
+ const challengeId = crypto.randomUUID();
5966
+ const now = Date.now();
5967
+ const claimed = await mfaStore.claimSmsChallenge({
5968
+ challengeId,
5969
+ cooldownCutoff: now - resendCooldownMs,
5970
+ enrollment: {
5971
+ ...enrollment,
5972
+ smsChallengeId: challengeId,
5973
+ smsCodeSentAt: now,
5974
+ smsFailedAttempts: 0,
5975
+ smsPendingCodeExpiresAt: now + ttlMs,
5976
+ smsPendingCodeHash: undefined,
5977
+ smsPendingPurpose: purpose,
5978
+ smsProviderReference: undefined,
5979
+ updatedAt: now
5980
+ }
5981
+ });
5982
+ if (!claimed)
5983
+ throw new SmsChallengeConflictError("SMS resend cooldown active");
5984
+ if (verificationProvider !== undefined) {
5985
+ const started = await startProviderSmsChallenge({
5986
+ challengeId,
5987
+ mfaStore,
5988
+ phone,
5989
+ previousEnrollment,
5990
+ purpose,
5991
+ userId,
5992
+ verificationProvider
5993
+ });
5994
+ return started.expiresAt;
5995
+ }
5891
5996
  const { code, expiresAt, hash } = await issueSmsCode(codeLength, ttlMs);
5892
- await mfaStore.saveEnrollment({
5893
- ...enrollment,
5894
- smsFailedAttempts: 0,
5895
- smsPendingCodeExpiresAt: expiresAt,
5896
- smsPendingCodeHash: hash,
5897
- updatedAt: Date.now()
5997
+ const finalized = await mfaStore.finalizeSmsChallenge({
5998
+ challengeId,
5999
+ expiresAt,
6000
+ hash,
6001
+ userId
5898
6002
  });
5899
- await onSendSmsCode?.({ code, expiresAt, phone });
6003
+ if (!finalized)
6004
+ throw new SmsChallengeConflictError("SMS challenge claim was lost");
6005
+ try {
6006
+ await onSendSmsCode?.({ code, expiresAt, phone, purpose, userId });
6007
+ } catch (error) {
6008
+ await mfaStore.rollbackSmsChallenge({
6009
+ challengeId,
6010
+ previous: previousEnrollment,
6011
+ userId
6012
+ });
6013
+ throw error;
6014
+ }
5900
6015
  return expiresAt;
5901
6016
  };
6017
+ var mapVerificationProviderError = (error) => {
6018
+ if (error instanceof SmsChallengeConflictError) {
6019
+ return { message: error.message, status: "Too Many Requests" };
6020
+ }
6021
+ if (!(error instanceof VerificationProviderError))
6022
+ return;
6023
+ if (error.kind === "rate_limited") {
6024
+ return {
6025
+ message: "Verification provider rate limit reached",
6026
+ status: "Too Many Requests"
6027
+ };
6028
+ }
6029
+ if (error.kind === "invalid_destination") {
6030
+ return {
6031
+ message: "Phone number cannot receive verification codes",
6032
+ status: "Bad Request"
6033
+ };
6034
+ }
6035
+ return {
6036
+ message: "Verification provider unavailable",
6037
+ status: "Service Unavailable"
6038
+ };
6039
+ };
5902
6040
  var maskPhone = (phone) => {
5903
6041
  const visible = phone.slice(-MASK_VISIBLE_DIGITS);
5904
6042
  const maskedLength = Math.max(phone.length - MASK_VISIBLE_DIGITS, 0);
@@ -5908,11 +6046,14 @@ var mfaSmsRoutes = ({
5908
6046
  authSessionStore,
5909
6047
  getUserId,
5910
6048
  mfaStore,
6049
+ managementAuthMaxAgeMs = DEFAULT_MFA_MANAGEMENT_AUTH_MAX_AGE_MS,
5911
6050
  onMfaEnrolled,
5912
6051
  onSendSmsCode,
6052
+ verificationProvider,
5913
6053
  smsCodeLength = DEFAULT_SMS_CODE_LENGTH,
5914
6054
  smsCodeTtlMs = DEFAULT_SMS_CODE_TTL_MS,
5915
6055
  smsMaxAttempts = DEFAULT_SMS_MAX_ATTEMPTS,
6056
+ smsResendCooldownMs = DEFAULT_SMS_RESEND_COOLDOWN_MS,
5916
6057
  smsSetupRoute = "/auth/mfa/sms/setup",
5917
6058
  smsVerifyRoute = "/auth/mfa/sms/verify"
5918
6059
  }) => new Elysia17().use(sessionStore()).post(smsSetupRoute, async ({
@@ -5921,7 +6062,7 @@ var mfaSmsRoutes = ({
5921
6062
  status,
5922
6063
  store: { session }
5923
6064
  }) => {
5924
- if (onSendSmsCode === undefined) {
6065
+ if (onSendSmsCode === undefined && verificationProvider === undefined) {
5925
6066
  return status("Not Implemented", "SMS MFA is not configured");
5926
6067
  }
5927
6068
  const userSession = await loadSessionFromSource({
@@ -5932,27 +6073,49 @@ var mfaSmsRoutes = ({
5932
6073
  if (!userSession) {
5933
6074
  return status("Unauthorized", "Authentication required");
5934
6075
  }
6076
+ if (!hasRecentAuthentication(userSession, managementAuthMaxAgeMs)) {
6077
+ return status("Unauthorized", "Recent authentication required");
6078
+ }
5935
6079
  if (!isE164Phone(phone)) {
5936
6080
  return status("Bad Request", "Phone must be in E.164 format");
5937
6081
  }
5938
6082
  const userId = getUserId(userSession.user);
5939
6083
  const existing = await mfaStore.getEnrollment(userId);
5940
6084
  const now = Date.now();
5941
- const { code, expiresAt, hash } = await issueSmsCode(smsCodeLength, smsCodeTtlMs);
5942
- await mfaStore.saveEnrollment({
5943
- backupCodeHashes: existing?.backupCodeHashes ?? [],
5944
- createdAt: existing?.createdAt ?? now,
5945
- smsFailedAttempts: 0,
5946
- smsPendingCodeExpiresAt: expiresAt,
5947
- smsPendingCodeHash: hash,
5948
- smsPhone: phone,
5949
- smsVerified: false,
5950
- totpSecretCiphertext: existing?.totpSecretCiphertext,
5951
- totpVerified: existing?.totpVerified ?? false,
5952
- updatedAt: now,
5953
- userId
5954
- });
5955
- await onSendSmsCode({ code, expiresAt, phone });
6085
+ if (existing?.smsCodeSentAt !== undefined && now - existing.smsCodeSentAt < smsResendCooldownMs) {
6086
+ return status("Too Many Requests", "SMS resend cooldown active");
6087
+ }
6088
+ try {
6089
+ await issueAndStoreSmsCode({
6090
+ codeLength: smsCodeLength,
6091
+ enrollment: {
6092
+ backupCodeHashes: existing?.backupCodeHashes ?? [],
6093
+ createdAt: existing?.createdAt ?? now,
6094
+ lastUsedAt: existing?.lastUsedAt,
6095
+ smsFailedAttempts: 0,
6096
+ smsPhone: phone,
6097
+ smsVerified: false,
6098
+ totpFailedAttempts: existing?.totpFailedAttempts ?? 0,
6099
+ totpSecretCiphertext: existing?.totpSecretCiphertext,
6100
+ totpVerified: existing?.totpVerified ?? false,
6101
+ updatedAt: now,
6102
+ userId
6103
+ },
6104
+ mfaStore,
6105
+ onSendSmsCode,
6106
+ previousEnrollment: existing,
6107
+ purpose: "mfa_enrollment",
6108
+ resendCooldownMs: smsResendCooldownMs,
6109
+ ttlMs: smsCodeTtlMs,
6110
+ userId,
6111
+ verificationProvider
6112
+ });
6113
+ } catch (error) {
6114
+ const mapped = mapVerificationProviderError(error);
6115
+ if (mapped === undefined)
6116
+ throw error;
6117
+ return status(mapped.status, mapped.message);
6118
+ }
5956
6119
  return status("OK", { phone: maskPhone(phone) });
5957
6120
  }, {
5958
6121
  body: t11.Object({ phone: t11.String() }),
@@ -5971,34 +6134,65 @@ var mfaSmsRoutes = ({
5971
6134
  if (!userSession) {
5972
6135
  return status("Unauthorized", "Authentication required");
5973
6136
  }
6137
+ if (!hasRecentAuthentication(userSession, managementAuthMaxAgeMs)) {
6138
+ return status("Unauthorized", "Recent authentication required");
6139
+ }
5974
6140
  const userId = getUserId(userSession.user);
5975
6141
  const enrollment = await mfaStore.getEnrollment(userId);
5976
- if (!enrollment?.smsPendingCodeHash || enrollment.smsPendingCodeExpiresAt === undefined) {
6142
+ if (!enrollment?.smsPhone) {
6143
+ return status("Bad Request", "No SMS enrollment in progress");
6144
+ }
6145
+ const localCodeHash = enrollment.smsPendingCodeHash;
6146
+ const challengeId = enrollment.smsChallengeId;
6147
+ const localCodeExpiresAt = enrollment.smsPendingCodeExpiresAt;
6148
+ const providerReference = enrollment.smsProviderReference;
6149
+ if (enrollment.smsPendingPurpose !== "mfa_enrollment" || localCodeExpiresAt === undefined || challengeId === undefined) {
5977
6150
  return status("Bad Request", "No SMS enrollment in progress");
5978
6151
  }
5979
- if (Date.now() > enrollment.smsPendingCodeExpiresAt) {
6152
+ if (verificationProvider === undefined && localCodeHash === undefined) {
6153
+ return status("Bad Request", "No SMS enrollment in progress");
6154
+ }
6155
+ if (Date.now() > localCodeExpiresAt) {
5980
6156
  return status("Bad Request", "SMS code expired");
5981
6157
  }
5982
6158
  if ((enrollment.smsFailedAttempts ?? 0) >= smsMaxAttempts) {
5983
6159
  return status("Too Many Requests", "Too many attempts");
5984
6160
  }
5985
- const codeValid = await constantTimeEqual(await hashToken(code), enrollment.smsPendingCodeHash);
6161
+ let providerResult;
6162
+ if (verificationProvider !== undefined) {
6163
+ if (providerReference === undefined) {
6164
+ return status("Bad Request", "No SMS enrollment in progress");
6165
+ }
6166
+ const checked = await checkWithVerificationProvider(verificationProvider, {
6167
+ channel: "sms",
6168
+ code,
6169
+ purpose: "mfa_enrollment",
6170
+ reference: providerReference,
6171
+ subject: userId,
6172
+ to: enrollment.smsPhone
6173
+ });
6174
+ if (checked.error !== undefined) {
6175
+ return status(checked.error.status, checked.error.message);
6176
+ }
6177
+ providerResult = checked.result;
6178
+ }
6179
+ const codeValid = providerResult ? providerResult.status === "approved" : localCodeHash !== undefined && await constantTimeEqual(await hashToken(code), localCodeHash);
5986
6180
  if (!codeValid) {
5987
- await mfaStore.saveEnrollment({
5988
- ...enrollment,
5989
- smsFailedAttempts: (enrollment.smsFailedAttempts ?? 0) + 1,
5990
- updatedAt: Date.now()
6181
+ const attempts = await mfaStore.recordSmsFailure({
6182
+ challengeId,
6183
+ maxAttempts: smsMaxAttempts,
6184
+ userId
5991
6185
  });
5992
- return status("Bad Request", "Invalid SMS code");
6186
+ return status(providerResult?.status === "max_attempts_reached" || attempts === undefined ? "Too Many Requests" : "Bad Request", providerResult?.status === "expired" ? "SMS code expired" : "Invalid SMS code");
5993
6187
  }
5994
- await mfaStore.saveEnrollment({
5995
- ...enrollment,
5996
- smsFailedAttempts: 0,
5997
- smsPendingCodeExpiresAt: undefined,
5998
- smsPendingCodeHash: undefined,
6188
+ const completed = await mfaStore.completeSmsChallenge({
6189
+ challengeId,
5999
6190
  smsVerified: true,
6000
- updatedAt: Date.now()
6191
+ userId
6001
6192
  });
6193
+ if (!completed) {
6194
+ return status("Bad Request", "SMS challenge is no longer active");
6195
+ }
6002
6196
  await onMfaEnrolled?.({ userId });
6003
6197
  return status("OK", { status: "enrolled" });
6004
6198
  }, {
@@ -6018,10 +6212,12 @@ var mfaChallenge = ({
6018
6212
  onMfaChallengeError,
6019
6213
  onMfaChallengeSuccess,
6020
6214
  onSendSmsCode,
6215
+ verificationProvider,
6021
6216
  sessionDurationMs = DEFAULT_MFA_SESSION_TTL_MS,
6022
6217
  smsCodeLength = DEFAULT_SMS_CODE_LENGTH,
6023
6218
  smsCodeTtlMs = DEFAULT_SMS_CODE_TTL_MS,
6024
6219
  smsMaxAttempts = DEFAULT_SMS_MAX_ATTEMPTS,
6220
+ smsResendCooldownMs = DEFAULT_SMS_RESEND_COOLDOWN_MS,
6025
6221
  totpMaxAttempts = DEFAULT_TOTP_MAX_ATTEMPTS
6026
6222
  }) => new Elysia18().use(sessionStore()).post(challengeRoute, async ({
6027
6223
  body: { action, code, factor },
@@ -6063,27 +6259,51 @@ var mfaChallenge = ({
6063
6259
  await onMfaChallengeSuccess?.({ user, userSessionId });
6064
6260
  return status("OK", { status: "authenticated" });
6065
6261
  };
6066
- const runSmsChallenge = async () => {
6067
- if (!enrollment.smsVerified || !enrollment.smsPhone) {
6068
- return status("Unauthorized", "No MFA challenge in progress");
6262
+ const sendSmsChallenge = async () => {
6263
+ if (enrollment.smsCodeSentAt !== undefined && Date.now() - enrollment.smsCodeSentAt < smsResendCooldownMs) {
6264
+ return status("Too Many Requests", "SMS resend cooldown active");
6069
6265
  }
6070
- if (action === "send") {
6266
+ try {
6071
6267
  await issueAndStoreSmsCode({
6072
6268
  codeLength: smsCodeLength,
6073
6269
  enrollment,
6074
6270
  mfaStore,
6075
6271
  onSendSmsCode,
6076
- ttlMs: smsCodeTtlMs
6272
+ previousEnrollment: enrollment,
6273
+ purpose: "mfa_challenge",
6274
+ resendCooldownMs: smsResendCooldownMs,
6275
+ ttlMs: smsCodeTtlMs,
6276
+ userId: getUserId(user),
6277
+ verificationProvider
6077
6278
  });
6078
- return status("OK", { status: "sent" });
6279
+ } catch (error) {
6280
+ const mapped = mapVerificationProviderError(error);
6281
+ if (mapped === undefined)
6282
+ throw error;
6283
+ return status(mapped.status, mapped.message);
6079
6284
  }
6285
+ return status("OK", { status: "sent" });
6286
+ };
6287
+ const runSmsChallenge = async () => {
6288
+ if (!enrollment.smsVerified || !enrollment.smsPhone) {
6289
+ return status("Unauthorized", "No MFA challenge in progress");
6290
+ }
6291
+ if (action === "send")
6292
+ return sendSmsChallenge();
6080
6293
  if (code === undefined) {
6081
6294
  return status("Bad Request", "SMS code required");
6082
6295
  }
6083
- if (!enrollment.smsPendingCodeHash || enrollment.smsPendingCodeExpiresAt === undefined) {
6296
+ const localCodeHash = enrollment.smsPendingCodeHash;
6297
+ const challengeId = enrollment.smsChallengeId;
6298
+ const localCodeExpiresAt = enrollment.smsPendingCodeExpiresAt;
6299
+ const providerReference = enrollment.smsProviderReference;
6300
+ if (enrollment.smsPendingPurpose !== "mfa_challenge" || localCodeExpiresAt === undefined || challengeId === undefined) {
6301
+ return status("Bad Request", "No SMS code in progress");
6302
+ }
6303
+ if (verificationProvider === undefined && localCodeHash === undefined) {
6084
6304
  return status("Bad Request", "No SMS code in progress");
6085
6305
  }
6086
- if (Date.now() > enrollment.smsPendingCodeExpiresAt) {
6306
+ if (Date.now() > localCodeExpiresAt) {
6087
6307
  return status("Unauthorized", "SMS code expired");
6088
6308
  }
6089
6309
  if ((enrollment.smsFailedAttempts ?? 0) >= smsMaxAttempts) {
@@ -6093,27 +6313,46 @@ var mfaChallenge = ({
6093
6313
  });
6094
6314
  return status("Unauthorized", "Too many attempts");
6095
6315
  }
6096
- const smsValid = await constantTimeEqual(await hashToken(code), enrollment.smsPendingCodeHash);
6316
+ let providerResult;
6317
+ if (verificationProvider !== undefined) {
6318
+ if (providerReference === undefined) {
6319
+ return status("Bad Request", "No SMS code in progress");
6320
+ }
6321
+ const checked = await checkWithVerificationProvider(verificationProvider, {
6322
+ channel: "sms",
6323
+ code,
6324
+ purpose: "mfa_challenge",
6325
+ reference: providerReference,
6326
+ subject: getUserId(user),
6327
+ to: enrollment.smsPhone
6328
+ });
6329
+ if (checked.error !== undefined) {
6330
+ return status(checked.error.status, checked.error.message);
6331
+ }
6332
+ providerResult = checked.result;
6333
+ }
6334
+ const smsValid = providerResult ? providerResult.status === "approved" : localCodeHash !== undefined && await constantTimeEqual(await hashToken(code), localCodeHash);
6097
6335
  if (!smsValid) {
6098
- await mfaStore.saveEnrollment({
6099
- ...enrollment,
6100
- smsFailedAttempts: (enrollment.smsFailedAttempts ?? 0) + 1,
6101
- updatedAt: Date.now()
6336
+ const attempts = await mfaStore.recordSmsFailure({
6337
+ challengeId,
6338
+ maxAttempts: smsMaxAttempts,
6339
+ userId: getUserId(user)
6102
6340
  });
6103
6341
  await onMfaChallengeError?.({
6104
6342
  error: new Error("invalid_mfa_code"),
6105
6343
  userId: getUserId(user)
6106
6344
  });
6107
- return status("Unauthorized", "Invalid MFA code");
6345
+ return status(providerResult?.status === "max_attempts_reached" || attempts === undefined ? "Too Many Requests" : "Unauthorized", providerResult?.status === "expired" ? "SMS code expired" : "Invalid MFA code");
6108
6346
  }
6109
- await mfaStore.saveEnrollment({
6110
- ...enrollment,
6347
+ const completed = await mfaStore.completeSmsChallenge({
6348
+ challengeId,
6111
6349
  lastUsedAt: Date.now(),
6112
- smsFailedAttempts: 0,
6113
- smsPendingCodeExpiresAt: undefined,
6114
- smsPendingCodeHash: undefined,
6115
- updatedAt: Date.now()
6350
+ smsVerified: true,
6351
+ userId: getUserId(user)
6116
6352
  });
6353
+ if (!completed) {
6354
+ return status("Unauthorized", "SMS challenge is no longer active");
6355
+ }
6117
6356
  return promote();
6118
6357
  };
6119
6358
  if (factor === "sms")
@@ -6175,6 +6414,7 @@ var mfaManagementRoutes = ({
6175
6414
  authSessionStore,
6176
6415
  getUserId,
6177
6416
  managementRoute = "/auth/mfa",
6417
+ managementAuthMaxAgeMs = DEFAULT_MFA_MANAGEMENT_AUTH_MAX_AGE_MS,
6178
6418
  mfaStore
6179
6419
  }) => new Elysia19().use(sessionStore()).get(managementRoute, async ({
6180
6420
  cookie: { user_session_id },
@@ -6213,6 +6453,9 @@ var mfaManagementRoutes = ({
6213
6453
  if (!userSession) {
6214
6454
  return status("Unauthorized", "Authentication required");
6215
6455
  }
6456
+ if (!hasRecentAuthentication(userSession, managementAuthMaxAgeMs)) {
6457
+ return status("Unauthorized", "Recent authentication required");
6458
+ }
6216
6459
  await mfaStore.removeEnrollment(getUserId(userSession.user));
6217
6460
  return status("OK", { status: "disabled" });
6218
6461
  }, { cookie: t13.Cookie({ user_session_id: userSessionIdTypebox }) });
@@ -6226,6 +6469,7 @@ var mfaTotpRoutes = ({
6226
6469
  encryptionKey,
6227
6470
  getUserId,
6228
6471
  issuer = DEFAULT_MFA_ISSUER,
6472
+ managementAuthMaxAgeMs = DEFAULT_MFA_MANAGEMENT_AUTH_MAX_AGE_MS,
6229
6473
  mfaStore,
6230
6474
  onMfaEnrolled,
6231
6475
  totpSetupRoute = "/auth/mfa/totp/setup",
@@ -6243,6 +6487,9 @@ var mfaTotpRoutes = ({
6243
6487
  if (!userSession) {
6244
6488
  return status("Unauthorized", "Authentication required");
6245
6489
  }
6490
+ if (!hasRecentAuthentication(userSession, managementAuthMaxAgeMs)) {
6491
+ return status("Unauthorized", "Recent authentication required");
6492
+ }
6246
6493
  const userId = getUserId(userSession.user);
6247
6494
  const secret = generateTotpSecret();
6248
6495
  const existing = await mfaStore.getEnrollment(userId);
@@ -6282,6 +6529,9 @@ var mfaTotpRoutes = ({
6282
6529
  if (!userSession) {
6283
6530
  return status("Unauthorized", "Authentication required");
6284
6531
  }
6532
+ if (!hasRecentAuthentication(userSession, managementAuthMaxAgeMs)) {
6533
+ return status("Unauthorized", "Recent authentication required");
6534
+ }
6285
6535
  const userId = getUserId(userSession.user);
6286
6536
  const enrollment = await mfaStore.getEnrollment(userId);
6287
6537
  if (!enrollment?.totpSecretCiphertext) {
@@ -31820,14 +32070,85 @@ var cloneEnrollment = (value) => ({
31820
32070
  var createInMemoryMfaStore = () => {
31821
32071
  const enrollments = new Map;
31822
32072
  return {
32073
+ claimSmsChallenge: async ({
32074
+ challengeId,
32075
+ cooldownCutoff,
32076
+ enrollment
32077
+ }) => {
32078
+ const current = enrollments.get(enrollment.userId);
32079
+ if (current?.smsCodeSentAt !== undefined && current.smsCodeSentAt > cooldownCutoff) {
32080
+ return false;
32081
+ }
32082
+ enrollments.set(enrollment.userId, cloneEnrollment({
32083
+ ...enrollment,
32084
+ smsChallengeId: challengeId
32085
+ }));
32086
+ return true;
32087
+ },
32088
+ completeSmsChallenge: async ({
32089
+ challengeId,
32090
+ lastUsedAt,
32091
+ smsVerified,
32092
+ userId
32093
+ }) => {
32094
+ const current = enrollments.get(userId);
32095
+ if (current?.smsChallengeId !== challengeId)
32096
+ return false;
32097
+ enrollments.set(userId, cloneEnrollment({
32098
+ ...current,
32099
+ lastUsedAt: lastUsedAt ?? current.lastUsedAt,
32100
+ smsChallengeId: undefined,
32101
+ smsFailedAttempts: 0,
32102
+ smsPendingCodeExpiresAt: undefined,
32103
+ smsPendingCodeHash: undefined,
32104
+ smsPendingPurpose: undefined,
32105
+ smsProviderReference: undefined,
32106
+ smsVerified,
32107
+ updatedAt: Date.now()
32108
+ }));
32109
+ return true;
32110
+ },
32111
+ finalizeSmsChallenge: async (input) => {
32112
+ const current = enrollments.get(input.userId);
32113
+ if (current?.smsChallengeId !== input.challengeId)
32114
+ return false;
32115
+ enrollments.set(input.userId, cloneEnrollment({
32116
+ ...current,
32117
+ smsPendingCodeExpiresAt: input.expiresAt,
32118
+ smsPendingCodeHash: input.hash,
32119
+ smsProviderReference: input.providerReference,
32120
+ updatedAt: Date.now()
32121
+ }));
32122
+ return true;
32123
+ },
31823
32124
  getEnrollment: async (userId) => {
31824
32125
  const enrollment = enrollments.get(userId);
31825
32126
  return enrollment ? cloneEnrollment(enrollment) : undefined;
31826
32127
  },
31827
32128
  listEnrollments: async () => Array.from(enrollments.values()).map(cloneEnrollment),
32129
+ recordSmsFailure: async ({ challengeId, maxAttempts, userId }) => {
32130
+ const current = enrollments.get(userId);
32131
+ if (current?.smsChallengeId !== challengeId || (current.smsFailedAttempts ?? 0) >= maxAttempts)
32132
+ return;
32133
+ const attempts = (current.smsFailedAttempts ?? 0) + 1;
32134
+ enrollments.set(userId, cloneEnrollment({
32135
+ ...current,
32136
+ smsFailedAttempts: attempts,
32137
+ updatedAt: Date.now()
32138
+ }));
32139
+ return attempts;
32140
+ },
31828
32141
  removeEnrollment: async (userId) => {
31829
32142
  enrollments.delete(userId);
31830
32143
  },
32144
+ rollbackSmsChallenge: async ({ challengeId, previous, userId }) => {
32145
+ if (enrollments.get(userId)?.smsChallengeId !== challengeId)
32146
+ return;
32147
+ if (previous)
32148
+ enrollments.set(userId, cloneEnrollment(previous));
32149
+ else
32150
+ enrollments.delete(userId);
32151
+ },
31831
32152
  saveEnrollment: async (enrollment) => {
31832
32153
  enrollments.set(enrollment.userId, cloneEnrollment(enrollment));
31833
32154
  }
@@ -31840,12 +32161,16 @@ var mfaEnrollmentsTable = pgTable("auth_mfa_enrollments", {
31840
32161
  backup_code_hashes: jsonb("backup_code_hashes").$type().notNull().default([]),
31841
32162
  created_at_ms: bigint("created_at_ms", { mode: "number" }).notNull(),
31842
32163
  last_used_at_ms: bigint("last_used_at_ms", { mode: "number" }),
32164
+ sms_challenge_id: text("sms_challenge_id"),
32165
+ sms_code_sent_at_ms: bigint("sms_code_sent_at_ms", { mode: "number" }),
31843
32166
  sms_failed_attempts: smallint("sms_failed_attempts").notNull().default(0),
31844
32167
  sms_pending_code_expires_at_ms: bigint("sms_pending_code_expires_at_ms", {
31845
32168
  mode: "number"
31846
32169
  }),
31847
32170
  sms_pending_code_hash: text("sms_pending_code_hash"),
32171
+ sms_pending_purpose: text("sms_pending_purpose").$type(),
31848
32172
  sms_phone: varchar("sms_phone", { length: PHONE_LENGTH }),
32173
+ sms_provider_reference: text("sms_provider_reference"),
31849
32174
  sms_verified: boolean("sms_verified").notNull().default(false),
31850
32175
  totp_failed_attempts: smallint("totp_failed_attempts").notNull().default(0),
31851
32176
  totp_secret_ciphertext: text("totp_secret_ciphertext"),
@@ -31857,10 +32182,14 @@ var toEnrollment = (row) => ({
31857
32182
  backupCodeHashes: row.backup_code_hashes,
31858
32183
  createdAt: row.created_at_ms,
31859
32184
  lastUsedAt: row.last_used_at_ms ?? undefined,
32185
+ smsChallengeId: row.sms_challenge_id ?? undefined,
32186
+ smsCodeSentAt: row.sms_code_sent_at_ms ?? undefined,
31860
32187
  smsFailedAttempts: row.sms_failed_attempts,
31861
32188
  smsPendingCodeExpiresAt: row.sms_pending_code_expires_at_ms ?? undefined,
31862
32189
  smsPendingCodeHash: row.sms_pending_code_hash ?? undefined,
32190
+ smsPendingPurpose: row.sms_pending_purpose ?? undefined,
31863
32191
  smsPhone: row.sms_phone ?? undefined,
32192
+ smsProviderReference: row.sms_provider_reference ?? undefined,
31864
32193
  smsVerified: row.sms_verified,
31865
32194
  totpFailedAttempts: row.totp_failed_attempts,
31866
32195
  totpSecretCiphertext: row.totp_secret_ciphertext ?? undefined,
@@ -31869,40 +32198,118 @@ var toEnrollment = (row) => ({
31869
32198
  userId: row.user_id
31870
32199
  });
31871
32200
  var createNeonMfaStore = (databaseUrl) => createPostgresMfaStore(createNeonDatabase(databaseUrl));
31872
- var createPostgresMfaStore = (db) => ({
31873
- getEnrollment: async (userId) => {
31874
- const [row] = await db.select().from(mfaEnrollmentsTable).where(eq(mfaEnrollmentsTable.user_id, userId)).limit(1);
31875
- return row ? toEnrollment(row) : undefined;
31876
- },
31877
- listEnrollments: async () => {
31878
- const rows = await db.select().from(mfaEnrollmentsTable);
31879
- return rows.map(toEnrollment);
31880
- },
31881
- removeEnrollment: async (userId) => {
31882
- await db.delete(mfaEnrollmentsTable).where(eq(mfaEnrollmentsTable.user_id, userId));
31883
- },
31884
- saveEnrollment: async (enrollment) => {
31885
- const values = {
31886
- backup_code_hashes: enrollment.backupCodeHashes,
31887
- created_at_ms: enrollment.createdAt,
31888
- last_used_at_ms: enrollment.lastUsedAt ?? null,
31889
- sms_failed_attempts: enrollment.smsFailedAttempts ?? 0,
31890
- sms_pending_code_expires_at_ms: enrollment.smsPendingCodeExpiresAt ?? null,
31891
- sms_pending_code_hash: enrollment.smsPendingCodeHash ?? null,
31892
- sms_phone: enrollment.smsPhone ?? null,
31893
- sms_verified: enrollment.smsVerified,
31894
- totp_failed_attempts: enrollment.totpFailedAttempts ?? 0,
31895
- totp_secret_ciphertext: enrollment.totpSecretCiphertext ?? null,
31896
- totp_verified: enrollment.totpVerified,
31897
- updated_at_ms: enrollment.updatedAt,
31898
- user_id: enrollment.userId
31899
- };
31900
- await db.insert(mfaEnrollmentsTable).values(values).onConflictDoUpdate({
31901
- set: values,
31902
- target: mfaEnrollmentsTable.user_id
31903
- });
31904
- }
31905
- });
32201
+ var createPostgresMfaStore = (db) => {
32202
+ const toValues = (enrollment) => ({
32203
+ backup_code_hashes: enrollment.backupCodeHashes,
32204
+ created_at_ms: enrollment.createdAt,
32205
+ last_used_at_ms: enrollment.lastUsedAt ?? null,
32206
+ sms_challenge_id: enrollment.smsChallengeId ?? null,
32207
+ sms_code_sent_at_ms: enrollment.smsCodeSentAt ?? null,
32208
+ sms_failed_attempts: enrollment.smsFailedAttempts ?? 0,
32209
+ sms_pending_code_expires_at_ms: enrollment.smsPendingCodeExpiresAt ?? null,
32210
+ sms_pending_code_hash: enrollment.smsPendingCodeHash ?? null,
32211
+ sms_pending_purpose: enrollment.smsPendingPurpose ?? null,
32212
+ sms_phone: enrollment.smsPhone ?? null,
32213
+ sms_provider_reference: enrollment.smsProviderReference ?? null,
32214
+ sms_verified: enrollment.smsVerified,
32215
+ totp_failed_attempts: enrollment.totpFailedAttempts ?? 0,
32216
+ totp_secret_ciphertext: enrollment.totpSecretCiphertext ?? null,
32217
+ totp_verified: enrollment.totpVerified,
32218
+ updated_at_ms: enrollment.updatedAt,
32219
+ user_id: enrollment.userId
32220
+ });
32221
+ return {
32222
+ claimSmsChallenge: async ({
32223
+ challengeId,
32224
+ cooldownCutoff,
32225
+ enrollment
32226
+ }) => {
32227
+ const values = toValues({
32228
+ ...enrollment,
32229
+ smsChallengeId: challengeId
32230
+ });
32231
+ const rows = await db.insert(mfaEnrollmentsTable).values(values).onConflictDoUpdate({
32232
+ set: {
32233
+ sms_challenge_id: challengeId,
32234
+ sms_code_sent_at_ms: enrollment.smsCodeSentAt ?? null,
32235
+ sms_failed_attempts: 0,
32236
+ sms_pending_code_expires_at_ms: enrollment.smsPendingCodeExpiresAt ?? null,
32237
+ sms_pending_code_hash: null,
32238
+ sms_pending_purpose: enrollment.smsPendingPurpose ?? null,
32239
+ sms_phone: enrollment.smsPhone ?? null,
32240
+ sms_provider_reference: null,
32241
+ sms_verified: enrollment.smsVerified,
32242
+ updated_at_ms: enrollment.updatedAt
32243
+ },
32244
+ setWhere: or(isNull(mfaEnrollmentsTable.sms_code_sent_at_ms), lte(mfaEnrollmentsTable.sms_code_sent_at_ms, cooldownCutoff)),
32245
+ target: mfaEnrollmentsTable.user_id
32246
+ }).returning({ userId: mfaEnrollmentsTable.user_id });
32247
+ return rows.length === 1;
32248
+ },
32249
+ completeSmsChallenge: async ({
32250
+ challengeId,
32251
+ lastUsedAt,
32252
+ smsVerified,
32253
+ userId
32254
+ }) => {
32255
+ const rows = await db.update(mfaEnrollmentsTable).set({
32256
+ last_used_at_ms: lastUsedAt,
32257
+ sms_challenge_id: null,
32258
+ sms_failed_attempts: 0,
32259
+ sms_pending_code_expires_at_ms: null,
32260
+ sms_pending_code_hash: null,
32261
+ sms_pending_purpose: null,
32262
+ sms_provider_reference: null,
32263
+ sms_verified: smsVerified,
32264
+ updated_at_ms: Date.now()
32265
+ }).where(and(eq(mfaEnrollmentsTable.user_id, userId), eq(mfaEnrollmentsTable.sms_challenge_id, challengeId))).returning({ userId: mfaEnrollmentsTable.user_id });
32266
+ return rows.length === 1;
32267
+ },
32268
+ finalizeSmsChallenge: async (input) => {
32269
+ const rows = await db.update(mfaEnrollmentsTable).set({
32270
+ sms_pending_code_expires_at_ms: input.expiresAt,
32271
+ sms_pending_code_hash: input.hash ?? null,
32272
+ sms_provider_reference: input.providerReference ?? null,
32273
+ updated_at_ms: Date.now()
32274
+ }).where(and(eq(mfaEnrollmentsTable.user_id, input.userId), eq(mfaEnrollmentsTable.sms_challenge_id, input.challengeId))).returning({ userId: mfaEnrollmentsTable.user_id });
32275
+ return rows.length === 1;
32276
+ },
32277
+ getEnrollment: async (userId) => {
32278
+ const [row] = await db.select().from(mfaEnrollmentsTable).where(eq(mfaEnrollmentsTable.user_id, userId)).limit(1);
32279
+ return row ? toEnrollment(row) : undefined;
32280
+ },
32281
+ listEnrollments: async () => {
32282
+ const rows = await db.select().from(mfaEnrollmentsTable);
32283
+ return rows.map(toEnrollment);
32284
+ },
32285
+ recordSmsFailure: async ({ challengeId, maxAttempts, userId }) => {
32286
+ const rows = await db.update(mfaEnrollmentsTable).set({
32287
+ sms_failed_attempts: sql`${mfaEnrollmentsTable.sms_failed_attempts} + 1`,
32288
+ updated_at_ms: Date.now()
32289
+ }).where(and(eq(mfaEnrollmentsTable.user_id, userId), eq(mfaEnrollmentsTable.sms_challenge_id, challengeId), lte(mfaEnrollmentsTable.sms_failed_attempts, maxAttempts - 1))).returning({
32290
+ attempts: mfaEnrollmentsTable.sms_failed_attempts
32291
+ });
32292
+ return rows[0]?.attempts;
32293
+ },
32294
+ removeEnrollment: async (userId) => {
32295
+ await db.delete(mfaEnrollmentsTable).where(eq(mfaEnrollmentsTable.user_id, userId));
32296
+ },
32297
+ rollbackSmsChallenge: async ({ challengeId, previous, userId }) => {
32298
+ if (previous) {
32299
+ await db.update(mfaEnrollmentsTable).set(toValues(previous)).where(and(eq(mfaEnrollmentsTable.user_id, userId), eq(mfaEnrollmentsTable.sms_challenge_id, challengeId)));
32300
+ return;
32301
+ }
32302
+ await db.delete(mfaEnrollmentsTable).where(and(eq(mfaEnrollmentsTable.user_id, userId), eq(mfaEnrollmentsTable.sms_challenge_id, challengeId)));
32303
+ },
32304
+ saveEnrollment: async (enrollment) => {
32305
+ const values = toValues(enrollment);
32306
+ await db.insert(mfaEnrollmentsTable).values(values).onConflictDoUpdate({
32307
+ set: values,
32308
+ target: mfaEnrollmentsTable.user_id
32309
+ });
32310
+ }
32311
+ };
32312
+ };
31906
32313
  // src/audit/types.ts
31907
32314
  var AUDIT_EVENT_TYPES = [
31908
32315
  "account_deleted",
@@ -36238,6 +36645,19 @@ var mfaTotpLockoutMigration = {
36238
36645
  id: "0003_totp_lockout",
36239
36646
  sql: 'ALTER TABLE "auth_mfa_enrollments" ADD COLUMN IF NOT EXISTS "totp_failed_attempts" smallint NOT NULL DEFAULT 0;'
36240
36647
  };
36648
+ var mfaSmsDeliveryPolicyMigration = {
36649
+ id: "0004_sms_delivery_policy",
36650
+ sql: [
36651
+ 'ALTER TABLE "auth_mfa_enrollments" ADD COLUMN IF NOT EXISTS "sms_code_sent_at_ms" bigint;',
36652
+ 'ALTER TABLE "auth_mfa_enrollments" ADD COLUMN IF NOT EXISTS "sms_pending_purpose" text;',
36653
+ 'ALTER TABLE "auth_mfa_enrollments" ADD COLUMN IF NOT EXISTS "sms_provider_reference" text;'
36654
+ ].join(`
36655
+ `)
36656
+ };
36657
+ var mfaSmsAtomicChallengeMigration = {
36658
+ id: "0005_sms_atomic_challenge",
36659
+ sql: 'ALTER TABLE "auth_mfa_enrollments" ADD COLUMN IF NOT EXISTS "sms_challenge_id" text;'
36660
+ };
36241
36661
  var oidcResourceAudienceMigration = {
36242
36662
  id: "0002_resource_audience",
36243
36663
  sql: [
@@ -36292,7 +36712,9 @@ var blockMigrations = {
36292
36712
  migrations: [
36293
36713
  ...initMigration("mfa", [mfaEnrollmentsTable]).migrations,
36294
36714
  mfaSmsColumnsMigration,
36295
- mfaTotpLockoutMigration
36715
+ mfaTotpLockoutMigration,
36716
+ mfaSmsDeliveryPolicyMigration,
36717
+ mfaSmsAtomicChallengeMigration
36296
36718
  ]
36297
36719
  },
36298
36720
  oidc: {
@@ -36805,6 +37227,7 @@ var buildAuthApplications = async (configuration) => {
36805
37227
  credentials,
36806
37228
  customProviders,
36807
37229
  mfa,
37230
+ verificationProvider,
36808
37231
  passwordless,
36809
37232
  lockout,
36810
37233
  sessions,
@@ -37037,7 +37460,8 @@ var buildAuthApplications = async (configuration) => {
37037
37460
  auditedMfa ? mfaRoutes({
37038
37461
  ...auditedMfa,
37039
37462
  authSessionStore,
37040
- cookieSecure: resolvedCookieSecure
37463
+ cookieSecure: resolvedCookieSecure,
37464
+ verificationProvider
37041
37465
  }) : new Elysia45,
37042
37466
  passwordless ? passwordlessRoutes({
37043
37467
  ...passwordless,
@@ -37564,6 +37988,7 @@ export {
37564
37988
  accessTokensTable,
37565
37989
  acceptInvitation,
37566
37990
  WEBAUTHN_CHALLENGE_COOKIE,
37991
+ VerificationProviderError,
37567
37992
  STATUS_LIST_TYP,
37568
37993
  STATUS_LIST_SUB_TYP,
37569
37994
  REQUEST_URI_PREFIX,
@@ -37581,6 +38006,7 @@ export {
37581
38006
  DEFAULT_STATUS_ROUTE,
37582
38007
  DEFAULT_SSO_SESSION_TTL_MS,
37583
38008
  DEFAULT_SSO_ROUTE,
38009
+ DEFAULT_SMS_RESEND_COOLDOWN_MS,
37584
38010
  DEFAULT_SMS_MAX_ATTEMPTS,
37585
38011
  DEFAULT_SMS_CODE_TTL_MS,
37586
38012
  DEFAULT_SMS_CODE_LENGTH,
@@ -37598,6 +38024,7 @@ export {
37598
38024
  DEFAULT_ORGANIZATIONS_ROUTE,
37599
38025
  DEFAULT_OIDC_ROUTE,
37600
38026
  DEFAULT_MFA_SESSION_TTL_MS,
38027
+ DEFAULT_MFA_MANAGEMENT_AUTH_MAX_AGE_MS,
37601
38028
  DEFAULT_MFA_ISSUER,
37602
38029
  DEFAULT_MAGIC_LINK_TTL_MS,
37603
38030
  DEFAULT_INVITATION_TTL_MS,
@@ -37613,5 +38040,5 @@ export {
37613
38040
  AGENT_CLAIM_GRANT_TYPE
37614
38041
  };
37615
38042
 
37616
- //# debugId=0FBF734780C91B5D64756E2164756E21
38043
+ //# debugId=FC3F27A521162E4064756E2164756E21
37617
38044
  //# sourceMappingURL=index.js.map