@puzzle-ui/auth 0.0.0-stage → 0.5.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 (95) hide show
  1. package/README.md +301 -2
  2. package/dist/LICENSE +21 -0
  3. package/dist/auth.lifecycle.manager.cjs +835 -0
  4. package/dist/auth.lifecycle.manager.cjs.map +1 -0
  5. package/dist/auth.lifecycle.manager.d.cts +115 -0
  6. package/dist/auth.lifecycle.manager.d.ts +115 -0
  7. package/dist/auth.lifecycle.manager.js +835 -0
  8. package/dist/auth.lifecycle.manager.js.map +1 -0
  9. package/dist/auth.manager.cjs +454 -0
  10. package/dist/auth.manager.cjs.map +1 -0
  11. package/dist/auth.manager.d.cts +54 -0
  12. package/dist/auth.manager.d.ts +54 -0
  13. package/dist/auth.manager.js +454 -0
  14. package/dist/auth.manager.js.map +1 -0
  15. package/dist/auth.mfa.manager.cjs +1488 -0
  16. package/dist/auth.mfa.manager.cjs.map +1 -0
  17. package/dist/auth.mfa.manager.d.cts +100 -0
  18. package/dist/auth.mfa.manager.d.ts +100 -0
  19. package/dist/auth.mfa.manager.js +1488 -0
  20. package/dist/auth.mfa.manager.js.map +1 -0
  21. package/dist/auth.rate-limit.cjs +78 -0
  22. package/dist/auth.rate-limit.cjs.map +1 -0
  23. package/dist/auth.rate-limit.d.cts +24 -0
  24. package/dist/auth.rate-limit.d.ts +24 -0
  25. package/dist/auth.rate-limit.js +78 -0
  26. package/dist/auth.rate-limit.js.map +1 -0
  27. package/dist/auth.step-up.manager.cjs +269 -0
  28. package/dist/auth.step-up.manager.cjs.map +1 -0
  29. package/dist/auth.step-up.manager.d.cts +74 -0
  30. package/dist/auth.step-up.manager.d.ts +74 -0
  31. package/dist/auth.step-up.manager.js +268 -0
  32. package/dist/auth.step-up.manager.js.map +1 -0
  33. package/dist/auth.storage.cjs +164 -0
  34. package/dist/auth.storage.cjs.map +1 -0
  35. package/dist/auth.storage.d.cts +29 -0
  36. package/dist/auth.storage.d.ts +29 -0
  37. package/dist/auth.storage.js +148 -0
  38. package/dist/auth.storage.js.map +1 -0
  39. package/dist/errors/auth.errors.cjs +101 -0
  40. package/dist/errors/auth.errors.cjs.map +1 -0
  41. package/dist/errors/auth.errors.d.cts +62 -0
  42. package/dist/errors/auth.errors.d.ts +62 -0
  43. package/dist/errors/auth.errors.js +90 -0
  44. package/dist/errors/auth.errors.js.map +1 -0
  45. package/dist/index.cjs +54 -0
  46. package/dist/index.d.cts +21 -0
  47. package/dist/index.d.ts +21 -0
  48. package/dist/index.js +13 -0
  49. package/dist/providers/mfa-provider.types.d.cts +66 -0
  50. package/dist/providers/mfa-provider.types.d.ts +66 -0
  51. package/dist/providers/provider.types.d.cts +34 -0
  52. package/dist/providers/provider.types.d.ts +34 -0
  53. package/dist/providers/simplewebauthn-provider.cjs +185 -0
  54. package/dist/providers/simplewebauthn-provider.cjs.map +1 -0
  55. package/dist/providers/simplewebauthn-provider.d.cts +35 -0
  56. package/dist/providers/simplewebauthn-provider.d.ts +35 -0
  57. package/dist/providers/simplewebauthn-provider.js +185 -0
  58. package/dist/providers/simplewebauthn-provider.js.map +1 -0
  59. package/dist/providers/webcrypto-secret-protector.cjs +118 -0
  60. package/dist/providers/webcrypto-secret-protector.cjs.map +1 -0
  61. package/dist/providers/webcrypto-secret-protector.d.cts +14 -0
  62. package/dist/providers/webcrypto-secret-protector.d.ts +14 -0
  63. package/dist/providers/webcrypto-secret-protector.js +118 -0
  64. package/dist/providers/webcrypto-secret-protector.js.map +1 -0
  65. package/dist/types/auth-lifecycle.types.d.cts +53 -0
  66. package/dist/types/auth-lifecycle.types.d.ts +53 -0
  67. package/dist/types/auth-manager.types.d.cts +106 -0
  68. package/dist/types/auth-manager.types.d.ts +106 -0
  69. package/dist/types/credential.types.d.cts +9 -0
  70. package/dist/types/credential.types.d.ts +9 -0
  71. package/dist/types/mfa.types.d.cts +244 -0
  72. package/dist/types/mfa.types.d.ts +244 -0
  73. package/dist/types/state.types.d.cts +33 -0
  74. package/dist/types/state.types.d.ts +33 -0
  75. package/dist/types/user.types.d.cts +9 -0
  76. package/dist/types/user.types.d.ts +9 -0
  77. package/dist/utils/auth-crypto.utils.cjs +38 -0
  78. package/dist/utils/auth-crypto.utils.cjs.map +1 -0
  79. package/dist/utils/auth-crypto.utils.d.cts +4 -0
  80. package/dist/utils/auth-crypto.utils.d.ts +4 -0
  81. package/dist/utils/auth-crypto.utils.js +35 -0
  82. package/dist/utils/auth-crypto.utils.js.map +1 -0
  83. package/dist/utils/email.utils.cjs +20 -0
  84. package/dist/utils/email.utils.cjs.map +1 -0
  85. package/dist/utils/email.utils.d.cts +2 -0
  86. package/dist/utils/email.utils.d.ts +2 -0
  87. package/dist/utils/email.utils.js +19 -0
  88. package/dist/utils/email.utils.js.map +1 -0
  89. package/dist/utils/totp.utils.cjs +145 -0
  90. package/dist/utils/totp.utils.cjs.map +1 -0
  91. package/dist/utils/totp.utils.d.cts +26 -0
  92. package/dist/utils/totp.utils.d.ts +26 -0
  93. package/dist/utils/totp.utils.js +140 -0
  94. package/dist/utils/totp.utils.js.map +1 -0
  95. package/package.json +45 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.mfa.manager.cjs","names":["AuthSessionError","generateSecureAuthToken","AuthMfaChallengeError","AuthConfigurationError","AuthValidationError","AuthProviderError","initializeAuthStorage","createRateLimitKey","AuthRateLimitError","generateAuthId","AuthEmailVerificationRequiredError","AuthInvalidCredentialsError","hashAuthToken","StorageConflictError","StorageDuplicateError","generateTotpSecret","createTotpUri","AuthMfaFactorError","verifyTotpCode"],"sources":["../src/auth.mfa.manager.ts"],"sourcesContent":["import {\n StorageConflictError,\n StorageDuplicateError,\n type StorageAdapter,\n type StorageBatchOperation,\n} from \"@puzzle-ui/storage\";\n\nimport type { SessionManager, SessionRecord } from \"@puzzle-ui/session\";\n\nimport type {\n PasswordHasher,\n RateLimitProvider,\n} from \"./providers/provider.types\";\n\nimport type {\n AuthSecretProtector,\n ProtectedAuthSecret,\n WebAuthnProvider,\n WebAuthnRegistrationCredential,\n WebAuthnRegistrationVerification,\n WebAuthnAuthenticationVerification,\n} from \"./providers/mfa-provider.types\";\n\nimport type {\n AuthMfaManagerOptions,\n AuthMfaFactor,\n AuthMfaFactorStorageRecord,\n AuthMfaChallengeStorageRecord,\n AuthRecoveryCodeStorageRecord,\n AuthMfaPendingResult,\n AuthMfaGateway,\n BeginTotpEnrollmentInput,\n TotpEnrollmentStarted,\n VerifyTotpEnrollmentInput,\n MfaEnrollmentCompleted,\n BeginPasskeyRegistrationInput,\n PasskeyRegistrationStarted,\n FinishPasskeyRegistrationInput,\n BeginPasskeySignInInput,\n PasskeySignInStarted,\n CompleteMfaSignInInput,\n PasskeyMfaStarted,\n PasskeyMfaCompletedInput,\n RemoveMfaFactorInput,\n RegenerateRecoveryCodesInput,\n AuthMfaAvailableFactor,\n AuthMfaEnrollmentPendingResult,\n VerifyRequiredTotpEnrollmentInput,\n AuthMfaEnrollmentCompleted,\n BeginRequiredMfaEnrollmentInput,\n FinishRequiredPasskeyEnrollmentInput,\n} from \"./types/mfa.types\";\n\nimport type { AuthSession, AuthResult } from \"./types/auth-manager.types\";\n\nimport type { AuthAuditEventStorageRecord } from \"./types/auth-lifecycle.types\";\n\nimport type { AuthUser } from \"./types/user.types\";\n\nimport {\n AUTH_AUDIT_COLLECTION,\n AUTH_CREDENTIAL_COLLECTION,\n AUTH_USER_COLLECTION,\n AUTH_MFA_FACTOR_COLLECTION,\n AUTH_MFA_CHALLENGE_COLLECTION,\n AUTH_RECOVERY_CODE_COLLECTION,\n initializeAuthStorage,\n type AuthUserStorageRecord,\n} from \"./auth.storage\";\n\nimport {\n AuthConfigurationError,\n AuthEmailVerificationRequiredError,\n AuthInvalidCredentialsError,\n AuthMfaChallengeError,\n AuthMfaFactorError,\n AuthProviderError,\n AuthRateLimitError,\n AuthSessionError,\n AuthValidationError,\n} from \"./errors/auth.errors\";\n\nimport {\n generateAuthId,\n generateSecureAuthToken,\n hashAuthToken,\n createRateLimitKey,\n} from \"./utils/auth-crypto.utils\";\n\nimport {\n createTotpUri,\n generateTotpSecret,\n verifyTotpCode,\n} from \"./utils/totp.utils\";\n\nimport type { PasswordCredentialStorageRecord } from \"./types/credential.types\";\nimport type { AuthState } from \"./types/state.types\";\n\ntype MfaStorageRecord =\n | AuthMfaFactorStorageRecord\n | AuthMfaChallengeStorageRecord\n | AuthRecoveryCodeStorageRecord\n | AuthAuditEventStorageRecord\n | AuthUserStorageRecord\n | PasswordCredentialStorageRecord;\n\ntype AuthenticatedContext = {\n readonly session: SessionRecord;\n readonly user: AuthUserStorageRecord;\n};\n\ntype RecoveryCodeBundle = {\n readonly codes: readonly string[];\n readonly records: readonly AuthRecoveryCodeStorageRecord[];\n};\n\ntype FactorUpdate = {\n readonly factor: AuthMfaFactorStorageRecord;\n readonly changes: {\n readonly status?: \"pending\" | \"active\";\n readonly expiresAt?: Date | null;\n readonly lastUsedAt?: Date;\n readonly lastUsedStep?: number | null;\n readonly counter?: number;\n readonly updatedAt: Date;\n };\n};\n\nconst DEFAULT_CHALLENGE_LIFETIME_SECONDS = 5 * 60;\nconst DEFAULT_RECOVERY_CODE_COUNT = 10;\nconst DEFAULT_MAXIMUM_CHALLENGE_ATTEMPTS = 5;\n\nconst DEFAULT_TOTP = {\n digits: 6,\n periodSeconds: 30,\n window: 1,\n} as const;\n\nconst whereEquals = <const T>(value: T) => ({\n operator: \"eq\" as const,\n value,\n});\n\nconst toPublicUser = (record: AuthUserStorageRecord): AuthUser => ({\n id: record.id,\n email: record.email,\n emailVerifiedAt: record.emailVerifiedAt,\n name: record.name,\n image: record.image,\n createdAt: record.createdAt,\n updatedAt: record.updatedAt,\n});\n\nconst toPublicSession = (record: SessionRecord): AuthSession => {\n if (record.kind !== \"authenticated\" || !record.userId) {\n throw new AuthSessionError(\n \"An authenticated session must contain a user ID.\",\n );\n }\n\n return {\n id: record.id,\n userId: record.userId,\n kind: \"authenticated\",\n createdAt: record.createdAt,\n lastSeenAt: record.lastSeenAt,\n expiresAt: record.expiresAt,\n revokedAt: record.revokedAt,\n };\n};\n\nconst toPublicFactor = (record: AuthMfaFactorStorageRecord): AuthMfaFactor => ({\n id: record.id,\n type: record.type,\n status: record.status,\n label: record.label,\n createdAt: record.createdAt,\n lastUsedAt: record.lastUsedAt,\n});\n\nconst normalizeRecoveryCode = (value: string): string | null => {\n if (typeof value !== \"string\") {\n return null;\n }\n\n const normalized = value.replace(/[\\s-]/g, \"\").toUpperCase();\n\n return /^[A-F0-9]{32}$/.test(normalized) ? normalized : null;\n};\n\nconst generateRecoveryCode = (): string => {\n const raw = generateSecureAuthToken().slice(0, 32).toUpperCase();\n\n return raw.match(/.{4}/g)!.join(\"-\");\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\nconst getCredentialId = (response: unknown): string => {\n if (\n !isRecord(response) ||\n typeof response.id !== \"string\" ||\n response.id.length === 0 ||\n response.id.length > 2048\n ) {\n throw new AuthMfaChallengeError();\n }\n\n return response.id;\n};\n\n/**\n * Encodes random bytes using unpadded base64url.\n * Does not depend on Node.js Buffer or browser btoa.\n */\nconst encodeBase64Url = (bytes: Uint8Array): string => {\n const alphabet =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\n\n let result = \"\";\n let index = 0;\n\n while (index + 2 < bytes.length) {\n const a = bytes[index++]!;\n const b = bytes[index++]!;\n const c = bytes[index++]!;\n\n result += alphabet.charAt(a >> 2);\n result += alphabet.charAt(((a & 3) << 4) | (b >> 4));\n result += alphabet.charAt(((b & 15) << 2) | (c >> 6));\n result += alphabet.charAt(c & 63);\n }\n\n if (index < bytes.length) {\n const a = bytes[index++]!;\n\n result += alphabet.charAt(a >> 2);\n\n if (index < bytes.length) {\n const b = bytes[index]!;\n\n result += alphabet.charAt(((a & 3) << 4) | (b >> 4));\n result += alphabet.charAt((b & 15) << 2);\n } else {\n result += alphabet.charAt((a & 3) << 4);\n }\n }\n\n return result;\n};\n\nconst generateWebAuthnChallenge = (): string => {\n const cryptoApi = globalThis.crypto;\n\n if (!cryptoApi?.getRandomValues) {\n throw new AuthConfigurationError(\n \"This runtime does not provide the required Web Crypto APIs.\",\n );\n }\n\n const bytes = new Uint8Array(32);\n cryptoApi.getRandomValues(bytes);\n\n return encodeBase64Url(bytes);\n};\n\nconst validateLabel = (label: string | undefined, fallback: string): string => {\n const value = label === undefined ? fallback : label.trim();\n\n if (typeof value !== \"string\" || value.length === 0 || value.length > 64) {\n throw new AuthValidationError(\n \"The authentication factor label must contain between 1 and 64 characters.\",\n );\n }\n\n return value;\n};\n\nexport type StorageAuthMfaManagerDependencies = {\n readonly storage: StorageAdapter;\n readonly sessions: SessionManager;\n readonly passwordHasher: PasswordHasher;\n readonly rateLimitProvider: RateLimitProvider;\n readonly secretProtector: AuthSecretProtector;\n readonly webAuthnProvider?: WebAuthnProvider;\n};\n\nexport class StorageAuthMfaManager implements AuthMfaGateway {\n private readonly storage: StorageAdapter;\n private readonly sessions: SessionManager;\n private readonly passwordHasher: PasswordHasher;\n private readonly rateLimitProvider: RateLimitProvider;\n private readonly secretProtector: AuthSecretProtector;\n private readonly webAuthnProvider: WebAuthnProvider | null;\n\n private readonly usersCollection: string;\n private readonly credentialsCollection: string;\n private readonly auditCollection: string;\n\n private readonly factorsCollection: string;\n private readonly challengesCollection: string;\n private readonly recoveryCodesCollection: string;\n\n private readonly rateLimitKeySecret: string;\n private readonly challengeLifetimeSeconds: number;\n private readonly recoveryCodeCount: number;\n private readonly issuer: string;\n\n private readonly totp: {\n readonly digits: number;\n readonly periodSeconds: number;\n readonly window: number;\n };\n\n private readonly webAuthn: {\n readonly rpId: string;\n readonly rpName: string;\n readonly origin: string;\n } | null;\n\n private readonly requireVerifiedEmail: boolean;\n private readonly clock: () => Date;\n\n private initialized = false;\n private initializationPromise: Promise<void> | null = null;\n\n constructor(\n dependencies: StorageAuthMfaManagerDependencies,\n options: AuthMfaManagerOptions,\n ) {\n this.storage = dependencies.storage;\n this.sessions = dependencies.sessions;\n this.passwordHasher = dependencies.passwordHasher;\n this.rateLimitProvider = dependencies.rateLimitProvider;\n this.secretProtector = dependencies.secretProtector;\n this.webAuthnProvider = dependencies.webAuthnProvider ?? null;\n\n if (\n typeof options.rateLimitKeySecret !== \"string\" ||\n options.rateLimitKeySecret.length < 32\n ) {\n throw new AuthConfigurationError(\n \"rateLimitKeySecret must contain at least 32 characters.\",\n );\n }\n\n this.rateLimitKeySecret = options.rateLimitKeySecret;\n\n this.usersCollection = options.usersCollection ?? AUTH_USER_COLLECTION;\n\n this.credentialsCollection =\n options.credentialsCollection ?? AUTH_CREDENTIAL_COLLECTION;\n\n this.auditCollection = options.auditCollection ?? AUTH_AUDIT_COLLECTION;\n\n this.factorsCollection =\n options.factorsCollection ?? AUTH_MFA_FACTOR_COLLECTION;\n\n this.challengesCollection =\n options.challengesCollection ?? AUTH_MFA_CHALLENGE_COLLECTION;\n\n this.recoveryCodesCollection =\n options.recoveryCodesCollection ?? AUTH_RECOVERY_CODE_COLLECTION;\n\n this.challengeLifetimeSeconds =\n options.challengeLifetimeSeconds ?? DEFAULT_CHALLENGE_LIFETIME_SECONDS;\n\n this.recoveryCodeCount =\n options.recoveryCodeCount ?? DEFAULT_RECOVERY_CODE_COUNT;\n\n this.issuer = options.issuer ?? \"Puzzle Auth\";\n\n this.totp = {\n ...DEFAULT_TOTP,\n ...options.totp,\n };\n\n this.webAuthn = options.webAuthn ?? null;\n\n this.requireVerifiedEmail = options.requireVerifiedEmail ?? false;\n\n this.clock = options.clock ?? (() => new Date());\n\n if (\n !Number.isInteger(this.challengeLifetimeSeconds) ||\n this.challengeLifetimeSeconds < 30 ||\n this.challengeLifetimeSeconds > 600\n ) {\n throw new AuthConfigurationError(\n \"MFA challenge lifetime must be between 30 and 600 seconds.\",\n );\n }\n\n if (\n !Number.isInteger(this.recoveryCodeCount) ||\n this.recoveryCodeCount < 5 ||\n this.recoveryCodeCount > 20\n ) {\n throw new AuthConfigurationError(\n \"Recovery code count must be between 5 and 20.\",\n );\n }\n\n if (\n !Number.isInteger(this.totp.digits) ||\n this.totp.digits < 6 ||\n this.totp.digits > 8 ||\n !Number.isInteger(this.totp.periodSeconds) ||\n this.totp.periodSeconds < 15 ||\n this.totp.periodSeconds > 120 ||\n !Number.isInteger(this.totp.window) ||\n this.totp.window < 0 ||\n this.totp.window > 2\n ) {\n throw new AuthConfigurationError(\"The TOTP configuration is invalid.\");\n }\n\n if (Boolean(this.webAuthn) !== Boolean(this.webAuthnProvider)) {\n throw new AuthConfigurationError(\n \"WebAuthn configuration and WebAuthnProvider must be supplied together.\",\n );\n }\n\n if (this.webAuthn) {\n this.validateWebAuthnConfiguration(this.webAuthn);\n }\n }\n\n async revokeChallenges(userId: string): Promise<void> {\n this.assertInitialized();\n\n if (typeof userId !== \"string\" || userId.trim().length === 0) {\n throw new AuthValidationError(\"A valid user ID is required.\");\n }\n\n const [signInChallenges, enrollmentChallenges] = await Promise.all([\n this.storage.findMany<AuthMfaChallengeStorageRecord>(\n this.challengesCollection,\n {\n where: {\n userId: whereEquals(userId),\n purpose: whereEquals(\"sign-in\"),\n consumedAt: whereEquals(null),\n },\n },\n ),\n this.storage.findMany<AuthMfaChallengeStorageRecord>(\n this.challengesCollection,\n {\n where: {\n userId: whereEquals(userId),\n purpose: whereEquals(\"enrollment\"),\n consumedAt: whereEquals(null),\n },\n },\n ),\n ]);\n\n const challenges = [...signInChallenges, ...enrollmentChallenges];\n\n if (challenges.length === 0) {\n return;\n }\n\n const now = this.now();\n\n const operations: StorageBatchOperation<MfaStorageRecord>[] =\n challenges.map((challenge) => ({\n type: \"update\",\n collection: this.challengesCollection,\n id: challenge.id,\n condition: {\n expectedRevision: challenge.revision,\n },\n changes: {\n consumedAt: now,\n updatedAt: now,\n },\n }));\n\n try {\n await this.storage.atomicBatch<MfaStorageRecord>(operations);\n } catch (error) {\n throw new AuthProviderError(\"Could not revoke pending MFA challenges.\", {\n cause: error,\n });\n }\n }\n\n private validateWebAuthnConfiguration(config: {\n readonly rpId: string;\n readonly rpName: string;\n readonly origin: string;\n }): void {\n let origin: URL;\n\n try {\n origin = new URL(config.origin);\n } catch {\n throw new AuthConfigurationError(\"WebAuthn origin must be a valid URL.\");\n }\n\n const isLocalhost =\n origin.hostname === \"localhost\" ||\n origin.hostname === \"127.0.0.1\" ||\n origin.hostname === \"[::1]\";\n\n if (\n origin.origin !== config.origin ||\n (origin.protocol !== \"https:\" && !isLocalhost)\n ) {\n throw new AuthConfigurationError(\n \"WebAuthn requires an exact HTTPS origin, except for localhost development.\",\n );\n }\n\n if (\n origin.hostname !== config.rpId &&\n !origin.hostname.endsWith(`.${config.rpId}`)\n ) {\n throw new AuthConfigurationError(\n \"The WebAuthn RP ID must be the origin hostname or a parent domain.\",\n );\n }\n\n if (config.rpName.trim().length === 0 || config.rpId.trim().length === 0) {\n throw new AuthConfigurationError(\n \"WebAuthn RP name and RP ID are required.\",\n );\n }\n }\n\n async initialize(): Promise<void> {\n if (!this.initializationPromise) {\n const initialization = (async () => {\n if (\n !this.storage.capabilities.uniqueConstraints ||\n !this.storage.capabilities.conditionalWrites ||\n !this.storage.capabilities.atomicBatch\n ) {\n throw new AuthConfigurationError(\n \"MFA requires unique constraints, conditional writes and atomic batches.\",\n );\n }\n\n await initializeAuthStorage(this.storage, {\n usersCollection: this.usersCollection,\n credentialsCollection: this.credentialsCollection,\n auditCollection: this.auditCollection,\n factorsCollection: this.factorsCollection,\n challengesCollection: this.challengesCollection,\n recoveryCodesCollection: this.recoveryCodesCollection,\n });\n\n this.initialized = true;\n })();\n\n this.initializationPromise = initialization.catch((error) => {\n this.initializationPromise = null;\n this.initialized = false;\n throw error;\n });\n }\n\n return this.initializationPromise;\n }\n\n private assertInitialized(): void {\n if (!this.initialized) {\n throw new AuthConfigurationError(\n \"MFA manager must be initialized before use.\",\n );\n }\n }\n\n private now(): Date {\n const value = this.clock();\n\n if (!(value instanceof Date) || !Number.isFinite(value.getTime())) {\n throw new AuthConfigurationError(\n \"The configured MFA clock returned an invalid date.\",\n );\n }\n\n return value;\n }\n\n private async enforceRateLimit(\n action: string,\n ipAddress: string,\n userId?: string,\n ): Promise<void> {\n if (\n typeof ipAddress !== \"string\" ||\n ipAddress.trim().length === 0 ||\n ipAddress.trim().length > 128\n ) {\n throw new AuthValidationError(\"A valid client IP address is required.\");\n }\n\n const sensitiveVerification = action.includes(\"verify\");\n\n const rule = sensitiveVerification\n ? { limit: 5, windowSeconds: 300 }\n : { limit: 10, windowSeconds: 900 };\n\n const scopes: readonly {\n readonly type: \"ip\" | \"user\";\n readonly value: string;\n }[] = [\n {\n type: \"ip\",\n value: ipAddress.trim(),\n },\n ...(userId\n ? [\n {\n type: \"user\" as const,\n value: userId,\n },\n ]\n : []),\n ];\n\n for (const scope of scopes) {\n const key = await createRateLimitKey(\n this.rateLimitKeySecret,\n `auth:mfa:${action}:${scope.type}:${scope.value}`,\n );\n\n let result: Awaited<ReturnType<RateLimitProvider[\"consume\"]>>;\n\n try {\n result = await this.rateLimitProvider.consume({\n key,\n limit: rule.limit,\n windowSeconds: rule.windowSeconds,\n });\n } catch (error) {\n throw new AuthProviderError(\"The MFA rate limiter is unavailable.\", {\n cause: error,\n });\n }\n\n if (\n !result ||\n typeof result.allowed !== \"boolean\" ||\n !(result.resetAt instanceof Date) ||\n !Number.isFinite(result.resetAt.getTime())\n ) {\n throw new AuthProviderError(\n \"The MFA rate limiter returned an invalid result.\",\n );\n }\n\n if (!result.allowed) {\n throw new AuthRateLimitError(result.resetAt);\n }\n }\n }\n\n private createAuditRecord(\n action: AuthAuditEventStorageRecord[\"action\"],\n userId: string | null,\n metadata?: AuthAuditEventStorageRecord[\"metadata\"],\n sessionId: string | null = null,\n ): AuthAuditEventStorageRecord {\n const now = this.now();\n\n return {\n id: generateAuthId(\"auth-audit\"),\n action,\n userId,\n sessionId,\n metadata,\n createdAt: now,\n updatedAt: now,\n revision: 1,\n };\n }\n\n private async getAuthenticatedContext(\n sessionToken: string,\n ): Promise<AuthenticatedContext> {\n if (typeof sessionToken !== \"string\" || sessionToken.length === 0) {\n throw new AuthSessionError(\"An authenticated session is required.\");\n }\n\n let session: SessionRecord | null;\n\n try {\n session = await this.sessions.validate(sessionToken);\n } catch (error) {\n throw new AuthSessionError(\"Could not validate the session.\", {\n cause: error,\n });\n }\n\n if (!session || session.kind !== \"authenticated\" || !session.userId) {\n throw new AuthSessionError(\"An authenticated session is required.\");\n }\n\n const user = await this.storage.findById<AuthUserStorageRecord>(\n this.usersCollection,\n session.userId,\n );\n\n if (!user || user.deactivatedAt !== null) {\n throw new AuthSessionError(\"The authenticated account is unavailable.\");\n }\n\n if (\n user.sessionsInvalidatedAt &&\n session.createdAt.getTime() <= user.sessionsInvalidatedAt.getTime()\n ) {\n throw new AuthSessionError(\"The session is no longer valid.\");\n }\n\n if (this.requireVerifiedEmail && !user.emailVerifiedAt) {\n throw new AuthEmailVerificationRequiredError();\n }\n\n return { session, user };\n }\n\n private async verifyCurrentPassword(\n userId: string,\n password: string,\n ): Promise<void> {\n if (typeof password !== \"string\" || password.length === 0) {\n throw new AuthValidationError(\n \"The current password is required for this operation.\",\n );\n }\n\n const credential =\n await this.storage.findOne<PasswordCredentialStorageRecord>(\n this.credentialsCollection,\n {\n where: {\n userId: whereEquals(userId),\n kind: whereEquals(\"password\"),\n },\n },\n );\n\n if (!credential || credential.revokedAt !== null) {\n throw new AuthInvalidCredentialsError();\n }\n\n let valid: boolean;\n\n try {\n valid = await this.passwordHasher.verify(\n password,\n credential.passwordHash,\n );\n } catch (error) {\n throw new AuthProviderError(\"Password verification failed.\", {\n cause: error,\n });\n }\n\n if (!valid) {\n throw new AuthInvalidCredentialsError();\n }\n }\n\n private async findActiveFactors(\n userId: string,\n ): Promise<readonly AuthMfaFactorStorageRecord[]> {\n return this.storage.findMany<AuthMfaFactorStorageRecord>(\n this.factorsCollection,\n {\n where: {\n userId: whereEquals(userId),\n status: whereEquals(\"active\"),\n },\n },\n );\n }\n\n private async getPasswordSnapshot(\n challenge: AuthMfaChallengeStorageRecord,\n ): Promise<{\n readonly user: AuthUserStorageRecord;\n readonly credential: PasswordCredentialStorageRecord;\n } | null> {\n if (\n (challenge.purpose !== \"sign-in\" && challenge.purpose !== \"enrollment\") ||\n !challenge.userId ||\n challenge.userRevision === null ||\n challenge.credentialRevision === null\n ) {\n return null;\n }\n\n const user = await this.storage.findById<AuthUserStorageRecord>(\n this.usersCollection,\n challenge.userId,\n );\n\n const credential =\n await this.storage.findOne<PasswordCredentialStorageRecord>(\n this.credentialsCollection,\n {\n where: {\n userId: whereEquals(challenge.userId),\n kind: whereEquals(\"password\"),\n },\n },\n );\n\n if (\n !user ||\n user.deactivatedAt !== null ||\n user.revision !== challenge.userRevision ||\n !credential ||\n credential.revokedAt !== null ||\n credential.revision !== challenge.credentialRevision\n ) {\n return null;\n }\n\n return { user, credential };\n }\n\n private async createChallengeRecord(\n purpose: AuthMfaChallengeStorageRecord[\"purpose\"],\n userId: string | null,\n factorIds: readonly string[],\n webAuthnChallenge: string | null = null,\n metadata: {\n readonly factorLabel?: string | null;\n readonly userRevision?: number | null;\n readonly credentialRevision?: number | null;\n } = {},\n ): Promise<{\n readonly record: AuthMfaChallengeStorageRecord;\n readonly token: string;\n }> {\n const now = this.now();\n const token = generateSecureAuthToken();\n\n const record: AuthMfaChallengeStorageRecord = {\n id: generateAuthId(\"mfa-challenge\"),\n purpose,\n userId,\n tokenHash: await hashAuthToken(token),\n factorIds,\n webAuthnFactorIds: [],\n webAuthnChallenge,\n factorLabel: metadata.factorLabel ?? null,\n userRevision: metadata.userRevision ?? null,\n credentialRevision: metadata.credentialRevision ?? null,\n attemptCount: 0,\n maximumAttempts: DEFAULT_MAXIMUM_CHALLENGE_ATTEMPTS,\n expiresAt: new Date(now.getTime() + this.challengeLifetimeSeconds * 1000),\n consumedAt: null,\n createdAt: now,\n updatedAt: now,\n revision: 1,\n };\n\n return { record, token };\n }\n\n private async getChallenge(\n token: string,\n purpose: AuthMfaChallengeStorageRecord[\"purpose\"],\n ): Promise<AuthMfaChallengeStorageRecord> {\n if (typeof token !== \"string\" || token.length === 0) {\n throw new AuthMfaChallengeError();\n }\n\n const tokenHash = await hashAuthToken(token);\n\n const record = await this.storage.findOne<AuthMfaChallengeStorageRecord>(\n this.challengesCollection,\n {\n where: {\n tokenHash: whereEquals(tokenHash),\n },\n },\n );\n\n if (\n !record ||\n record.purpose !== purpose ||\n record.consumedAt !== null ||\n record.expiresAt.getTime() <= this.now().getTime() ||\n record.attemptCount >= record.maximumAttempts\n ) {\n throw new AuthMfaChallengeError();\n }\n\n if (\n (purpose === \"sign-in\" || purpose === \"enrollment\") &&\n !(await this.getPasswordSnapshot(record))\n ) {\n throw new AuthMfaChallengeError();\n }\n\n return record;\n }\n\n async createSignInChallenge(input: {\n readonly userId: string;\n readonly ipAddress: string;\n }): Promise<AuthMfaPendingResult | null> {\n this.assertInitialized();\n\n await this.enforceRateLimit(\"challenge\", input.ipAddress, input.userId);\n\n const factors = await this.findActiveFactors(input.userId);\n\n if (factors.length === 0) {\n return null;\n }\n\n const recoveryCodeCount =\n await this.storage.count<AuthRecoveryCodeStorageRecord>(\n this.recoveryCodesCollection,\n {\n where: {\n userId: whereEquals(input.userId),\n consumedAt: whereEquals(null),\n },\n },\n );\n\n const availableFactors = new Set<\"totp\" | \"passkey\" | \"recovery-code\">();\n\n for (const factor of factors) {\n if (factor.type === \"totp\") {\n availableFactors.add(\"totp\");\n }\n\n if (factor.type === \"passkey\") {\n availableFactors.add(\"passkey\");\n }\n }\n\n if (recoveryCodeCount > 0) {\n availableFactors.add(\"recovery-code\");\n }\n\n const user = await this.storage.findById<AuthUserStorageRecord>(\n this.usersCollection,\n input.userId,\n );\n\n const credential =\n await this.storage.findOne<PasswordCredentialStorageRecord>(\n this.credentialsCollection,\n {\n where: {\n userId: whereEquals(input.userId),\n kind: whereEquals(\"password\"),\n },\n },\n );\n\n if (\n !user ||\n user.deactivatedAt !== null ||\n !credential ||\n credential.revokedAt !== null\n ) {\n throw new AuthMfaChallengeError();\n }\n\n const created = await this.createChallengeRecord(\n \"sign-in\",\n input.userId,\n factors.map((factor) => factor.id),\n null,\n {\n userRevision: user.revision,\n credentialRevision: credential.revision,\n },\n );\n\n const audit = this.createAuditRecord(\n \"mfa-challenge-created\",\n input.userId,\n {\n factors: factors.length,\n },\n );\n\n try {\n await this.storage.atomicBatch<MfaStorageRecord>([\n {\n type: \"insert\",\n collection: this.challengesCollection,\n record: created.record,\n },\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: audit,\n },\n ]);\n } catch (error) {\n throw new AuthProviderError(\"Could not create the MFA challenge.\", {\n cause: error,\n });\n }\n\n return {\n status: \"mfa-pending\",\n challengeId: created.record.id,\n challengeToken: created.token,\n expiresAt: created.record.expiresAt,\n availableFactors: [...availableFactors],\n };\n }\n\n async createEnrollmentChallenge(input: {\n readonly userId: string;\n readonly ipAddress: string;\n }): Promise<AuthMfaEnrollmentPendingResult> {\n this.assertInitialized();\n\n await this.enforceRateLimit(\n \"enrollment-challenge\",\n input.ipAddress,\n input.userId,\n );\n\n const activeFactors = await this.findActiveFactors(input.userId);\n\n if (activeFactors.length > 0) {\n throw new AuthMfaChallengeError();\n }\n\n const user = await this.storage.findById<AuthUserStorageRecord>(\n this.usersCollection,\n input.userId,\n );\n\n const credential =\n await this.storage.findOne<PasswordCredentialStorageRecord>(\n this.credentialsCollection,\n {\n where: {\n userId: whereEquals(input.userId),\n kind: whereEquals(\"password\"),\n },\n },\n );\n\n if (\n !user ||\n user.deactivatedAt !== null ||\n !credential ||\n credential.revokedAt !== null\n ) {\n throw new AuthMfaChallengeError();\n }\n\n const created = await this.createChallengeRecord(\n \"enrollment\",\n input.userId,\n [],\n null,\n {\n userRevision: user.revision,\n credentialRevision: credential.revision,\n },\n );\n\n const audit = this.createAuditRecord(\n \"mfa-challenge-created\",\n input.userId,\n {\n purpose: \"required-enrollment\",\n },\n );\n\n try {\n await this.storage.atomicBatch<MfaStorageRecord>([\n {\n type: \"insert\",\n collection: this.challengesCollection,\n record: created.record,\n },\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: audit,\n },\n ]);\n } catch (error) {\n throw new AuthProviderError(\n \"Could not create the required MFA enrollment challenge.\",\n { cause: error },\n );\n }\n\n const availableFactors: readonly (\"totp\" | \"passkey\")[] = this.webAuthn\n ? [\"totp\", \"passkey\"]\n : [\"totp\"];\n\n return {\n status: \"mfa-enrollment-pending\",\n challengeId: created.record.id,\n challengeToken: created.token,\n expiresAt: created.record.expiresAt,\n availableFactors,\n };\n }\n\n async getPendingAuthState(token: string): Promise<AuthState | null> {\n this.assertInitialized();\n\n if (typeof token !== \"string\" || token.length === 0) {\n return null;\n }\n\n const tokenHash = await hashAuthToken(token);\n\n const challenge = await this.storage.findOne<AuthMfaChallengeStorageRecord>(\n this.challengesCollection,\n {\n where: {\n tokenHash: whereEquals(tokenHash),\n },\n },\n );\n\n if (\n !challenge ||\n (challenge.purpose !== \"sign-in\" && challenge.purpose !== \"enrollment\") ||\n !challenge.userId ||\n challenge.consumedAt !== null ||\n challenge.expiresAt.getTime() <= this.now().getTime() ||\n challenge.attemptCount >= challenge.maximumAttempts\n ) {\n return null;\n }\n\n if (!(await this.getPasswordSnapshot(challenge))) {\n return null;\n }\n\n if (challenge.purpose === \"enrollment\") {\n const availableFactors: readonly (\"totp\" | \"passkey\")[] = this.webAuthn\n ? [\"totp\", \"passkey\"]\n : [\"totp\"];\n\n return {\n status: \"mfa-enrollment-pending\",\n userId: challenge.userId,\n challengeId: challenge.id,\n expiresAt: challenge.expiresAt,\n availableFactors,\n };\n }\n\n return {\n status: \"mfa-pending\",\n userId: challenge.userId,\n challengeId: challenge.id,\n expiresAt: challenge.expiresAt,\n };\n }\n\n async getAvailableFactors(\n challengeToken: string,\n ): Promise<readonly AuthMfaAvailableFactor[]> {\n this.assertInitialized();\n\n const challenge = await this.getChallenge(challengeToken, \"sign-in\");\n\n if (!challenge.userId) {\n throw new AuthMfaChallengeError();\n }\n\n const factors = await this.findActiveFactors(challenge.userId);\n\n // A factor added after challenge creation cannot be used to complete\n // this challenge. Only the original factor snapshot is accepted.\n const challengeFactors = factors.filter((factor) =>\n challenge.factorIds.includes(factor.id),\n );\n\n const available = new Set<AuthMfaAvailableFactor>();\n\n for (const factor of challengeFactors) {\n available.add(factor.type);\n }\n\n const recoveryCodes =\n await this.storage.findMany<AuthRecoveryCodeStorageRecord>(\n this.recoveryCodesCollection,\n {\n where: {\n userId: whereEquals(challenge.userId),\n consumedAt: whereEquals(null),\n },\n },\n );\n\n if (\n recoveryCodes.some((record) =>\n challenge.factorIds.includes(record.factorId),\n )\n ) {\n available.add(\"recovery-code\");\n }\n\n return [...available];\n }\n\n private async failChallenge(\n challenge: AuthMfaChallengeStorageRecord,\n method: string,\n ): Promise<never> {\n const now = this.now();\n const nextAttempt = challenge.attemptCount + 1;\n\n const operations: StorageBatchOperation<MfaStorageRecord>[] = [\n {\n type: \"update\",\n collection: this.challengesCollection,\n id: challenge.id,\n condition: {\n expectedRevision: challenge.revision,\n },\n changes: {\n attemptCount: nextAttempt,\n consumedAt: nextAttempt >= challenge.maximumAttempts ? now : null,\n updatedAt: now,\n },\n },\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: this.createAuditRecord(\n \"mfa-challenge-failed\",\n challenge.userId,\n {\n method,\n attempt: nextAttempt,\n },\n ),\n },\n ];\n\n try {\n await this.storage.atomicBatch<MfaStorageRecord>(operations);\n } catch (error) {\n throw new AuthMfaChallengeError({ cause: error });\n }\n\n throw new AuthMfaChallengeError();\n }\n\n private async createRecoveryCodeBundle(\n userId: string,\n factorId: string,\n force = false,\n ): Promise<RecoveryCodeBundle> {\n if (!force) {\n const existing = await this.storage.count<AuthRecoveryCodeStorageRecord>(\n this.recoveryCodesCollection,\n {\n where: {\n userId: whereEquals(userId),\n consumedAt: whereEquals(null),\n },\n },\n );\n\n if (existing > 0) {\n return { codes: [], records: [] };\n }\n }\n\n const codes: string[] = [];\n const records: AuthRecoveryCodeStorageRecord[] = [];\n const now = this.now();\n\n for (let index = 0; index < this.recoveryCodeCount; index++) {\n const code = generateRecoveryCode();\n const normalized = normalizeRecoveryCode(code)!;\n\n codes.push(code);\n\n records.push({\n id: generateAuthId(\"recovery-code\"),\n userId,\n factorId,\n codeHash: await hashAuthToken(normalized),\n consumedAt: null,\n createdAt: now,\n updatedAt: now,\n revision: 1,\n });\n }\n\n return { codes, records };\n }\n\n private async getRecoveryCodeRecord(\n userId: string,\n code: string,\n ): Promise<AuthRecoveryCodeStorageRecord | null> {\n const normalized = normalizeRecoveryCode(code);\n\n if (!normalized) {\n return null;\n }\n\n const codeHash = await hashAuthToken(normalized);\n\n return this.storage.findOne<AuthRecoveryCodeStorageRecord>(\n this.recoveryCodesCollection,\n {\n where: {\n userId: whereEquals(userId),\n codeHash: whereEquals(codeHash),\n consumedAt: whereEquals(null),\n },\n },\n );\n }\n\n private async createAuthenticatedResult(\n userId: string,\n metadata: Readonly<Record<string, unknown>>,\n ): Promise<{\n readonly result: AuthResult;\n readonly sessionId: string;\n }> {\n const user = await this.storage.findById<AuthUserStorageRecord>(\n this.usersCollection,\n userId,\n );\n\n if (!user || user.deactivatedAt !== null) {\n throw new AuthMfaChallengeError();\n }\n\n if (this.requireVerifiedEmail && !user.emailVerifiedAt) {\n throw new AuthEmailVerificationRequiredError();\n }\n\n try {\n const created = await this.sessions.create({\n userId,\n kind: \"authenticated\",\n metadata,\n });\n\n return {\n sessionId: created.session.id,\n result: {\n user: toPublicUser(user),\n session: toPublicSession(created.session),\n token: created.token,\n },\n };\n } catch (error) {\n throw new AuthSessionError(\n \"Could not create a session after MFA verification.\",\n { cause: error },\n );\n }\n }\n\n /**\n * Atomically consumes the challenge and the verified factor state.\n *\n * A session is created before the batch, but its raw token is never\n * returned until the batch has successfully committed.\n */\n private async completeChallenge(input: {\n readonly challenge: AuthMfaChallengeStorageRecord;\n readonly userId: string;\n readonly method: string;\n readonly factorInsert?: AuthMfaFactorStorageRecord;\n readonly factorUpdate?: FactorUpdate;\n readonly recoveryCode?: AuthRecoveryCodeStorageRecord;\n readonly recoveryRecords?: readonly AuthRecoveryCodeStorageRecord[];\n readonly auditAction?: AuthAuditEventStorageRecord[\"action\"];\n }): Promise<AuthResult> {\n const now = this.now();\n\n const requiresPasswordSnapshot =\n input.challenge.purpose === \"sign-in\" ||\n input.challenge.purpose === \"enrollment\";\n\n const passwordSnapshot = requiresPasswordSnapshot\n ? await this.getPasswordSnapshot(input.challenge)\n : null;\n\n if (requiresPasswordSnapshot && !passwordSnapshot) {\n throw new AuthMfaChallengeError();\n }\n\n const authenticated = await this.createAuthenticatedResult(input.userId, {\n authenticationMethod: input.method,\n mfaVerifiedAt: now.toISOString(),\n });\n\n const operations: StorageBatchOperation<MfaStorageRecord>[] = [\n {\n type: \"update\",\n collection: this.challengesCollection,\n id: input.challenge.id,\n condition: {\n expectedRevision: input.challenge.revision,\n },\n changes: {\n consumedAt: now,\n updatedAt: now,\n },\n },\n ];\n\n if (input.factorInsert) {\n operations.push({\n type: \"insert\",\n collection: this.factorsCollection,\n record: input.factorInsert,\n });\n }\n\n if (input.factorUpdate) {\n operations.push({\n type: \"update\",\n collection: this.factorsCollection,\n id: input.factorUpdate.factor.id,\n condition: {\n expectedRevision: input.factorUpdate.factor.revision,\n },\n changes: input.factorUpdate.changes,\n });\n }\n\n if (input.recoveryCode) {\n operations.push({\n type: \"update\",\n collection: this.recoveryCodesCollection,\n id: input.recoveryCode.id,\n condition: {\n expectedRevision: input.recoveryCode.revision,\n },\n changes: {\n consumedAt: now,\n updatedAt: now,\n },\n });\n }\n\n for (const recoveryRecord of input.recoveryRecords ?? []) {\n operations.push({\n type: \"insert\",\n collection: this.recoveryCodesCollection,\n record: recoveryRecord,\n });\n }\n\n if (passwordSnapshot) {\n operations.push(\n {\n type: \"update\",\n collection: this.usersCollection,\n id: passwordSnapshot.user.id,\n condition: {\n expectedRevision: passwordSnapshot.user.revision,\n },\n changes: {\n updatedAt: passwordSnapshot.user.updatedAt,\n },\n },\n {\n type: \"update\",\n collection: this.credentialsCollection,\n id: passwordSnapshot.credential.id,\n condition: {\n expectedRevision: passwordSnapshot.credential.revision,\n },\n changes: {\n updatedAt: passwordSnapshot.credential.updatedAt,\n passwordUpdatedAt: passwordSnapshot.credential.passwordUpdatedAt,\n },\n },\n );\n }\n\n operations.push({\n type: \"insert\",\n collection: this.auditCollection,\n record: this.createAuditRecord(\n input.auditAction ??\n (input.method === \"passkey\"\n ? \"passkey-authenticated\"\n : \"mfa-completed\"),\n input.userId,\n {\n method: input.method,\n },\n authenticated.sessionId,\n ),\n });\n\n try {\n await this.storage.atomicBatch<MfaStorageRecord>(operations);\n } catch (error) {\n try {\n await this.sessions.revoke(authenticated.sessionId);\n } catch (revokeError) {\n throw new AuthSessionError(\n \"Could not revoke an uncommitted MFA session.\",\n { cause: revokeError },\n );\n }\n\n if (\n error instanceof StorageConflictError ||\n error instanceof StorageDuplicateError\n ) {\n throw new AuthMfaChallengeError({ cause: error });\n }\n\n throw new AuthProviderError(\"Could not complete MFA authentication.\", {\n cause: error,\n });\n }\n\n return authenticated.result;\n }\n\n async beginRequiredTotpEnrollment(\n input: BeginRequiredMfaEnrollmentInput,\n ): Promise<TotpEnrollmentStarted> {\n this.assertInitialized();\n\n const challenge = await this.getChallenge(\n input.challengeToken,\n \"enrollment\",\n );\n\n if (!challenge.userId || challenge.factorIds.length > 0) {\n throw new AuthMfaChallengeError();\n }\n\n if (challenge.webAuthnChallenge) {\n throw new AuthMfaChallengeError();\n }\n\n await this.enforceRateLimit(\n \"required-enrollment\",\n input.ipAddress,\n challenge.userId,\n );\n\n const snapshot = await this.getPasswordSnapshot(challenge);\n\n if (!snapshot) {\n throw new AuthMfaChallengeError();\n }\n\n const secret = generateTotpSecret();\n\n let protectedSecret: ProtectedAuthSecret;\n\n try {\n protectedSecret = await this.secretProtector.encrypt(secret);\n } catch (error) {\n throw new AuthProviderError(\"Could not encrypt the TOTP secret.\", {\n cause: error,\n });\n }\n\n if (\n !protectedSecret ||\n typeof protectedSecret.ciphertext !== \"string\" ||\n protectedSecret.ciphertext.length === 0 ||\n typeof protectedSecret.iv !== \"string\" ||\n protectedSecret.iv.length === 0 ||\n typeof protectedSecret.keyId !== \"string\" ||\n protectedSecret.keyId.length === 0\n ) {\n throw new AuthProviderError(\n \"The secret protector returned an invalid encrypted secret.\",\n );\n }\n\n const now = this.now();\n const factorId = generateAuthId(\"mfa-factor\");\n\n const factor: AuthMfaFactorStorageRecord = {\n id: factorId,\n userId: challenge.userId,\n factorKey: `totp:${factorId}`,\n type: \"totp\",\n status: \"pending\",\n label: validateLabel(input.label, \"Authenticator app\"),\n secret: protectedSecret,\n credentialId: null,\n publicKey: null,\n counter: 0,\n transports: [],\n lastUsedStep: null,\n lastUsedAt: null,\n expiresAt: challenge.expiresAt,\n createdAt: now,\n updatedAt: now,\n revision: 1,\n };\n\n const operations: StorageBatchOperation<MfaStorageRecord>[] = [\n {\n type: \"insert\",\n collection: this.factorsCollection,\n record: factor,\n },\n {\n type: \"update\",\n collection: this.challengesCollection,\n id: challenge.id,\n condition: {\n expectedRevision: challenge.revision,\n },\n changes: {\n factorIds: [factor.id],\n updatedAt: now,\n },\n },\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: this.createAuditRecord(\n \"mfa-challenge-created\",\n challenge.userId,\n {\n purpose: \"required-totp-enrollment\",\n factorId,\n },\n ),\n },\n ];\n\n try {\n await this.storage.atomicBatch<MfaStorageRecord>(operations);\n } catch (error) {\n if (\n error instanceof StorageConflictError ||\n error instanceof StorageDuplicateError\n ) {\n throw new AuthMfaChallengeError({ cause: error });\n }\n\n throw new AuthProviderError(\"Could not create the pending TOTP factor.\", {\n cause: error,\n });\n }\n\n return {\n factorId,\n secret,\n otpauthUrl: createTotpUri({\n issuer: this.issuer,\n account: snapshot.user.email,\n secret,\n digits: this.totp.digits,\n periodSeconds: this.totp.periodSeconds,\n }),\n expiresAt: challenge.expiresAt,\n };\n }\n\n async verifyRequiredTotpEnrollment(\n input: VerifyRequiredTotpEnrollmentInput,\n ): Promise<AuthMfaEnrollmentCompleted> {\n this.assertInitialized();\n\n const challenge = await this.getChallenge(\n input.challengeToken,\n \"enrollment\",\n );\n\n if (\n !challenge.userId ||\n challenge.factorIds.length !== 1 ||\n !challenge.factorIds.includes(input.factorId)\n ) {\n throw new AuthMfaChallengeError();\n }\n\n await this.enforceRateLimit(\n \"verify-required-totp-enrollment\",\n input.ipAddress,\n challenge.userId,\n );\n\n const factor = await this.storage.findById<AuthMfaFactorStorageRecord>(\n this.factorsCollection,\n input.factorId,\n );\n\n if (\n !factor ||\n factor.userId !== challenge.userId ||\n factor.type !== \"totp\" ||\n factor.status !== \"pending\" ||\n !factor.secret ||\n !factor.expiresAt ||\n factor.expiresAt.getTime() <= this.now().getTime()\n ) {\n throw new AuthMfaFactorError(\n \"The pending TOTP enrollment is invalid or expired.\",\n );\n }\n\n let secret: string;\n\n try {\n secret = await this.secretProtector.decrypt(factor.secret);\n } catch (error) {\n throw new AuthProviderError(\"Could not decrypt the TOTP secret.\", {\n cause: error,\n });\n }\n\n const now = this.now();\n\n const matchedStep = await verifyTotpCode(\n secret,\n input.code,\n now.getTime(),\n this.totp,\n );\n\n if (\n matchedStep === null ||\n (factor.lastUsedStep !== null && matchedStep <= factor.lastUsedStep)\n ) {\n return this.failChallenge(challenge, \"required-totp-enrollment\");\n }\n\n const recovery = await this.createRecoveryCodeBundle(\n challenge.userId,\n factor.id,\n );\n\n const auth = await this.completeChallenge({\n challenge,\n userId: challenge.userId,\n method: \"totp\",\n factorUpdate: {\n factor,\n changes: {\n status: \"active\",\n lastUsedStep: matchedStep,\n lastUsedAt: now,\n expiresAt: null,\n updatedAt: now,\n },\n },\n recoveryRecords: recovery.records,\n auditAction: \"mfa-factor-added\",\n });\n\n return {\n auth,\n factor: toPublicFactor({\n ...factor,\n status: \"active\",\n lastUsedStep: matchedStep,\n lastUsedAt: now,\n expiresAt: null,\n updatedAt: now,\n revision: factor.revision + 1,\n }),\n recoveryCodes: recovery.codes,\n };\n }\n\n async beginRequiredPasskeyEnrollment(\n input: BeginRequiredMfaEnrollmentInput,\n ): Promise<PasskeyRegistrationStarted> {\n this.assertInitialized();\n\n const challenge = await this.getChallenge(\n input.challengeToken,\n \"enrollment\",\n );\n\n if (\n !challenge.userId ||\n challenge.factorIds.length > 0 ||\n challenge.webAuthnChallenge\n ) {\n throw new AuthMfaChallengeError();\n }\n\n await this.enforceRateLimit(\n \"required-passkey-enrollment\",\n input.ipAddress,\n challenge.userId,\n );\n\n const snapshot = await this.getPasswordSnapshot(challenge);\n\n if (!snapshot) {\n throw new AuthMfaChallengeError();\n }\n\n const { provider, config } = this.getWebAuthn();\n\n const activeFactors = await this.findActiveFactors(challenge.userId);\n\n const excludeCredentialIds = activeFactors\n .filter(\n (factor) => factor.type === \"passkey\" && factor.credentialId !== null,\n )\n .map((factor) => factor.credentialId!);\n\n const webAuthnChallenge = generateWebAuthnChallenge();\n\n let options: Readonly<Record<string, unknown>>;\n\n try {\n options = await provider.generateRegistrationOptions({\n challenge: webAuthnChallenge,\n rpId: config.rpId,\n rpName: config.rpName,\n userId: challenge.userId,\n userName: snapshot.user.email,\n excludeCredentialIds,\n });\n } catch (error) {\n throw new AuthProviderError(\n \"Could not create WebAuthn registration options.\",\n { cause: error },\n );\n }\n\n this.assertWebAuthnOptions(options, webAuthnChallenge);\n\n const now = this.now();\n const factorLabel = validateLabel(input.label, \"Passkey\");\n\n const operations: StorageBatchOperation<MfaStorageRecord>[] = [\n {\n type: \"update\",\n collection: this.challengesCollection,\n id: challenge.id,\n condition: {\n expectedRevision: challenge.revision,\n },\n changes: {\n webAuthnChallenge,\n factorLabel,\n updatedAt: now,\n },\n },\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: this.createAuditRecord(\n \"mfa-challenge-created\",\n challenge.userId,\n {\n purpose: \"required-passkey-enrollment\",\n },\n ),\n },\n ];\n\n try {\n await this.storage.atomicBatch<MfaStorageRecord>(operations);\n } catch (error) {\n if (\n error instanceof StorageConflictError ||\n error instanceof StorageDuplicateError\n ) {\n throw new AuthMfaChallengeError({ cause: error });\n }\n\n throw new AuthProviderError(\n \"Could not persist the required passkey enrollment challenge.\",\n { cause: error },\n );\n }\n\n return {\n challengeId: challenge.id,\n challengeToken: input.challengeToken,\n options,\n expiresAt: challenge.expiresAt,\n };\n }\n\n async finishRequiredPasskeyEnrollment(\n input: FinishRequiredPasskeyEnrollmentInput,\n ): Promise<AuthMfaEnrollmentCompleted> {\n this.assertInitialized();\n\n const challenge = await this.getChallenge(\n input.challengeToken,\n \"enrollment\",\n );\n\n if (\n !challenge.userId ||\n challenge.factorIds.length > 0 ||\n !challenge.webAuthnChallenge\n ) {\n throw new AuthMfaChallengeError();\n }\n\n await this.enforceRateLimit(\n \"verify-required-passkey-enrollment\",\n input.ipAddress,\n challenge.userId,\n );\n\n const snapshot = await this.getPasswordSnapshot(challenge);\n\n if (!snapshot) {\n throw new AuthMfaChallengeError();\n }\n\n const { provider, config } = this.getWebAuthn();\n\n let verification: WebAuthnRegistrationVerification;\n\n try {\n verification = await provider.verifyRegistrationResponse({\n response: input.response,\n expectedChallenge: challenge.webAuthnChallenge,\n expectedOrigin: config.origin,\n expectedRpId: config.rpId,\n });\n } catch (error) {\n throw new AuthProviderError(\n \"WebAuthn registration verification failed.\",\n { cause: error },\n );\n }\n\n if (\n !verification.verified ||\n !verification.userVerified ||\n !verification.credential\n ) {\n return this.failChallenge(challenge, \"required-passkey-enrollment\");\n }\n\n const credential: WebAuthnRegistrationCredential = verification.credential;\n const responseCredentialId = getCredentialId(input.response);\n\n if (\n responseCredentialId !== credential.credentialId ||\n !credential.publicKey ||\n !Number.isSafeInteger(credential.counter) ||\n credential.counter < 0\n ) {\n return this.failChallenge(challenge, \"required-passkey-enrollment\");\n }\n\n const now = this.now();\n const factorId = generateAuthId(\"mfa-factor\");\n\n const factor: AuthMfaFactorStorageRecord = {\n id: factorId,\n userId: challenge.userId,\n factorKey: `passkey:${credential.credentialId}`,\n type: \"passkey\",\n status: \"active\",\n label: challenge.factorLabel ?? \"Passkey\",\n secret: null,\n credentialId: credential.credentialId,\n publicKey: credential.publicKey,\n counter: credential.counter,\n transports: credential.transports ?? [],\n lastUsedStep: null,\n lastUsedAt: now,\n expiresAt: null,\n createdAt: now,\n updatedAt: now,\n revision: 1,\n };\n\n const recovery = await this.createRecoveryCodeBundle(\n challenge.userId,\n factor.id,\n );\n\n const auth = await this.completeChallenge({\n challenge,\n userId: challenge.userId,\n method: \"passkey\",\n factorInsert: factor,\n recoveryRecords: recovery.records,\n auditAction: \"passkey-registered\",\n });\n\n return {\n auth,\n factor: toPublicFactor(factor),\n recoveryCodes: recovery.codes,\n };\n }\n\n async beginTotpEnrollment(\n input: BeginTotpEnrollmentInput,\n ): Promise<TotpEnrollmentStarted> {\n this.assertInitialized();\n\n const context = await this.getAuthenticatedContext(input.sessionToken);\n\n await this.verifyCurrentPassword(context.user.id, input.currentPassword);\n\n await this.enforceRateLimit(\"enrollment\", input.ipAddress, context.user.id);\n\n const secret = generateTotpSecret();\n\n let protectedSecret: ProtectedAuthSecret;\n\n try {\n protectedSecret = await this.secretProtector.encrypt(secret);\n } catch (error) {\n throw new AuthProviderError(\"Could not encrypt the TOTP secret.\", {\n cause: error,\n });\n }\n\n if (\n !protectedSecret ||\n typeof protectedSecret.ciphertext !== \"string\" ||\n protectedSecret.ciphertext.length === 0 ||\n typeof protectedSecret.iv !== \"string\" ||\n protectedSecret.iv.length === 0 ||\n typeof protectedSecret.keyId !== \"string\" ||\n protectedSecret.keyId.length === 0\n ) {\n throw new AuthProviderError(\n \"The secret protector returned an invalid encrypted secret.\",\n );\n }\n\n const now = this.now();\n const factorId = generateAuthId(\"mfa-factor\");\n const expiresAt = new Date(\n now.getTime() + this.challengeLifetimeSeconds * 1000,\n );\n\n const factor: AuthMfaFactorStorageRecord = {\n id: factorId,\n userId: context.user.id,\n factorKey: `totp:${factorId}`,\n type: \"totp\",\n status: \"pending\",\n label: validateLabel(input.label, \"Authenticator app\"),\n secret: protectedSecret,\n credentialId: null,\n publicKey: null,\n counter: 0,\n transports: [],\n lastUsedStep: null,\n lastUsedAt: null,\n expiresAt,\n createdAt: now,\n updatedAt: now,\n revision: 1,\n };\n\n const operations: StorageBatchOperation<MfaStorageRecord>[] = [\n {\n type: \"insert\",\n collection: this.factorsCollection,\n record: factor,\n },\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: this.createAuditRecord(\n \"mfa-factor-added\",\n context.user.id,\n {\n type: \"totp\",\n status: \"pending\",\n },\n context.session.id,\n ),\n },\n ];\n\n try {\n await this.storage.atomicBatch<MfaStorageRecord>(operations);\n } catch (error) {\n throw new AuthProviderError(\"Could not create the pending TOTP factor.\", {\n cause: error,\n });\n }\n\n return {\n factorId,\n secret,\n otpauthUrl: createTotpUri({\n issuer: this.issuer,\n account: context.user.email,\n secret,\n digits: this.totp.digits,\n periodSeconds: this.totp.periodSeconds,\n }),\n expiresAt,\n };\n }\n\n async verifyTotpEnrollment(\n input: VerifyTotpEnrollmentInput,\n ): Promise<MfaEnrollmentCompleted> {\n this.assertInitialized();\n\n const context = await this.getAuthenticatedContext(input.sessionToken);\n\n await this.enforceRateLimit(\n \"verify-enrollment\",\n input.ipAddress,\n context.user.id,\n );\n\n const factor = await this.storage.findById<AuthMfaFactorStorageRecord>(\n this.factorsCollection,\n input.factorId,\n );\n\n if (\n !factor ||\n factor.userId !== context.user.id ||\n factor.type !== \"totp\" ||\n factor.status !== \"pending\" ||\n !factor.secret ||\n !factor.expiresAt ||\n factor.expiresAt.getTime() <= this.now().getTime()\n ) {\n throw new AuthMfaFactorError(\n \"The pending TOTP enrollment is invalid or expired.\",\n );\n }\n\n let secret: string;\n\n try {\n secret = await this.secretProtector.decrypt(factor.secret);\n } catch (error) {\n throw new AuthProviderError(\"Could not decrypt the TOTP secret.\", {\n cause: error,\n });\n }\n\n const now = this.now();\n\n const matchedStep = await verifyTotpCode(\n secret,\n input.code,\n now.getTime(),\n this.totp,\n );\n\n if (matchedStep === null) {\n throw new AuthMfaFactorError(\"The authenticator code is invalid.\");\n }\n\n const recovery = await this.createRecoveryCodeBundle(\n context.user.id,\n factor.id,\n );\n\n const activatedFactor: AuthMfaFactorStorageRecord = {\n ...factor,\n status: \"active\",\n lastUsedStep: matchedStep,\n lastUsedAt: now,\n expiresAt: null,\n updatedAt: now,\n revision: factor.revision + 1,\n };\n\n const operations: StorageBatchOperation<MfaStorageRecord>[] = [\n {\n type: \"update\",\n collection: this.factorsCollection,\n id: factor.id,\n condition: {\n expectedRevision: factor.revision,\n },\n changes: {\n status: \"active\",\n lastUsedStep: matchedStep,\n lastUsedAt: now,\n expiresAt: null,\n updatedAt: now,\n },\n },\n ...recovery.records.map((record) => ({\n type: \"insert\" as const,\n collection: this.recoveryCodesCollection,\n record,\n })),\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: this.createAuditRecord(\n \"mfa-factor-added\",\n context.user.id,\n {\n type: \"totp\",\n recoveryCodesGenerated: recovery.codes.length,\n },\n context.session.id,\n ),\n },\n ];\n\n try {\n await this.storage.atomicBatch<MfaStorageRecord>(operations);\n } catch (error) {\n if (error instanceof StorageConflictError) {\n throw new AuthMfaFactorError(\n \"The TOTP enrollment has already been completed.\",\n { cause: error },\n );\n }\n\n throw new AuthProviderError(\"Could not activate the TOTP factor.\", {\n cause: error,\n });\n }\n\n return {\n factor: toPublicFactor(activatedFactor),\n recoveryCodes: recovery.codes,\n };\n }\n\n async verifySignInTotp(input: CompleteMfaSignInInput): Promise<AuthResult> {\n this.assertInitialized();\n\n const challenge = await this.getChallenge(input.challengeToken, \"sign-in\");\n\n if (!challenge.userId) {\n throw new AuthMfaChallengeError();\n }\n\n await this.enforceRateLimit(\n \"verify-totp\",\n input.ipAddress,\n challenge.userId,\n );\n\n for (const factorId of challenge.factorIds) {\n const factor = await this.storage.findById<AuthMfaFactorStorageRecord>(\n this.factorsCollection,\n factorId,\n );\n\n if (\n !factor ||\n factor.userId !== challenge.userId ||\n factor.status !== \"active\" ||\n factor.type !== \"totp\" ||\n !factor.secret\n ) {\n continue;\n }\n\n let secret: string;\n\n try {\n secret = await this.secretProtector.decrypt(factor.secret);\n } catch (error) {\n throw new AuthProviderError(\"Could not decrypt the TOTP secret.\", {\n cause: error,\n });\n }\n\n const now = this.now();\n\n const matchedStep = await verifyTotpCode(\n secret,\n input.code,\n now.getTime(),\n this.totp,\n );\n\n if (\n matchedStep === null ||\n (factor.lastUsedStep !== null && matchedStep <= factor.lastUsedStep)\n ) {\n continue;\n }\n\n return this.completeChallenge({\n challenge,\n userId: challenge.userId,\n method: \"totp\",\n factorUpdate: {\n factor,\n changes: {\n lastUsedStep: matchedStep,\n lastUsedAt: now,\n updatedAt: now,\n },\n },\n });\n }\n\n return this.failChallenge(challenge, \"totp\");\n }\n\n async verifySignInRecoveryCode(\n input: CompleteMfaSignInInput,\n ): Promise<AuthResult> {\n this.assertInitialized();\n\n const challenge = await this.getChallenge(input.challengeToken, \"sign-in\");\n\n if (!challenge.userId) {\n throw new AuthMfaChallengeError();\n }\n\n await this.enforceRateLimit(\n \"verify-recovery\",\n input.ipAddress,\n challenge.userId,\n );\n\n const recoveryCode = await this.getRecoveryCodeRecord(\n challenge.userId,\n input.code,\n );\n\n if (!recoveryCode) {\n return this.failChallenge(challenge, \"recovery-code\");\n }\n\n if (!challenge.factorIds.length) {\n throw new AuthMfaChallengeError();\n }\n\n const factor = await this.storage.findById<AuthMfaFactorStorageRecord>(\n this.factorsCollection,\n recoveryCode.factorId,\n );\n\n if (\n !factor ||\n factor.userId !== challenge.userId ||\n factor.status !== \"active\" ||\n !challenge.factorIds.includes(factor.id)\n ) {\n return this.failChallenge(challenge, \"recovery-code\");\n }\n\n return this.completeChallenge({\n challenge,\n userId: challenge.userId,\n method: \"recovery-code\",\n recoveryCode,\n });\n }\n\n async hasActiveFactor(userId: string): Promise<boolean> {\n this.assertInitialized();\n\n if (typeof userId !== \"string\" || userId.trim().length === 0) {\n return false;\n }\n\n const factor = await this.storage.findOne<AuthMfaFactorStorageRecord>(\n this.factorsCollection,\n {\n where: {\n userId: whereEquals(userId),\n status: whereEquals(\"active\"),\n },\n },\n );\n\n return factor !== null;\n }\n\n async listFactors(sessionToken: string): Promise<readonly AuthMfaFactor[]> {\n this.assertInitialized();\n\n const context = await this.getAuthenticatedContext(sessionToken);\n\n const factors = await this.storage.findMany<AuthMfaFactorStorageRecord>(\n this.factorsCollection,\n {\n where: {\n userId: whereEquals(context.user.id),\n },\n orderBy: [\n {\n field: \"createdAt\",\n direction: \"asc\",\n },\n ],\n },\n );\n\n return factors.map(toPublicFactor);\n }\n\n async getRecoveryCodeCount(sessionToken: string): Promise<number> {\n this.assertInitialized();\n\n const context = await this.getAuthenticatedContext(sessionToken);\n\n return this.storage.count<AuthRecoveryCodeStorageRecord>(\n this.recoveryCodesCollection,\n {\n where: {\n userId: whereEquals(context.user.id),\n consumedAt: whereEquals(null),\n },\n },\n );\n }\n\n async regenerateRecoveryCodes(\n input: RegenerateRecoveryCodesInput,\n ): Promise<readonly string[]> {\n this.assertInitialized();\n\n const context = await this.getAuthenticatedContext(input.sessionToken);\n\n await this.verifyCurrentPassword(context.user.id, input.currentPassword);\n\n await this.enforceRateLimit(\n \"regenerate-recovery\",\n input.ipAddress,\n context.user.id,\n );\n\n const factors = await this.findActiveFactors(context.user.id);\n\n if (factors.length === 0) {\n throw new AuthMfaFactorError(\n \"An active MFA factor is required to generate recovery codes.\",\n );\n }\n\n const existing = await this.storage.findMany<AuthRecoveryCodeStorageRecord>(\n this.recoveryCodesCollection,\n {\n where: {\n userId: whereEquals(context.user.id),\n consumedAt: whereEquals(null),\n },\n },\n );\n\n const firstFactor = factors[0];\n\n if (!firstFactor) {\n throw new AuthMfaFactorError(\n \"An active MFA factor is required to generate recovery codes.\",\n );\n }\n\n const bundle = await this.createRecoveryCodeBundle(\n context.user.id,\n firstFactor.id,\n true,\n );\n\n const operations: StorageBatchOperation<MfaStorageRecord>[] = [\n ...existing.map((record) => ({\n type: \"delete\" as const,\n collection: this.recoveryCodesCollection,\n id: record.id,\n condition: {\n expectedRevision: record.revision,\n },\n })),\n ...bundle.records.map((record) => ({\n type: \"insert\" as const,\n collection: this.recoveryCodesCollection,\n record,\n })),\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: this.createAuditRecord(\n \"mfa-recovery-codes-regenerated\",\n context.user.id,\n {\n count: bundle.records.length,\n },\n context.session.id,\n ),\n },\n ];\n\n try {\n await this.storage.atomicBatch<MfaStorageRecord>(operations);\n } catch (error) {\n throw new AuthProviderError(\"Could not regenerate recovery codes.\", {\n cause: error,\n });\n }\n\n return bundle.codes;\n }\n\n async removeFactor(input: RemoveMfaFactorInput): Promise<void> {\n this.assertInitialized();\n\n const context = await this.getAuthenticatedContext(input.sessionToken);\n\n await this.verifyCurrentPassword(context.user.id, input.currentPassword);\n\n await this.enforceRateLimit(\n \"remove-factor\",\n input.ipAddress,\n context.user.id,\n );\n\n const factor = await this.storage.findById<AuthMfaFactorStorageRecord>(\n this.factorsCollection,\n input.factorId,\n );\n\n if (!factor || factor.userId !== context.user.id) {\n throw new AuthMfaFactorError();\n }\n\n const recoveryCodes =\n await this.storage.findMany<AuthRecoveryCodeStorageRecord>(\n this.recoveryCodesCollection,\n {\n where: {\n factorId: whereEquals(factor.id),\n },\n },\n );\n\n const pendingChallenges =\n await this.storage.findMany<AuthMfaChallengeStorageRecord>(\n this.challengesCollection,\n {\n where: {\n userId: whereEquals(context.user.id),\n purpose: whereEquals(\"sign-in\"),\n consumedAt: whereEquals(null),\n },\n },\n );\n\n const now = this.now();\n\n const operations: StorageBatchOperation<MfaStorageRecord>[] = [\n {\n type: \"delete\",\n collection: this.factorsCollection,\n id: factor.id,\n condition: {\n expectedRevision: factor.revision,\n },\n },\n ...recoveryCodes.map((record) => ({\n type: \"delete\" as const,\n collection: this.recoveryCodesCollection,\n id: record.id,\n condition: {\n expectedRevision: record.revision,\n },\n })),\n ...pendingChallenges.map((challenge) => ({\n type: \"update\" as const,\n collection: this.challengesCollection,\n id: challenge.id,\n condition: {\n expectedRevision: challenge.revision,\n },\n changes: {\n consumedAt: now,\n updatedAt: now,\n },\n })),\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: this.createAuditRecord(\n \"mfa-factor-removed\",\n context.user.id,\n {\n type: factor.type,\n },\n context.session.id,\n ),\n },\n ];\n\n try {\n await this.storage.atomicBatch<MfaStorageRecord>(operations);\n } catch (error) {\n if (error instanceof StorageConflictError) {\n throw new AuthMfaFactorError(\"The factor changed during removal.\", {\n cause: error,\n });\n }\n\n throw new AuthProviderError(\"Could not remove the MFA factor.\", {\n cause: error,\n });\n }\n }\n\n private getWebAuthn(): {\n readonly provider: WebAuthnProvider;\n readonly config: NonNullable<AuthMfaManagerOptions[\"webAuthn\"]>;\n } {\n if (!this.webAuthn || !this.webAuthnProvider) {\n throw new AuthConfigurationError(\n \"WebAuthn is not configured for this application.\",\n );\n }\n\n return {\n provider: this.webAuthnProvider,\n config: this.webAuthn,\n };\n }\n\n private assertWebAuthnOptions(\n options: Readonly<Record<string, unknown>>,\n expectedChallenge: string,\n ): void {\n if (options.challenge !== expectedChallenge) {\n throw new AuthProviderError(\n \"The WebAuthn provider returned an unexpected challenge.\",\n );\n }\n }\n\n async beginPasskeyRegistration(\n input: BeginPasskeyRegistrationInput,\n ): Promise<PasskeyRegistrationStarted> {\n this.assertInitialized();\n\n const context = await this.getAuthenticatedContext(input.sessionToken);\n\n await this.verifyCurrentPassword(context.user.id, input.currentPassword);\n\n await this.enforceRateLimit(\n \"passkey-register\",\n input.ipAddress,\n context.user.id,\n );\n\n const { provider, config } = this.getWebAuthn();\n\n const activeFactors = await this.findActiveFactors(context.user.id);\n\n const excludeCredentialIds = activeFactors\n .filter(\n (factor) => factor.type === \"passkey\" && factor.credentialId !== null,\n )\n .map((factor) => factor.credentialId!);\n\n const challenge = generateWebAuthnChallenge();\n\n let options: Readonly<Record<string, unknown>>;\n\n try {\n options = await provider.generateRegistrationOptions({\n challenge,\n rpId: config.rpId,\n rpName: config.rpName,\n userId: context.user.id,\n userName: context.user.email,\n excludeCredentialIds,\n });\n } catch (error) {\n throw new AuthProviderError(\n \"Could not create WebAuthn registration options.\",\n { cause: error },\n );\n }\n\n this.assertWebAuthnOptions(options, challenge);\n\n const created = await this.createChallengeRecord(\n \"passkey-registration\",\n context.user.id,\n activeFactors.map((factor) => factor.id),\n challenge,\n {\n factorLabel: validateLabel(input.label, \"Passkey\"),\n },\n );\n\n const audit = this.createAuditRecord(\n \"mfa-challenge-created\",\n context.user.id,\n {\n purpose: \"passkey-registration\",\n },\n context.session.id,\n );\n\n try {\n await this.storage.atomicBatch<MfaStorageRecord>([\n {\n type: \"insert\",\n collection: this.challengesCollection,\n record: created.record,\n },\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: audit,\n },\n ]);\n } catch (error) {\n throw new AuthProviderError(\n \"Could not persist the WebAuthn registration challenge.\",\n { cause: error },\n );\n }\n\n return {\n challengeId: created.record.id,\n challengeToken: created.token,\n options,\n expiresAt: created.record.expiresAt,\n };\n }\n\n async finishPasskeyRegistration(\n input: FinishPasskeyRegistrationInput,\n ): Promise<MfaEnrollmentCompleted> {\n this.assertInitialized();\n\n const context = await this.getAuthenticatedContext(input.sessionToken);\n\n await this.enforceRateLimit(\n \"verify-passkey-registration\",\n input.ipAddress,\n context.user.id,\n );\n\n const challenge = await this.getChallenge(\n input.challengeToken,\n \"passkey-registration\",\n );\n\n if (challenge.userId !== context.user.id || !challenge.webAuthnChallenge) {\n throw new AuthMfaChallengeError();\n }\n\n const { provider, config } = this.getWebAuthn();\n\n let verification: WebAuthnRegistrationVerification;\n\n try {\n verification = await provider.verifyRegistrationResponse({\n response: input.response,\n expectedChallenge: challenge.webAuthnChallenge,\n expectedOrigin: config.origin,\n expectedRpId: config.rpId,\n });\n } catch (error) {\n throw new AuthProviderError(\n \"WebAuthn registration verification failed.\",\n { cause: error },\n );\n }\n\n if (\n !verification.verified ||\n !verification.userVerified ||\n !verification.credential\n ) {\n return this.failChallenge(challenge, \"passkey-registration\");\n }\n\n const credential: WebAuthnRegistrationCredential = verification.credential;\n\n const responseCredentialId = getCredentialId(input.response);\n\n if (\n responseCredentialId !== credential.credentialId ||\n !credential.publicKey ||\n !Number.isSafeInteger(credential.counter) ||\n credential.counter < 0\n ) {\n return this.failChallenge(challenge, \"passkey-registration\");\n }\n\n const now = this.now();\n const factorId = generateAuthId(\"mfa-factor\");\n\n const factor: AuthMfaFactorStorageRecord = {\n id: factorId,\n userId: context.user.id,\n factorKey: `passkey:${credential.credentialId}`,\n type: \"passkey\",\n status: \"active\",\n label: challenge.factorLabel ?? \"Passkey\",\n secret: null,\n credentialId: credential.credentialId,\n publicKey: credential.publicKey,\n counter: credential.counter,\n transports: credential.transports ?? [],\n lastUsedStep: null,\n lastUsedAt: now,\n expiresAt: null,\n createdAt: now,\n updatedAt: now,\n revision: 1,\n };\n\n const recovery = await this.createRecoveryCodeBundle(\n context.user.id,\n factor.id,\n );\n\n const operations: StorageBatchOperation<MfaStorageRecord>[] = [\n {\n type: \"insert\",\n collection: this.factorsCollection,\n record: factor,\n },\n {\n type: \"update\",\n collection: this.challengesCollection,\n id: challenge.id,\n condition: {\n expectedRevision: challenge.revision,\n },\n changes: {\n consumedAt: now,\n updatedAt: now,\n },\n },\n ...recovery.records.map((record) => ({\n type: \"insert\" as const,\n collection: this.recoveryCodesCollection,\n record,\n })),\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: this.createAuditRecord(\n \"passkey-registered\",\n context.user.id,\n {\n transports: factor.transports.length,\n recoveryCodesGenerated: recovery.codes.length,\n },\n context.session.id,\n ),\n },\n ];\n\n try {\n await this.storage.atomicBatch<MfaStorageRecord>(operations);\n } catch (error) {\n if (\n error instanceof StorageDuplicateError ||\n error instanceof StorageConflictError\n ) {\n throw new AuthMfaFactorError(\n \"This passkey is already registered or the challenge was consumed.\",\n { cause: error },\n );\n }\n\n throw new AuthProviderError(\"Could not store the passkey.\", {\n cause: error,\n });\n }\n\n return {\n factor: toPublicFactor(factor),\n recoveryCodes: recovery.codes,\n };\n }\n\n async beginPasskeySignIn(\n input: BeginPasskeySignInInput,\n ): Promise<PasskeySignInStarted> {\n this.assertInitialized();\n\n await this.enforceRateLimit(\"passkey-sign-in\", input.ipAddress);\n\n const { provider, config } = this.getWebAuthn();\n\n const challenge = generateWebAuthnChallenge();\n\n let options: Readonly<Record<string, unknown>>;\n\n try {\n options = await provider.generateAuthenticationOptions({\n challenge,\n rpId: config.rpId,\n userVerification: \"required\",\n });\n } catch (error) {\n throw new AuthProviderError(\n \"Could not create passkey authentication options.\",\n { cause: error },\n );\n }\n\n this.assertWebAuthnOptions(options, challenge);\n\n const created = await this.createChallengeRecord(\n \"passkey-sign-in\",\n null,\n [],\n challenge,\n );\n\n const audit = this.createAuditRecord(\"mfa-challenge-created\", null, {\n purpose: \"passkey-sign-in\",\n });\n\n try {\n await this.storage.atomicBatch<MfaStorageRecord>([\n {\n type: \"insert\",\n collection: this.challengesCollection,\n record: created.record,\n },\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: audit,\n },\n ]);\n } catch (error) {\n throw new AuthProviderError(\n \"Could not persist the passkey sign-in challenge.\",\n { cause: error },\n );\n }\n\n return {\n challengeId: created.record.id,\n challengeToken: created.token,\n options,\n expiresAt: created.record.expiresAt,\n };\n }\n\n private async verifyPasskeyAssertion(input: {\n readonly response: unknown;\n readonly challenge: AuthMfaChallengeStorageRecord;\n readonly factor: AuthMfaFactorStorageRecord;\n }): Promise<number> {\n if (!input.challenge.webAuthnChallenge) {\n throw new AuthMfaChallengeError();\n }\n\n if (!input.factor.credentialId || !input.factor.publicKey) {\n throw new AuthMfaFactorError();\n }\n\n const { provider, config } = this.getWebAuthn();\n\n let verification: WebAuthnAuthenticationVerification;\n\n try {\n verification = await provider.verifyAuthenticationResponse({\n response: input.response,\n expectedChallenge: input.challenge.webAuthnChallenge,\n expectedOrigin: config.origin,\n expectedRpId: config.rpId,\n credential: {\n credentialId: input.factor.credentialId,\n publicKey: input.factor.publicKey,\n counter: input.factor.counter,\n },\n });\n } catch (error) {\n throw new AuthProviderError(\n \"WebAuthn authentication verification failed.\",\n { cause: error },\n );\n }\n\n if (\n !verification.verified ||\n !verification.userVerified ||\n verification.credentialId !== input.factor.credentialId ||\n !Number.isSafeInteger(verification.newCounter) ||\n verification.newCounter < 0\n ) {\n return this.failChallenge(input.challenge, \"passkey-authentication\");\n }\n\n if (\n (input.factor.counter > 0 || verification.newCounter > 0) &&\n verification.newCounter <= input.factor.counter\n ) {\n return this.failChallenge(input.challenge, \"passkey-counter-regression\");\n }\n\n return verification.newCounter;\n }\n\n async finishPasskeySignIn(input: {\n readonly challengeToken: string;\n readonly response: unknown;\n readonly ipAddress: string;\n }): Promise<AuthResult> {\n this.assertInitialized();\n\n const challenge = await this.getChallenge(\n input.challengeToken,\n \"passkey-sign-in\",\n );\n\n await this.enforceRateLimit(\"verify-passkey-sign-in\", input.ipAddress);\n\n const credentialId = getCredentialId(input.response);\n\n const factor = await this.storage.findOne<AuthMfaFactorStorageRecord>(\n this.factorsCollection,\n {\n where: {\n credentialId: whereEquals(credentialId),\n type: whereEquals(\"passkey\"),\n status: whereEquals(\"active\"),\n },\n },\n );\n\n if (!factor || !factor.userId) {\n return this.failChallenge(challenge, \"passkey-sign-in\");\n }\n\n const newCounter = await this.verifyPasskeyAssertion({\n response: input.response,\n challenge,\n factor,\n });\n\n return this.completeChallenge({\n challenge,\n userId: factor.userId,\n method: \"passkey\",\n auditAction: \"passkey-authenticated\",\n factorUpdate: {\n factor,\n changes: {\n counter: newCounter,\n lastUsedAt: this.now(),\n updatedAt: this.now(),\n },\n },\n });\n }\n\n async beginPasskeyMfa(\n challengeToken: string,\n ipAddress: string,\n ): Promise<PasskeyMfaStarted> {\n this.assertInitialized();\n\n const challenge = await this.getChallenge(challengeToken, \"sign-in\");\n\n if (!challenge.userId) {\n throw new AuthMfaChallengeError();\n }\n\n await this.enforceRateLimit(\"passkey-mfa\", ipAddress, challenge.userId);\n\n const factors = await this.findActiveFactors(challenge.userId);\n\n const passkeys = factors.filter(\n (factor) =>\n factor.type === \"passkey\" &&\n factor.credentialId !== null &&\n challenge.factorIds.includes(factor.id),\n );\n\n if (passkeys.length === 0) {\n throw new AuthMfaFactorError(\n \"No passkey is available for this challenge.\",\n );\n }\n\n const { provider, config } = this.getWebAuthn();\n const webAuthnChallenge = generateWebAuthnChallenge();\n\n let options: Readonly<Record<string, unknown>>;\n\n try {\n options = await provider.generateAuthenticationOptions({\n challenge: webAuthnChallenge,\n rpId: config.rpId,\n allowCredentialIds: passkeys.map((factor) => factor.credentialId!),\n userVerification: \"required\",\n });\n } catch (error) {\n throw new AuthProviderError(\"Could not create passkey MFA options.\", {\n cause: error,\n });\n }\n\n this.assertWebAuthnOptions(options, webAuthnChallenge);\n\n const updated = await this.storage.update<AuthMfaChallengeStorageRecord>(\n this.challengesCollection,\n challenge.id,\n {\n webAuthnChallenge,\n webAuthnFactorIds: passkeys.map((factor) => factor.id),\n updatedAt: this.now(),\n },\n {\n expectedRevision: challenge.revision,\n },\n );\n\n if (!updated) {\n throw new AuthMfaChallengeError();\n }\n\n return {\n options,\n expiresAt: challenge.expiresAt,\n };\n }\n\n async finishPasskeyMfa(input: PasskeyMfaCompletedInput): Promise<AuthResult> {\n this.assertInitialized();\n\n const challenge = await this.getChallenge(input.challengeToken, \"sign-in\");\n\n if (!challenge.userId || !challenge.webAuthnChallenge) {\n throw new AuthMfaChallengeError();\n }\n\n await this.enforceRateLimit(\n \"verify-passkey-mfa\",\n input.ipAddress,\n challenge.userId,\n );\n\n const credentialId = getCredentialId(input.response);\n\n const factor = await this.storage.findOne<AuthMfaFactorStorageRecord>(\n this.factorsCollection,\n {\n where: {\n credentialId: whereEquals(credentialId),\n type: whereEquals(\"passkey\"),\n status: whereEquals(\"active\"),\n userId: whereEquals(challenge.userId),\n },\n },\n );\n\n if (!factor || !challenge.webAuthnFactorIds.includes(factor.id)) {\n return this.failChallenge(challenge, \"passkey-mfa\");\n }\n\n const newCounter = await this.verifyPasskeyAssertion({\n response: input.response,\n challenge,\n factor,\n });\n\n return this.completeChallenge({\n challenge,\n userId: challenge.userId,\n method: \"passkey\",\n factorUpdate: {\n factor,\n changes: {\n counter: newCounter,\n lastUsedAt: this.now(),\n updatedAt: this.now(),\n },\n },\n });\n }\n}\n"],"mappings":";;;;;;AAgIA,MAAM,qCAAqC;AAC3C,MAAM,8BAA8B;AACpC,MAAM,qCAAqC;AAE3C,MAAM,eAAe;CACnB,QAAQ;CACR,eAAe;CACf,QAAQ;AACV;AAEA,MAAM,eAAwB,WAAc;CAC1C,UAAU;CACV;AACF;AAEA,MAAM,gBAAgB,YAA6C;CACjE,IAAI,OAAO;CACX,OAAO,OAAO;CACd,iBAAiB,OAAO;CACxB,MAAM,OAAO;CACb,OAAO,OAAO;CACd,WAAW,OAAO;CAClB,WAAW,OAAO;AACpB;AAEA,MAAM,mBAAmB,WAAuC;CAC9D,IAAI,OAAO,SAAS,mBAAmB,CAAC,OAAO,QAC7C,MAAM,IAAIA,oBAAAA,iBACR,kDACF;CAGF,OAAO;EACL,IAAI,OAAO;EACX,QAAQ,OAAO;EACf,MAAM;EACN,WAAW,OAAO;EAClB,YAAY,OAAO;EACnB,WAAW,OAAO;EAClB,WAAW,OAAO;CACpB;AACF;AAEA,MAAM,kBAAkB,YAAuD;CAC7E,IAAI,OAAO;CACX,MAAM,OAAO;CACb,QAAQ,OAAO;CACf,OAAO,OAAO;CACd,WAAW,OAAO;CAClB,YAAY,OAAO;AACrB;AAEA,MAAM,yBAAyB,UAAiC;CAC9D,IAAI,OAAO,UAAU,UACnB,OAAO;CAGT,MAAM,aAAa,MAAM,QAAQ,UAAU,EAAE,CAAC,CAAC,YAAY;CAE3D,OAAO,iBAAiB,KAAK,UAAU,IAAI,aAAa;AAC1D;AAEA,MAAM,6BAAqC;CAGzC,OAFYC,0BAAAA,wBAAwB,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,YAE1C,CAAC,CAAC,MAAM,OAAO,CAAC,CAAE,KAAK,GAAG;AACrC;AAEA,MAAM,YAAY,UAChB,OAAO,UAAU,YAAY,UAAU;AAEzC,MAAM,mBAAmB,aAA8B;CACrD,IACE,CAAC,SAAS,QAAQ,KAClB,OAAO,SAAS,OAAO,YACvB,SAAS,GAAG,WAAW,KACvB,SAAS,GAAG,SAAS,MAErB,MAAM,IAAIC,oBAAAA,sBAAsB;CAGlC,OAAO,SAAS;AAClB;;;;;AAMA,MAAM,mBAAmB,UAA8B;CACrD,MAAM,WACJ;CAEF,IAAI,SAAS;CACb,IAAI,QAAQ;CAEZ,OAAO,QAAQ,IAAI,MAAM,QAAQ;EAC/B,MAAM,IAAI,MAAM;EAChB,MAAM,IAAI,MAAM;EAChB,MAAM,IAAI,MAAM;EAEhB,UAAU,SAAS,OAAO,KAAK,CAAC;EAChC,UAAU,SAAS,QAAS,IAAI,MAAM,IAAM,KAAK,CAAE;EACnD,UAAU,SAAS,QAAS,IAAI,OAAO,IAAM,KAAK,CAAE;EACpD,UAAU,SAAS,OAAO,IAAI,EAAE;CAClC;CAEA,IAAI,QAAQ,MAAM,QAAQ;EACxB,MAAM,IAAI,MAAM;EAEhB,UAAU,SAAS,OAAO,KAAK,CAAC;EAEhC,IAAI,QAAQ,MAAM,QAAQ;GACxB,MAAM,IAAI,MAAM;GAEhB,UAAU,SAAS,QAAS,IAAI,MAAM,IAAM,KAAK,CAAE;GACnD,UAAU,SAAS,QAAQ,IAAI,OAAO,CAAC;EACzC,OACE,UAAU,SAAS,QAAQ,IAAI,MAAM,CAAC;CAE1C;CAEA,OAAO;AACT;AAEA,MAAM,kCAA0C;CAC9C,MAAM,YAAY,WAAW;CAE7B,IAAI,CAAC,WAAW,iBACd,MAAM,IAAIC,oBAAAA,uBACR,6DACF;CAGF,MAAM,wBAAQ,IAAI,WAAW,EAAE;CAC/B,UAAU,gBAAgB,KAAK;CAE/B,OAAO,gBAAgB,KAAK;AAC9B;AAEA,MAAM,iBAAiB,OAA2B,aAA6B;CAC7E,MAAM,QAAQ,UAAU,KAAA,IAAY,WAAW,MAAM,KAAK;CAE1D,IAAI,OAAO,UAAU,YAAY,MAAM,WAAW,KAAK,MAAM,SAAS,IACpE,MAAM,IAAIC,oBAAAA,oBACR,2EACF;CAGF,OAAO;AACT;AAWA,IAAa,wBAAb,MAA6D;CAC3D;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CAEA;CACA;CACA;CAEA;CACA;CACA;CACA;CAEA;CAMA;CAMA;CACA;CAEA,cAAsB;CACtB,wBAAsD;CAEtD,YACE,cACA,SACA;EACA,KAAK,UAAU,aAAa;EAC5B,KAAK,WAAW,aAAa;EAC7B,KAAK,iBAAiB,aAAa;EACnC,KAAK,oBAAoB,aAAa;EACtC,KAAK,kBAAkB,aAAa;EACpC,KAAK,mBAAmB,aAAa,oBAAoB;EAEzD,IACE,OAAO,QAAQ,uBAAuB,YACtC,QAAQ,mBAAmB,SAAS,IAEpC,MAAM,IAAID,oBAAAA,uBACR,yDACF;EAGF,KAAK,qBAAqB,QAAQ;EAElC,KAAK,kBAAkB,QAAQ,mBAAA;EAE/B,KAAK,wBACH,QAAQ,yBAAA;EAEV,KAAK,kBAAkB,QAAQ,mBAAA;EAE/B,KAAK,oBACH,QAAQ,qBAAA;EAEV,KAAK,uBACH,QAAQ,wBAAA;EAEV,KAAK,0BACH,QAAQ,2BAAA;EAEV,KAAK,2BACH,QAAQ,4BAA4B;EAEtC,KAAK,oBACH,QAAQ,qBAAqB;EAE/B,KAAK,SAAS,QAAQ,UAAU;EAEhC,KAAK,OAAO;GACV,GAAG;GACH,GAAG,QAAQ;EACb;EAEA,KAAK,WAAW,QAAQ,YAAY;EAEpC,KAAK,uBAAuB,QAAQ,wBAAwB;EAE5D,KAAK,QAAQ,QAAQ,gCAAgB,IAAI,KAAK;EAE9C,IACE,CAAC,OAAO,UAAU,KAAK,wBAAwB,KAC/C,KAAK,2BAA2B,MAChC,KAAK,2BAA2B,KAEhC,MAAM,IAAIA,oBAAAA,uBACR,4DACF;EAGF,IACE,CAAC,OAAO,UAAU,KAAK,iBAAiB,KACxC,KAAK,oBAAoB,KACzB,KAAK,oBAAoB,IAEzB,MAAM,IAAIA,oBAAAA,uBACR,+CACF;EAGF,IACE,CAAC,OAAO,UAAU,KAAK,KAAK,MAAM,KAClC,KAAK,KAAK,SAAS,KACnB,KAAK,KAAK,SAAS,KACnB,CAAC,OAAO,UAAU,KAAK,KAAK,aAAa,KACzC,KAAK,KAAK,gBAAgB,MAC1B,KAAK,KAAK,gBAAgB,OAC1B,CAAC,OAAO,UAAU,KAAK,KAAK,MAAM,KAClC,KAAK,KAAK,SAAS,KACnB,KAAK,KAAK,SAAS,GAEnB,MAAM,IAAIA,oBAAAA,uBAAuB,oCAAoC;EAGvE,IAAI,QAAQ,KAAK,QAAQ,MAAM,QAAQ,KAAK,gBAAgB,GAC1D,MAAM,IAAIA,oBAAAA,uBACR,wEACF;EAGF,IAAI,KAAK,UACP,KAAK,8BAA8B,KAAK,QAAQ;CAEpD;CAEA,MAAM,iBAAiB,QAA+B;EACpD,KAAK,kBAAkB;EAEvB,IAAI,OAAO,WAAW,YAAY,OAAO,KAAK,CAAC,CAAC,WAAW,GACzD,MAAM,IAAIC,oBAAAA,oBAAoB,8BAA8B;EAG9D,MAAM,CAAC,kBAAkB,wBAAwB,MAAM,QAAQ,IAAI,CACjE,KAAK,QAAQ,SACX,KAAK,sBACL,EACE,OAAO;GACL,QAAQ,YAAY,MAAM;GAC1B,SAAS,YAAY,SAAS;GAC9B,YAAY,YAAY,IAAI;EAC9B,EACF,CACF,GACA,KAAK,QAAQ,SACX,KAAK,sBACL,EACE,OAAO;GACL,QAAQ,YAAY,MAAM;GAC1B,SAAS,YAAY,YAAY;GACjC,YAAY,YAAY,IAAI;EAC9B,EACF,CACF,CACF,CAAC;EAED,MAAM,aAAa,CAAC,GAAG,kBAAkB,GAAG,oBAAoB;EAEhE,IAAI,WAAW,WAAW,GACxB;EAGF,MAAM,MAAM,KAAK,IAAI;EAErB,MAAM,aACJ,WAAW,KAAK,eAAe;GAC7B,MAAM;GACN,YAAY,KAAK;GACjB,IAAI,UAAU;GACd,WAAW,EACT,kBAAkB,UAAU,SAC9B;GACA,SAAS;IACP,YAAY;IACZ,WAAW;GACb;EACF,EAAE;EAEJ,IAAI;GACF,MAAM,KAAK,QAAQ,YAA8B,UAAU;EAC7D,SAAS,OAAO;GACd,MAAM,IAAIC,oBAAAA,kBAAkB,4CAA4C,EACtE,OAAO,MACT,CAAC;EACH;CACF;CAEA,8BAAsC,QAI7B;EACP,IAAI;EAEJ,IAAI;GACF,SAAS,IAAI,IAAI,OAAO,MAAM;EAChC,QAAQ;GACN,MAAM,IAAIF,oBAAAA,uBAAuB,sCAAsC;EACzE;EAEA,MAAM,cACJ,OAAO,aAAa,eACpB,OAAO,aAAa,eACpB,OAAO,aAAa;EAEtB,IACE,OAAO,WAAW,OAAO,UACxB,OAAO,aAAa,YAAY,CAAC,aAElC,MAAM,IAAIA,oBAAAA,uBACR,4EACF;EAGF,IACE,OAAO,aAAa,OAAO,QAC3B,CAAC,OAAO,SAAS,SAAS,IAAI,OAAO,MAAM,GAE3C,MAAM,IAAIA,oBAAAA,uBACR,oEACF;EAGF,IAAI,OAAO,OAAO,KAAK,CAAC,CAAC,WAAW,KAAK,OAAO,KAAK,KAAK,CAAC,CAAC,WAAW,GACrE,MAAM,IAAIA,oBAAAA,uBACR,0CACF;CAEJ;CAEA,MAAM,aAA4B;EAChC,IAAI,CAAC,KAAK,uBAAuB;GAC/B,MAAM,kBAAkB,YAAY;IAClC,IACE,CAAC,KAAK,QAAQ,aAAa,qBAC3B,CAAC,KAAK,QAAQ,aAAa,qBAC3B,CAAC,KAAK,QAAQ,aAAa,aAE3B,MAAM,IAAIA,oBAAAA,uBACR,yEACF;IAGF,MAAMG,qBAAAA,sBAAsB,KAAK,SAAS;KACxC,iBAAiB,KAAK;KACtB,uBAAuB,KAAK;KAC5B,iBAAiB,KAAK;KACtB,mBAAmB,KAAK;KACxB,sBAAsB,KAAK;KAC3B,yBAAyB,KAAK;IAChC,CAAC;IAED,KAAK,cAAc;GACrB,EAAA,CAAG;GAEH,KAAK,wBAAwB,eAAe,OAAO,UAAU;IAC3D,KAAK,wBAAwB;IAC7B,KAAK,cAAc;IACnB,MAAM;GACR,CAAC;EACH;EAEA,OAAO,KAAK;CACd;CAEA,oBAAkC;EAChC,IAAI,CAAC,KAAK,aACR,MAAM,IAAIH,oBAAAA,uBACR,6CACF;CAEJ;CAEA,MAAoB;EAClB,MAAM,QAAQ,KAAK,MAAM;EAEzB,IAAI,EAAE,iBAAiB,SAAS,CAAC,OAAO,SAAS,MAAM,QAAQ,CAAC,GAC9D,MAAM,IAAIA,oBAAAA,uBACR,oDACF;EAGF,OAAO;CACT;CAEA,MAAc,iBACZ,QACA,WACA,QACe;EACf,IACE,OAAO,cAAc,YACrB,UAAU,KAAK,CAAC,CAAC,WAAW,KAC5B,UAAU,KAAK,CAAC,CAAC,SAAS,KAE1B,MAAM,IAAIC,oBAAAA,oBAAoB,wCAAwC;EAKxE,MAAM,OAFwB,OAAO,SAAS,QAEb,IAC7B;GAAE,OAAO;GAAG,eAAe;EAAI,IAC/B;GAAE,OAAO;GAAI,eAAe;EAAI;EAEpC,MAAM,SAGA,CACJ;GACE,MAAM;GACN,OAAO,UAAU,KAAK;EACxB,GACA,GAAI,SACA,CACE;GACE,MAAM;GACN,OAAO;EACT,CACF,IACA,CAAC,CACP;EAEA,KAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,MAAM,MAAMG,0BAAAA,mBAChB,KAAK,oBACL,YAAY,OAAO,GAAG,MAAM,KAAK,GAAG,MAAM,OAC5C;GAEA,IAAI;GAEJ,IAAI;IACF,SAAS,MAAM,KAAK,kBAAkB,QAAQ;KAC5C;KACA,OAAO,KAAK;KACZ,eAAe,KAAK;IACtB,CAAC;GACH,SAAS,OAAO;IACd,MAAM,IAAIF,oBAAAA,kBAAkB,wCAAwC,EAClE,OAAO,MACT,CAAC;GACH;GAEA,IACE,CAAC,UACD,OAAO,OAAO,YAAY,aAC1B,EAAE,OAAO,mBAAmB,SAC5B,CAAC,OAAO,SAAS,OAAO,QAAQ,QAAQ,CAAC,GAEzC,MAAM,IAAIA,oBAAAA,kBACR,kDACF;GAGF,IAAI,CAAC,OAAO,SACV,MAAM,IAAIG,oBAAAA,mBAAmB,OAAO,OAAO;EAE/C;CACF;CAEA,kBACE,QACA,QACA,UACA,YAA2B,MACE;EAC7B,MAAM,MAAM,KAAK,IAAI;EAErB,OAAO;GACL,IAAIC,0BAAAA,eAAe,YAAY;GAC/B;GACA;GACA;GACA;GACA,WAAW;GACX,WAAW;GACX,UAAU;EACZ;CACF;CAEA,MAAc,wBACZ,cAC+B;EAC/B,IAAI,OAAO,iBAAiB,YAAY,aAAa,WAAW,GAC9D,MAAM,IAAIT,oBAAAA,iBAAiB,uCAAuC;EAGpE,IAAI;EAEJ,IAAI;GACF,UAAU,MAAM,KAAK,SAAS,SAAS,YAAY;EACrD,SAAS,OAAO;GACd,MAAM,IAAIA,oBAAAA,iBAAiB,mCAAmC,EAC5D,OAAO,MACT,CAAC;EACH;EAEA,IAAI,CAAC,WAAW,QAAQ,SAAS,mBAAmB,CAAC,QAAQ,QAC3D,MAAM,IAAIA,oBAAAA,iBAAiB,uCAAuC;EAGpE,MAAM,OAAO,MAAM,KAAK,QAAQ,SAC9B,KAAK,iBACL,QAAQ,MACV;EAEA,IAAI,CAAC,QAAQ,KAAK,kBAAkB,MAClC,MAAM,IAAIA,oBAAAA,iBAAiB,2CAA2C;EAGxE,IACE,KAAK,yBACL,QAAQ,UAAU,QAAQ,KAAK,KAAK,sBAAsB,QAAQ,GAElE,MAAM,IAAIA,oBAAAA,iBAAiB,iCAAiC;EAG9D,IAAI,KAAK,wBAAwB,CAAC,KAAK,iBACrC,MAAM,IAAIU,oBAAAA,mCAAmC;EAG/C,OAAO;GAAE;GAAS;EAAK;CACzB;CAEA,MAAc,sBACZ,QACA,UACe;EACf,IAAI,OAAO,aAAa,YAAY,SAAS,WAAW,GACtD,MAAM,IAAIN,oBAAAA,oBACR,sDACF;EAGF,MAAM,aACJ,MAAM,KAAK,QAAQ,QACjB,KAAK,uBACL,EACE,OAAO;GACL,QAAQ,YAAY,MAAM;GAC1B,MAAM,YAAY,UAAU;EAC9B,EACF,CACF;EAEF,IAAI,CAAC,cAAc,WAAW,cAAc,MAC1C,MAAM,IAAIO,oBAAAA,4BAA4B;EAGxC,IAAI;EAEJ,IAAI;GACF,QAAQ,MAAM,KAAK,eAAe,OAChC,UACA,WAAW,YACb;EACF,SAAS,OAAO;GACd,MAAM,IAAIN,oBAAAA,kBAAkB,iCAAiC,EAC3D,OAAO,MACT,CAAC;EACH;EAEA,IAAI,CAAC,OACH,MAAM,IAAIM,oBAAAA,4BAA4B;CAE1C;CAEA,MAAc,kBACZ,QACgD;EAChD,OAAO,KAAK,QAAQ,SAClB,KAAK,mBACL,EACE,OAAO;GACL,QAAQ,YAAY,MAAM;GAC1B,QAAQ,YAAY,QAAQ;EAC9B,EACF,CACF;CACF;CAEA,MAAc,oBACZ,WAIQ;EACR,IACG,UAAU,YAAY,aAAa,UAAU,YAAY,gBAC1D,CAAC,UAAU,UACX,UAAU,iBAAiB,QAC3B,UAAU,uBAAuB,MAEjC,OAAO;EAGT,MAAM,OAAO,MAAM,KAAK,QAAQ,SAC9B,KAAK,iBACL,UAAU,MACZ;EAEA,MAAM,aACJ,MAAM,KAAK,QAAQ,QACjB,KAAK,uBACL,EACE,OAAO;GACL,QAAQ,YAAY,UAAU,MAAM;GACpC,MAAM,YAAY,UAAU;EAC9B,EACF,CACF;EAEF,IACE,CAAC,QACD,KAAK,kBAAkB,QACvB,KAAK,aAAa,UAAU,gBAC5B,CAAC,cACD,WAAW,cAAc,QACzB,WAAW,aAAa,UAAU,oBAElC,OAAO;EAGT,OAAO;GAAE;GAAM;EAAW;CAC5B;CAEA,MAAc,sBACZ,SACA,QACA,WACA,oBAAmC,MACnC,WAII,CAAC,GAIJ;EACD,MAAM,MAAM,KAAK,IAAI;EACrB,MAAM,QAAQV,0BAAAA,wBAAwB;EAsBtC,OAAO;GAAE,QAAA;IAnBP,IAAIQ,0BAAAA,eAAe,eAAe;IAClC;IACA;IACA,WAAW,MAAMG,0BAAAA,cAAc,KAAK;IACpC;IACA,mBAAmB,CAAC;IACpB;IACA,aAAa,SAAS,eAAe;IACrC,cAAc,SAAS,gBAAgB;IACvC,oBAAoB,SAAS,sBAAsB;IACnD,cAAc;IACd,iBAAiB;IACjB,WAAW,IAAI,KAAK,IAAI,QAAQ,IAAI,KAAK,2BAA2B,GAAI;IACxE,YAAY;IACZ,WAAW;IACX,WAAW;IACX,UAAU;GAGE;GAAG;EAAM;CACzB;CAEA,MAAc,aACZ,OACA,SACwC;EACxC,IAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GAChD,MAAM,IAAIV,oBAAAA,sBAAsB;EAGlC,MAAM,YAAY,MAAMU,0BAAAA,cAAc,KAAK;EAE3C,MAAM,SAAS,MAAM,KAAK,QAAQ,QAChC,KAAK,sBACL,EACE,OAAO,EACL,WAAW,YAAY,SAAS,EAClC,EACF,CACF;EAEA,IACE,CAAC,UACD,OAAO,YAAY,WACnB,OAAO,eAAe,QACtB,OAAO,UAAU,QAAQ,KAAK,KAAK,IAAI,CAAC,CAAC,QAAQ,KACjD,OAAO,gBAAgB,OAAO,iBAE9B,MAAM,IAAIV,oBAAAA,sBAAsB;EAGlC,KACG,YAAY,aAAa,YAAY,iBACtC,CAAE,MAAM,KAAK,oBAAoB,MAAM,GAEvC,MAAM,IAAIA,oBAAAA,sBAAsB;EAGlC,OAAO;CACT;CAEA,MAAM,sBAAsB,OAGa;EACvC,KAAK,kBAAkB;EAEvB,MAAM,KAAK,iBAAiB,aAAa,MAAM,WAAW,MAAM,MAAM;EAEtE,MAAM,UAAU,MAAM,KAAK,kBAAkB,MAAM,MAAM;EAEzD,IAAI,QAAQ,WAAW,GACrB,OAAO;EAGT,MAAM,oBACJ,MAAM,KAAK,QAAQ,MACjB,KAAK,yBACL,EACE,OAAO;GACL,QAAQ,YAAY,MAAM,MAAM;GAChC,YAAY,YAAY,IAAI;EAC9B,EACF,CACF;EAEF,MAAM,mCAAmB,IAAI,IAA0C;EAEvE,KAAK,MAAM,UAAU,SAAS;GAC5B,IAAI,OAAO,SAAS,QAClB,iBAAiB,IAAI,MAAM;GAG7B,IAAI,OAAO,SAAS,WAClB,iBAAiB,IAAI,SAAS;EAElC;EAEA,IAAI,oBAAoB,GACtB,iBAAiB,IAAI,eAAe;EAGtC,MAAM,OAAO,MAAM,KAAK,QAAQ,SAC9B,KAAK,iBACL,MAAM,MACR;EAEA,MAAM,aACJ,MAAM,KAAK,QAAQ,QACjB,KAAK,uBACL,EACE,OAAO;GACL,QAAQ,YAAY,MAAM,MAAM;GAChC,MAAM,YAAY,UAAU;EAC9B,EACF,CACF;EAEF,IACE,CAAC,QACD,KAAK,kBAAkB,QACvB,CAAC,cACD,WAAW,cAAc,MAEzB,MAAM,IAAIA,oBAAAA,sBAAsB;EAGlC,MAAM,UAAU,MAAM,KAAK,sBACzB,WACA,MAAM,QACN,QAAQ,KAAK,WAAW,OAAO,EAAE,GACjC,MACA;GACE,cAAc,KAAK;GACnB,oBAAoB,WAAW;EACjC,CACF;EAEA,MAAM,QAAQ,KAAK,kBACjB,yBACA,MAAM,QACN,EACE,SAAS,QAAQ,OACnB,CACF;EAEA,IAAI;GACF,MAAM,KAAK,QAAQ,YAA8B,CAC/C;IACE,MAAM;IACN,YAAY,KAAK;IACjB,QAAQ,QAAQ;GAClB,GACA;IACE,MAAM;IACN,YAAY,KAAK;IACjB,QAAQ;GACV,CACF,CAAC;EACH,SAAS,OAAO;GACd,MAAM,IAAIG,oBAAAA,kBAAkB,uCAAuC,EACjE,OAAO,MACT,CAAC;EACH;EAEA,OAAO;GACL,QAAQ;GACR,aAAa,QAAQ,OAAO;GAC5B,gBAAgB,QAAQ;GACxB,WAAW,QAAQ,OAAO;GAC1B,kBAAkB,CAAC,GAAG,gBAAgB;EACxC;CACF;CAEA,MAAM,0BAA0B,OAGY;EAC1C,KAAK,kBAAkB;EAEvB,MAAM,KAAK,iBACT,wBACA,MAAM,WACN,MAAM,MACR;EAIA,KAAI,MAFwB,KAAK,kBAAkB,MAAM,MAAM,EAAA,CAE7C,SAAS,GACzB,MAAM,IAAIH,oBAAAA,sBAAsB;EAGlC,MAAM,OAAO,MAAM,KAAK,QAAQ,SAC9B,KAAK,iBACL,MAAM,MACR;EAEA,MAAM,aACJ,MAAM,KAAK,QAAQ,QACjB,KAAK,uBACL,EACE,OAAO;GACL,QAAQ,YAAY,MAAM,MAAM;GAChC,MAAM,YAAY,UAAU;EAC9B,EACF,CACF;EAEF,IACE,CAAC,QACD,KAAK,kBAAkB,QACvB,CAAC,cACD,WAAW,cAAc,MAEzB,MAAM,IAAIA,oBAAAA,sBAAsB;EAGlC,MAAM,UAAU,MAAM,KAAK,sBACzB,cACA,MAAM,QACN,CAAC,GACD,MACA;GACE,cAAc,KAAK;GACnB,oBAAoB,WAAW;EACjC,CACF;EAEA,MAAM,QAAQ,KAAK,kBACjB,yBACA,MAAM,QACN,EACE,SAAS,sBACX,CACF;EAEA,IAAI;GACF,MAAM,KAAK,QAAQ,YAA8B,CAC/C;IACE,MAAM;IACN,YAAY,KAAK;IACjB,QAAQ,QAAQ;GAClB,GACA;IACE,MAAM;IACN,YAAY,KAAK;IACjB,QAAQ;GACV,CACF,CAAC;EACH,SAAS,OAAO;GACd,MAAM,IAAIG,oBAAAA,kBACR,2DACA,EAAE,OAAO,MAAM,CACjB;EACF;EAEA,MAAM,mBAAoD,KAAK,WAC3D,CAAC,QAAQ,SAAS,IAClB,CAAC,MAAM;EAEX,OAAO;GACL,QAAQ;GACR,aAAa,QAAQ,OAAO;GAC5B,gBAAgB,QAAQ;GACxB,WAAW,QAAQ,OAAO;GAC1B;EACF;CACF;CAEA,MAAM,oBAAoB,OAA0C;EAClE,KAAK,kBAAkB;EAEvB,IAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GAChD,OAAO;EAGT,MAAM,YAAY,MAAMO,0BAAAA,cAAc,KAAK;EAE3C,MAAM,YAAY,MAAM,KAAK,QAAQ,QACnC,KAAK,sBACL,EACE,OAAO,EACL,WAAW,YAAY,SAAS,EAClC,EACF,CACF;EAEA,IACE,CAAC,aACA,UAAU,YAAY,aAAa,UAAU,YAAY,gBAC1D,CAAC,UAAU,UACX,UAAU,eAAe,QACzB,UAAU,UAAU,QAAQ,KAAK,KAAK,IAAI,CAAC,CAAC,QAAQ,KACpD,UAAU,gBAAgB,UAAU,iBAEpC,OAAO;EAGT,IAAI,CAAE,MAAM,KAAK,oBAAoB,SAAS,GAC5C,OAAO;EAGT,IAAI,UAAU,YAAY,cAAc;GACtC,MAAM,mBAAoD,KAAK,WAC3D,CAAC,QAAQ,SAAS,IAClB,CAAC,MAAM;GAEX,OAAO;IACL,QAAQ;IACR,QAAQ,UAAU;IAClB,aAAa,UAAU;IACvB,WAAW,UAAU;IACrB;GACF;EACF;EAEA,OAAO;GACL,QAAQ;GACR,QAAQ,UAAU;GAClB,aAAa,UAAU;GACvB,WAAW,UAAU;EACvB;CACF;CAEA,MAAM,oBACJ,gBAC4C;EAC5C,KAAK,kBAAkB;EAEvB,MAAM,YAAY,MAAM,KAAK,aAAa,gBAAgB,SAAS;EAEnE,IAAI,CAAC,UAAU,QACb,MAAM,IAAIV,oBAAAA,sBAAsB;EAOlC,MAAM,oBAAmB,MAJH,KAAK,kBAAkB,UAAU,MAAM,EAAA,CAI5B,QAAQ,WACvC,UAAU,UAAU,SAAS,OAAO,EAAE,CACxC;EAEA,MAAM,4BAAY,IAAI,IAA4B;EAElD,KAAK,MAAM,UAAU,kBACnB,UAAU,IAAI,OAAO,IAAI;EAc3B,KACE,MAXM,KAAK,QAAQ,SACjB,KAAK,yBACL,EACE,OAAO;GACL,QAAQ,YAAY,UAAU,MAAM;GACpC,YAAY,YAAY,IAAI;EAC9B,EACF,CACF,EAAA,CAGc,MAAM,WAClB,UAAU,UAAU,SAAS,OAAO,QAAQ,CAC9C,GAEA,UAAU,IAAI,eAAe;EAG/B,OAAO,CAAC,GAAG,SAAS;CACtB;CAEA,MAAc,cACZ,WACA,QACgB;EAChB,MAAM,MAAM,KAAK,IAAI;EACrB,MAAM,cAAc,UAAU,eAAe;EAE7C,MAAM,aAAwD,CAC5D;GACE,MAAM;GACN,YAAY,KAAK;GACjB,IAAI,UAAU;GACd,WAAW,EACT,kBAAkB,UAAU,SAC9B;GACA,SAAS;IACP,cAAc;IACd,YAAY,eAAe,UAAU,kBAAkB,MAAM;IAC7D,WAAW;GACb;EACF,GACA;GACE,MAAM;GACN,YAAY,KAAK;GACjB,QAAQ,KAAK,kBACX,wBACA,UAAU,QACV;IACE;IACA,SAAS;GACX,CACF;EACF,CACF;EAEA,IAAI;GACF,MAAM,KAAK,QAAQ,YAA8B,UAAU;EAC7D,SAAS,OAAO;GACd,MAAM,IAAIA,oBAAAA,sBAAsB,EAAE,OAAO,MAAM,CAAC;EAClD;EAEA,MAAM,IAAIA,oBAAAA,sBAAsB;CAClC;CAEA,MAAc,yBACZ,QACA,UACA,QAAQ,OACqB;EAC7B,IAAI,CAAC,OAWC;OAAA,MAVmB,KAAK,QAAQ,MAClC,KAAK,yBACL,EACE,OAAO;IACL,QAAQ,YAAY,MAAM;IAC1B,YAAY,YAAY,IAAI;GAC9B,EACF,CACF,IAEe,GACb,OAAO;IAAE,OAAO,CAAC;IAAG,SAAS,CAAC;GAAE;EAAA;EAIpC,MAAM,QAAkB,CAAC;EACzB,MAAM,UAA2C,CAAC;EAClD,MAAM,MAAM,KAAK,IAAI;EAErB,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,mBAAmB,SAAS;GAC3D,MAAM,OAAO,qBAAqB;GAClC,MAAM,aAAa,sBAAsB,IAAI;GAE7C,MAAM,KAAK,IAAI;GAEf,QAAQ,KAAK;IACX,IAAIO,0BAAAA,eAAe,eAAe;IAClC;IACA;IACA,UAAU,MAAMG,0BAAAA,cAAc,UAAU;IACxC,YAAY;IACZ,WAAW;IACX,WAAW;IACX,UAAU;GACZ,CAAC;EACH;EAEA,OAAO;GAAE;GAAO;EAAQ;CAC1B;CAEA,MAAc,sBACZ,QACA,MAC+C;EAC/C,MAAM,aAAa,sBAAsB,IAAI;EAE7C,IAAI,CAAC,YACH,OAAO;EAGT,MAAM,WAAW,MAAMA,0BAAAA,cAAc,UAAU;EAE/C,OAAO,KAAK,QAAQ,QAClB,KAAK,yBACL,EACE,OAAO;GACL,QAAQ,YAAY,MAAM;GAC1B,UAAU,YAAY,QAAQ;GAC9B,YAAY,YAAY,IAAI;EAC9B,EACF,CACF;CACF;CAEA,MAAc,0BACZ,QACA,UAIC;EACD,MAAM,OAAO,MAAM,KAAK,QAAQ,SAC9B,KAAK,iBACL,MACF;EAEA,IAAI,CAAC,QAAQ,KAAK,kBAAkB,MAClC,MAAM,IAAIV,oBAAAA,sBAAsB;EAGlC,IAAI,KAAK,wBAAwB,CAAC,KAAK,iBACrC,MAAM,IAAIQ,oBAAAA,mCAAmC;EAG/C,IAAI;GACF,MAAM,UAAU,MAAM,KAAK,SAAS,OAAO;IACzC;IACA,MAAM;IACN;GACF,CAAC;GAED,OAAO;IACL,WAAW,QAAQ,QAAQ;IAC3B,QAAQ;KACN,MAAM,aAAa,IAAI;KACvB,SAAS,gBAAgB,QAAQ,OAAO;KACxC,OAAO,QAAQ;IACjB;GACF;EACF,SAAS,OAAO;GACd,MAAM,IAAIV,oBAAAA,iBACR,sDACA,EAAE,OAAO,MAAM,CACjB;EACF;CACF;;;;;;;CAQA,MAAc,kBAAkB,OASR;EACtB,MAAM,MAAM,KAAK,IAAI;EAErB,MAAM,2BACJ,MAAM,UAAU,YAAY,aAC5B,MAAM,UAAU,YAAY;EAE9B,MAAM,mBAAmB,2BACrB,MAAM,KAAK,oBAAoB,MAAM,SAAS,IAC9C;EAEJ,IAAI,4BAA4B,CAAC,kBAC/B,MAAM,IAAIE,oBAAAA,sBAAsB;EAGlC,MAAM,gBAAgB,MAAM,KAAK,0BAA0B,MAAM,QAAQ;GACvE,sBAAsB,MAAM;GAC5B,eAAe,IAAI,YAAY;EACjC,CAAC;EAED,MAAM,aAAwD,CAC5D;GACE,MAAM;GACN,YAAY,KAAK;GACjB,IAAI,MAAM,UAAU;GACpB,WAAW,EACT,kBAAkB,MAAM,UAAU,SACpC;GACA,SAAS;IACP,YAAY;IACZ,WAAW;GACb;EACF,CACF;EAEA,IAAI,MAAM,cACR,WAAW,KAAK;GACd,MAAM;GACN,YAAY,KAAK;GACjB,QAAQ,MAAM;EAChB,CAAC;EAGH,IAAI,MAAM,cACR,WAAW,KAAK;GACd,MAAM;GACN,YAAY,KAAK;GACjB,IAAI,MAAM,aAAa,OAAO;GAC9B,WAAW,EACT,kBAAkB,MAAM,aAAa,OAAO,SAC9C;GACA,SAAS,MAAM,aAAa;EAC9B,CAAC;EAGH,IAAI,MAAM,cACR,WAAW,KAAK;GACd,MAAM;GACN,YAAY,KAAK;GACjB,IAAI,MAAM,aAAa;GACvB,WAAW,EACT,kBAAkB,MAAM,aAAa,SACvC;GACA,SAAS;IACP,YAAY;IACZ,WAAW;GACb;EACF,CAAC;EAGH,KAAK,MAAM,kBAAkB,MAAM,mBAAmB,CAAC,GACrD,WAAW,KAAK;GACd,MAAM;GACN,YAAY,KAAK;GACjB,QAAQ;EACV,CAAC;EAGH,IAAI,kBACF,WAAW,KACT;GACE,MAAM;GACN,YAAY,KAAK;GACjB,IAAI,iBAAiB,KAAK;GAC1B,WAAW,EACT,kBAAkB,iBAAiB,KAAK,SAC1C;GACA,SAAS,EACP,WAAW,iBAAiB,KAAK,UACnC;EACF,GACA;GACE,MAAM;GACN,YAAY,KAAK;GACjB,IAAI,iBAAiB,WAAW;GAChC,WAAW,EACT,kBAAkB,iBAAiB,WAAW,SAChD;GACA,SAAS;IACP,WAAW,iBAAiB,WAAW;IACvC,mBAAmB,iBAAiB,WAAW;GACjD;EACF,CACF;EAGF,WAAW,KAAK;GACd,MAAM;GACN,YAAY,KAAK;GACjB,QAAQ,KAAK,kBACX,MAAM,gBACH,MAAM,WAAW,YACd,0BACA,kBACN,MAAM,QACN,EACE,QAAQ,MAAM,OAChB,GACA,cAAc,SAChB;EACF,CAAC;EAED,IAAI;GACF,MAAM,KAAK,QAAQ,YAA8B,UAAU;EAC7D,SAAS,OAAO;GACd,IAAI;IACF,MAAM,KAAK,SAAS,OAAO,cAAc,SAAS;GACpD,SAAS,aAAa;IACpB,MAAM,IAAIF,oBAAAA,iBACR,gDACA,EAAE,OAAO,YAAY,CACvB;GACF;GAEA,IACE,iBAAiBa,mBAAAA,wBACjB,iBAAiBC,mBAAAA,uBAEjB,MAAM,IAAIZ,oBAAAA,sBAAsB,EAAE,OAAO,MAAM,CAAC;GAGlD,MAAM,IAAIG,oBAAAA,kBAAkB,0CAA0C,EACpE,OAAO,MACT,CAAC;EACH;EAEA,OAAO,cAAc;CACvB;CAEA,MAAM,4BACJ,OACgC;EAChC,KAAK,kBAAkB;EAEvB,MAAM,YAAY,MAAM,KAAK,aAC3B,MAAM,gBACN,YACF;EAEA,IAAI,CAAC,UAAU,UAAU,UAAU,UAAU,SAAS,GACpD,MAAM,IAAIH,oBAAAA,sBAAsB;EAGlC,IAAI,UAAU,mBACZ,MAAM,IAAIA,oBAAAA,sBAAsB;EAGlC,MAAM,KAAK,iBACT,uBACA,MAAM,WACN,UAAU,MACZ;EAEA,MAAM,WAAW,MAAM,KAAK,oBAAoB,SAAS;EAEzD,IAAI,CAAC,UACH,MAAM,IAAIA,oBAAAA,sBAAsB;EAGlC,MAAM,SAASa,mBAAAA,mBAAmB;EAElC,IAAI;EAEJ,IAAI;GACF,kBAAkB,MAAM,KAAK,gBAAgB,QAAQ,MAAM;EAC7D,SAAS,OAAO;GACd,MAAM,IAAIV,oBAAAA,kBAAkB,sCAAsC,EAChE,OAAO,MACT,CAAC;EACH;EAEA,IACE,CAAC,mBACD,OAAO,gBAAgB,eAAe,YACtC,gBAAgB,WAAW,WAAW,KACtC,OAAO,gBAAgB,OAAO,YAC9B,gBAAgB,GAAG,WAAW,KAC9B,OAAO,gBAAgB,UAAU,YACjC,gBAAgB,MAAM,WAAW,GAEjC,MAAM,IAAIA,oBAAAA,kBACR,4DACF;EAGF,MAAM,MAAM,KAAK,IAAI;EACrB,MAAM,WAAWI,0BAAAA,eAAe,YAAY;EAE5C,MAAM,SAAqC;GACzC,IAAI;GACJ,QAAQ,UAAU;GAClB,WAAW,QAAQ;GACnB,MAAM;GACN,QAAQ;GACR,OAAO,cAAc,MAAM,OAAO,mBAAmB;GACrD,QAAQ;GACR,cAAc;GACd,WAAW;GACX,SAAS;GACT,YAAY,CAAC;GACb,cAAc;GACd,YAAY;GACZ,WAAW,UAAU;GACrB,WAAW;GACX,WAAW;GACX,UAAU;EACZ;EAEA,MAAM,aAAwD;GAC5D;IACE,MAAM;IACN,YAAY,KAAK;IACjB,QAAQ;GACV;GACA;IACE,MAAM;IACN,YAAY,KAAK;IACjB,IAAI,UAAU;IACd,WAAW,EACT,kBAAkB,UAAU,SAC9B;IACA,SAAS;KACP,WAAW,CAAC,OAAO,EAAE;KACrB,WAAW;IACb;GACF;GACA;IACE,MAAM;IACN,YAAY,KAAK;IACjB,QAAQ,KAAK,kBACX,yBACA,UAAU,QACV;KACE,SAAS;KACT;IACF,CACF;GACF;EACF;EAEA,IAAI;GACF,MAAM,KAAK,QAAQ,YAA8B,UAAU;EAC7D,SAAS,OAAO;GACd,IACE,iBAAiBI,mBAAAA,wBACjB,iBAAiBC,mBAAAA,uBAEjB,MAAM,IAAIZ,oBAAAA,sBAAsB,EAAE,OAAO,MAAM,CAAC;GAGlD,MAAM,IAAIG,oBAAAA,kBAAkB,6CAA6C,EACvE,OAAO,MACT,CAAC;EACH;EAEA,OAAO;GACL;GACA;GACA,YAAYW,mBAAAA,cAAc;IACxB,QAAQ,KAAK;IACb,SAAS,SAAS,KAAK;IACvB;IACA,QAAQ,KAAK,KAAK;IAClB,eAAe,KAAK,KAAK;GAC3B,CAAC;GACD,WAAW,UAAU;EACvB;CACF;CAEA,MAAM,6BACJ,OACqC;EACrC,KAAK,kBAAkB;EAEvB,MAAM,YAAY,MAAM,KAAK,aAC3B,MAAM,gBACN,YACF;EAEA,IACE,CAAC,UAAU,UACX,UAAU,UAAU,WAAW,KAC/B,CAAC,UAAU,UAAU,SAAS,MAAM,QAAQ,GAE5C,MAAM,IAAId,oBAAAA,sBAAsB;EAGlC,MAAM,KAAK,iBACT,mCACA,MAAM,WACN,UAAU,MACZ;EAEA,MAAM,SAAS,MAAM,KAAK,QAAQ,SAChC,KAAK,mBACL,MAAM,QACR;EAEA,IACE,CAAC,UACD,OAAO,WAAW,UAAU,UAC5B,OAAO,SAAS,UAChB,OAAO,WAAW,aAClB,CAAC,OAAO,UACR,CAAC,OAAO,aACR,OAAO,UAAU,QAAQ,KAAK,KAAK,IAAI,CAAC,CAAC,QAAQ,GAEjD,MAAM,IAAIe,oBAAAA,mBACR,oDACF;EAGF,IAAI;EAEJ,IAAI;GACF,SAAS,MAAM,KAAK,gBAAgB,QAAQ,OAAO,MAAM;EAC3D,SAAS,OAAO;GACd,MAAM,IAAIZ,oBAAAA,kBAAkB,sCAAsC,EAChE,OAAO,MACT,CAAC;EACH;EAEA,MAAM,MAAM,KAAK,IAAI;EAErB,MAAM,cAAc,MAAMa,mBAAAA,eACxB,QACA,MAAM,MACN,IAAI,QAAQ,GACZ,KAAK,IACP;EAEA,IACE,gBAAgB,QACf,OAAO,iBAAiB,QAAQ,eAAe,OAAO,cAEvD,OAAO,KAAK,cAAc,WAAW,0BAA0B;EAGjE,MAAM,WAAW,MAAM,KAAK,yBAC1B,UAAU,QACV,OAAO,EACT;EAoBA,OAAO;GACL,MAAA,MAnBiB,KAAK,kBAAkB;IACxC;IACA,QAAQ,UAAU;IAClB,QAAQ;IACR,cAAc;KACZ;KACA,SAAS;MACP,QAAQ;MACR,cAAc;MACd,YAAY;MACZ,WAAW;MACX,WAAW;KACb;IACF;IACA,iBAAiB,SAAS;IAC1B,aAAa;GACf,CAAC;GAIC,QAAQ,eAAe;IACrB,GAAG;IACH,QAAQ;IACR,cAAc;IACd,YAAY;IACZ,WAAW;IACX,WAAW;IACX,UAAU,OAAO,WAAW;GAC9B,CAAC;GACD,eAAe,SAAS;EAC1B;CACF;CAEA,MAAM,+BACJ,OACqC;EACrC,KAAK,kBAAkB;EAEvB,MAAM,YAAY,MAAM,KAAK,aAC3B,MAAM,gBACN,YACF;EAEA,IACE,CAAC,UAAU,UACX,UAAU,UAAU,SAAS,KAC7B,UAAU,mBAEV,MAAM,IAAIhB,oBAAAA,sBAAsB;EAGlC,MAAM,KAAK,iBACT,+BACA,MAAM,WACN,UAAU,MACZ;EAEA,MAAM,WAAW,MAAM,KAAK,oBAAoB,SAAS;EAEzD,IAAI,CAAC,UACH,MAAM,IAAIA,oBAAAA,sBAAsB;EAGlC,MAAM,EAAE,UAAU,WAAW,KAAK,YAAY;EAI9C,MAAM,wBAAuB,MAFD,KAAK,kBAAkB,UAAU,MAAM,EAAA,CAGhE,QACE,WAAW,OAAO,SAAS,aAAa,OAAO,iBAAiB,IACnE,CAAC,CACA,KAAK,WAAW,OAAO,YAAa;EAEvC,MAAM,oBAAoB,0BAA0B;EAEpD,IAAI;EAEJ,IAAI;GACF,UAAU,MAAM,SAAS,4BAA4B;IACnD,WAAW;IACX,MAAM,OAAO;IACb,QAAQ,OAAO;IACf,QAAQ,UAAU;IAClB,UAAU,SAAS,KAAK;IACxB;GACF,CAAC;EACH,SAAS,OAAO;GACd,MAAM,IAAIG,oBAAAA,kBACR,mDACA,EAAE,OAAO,MAAM,CACjB;EACF;EAEA,KAAK,sBAAsB,SAAS,iBAAiB;EAErD,MAAM,MAAM,KAAK,IAAI;EACrB,MAAM,cAAc,cAAc,MAAM,OAAO,SAAS;EAExD,MAAM,aAAwD,CAC5D;GACE,MAAM;GACN,YAAY,KAAK;GACjB,IAAI,UAAU;GACd,WAAW,EACT,kBAAkB,UAAU,SAC9B;GACA,SAAS;IACP;IACA;IACA,WAAW;GACb;EACF,GACA;GACE,MAAM;GACN,YAAY,KAAK;GACjB,QAAQ,KAAK,kBACX,yBACA,UAAU,QACV,EACE,SAAS,8BACX,CACF;EACF,CACF;EAEA,IAAI;GACF,MAAM,KAAK,QAAQ,YAA8B,UAAU;EAC7D,SAAS,OAAO;GACd,IACE,iBAAiBQ,mBAAAA,wBACjB,iBAAiBC,mBAAAA,uBAEjB,MAAM,IAAIZ,oBAAAA,sBAAsB,EAAE,OAAO,MAAM,CAAC;GAGlD,MAAM,IAAIG,oBAAAA,kBACR,gEACA,EAAE,OAAO,MAAM,CACjB;EACF;EAEA,OAAO;GACL,aAAa,UAAU;GACvB,gBAAgB,MAAM;GACtB;GACA,WAAW,UAAU;EACvB;CACF;CAEA,MAAM,gCACJ,OACqC;EACrC,KAAK,kBAAkB;EAEvB,MAAM,YAAY,MAAM,KAAK,aAC3B,MAAM,gBACN,YACF;EAEA,IACE,CAAC,UAAU,UACX,UAAU,UAAU,SAAS,KAC7B,CAAC,UAAU,mBAEX,MAAM,IAAIH,oBAAAA,sBAAsB;EAGlC,MAAM,KAAK,iBACT,sCACA,MAAM,WACN,UAAU,MACZ;EAIA,IAAI,CAAC,MAFkB,KAAK,oBAAoB,SAAS,GAGvD,MAAM,IAAIA,oBAAAA,sBAAsB;EAGlC,MAAM,EAAE,UAAU,WAAW,KAAK,YAAY;EAE9C,IAAI;EAEJ,IAAI;GACF,eAAe,MAAM,SAAS,2BAA2B;IACvD,UAAU,MAAM;IAChB,mBAAmB,UAAU;IAC7B,gBAAgB,OAAO;IACvB,cAAc,OAAO;GACvB,CAAC;EACH,SAAS,OAAO;GACd,MAAM,IAAIG,oBAAAA,kBACR,8CACA,EAAE,OAAO,MAAM,CACjB;EACF;EAEA,IACE,CAAC,aAAa,YACd,CAAC,aAAa,gBACd,CAAC,aAAa,YAEd,OAAO,KAAK,cAAc,WAAW,6BAA6B;EAGpE,MAAM,aAA6C,aAAa;EAGhE,IAF6B,gBAAgB,MAAM,QAG9B,MAAM,WAAW,gBACpC,CAAC,WAAW,aACZ,CAAC,OAAO,cAAc,WAAW,OAAO,KACxC,WAAW,UAAU,GAErB,OAAO,KAAK,cAAc,WAAW,6BAA6B;EAGpE,MAAM,MAAM,KAAK,IAAI;EAGrB,MAAM,SAAqC;GACzC,IAHeI,0BAAAA,eAAe,YAGnB;GACX,QAAQ,UAAU;GAClB,WAAW,WAAW,WAAW;GACjC,MAAM;GACN,QAAQ;GACR,OAAO,UAAU,eAAe;GAChC,QAAQ;GACR,cAAc,WAAW;GACzB,WAAW,WAAW;GACtB,SAAS,WAAW;GACpB,YAAY,WAAW,cAAc,CAAC;GACtC,cAAc;GACd,YAAY;GACZ,WAAW;GACX,WAAW;GACX,WAAW;GACX,UAAU;EACZ;EAEA,MAAM,WAAW,MAAM,KAAK,yBAC1B,UAAU,QACV,OAAO,EACT;EAWA,OAAO;GACL,MAAA,MAViB,KAAK,kBAAkB;IACxC;IACA,QAAQ,UAAU;IAClB,QAAQ;IACR,cAAc;IACd,iBAAiB,SAAS;IAC1B,aAAa;GACf,CAAC;GAIC,QAAQ,eAAe,MAAM;GAC7B,eAAe,SAAS;EAC1B;CACF;CAEA,MAAM,oBACJ,OACgC;EAChC,KAAK,kBAAkB;EAEvB,MAAM,UAAU,MAAM,KAAK,wBAAwB,MAAM,YAAY;EAErE,MAAM,KAAK,sBAAsB,QAAQ,KAAK,IAAI,MAAM,eAAe;EAEvE,MAAM,KAAK,iBAAiB,cAAc,MAAM,WAAW,QAAQ,KAAK,EAAE;EAE1E,MAAM,SAASM,mBAAAA,mBAAmB;EAElC,IAAI;EAEJ,IAAI;GACF,kBAAkB,MAAM,KAAK,gBAAgB,QAAQ,MAAM;EAC7D,SAAS,OAAO;GACd,MAAM,IAAIV,oBAAAA,kBAAkB,sCAAsC,EAChE,OAAO,MACT,CAAC;EACH;EAEA,IACE,CAAC,mBACD,OAAO,gBAAgB,eAAe,YACtC,gBAAgB,WAAW,WAAW,KACtC,OAAO,gBAAgB,OAAO,YAC9B,gBAAgB,GAAG,WAAW,KAC9B,OAAO,gBAAgB,UAAU,YACjC,gBAAgB,MAAM,WAAW,GAEjC,MAAM,IAAIA,oBAAAA,kBACR,4DACF;EAGF,MAAM,MAAM,KAAK,IAAI;EACrB,MAAM,WAAWI,0BAAAA,eAAe,YAAY;EAC5C,MAAM,YAAY,IAAI,KACpB,IAAI,QAAQ,IAAI,KAAK,2BAA2B,GAClD;EAEA,MAAM,SAAqC;GACzC,IAAI;GACJ,QAAQ,QAAQ,KAAK;GACrB,WAAW,QAAQ;GACnB,MAAM;GACN,QAAQ;GACR,OAAO,cAAc,MAAM,OAAO,mBAAmB;GACrD,QAAQ;GACR,cAAc;GACd,WAAW;GACX,SAAS;GACT,YAAY,CAAC;GACb,cAAc;GACd,YAAY;GACZ;GACA,WAAW;GACX,WAAW;GACX,UAAU;EACZ;EAEA,MAAM,aAAwD,CAC5D;GACE,MAAM;GACN,YAAY,KAAK;GACjB,QAAQ;EACV,GACA;GACE,MAAM;GACN,YAAY,KAAK;GACjB,QAAQ,KAAK,kBACX,oBACA,QAAQ,KAAK,IACb;IACE,MAAM;IACN,QAAQ;GACV,GACA,QAAQ,QAAQ,EAClB;EACF,CACF;EAEA,IAAI;GACF,MAAM,KAAK,QAAQ,YAA8B,UAAU;EAC7D,SAAS,OAAO;GACd,MAAM,IAAIJ,oBAAAA,kBAAkB,6CAA6C,EACvE,OAAO,MACT,CAAC;EACH;EAEA,OAAO;GACL;GACA;GACA,YAAYW,mBAAAA,cAAc;IACxB,QAAQ,KAAK;IACb,SAAS,QAAQ,KAAK;IACtB;IACA,QAAQ,KAAK,KAAK;IAClB,eAAe,KAAK,KAAK;GAC3B,CAAC;GACD;EACF;CACF;CAEA,MAAM,qBACJ,OACiC;EACjC,KAAK,kBAAkB;EAEvB,MAAM,UAAU,MAAM,KAAK,wBAAwB,MAAM,YAAY;EAErE,MAAM,KAAK,iBACT,qBACA,MAAM,WACN,QAAQ,KAAK,EACf;EAEA,MAAM,SAAS,MAAM,KAAK,QAAQ,SAChC,KAAK,mBACL,MAAM,QACR;EAEA,IACE,CAAC,UACD,OAAO,WAAW,QAAQ,KAAK,MAC/B,OAAO,SAAS,UAChB,OAAO,WAAW,aAClB,CAAC,OAAO,UACR,CAAC,OAAO,aACR,OAAO,UAAU,QAAQ,KAAK,KAAK,IAAI,CAAC,CAAC,QAAQ,GAEjD,MAAM,IAAIC,oBAAAA,mBACR,oDACF;EAGF,IAAI;EAEJ,IAAI;GACF,SAAS,MAAM,KAAK,gBAAgB,QAAQ,OAAO,MAAM;EAC3D,SAAS,OAAO;GACd,MAAM,IAAIZ,oBAAAA,kBAAkB,sCAAsC,EAChE,OAAO,MACT,CAAC;EACH;EAEA,MAAM,MAAM,KAAK,IAAI;EAErB,MAAM,cAAc,MAAMa,mBAAAA,eACxB,QACA,MAAM,MACN,IAAI,QAAQ,GACZ,KAAK,IACP;EAEA,IAAI,gBAAgB,MAClB,MAAM,IAAID,oBAAAA,mBAAmB,oCAAoC;EAGnE,MAAM,WAAW,MAAM,KAAK,yBAC1B,QAAQ,KAAK,IACb,OAAO,EACT;EAEA,MAAM,kBAA8C;GAClD,GAAG;GACH,QAAQ;GACR,cAAc;GACd,YAAY;GACZ,WAAW;GACX,WAAW;GACX,UAAU,OAAO,WAAW;EAC9B;EAEA,MAAM,aAAwD;GAC5D;IACE,MAAM;IACN,YAAY,KAAK;IACjB,IAAI,OAAO;IACX,WAAW,EACT,kBAAkB,OAAO,SAC3B;IACA,SAAS;KACP,QAAQ;KACR,cAAc;KACd,YAAY;KACZ,WAAW;KACX,WAAW;IACb;GACF;GACA,GAAG,SAAS,QAAQ,KAAK,YAAY;IACnC,MAAM;IACN,YAAY,KAAK;IACjB;GACF,EAAE;GACF;IACE,MAAM;IACN,YAAY,KAAK;IACjB,QAAQ,KAAK,kBACX,oBACA,QAAQ,KAAK,IACb;KACE,MAAM;KACN,wBAAwB,SAAS,MAAM;IACzC,GACA,QAAQ,QAAQ,EAClB;GACF;EACF;EAEA,IAAI;GACF,MAAM,KAAK,QAAQ,YAA8B,UAAU;EAC7D,SAAS,OAAO;GACd,IAAI,iBAAiBJ,mBAAAA,sBACnB,MAAM,IAAII,oBAAAA,mBACR,mDACA,EAAE,OAAO,MAAM,CACjB;GAGF,MAAM,IAAIZ,oBAAAA,kBAAkB,uCAAuC,EACjE,OAAO,MACT,CAAC;EACH;EAEA,OAAO;GACL,QAAQ,eAAe,eAAe;GACtC,eAAe,SAAS;EAC1B;CACF;CAEA,MAAM,iBAAiB,OAAoD;EACzE,KAAK,kBAAkB;EAEvB,MAAM,YAAY,MAAM,KAAK,aAAa,MAAM,gBAAgB,SAAS;EAEzE,IAAI,CAAC,UAAU,QACb,MAAM,IAAIH,oBAAAA,sBAAsB;EAGlC,MAAM,KAAK,iBACT,eACA,MAAM,WACN,UAAU,MACZ;EAEA,KAAK,MAAM,YAAY,UAAU,WAAW;GAC1C,MAAM,SAAS,MAAM,KAAK,QAAQ,SAChC,KAAK,mBACL,QACF;GAEA,IACE,CAAC,UACD,OAAO,WAAW,UAAU,UAC5B,OAAO,WAAW,YAClB,OAAO,SAAS,UAChB,CAAC,OAAO,QAER;GAGF,IAAI;GAEJ,IAAI;IACF,SAAS,MAAM,KAAK,gBAAgB,QAAQ,OAAO,MAAM;GAC3D,SAAS,OAAO;IACd,MAAM,IAAIG,oBAAAA,kBAAkB,sCAAsC,EAChE,OAAO,MACT,CAAC;GACH;GAEA,MAAM,MAAM,KAAK,IAAI;GAErB,MAAM,cAAc,MAAMa,mBAAAA,eACxB,QACA,MAAM,MACN,IAAI,QAAQ,GACZ,KAAK,IACP;GAEA,IACE,gBAAgB,QACf,OAAO,iBAAiB,QAAQ,eAAe,OAAO,cAEvD;GAGF,OAAO,KAAK,kBAAkB;IAC5B;IACA,QAAQ,UAAU;IAClB,QAAQ;IACR,cAAc;KACZ;KACA,SAAS;MACP,cAAc;MACd,YAAY;MACZ,WAAW;KACb;IACF;GACF,CAAC;EACH;EAEA,OAAO,KAAK,cAAc,WAAW,MAAM;CAC7C;CAEA,MAAM,yBACJ,OACqB;EACrB,KAAK,kBAAkB;EAEvB,MAAM,YAAY,MAAM,KAAK,aAAa,MAAM,gBAAgB,SAAS;EAEzE,IAAI,CAAC,UAAU,QACb,MAAM,IAAIhB,oBAAAA,sBAAsB;EAGlC,MAAM,KAAK,iBACT,mBACA,MAAM,WACN,UAAU,MACZ;EAEA,MAAM,eAAe,MAAM,KAAK,sBAC9B,UAAU,QACV,MAAM,IACR;EAEA,IAAI,CAAC,cACH,OAAO,KAAK,cAAc,WAAW,eAAe;EAGtD,IAAI,CAAC,UAAU,UAAU,QACvB,MAAM,IAAIA,oBAAAA,sBAAsB;EAGlC,MAAM,SAAS,MAAM,KAAK,QAAQ,SAChC,KAAK,mBACL,aAAa,QACf;EAEA,IACE,CAAC,UACD,OAAO,WAAW,UAAU,UAC5B,OAAO,WAAW,YAClB,CAAC,UAAU,UAAU,SAAS,OAAO,EAAE,GAEvC,OAAO,KAAK,cAAc,WAAW,eAAe;EAGtD,OAAO,KAAK,kBAAkB;GAC5B;GACA,QAAQ,UAAU;GAClB,QAAQ;GACR;EACF,CAAC;CACH;CAEA,MAAM,gBAAgB,QAAkC;EACtD,KAAK,kBAAkB;EAEvB,IAAI,OAAO,WAAW,YAAY,OAAO,KAAK,CAAC,CAAC,WAAW,GACzD,OAAO;EAaT,OAAO,MAVc,KAAK,QAAQ,QAChC,KAAK,mBACL,EACE,OAAO;GACL,QAAQ,YAAY,MAAM;GAC1B,QAAQ,YAAY,QAAQ;EAC9B,EACF,CACF,MAEkB;CACpB;CAEA,MAAM,YAAY,cAAyD;EACzE,KAAK,kBAAkB;EAEvB,MAAM,UAAU,MAAM,KAAK,wBAAwB,YAAY;EAiB/D,QAAO,MAfe,KAAK,QAAQ,SACjC,KAAK,mBACL;GACE,OAAO,EACL,QAAQ,YAAY,QAAQ,KAAK,EAAE,EACrC;GACA,SAAS,CACP;IACE,OAAO;IACP,WAAW;GACb,CACF;EACF,CACF,EAAA,CAEe,IAAI,cAAc;CACnC;CAEA,MAAM,qBAAqB,cAAuC;EAChE,KAAK,kBAAkB;EAEvB,MAAM,UAAU,MAAM,KAAK,wBAAwB,YAAY;EAE/D,OAAO,KAAK,QAAQ,MAClB,KAAK,yBACL,EACE,OAAO;GACL,QAAQ,YAAY,QAAQ,KAAK,EAAE;GACnC,YAAY,YAAY,IAAI;EAC9B,EACF,CACF;CACF;CAEA,MAAM,wBACJ,OAC4B;EAC5B,KAAK,kBAAkB;EAEvB,MAAM,UAAU,MAAM,KAAK,wBAAwB,MAAM,YAAY;EAErE,MAAM,KAAK,sBAAsB,QAAQ,KAAK,IAAI,MAAM,eAAe;EAEvE,MAAM,KAAK,iBACT,uBACA,MAAM,WACN,QAAQ,KAAK,EACf;EAEA,MAAM,UAAU,MAAM,KAAK,kBAAkB,QAAQ,KAAK,EAAE;EAE5D,IAAI,QAAQ,WAAW,GACrB,MAAM,IAAIe,oBAAAA,mBACR,8DACF;EAGF,MAAM,WAAW,MAAM,KAAK,QAAQ,SAClC,KAAK,yBACL,EACE,OAAO;GACL,QAAQ,YAAY,QAAQ,KAAK,EAAE;GACnC,YAAY,YAAY,IAAI;EAC9B,EACF,CACF;EAEA,MAAM,cAAc,QAAQ;EAE5B,IAAI,CAAC,aACH,MAAM,IAAIA,oBAAAA,mBACR,8DACF;EAGF,MAAM,SAAS,MAAM,KAAK,yBACxB,QAAQ,KAAK,IACb,YAAY,IACZ,IACF;EAEA,MAAM,aAAwD;GAC5D,GAAG,SAAS,KAAK,YAAY;IAC3B,MAAM;IACN,YAAY,KAAK;IACjB,IAAI,OAAO;IACX,WAAW,EACT,kBAAkB,OAAO,SAC3B;GACF,EAAE;GACF,GAAG,OAAO,QAAQ,KAAK,YAAY;IACjC,MAAM;IACN,YAAY,KAAK;IACjB;GACF,EAAE;GACF;IACE,MAAM;IACN,YAAY,KAAK;IACjB,QAAQ,KAAK,kBACX,kCACA,QAAQ,KAAK,IACb,EACE,OAAO,OAAO,QAAQ,OACxB,GACA,QAAQ,QAAQ,EAClB;GACF;EACF;EAEA,IAAI;GACF,MAAM,KAAK,QAAQ,YAA8B,UAAU;EAC7D,SAAS,OAAO;GACd,MAAM,IAAIZ,oBAAAA,kBAAkB,wCAAwC,EAClE,OAAO,MACT,CAAC;EACH;EAEA,OAAO,OAAO;CAChB;CAEA,MAAM,aAAa,OAA4C;EAC7D,KAAK,kBAAkB;EAEvB,MAAM,UAAU,MAAM,KAAK,wBAAwB,MAAM,YAAY;EAErE,MAAM,KAAK,sBAAsB,QAAQ,KAAK,IAAI,MAAM,eAAe;EAEvE,MAAM,KAAK,iBACT,iBACA,MAAM,WACN,QAAQ,KAAK,EACf;EAEA,MAAM,SAAS,MAAM,KAAK,QAAQ,SAChC,KAAK,mBACL,MAAM,QACR;EAEA,IAAI,CAAC,UAAU,OAAO,WAAW,QAAQ,KAAK,IAC5C,MAAM,IAAIY,oBAAAA,mBAAmB;EAG/B,MAAM,gBACJ,MAAM,KAAK,QAAQ,SACjB,KAAK,yBACL,EACE,OAAO,EACL,UAAU,YAAY,OAAO,EAAE,EACjC,EACF,CACF;EAEF,MAAM,oBACJ,MAAM,KAAK,QAAQ,SACjB,KAAK,sBACL,EACE,OAAO;GACL,QAAQ,YAAY,QAAQ,KAAK,EAAE;GACnC,SAAS,YAAY,SAAS;GAC9B,YAAY,YAAY,IAAI;EAC9B,EACF,CACF;EAEF,MAAM,MAAM,KAAK,IAAI;EAErB,MAAM,aAAwD;GAC5D;IACE,MAAM;IACN,YAAY,KAAK;IACjB,IAAI,OAAO;IACX,WAAW,EACT,kBAAkB,OAAO,SAC3B;GACF;GACA,GAAG,cAAc,KAAK,YAAY;IAChC,MAAM;IACN,YAAY,KAAK;IACjB,IAAI,OAAO;IACX,WAAW,EACT,kBAAkB,OAAO,SAC3B;GACF,EAAE;GACF,GAAG,kBAAkB,KAAK,eAAe;IACvC,MAAM;IACN,YAAY,KAAK;IACjB,IAAI,UAAU;IACd,WAAW,EACT,kBAAkB,UAAU,SAC9B;IACA,SAAS;KACP,YAAY;KACZ,WAAW;IACb;GACF,EAAE;GACF;IACE,MAAM;IACN,YAAY,KAAK;IACjB,QAAQ,KAAK,kBACX,sBACA,QAAQ,KAAK,IACb,EACE,MAAM,OAAO,KACf,GACA,QAAQ,QAAQ,EAClB;GACF;EACF;EAEA,IAAI;GACF,MAAM,KAAK,QAAQ,YAA8B,UAAU;EAC7D,SAAS,OAAO;GACd,IAAI,iBAAiBJ,mBAAAA,sBACnB,MAAM,IAAII,oBAAAA,mBAAmB,sCAAsC,EACjE,OAAO,MACT,CAAC;GAGH,MAAM,IAAIZ,oBAAAA,kBAAkB,oCAAoC,EAC9D,OAAO,MACT,CAAC;EACH;CACF;CAEA,cAGE;EACA,IAAI,CAAC,KAAK,YAAY,CAAC,KAAK,kBAC1B,MAAM,IAAIF,oBAAAA,uBACR,kDACF;EAGF,OAAO;GACL,UAAU,KAAK;GACf,QAAQ,KAAK;EACf;CACF;CAEA,sBACE,SACA,mBACM;EACN,IAAI,QAAQ,cAAc,mBACxB,MAAM,IAAIE,oBAAAA,kBACR,yDACF;CAEJ;CAEA,MAAM,yBACJ,OACqC;EACrC,KAAK,kBAAkB;EAEvB,MAAM,UAAU,MAAM,KAAK,wBAAwB,MAAM,YAAY;EAErE,MAAM,KAAK,sBAAsB,QAAQ,KAAK,IAAI,MAAM,eAAe;EAEvE,MAAM,KAAK,iBACT,oBACA,MAAM,WACN,QAAQ,KAAK,EACf;EAEA,MAAM,EAAE,UAAU,WAAW,KAAK,YAAY;EAE9C,MAAM,gBAAgB,MAAM,KAAK,kBAAkB,QAAQ,KAAK,EAAE;EAElE,MAAM,uBAAuB,cAC1B,QACE,WAAW,OAAO,SAAS,aAAa,OAAO,iBAAiB,IACnE,CAAC,CACA,KAAK,WAAW,OAAO,YAAa;EAEvC,MAAM,YAAY,0BAA0B;EAE5C,IAAI;EAEJ,IAAI;GACF,UAAU,MAAM,SAAS,4BAA4B;IACnD;IACA,MAAM,OAAO;IACb,QAAQ,OAAO;IACf,QAAQ,QAAQ,KAAK;IACrB,UAAU,QAAQ,KAAK;IACvB;GACF,CAAC;EACH,SAAS,OAAO;GACd,MAAM,IAAIA,oBAAAA,kBACR,mDACA,EAAE,OAAO,MAAM,CACjB;EACF;EAEA,KAAK,sBAAsB,SAAS,SAAS;EAE7C,MAAM,UAAU,MAAM,KAAK,sBACzB,wBACA,QAAQ,KAAK,IACb,cAAc,KAAK,WAAW,OAAO,EAAE,GACvC,WACA,EACE,aAAa,cAAc,MAAM,OAAO,SAAS,EACnD,CACF;EAEA,MAAM,QAAQ,KAAK,kBACjB,yBACA,QAAQ,KAAK,IACb,EACE,SAAS,uBACX,GACA,QAAQ,QAAQ,EAClB;EAEA,IAAI;GACF,MAAM,KAAK,QAAQ,YAA8B,CAC/C;IACE,MAAM;IACN,YAAY,KAAK;IACjB,QAAQ,QAAQ;GAClB,GACA;IACE,MAAM;IACN,YAAY,KAAK;IACjB,QAAQ;GACV,CACF,CAAC;EACH,SAAS,OAAO;GACd,MAAM,IAAIA,oBAAAA,kBACR,0DACA,EAAE,OAAO,MAAM,CACjB;EACF;EAEA,OAAO;GACL,aAAa,QAAQ,OAAO;GAC5B,gBAAgB,QAAQ;GACxB;GACA,WAAW,QAAQ,OAAO;EAC5B;CACF;CAEA,MAAM,0BACJ,OACiC;EACjC,KAAK,kBAAkB;EAEvB,MAAM,UAAU,MAAM,KAAK,wBAAwB,MAAM,YAAY;EAErE,MAAM,KAAK,iBACT,+BACA,MAAM,WACN,QAAQ,KAAK,EACf;EAEA,MAAM,YAAY,MAAM,KAAK,aAC3B,MAAM,gBACN,sBACF;EAEA,IAAI,UAAU,WAAW,QAAQ,KAAK,MAAM,CAAC,UAAU,mBACrD,MAAM,IAAIH,oBAAAA,sBAAsB;EAGlC,MAAM,EAAE,UAAU,WAAW,KAAK,YAAY;EAE9C,IAAI;EAEJ,IAAI;GACF,eAAe,MAAM,SAAS,2BAA2B;IACvD,UAAU,MAAM;IAChB,mBAAmB,UAAU;IAC7B,gBAAgB,OAAO;IACvB,cAAc,OAAO;GACvB,CAAC;EACH,SAAS,OAAO;GACd,MAAM,IAAIG,oBAAAA,kBACR,8CACA,EAAE,OAAO,MAAM,CACjB;EACF;EAEA,IACE,CAAC,aAAa,YACd,CAAC,aAAa,gBACd,CAAC,aAAa,YAEd,OAAO,KAAK,cAAc,WAAW,sBAAsB;EAG7D,MAAM,aAA6C,aAAa;EAIhE,IAF6B,gBAAgB,MAAM,QAG9B,MAAM,WAAW,gBACpC,CAAC,WAAW,aACZ,CAAC,OAAO,cAAc,WAAW,OAAO,KACxC,WAAW,UAAU,GAErB,OAAO,KAAK,cAAc,WAAW,sBAAsB;EAG7D,MAAM,MAAM,KAAK,IAAI;EAGrB,MAAM,SAAqC;GACzC,IAHeI,0BAAAA,eAAe,YAGnB;GACX,QAAQ,QAAQ,KAAK;GACrB,WAAW,WAAW,WAAW;GACjC,MAAM;GACN,QAAQ;GACR,OAAO,UAAU,eAAe;GAChC,QAAQ;GACR,cAAc,WAAW;GACzB,WAAW,WAAW;GACtB,SAAS,WAAW;GACpB,YAAY,WAAW,cAAc,CAAC;GACtC,cAAc;GACd,YAAY;GACZ,WAAW;GACX,WAAW;GACX,WAAW;GACX,UAAU;EACZ;EAEA,MAAM,WAAW,MAAM,KAAK,yBAC1B,QAAQ,KAAK,IACb,OAAO,EACT;EAEA,MAAM,aAAwD;GAC5D;IACE,MAAM;IACN,YAAY,KAAK;IACjB,QAAQ;GACV;GACA;IACE,MAAM;IACN,YAAY,KAAK;IACjB,IAAI,UAAU;IACd,WAAW,EACT,kBAAkB,UAAU,SAC9B;IACA,SAAS;KACP,YAAY;KACZ,WAAW;IACb;GACF;GACA,GAAG,SAAS,QAAQ,KAAK,YAAY;IACnC,MAAM;IACN,YAAY,KAAK;IACjB;GACF,EAAE;GACF;IACE,MAAM;IACN,YAAY,KAAK;IACjB,QAAQ,KAAK,kBACX,sBACA,QAAQ,KAAK,IACb;KACE,YAAY,OAAO,WAAW;KAC9B,wBAAwB,SAAS,MAAM;IACzC,GACA,QAAQ,QAAQ,EAClB;GACF;EACF;EAEA,IAAI;GACF,MAAM,KAAK,QAAQ,YAA8B,UAAU;EAC7D,SAAS,OAAO;GACd,IACE,iBAAiBK,mBAAAA,yBACjB,iBAAiBD,mBAAAA,sBAEjB,MAAM,IAAII,oBAAAA,mBACR,qEACA,EAAE,OAAO,MAAM,CACjB;GAGF,MAAM,IAAIZ,oBAAAA,kBAAkB,gCAAgC,EAC1D,OAAO,MACT,CAAC;EACH;EAEA,OAAO;GACL,QAAQ,eAAe,MAAM;GAC7B,eAAe,SAAS;EAC1B;CACF;CAEA,MAAM,mBACJ,OAC+B;EAC/B,KAAK,kBAAkB;EAEvB,MAAM,KAAK,iBAAiB,mBAAmB,MAAM,SAAS;EAE9D,MAAM,EAAE,UAAU,WAAW,KAAK,YAAY;EAE9C,MAAM,YAAY,0BAA0B;EAE5C,IAAI;EAEJ,IAAI;GACF,UAAU,MAAM,SAAS,8BAA8B;IACrD;IACA,MAAM,OAAO;IACb,kBAAkB;GACpB,CAAC;EACH,SAAS,OAAO;GACd,MAAM,IAAIA,oBAAAA,kBACR,oDACA,EAAE,OAAO,MAAM,CACjB;EACF;EAEA,KAAK,sBAAsB,SAAS,SAAS;EAE7C,MAAM,UAAU,MAAM,KAAK,sBACzB,mBACA,MACA,CAAC,GACD,SACF;EAEA,MAAM,QAAQ,KAAK,kBAAkB,yBAAyB,MAAM,EAClE,SAAS,kBACX,CAAC;EAED,IAAI;GACF,MAAM,KAAK,QAAQ,YAA8B,CAC/C;IACE,MAAM;IACN,YAAY,KAAK;IACjB,QAAQ,QAAQ;GAClB,GACA;IACE,MAAM;IACN,YAAY,KAAK;IACjB,QAAQ;GACV,CACF,CAAC;EACH,SAAS,OAAO;GACd,MAAM,IAAIA,oBAAAA,kBACR,oDACA,EAAE,OAAO,MAAM,CACjB;EACF;EAEA,OAAO;GACL,aAAa,QAAQ,OAAO;GAC5B,gBAAgB,QAAQ;GACxB;GACA,WAAW,QAAQ,OAAO;EAC5B;CACF;CAEA,MAAc,uBAAuB,OAIjB;EAClB,IAAI,CAAC,MAAM,UAAU,mBACnB,MAAM,IAAIH,oBAAAA,sBAAsB;EAGlC,IAAI,CAAC,MAAM,OAAO,gBAAgB,CAAC,MAAM,OAAO,WAC9C,MAAM,IAAIe,oBAAAA,mBAAmB;EAG/B,MAAM,EAAE,UAAU,WAAW,KAAK,YAAY;EAE9C,IAAI;EAEJ,IAAI;GACF,eAAe,MAAM,SAAS,6BAA6B;IACzD,UAAU,MAAM;IAChB,mBAAmB,MAAM,UAAU;IACnC,gBAAgB,OAAO;IACvB,cAAc,OAAO;IACrB,YAAY;KACV,cAAc,MAAM,OAAO;KAC3B,WAAW,MAAM,OAAO;KACxB,SAAS,MAAM,OAAO;IACxB;GACF,CAAC;EACH,SAAS,OAAO;GACd,MAAM,IAAIZ,oBAAAA,kBACR,gDACA,EAAE,OAAO,MAAM,CACjB;EACF;EAEA,IACE,CAAC,aAAa,YACd,CAAC,aAAa,gBACd,aAAa,iBAAiB,MAAM,OAAO,gBAC3C,CAAC,OAAO,cAAc,aAAa,UAAU,KAC7C,aAAa,aAAa,GAE1B,OAAO,KAAK,cAAc,MAAM,WAAW,wBAAwB;EAGrE,KACG,MAAM,OAAO,UAAU,KAAK,aAAa,aAAa,MACvD,aAAa,cAAc,MAAM,OAAO,SAExC,OAAO,KAAK,cAAc,MAAM,WAAW,4BAA4B;EAGzE,OAAO,aAAa;CACtB;CAEA,MAAM,oBAAoB,OAIF;EACtB,KAAK,kBAAkB;EAEvB,MAAM,YAAY,MAAM,KAAK,aAC3B,MAAM,gBACN,iBACF;EAEA,MAAM,KAAK,iBAAiB,0BAA0B,MAAM,SAAS;EAErE,MAAM,eAAe,gBAAgB,MAAM,QAAQ;EAEnD,MAAM,SAAS,MAAM,KAAK,QAAQ,QAChC,KAAK,mBACL,EACE,OAAO;GACL,cAAc,YAAY,YAAY;GACtC,MAAM,YAAY,SAAS;GAC3B,QAAQ,YAAY,QAAQ;EAC9B,EACF,CACF;EAEA,IAAI,CAAC,UAAU,CAAC,OAAO,QACrB,OAAO,KAAK,cAAc,WAAW,iBAAiB;EAGxD,MAAM,aAAa,MAAM,KAAK,uBAAuB;GACnD,UAAU,MAAM;GAChB;GACA;EACF,CAAC;EAED,OAAO,KAAK,kBAAkB;GAC5B;GACA,QAAQ,OAAO;GACf,QAAQ;GACR,aAAa;GACb,cAAc;IACZ;IACA,SAAS;KACP,SAAS;KACT,YAAY,KAAK,IAAI;KACrB,WAAW,KAAK,IAAI;IACtB;GACF;EACF,CAAC;CACH;CAEA,MAAM,gBACJ,gBACA,WAC4B;EAC5B,KAAK,kBAAkB;EAEvB,MAAM,YAAY,MAAM,KAAK,aAAa,gBAAgB,SAAS;EAEnE,IAAI,CAAC,UAAU,QACb,MAAM,IAAIH,oBAAAA,sBAAsB;EAGlC,MAAM,KAAK,iBAAiB,eAAe,WAAW,UAAU,MAAM;EAItE,MAAM,YAAW,MAFK,KAAK,kBAAkB,UAAU,MAAM,EAAA,CAEpC,QACtB,WACC,OAAO,SAAS,aAChB,OAAO,iBAAiB,QACxB,UAAU,UAAU,SAAS,OAAO,EAAE,CAC1C;EAEA,IAAI,SAAS,WAAW,GACtB,MAAM,IAAIe,oBAAAA,mBACR,6CACF;EAGF,MAAM,EAAE,UAAU,WAAW,KAAK,YAAY;EAC9C,MAAM,oBAAoB,0BAA0B;EAEpD,IAAI;EAEJ,IAAI;GACF,UAAU,MAAM,SAAS,8BAA8B;IACrD,WAAW;IACX,MAAM,OAAO;IACb,oBAAoB,SAAS,KAAK,WAAW,OAAO,YAAa;IACjE,kBAAkB;GACpB,CAAC;EACH,SAAS,OAAO;GACd,MAAM,IAAIZ,oBAAAA,kBAAkB,yCAAyC,EACnE,OAAO,MACT,CAAC;EACH;EAEA,KAAK,sBAAsB,SAAS,iBAAiB;EAerD,IAAI,CAAC,MAbiB,KAAK,QAAQ,OACjC,KAAK,sBACL,UAAU,IACV;GACE;GACA,mBAAmB,SAAS,KAAK,WAAW,OAAO,EAAE;GACrD,WAAW,KAAK,IAAI;EACtB,GACA,EACE,kBAAkB,UAAU,SAC9B,CACF,GAGE,MAAM,IAAIH,oBAAAA,sBAAsB;EAGlC,OAAO;GACL;GACA,WAAW,UAAU;EACvB;CACF;CAEA,MAAM,iBAAiB,OAAsD;EAC3E,KAAK,kBAAkB;EAEvB,MAAM,YAAY,MAAM,KAAK,aAAa,MAAM,gBAAgB,SAAS;EAEzE,IAAI,CAAC,UAAU,UAAU,CAAC,UAAU,mBAClC,MAAM,IAAIA,oBAAAA,sBAAsB;EAGlC,MAAM,KAAK,iBACT,sBACA,MAAM,WACN,UAAU,MACZ;EAEA,MAAM,eAAe,gBAAgB,MAAM,QAAQ;EAEnD,MAAM,SAAS,MAAM,KAAK,QAAQ,QAChC,KAAK,mBACL,EACE,OAAO;GACL,cAAc,YAAY,YAAY;GACtC,MAAM,YAAY,SAAS;GAC3B,QAAQ,YAAY,QAAQ;GAC5B,QAAQ,YAAY,UAAU,MAAM;EACtC,EACF,CACF;EAEA,IAAI,CAAC,UAAU,CAAC,UAAU,kBAAkB,SAAS,OAAO,EAAE,GAC5D,OAAO,KAAK,cAAc,WAAW,aAAa;EAGpD,MAAM,aAAa,MAAM,KAAK,uBAAuB;GACnD,UAAU,MAAM;GAChB;GACA;EACF,CAAC;EAED,OAAO,KAAK,kBAAkB;GAC5B;GACA,QAAQ,UAAU;GAClB,QAAQ;GACR,cAAc;IACZ;IACA,SAAS;KACP,SAAS;KACT,YAAY,KAAK,IAAI;KACrB,WAAW,KAAK,IAAI;IACtB;GACF;EACF,CAAC;CACH;AACF"}
@@ -0,0 +1,100 @@
1
+ import { PasswordHasher, RateLimitProvider } from "./providers/provider.types.cjs";
2
+ import { AuthSecretProtector, WebAuthnProvider } from "./providers/mfa-provider.types.cjs";
3
+ import { AuthMfaAvailableFactor, AuthMfaEnrollmentCompleted, AuthMfaEnrollmentPendingResult, AuthMfaFactor, AuthMfaGateway, AuthMfaManagerOptions, AuthMfaPendingResult, BeginPasskeyRegistrationInput, BeginPasskeySignInInput, BeginRequiredMfaEnrollmentInput, BeginTotpEnrollmentInput, CompleteMfaSignInInput, FinishPasskeyRegistrationInput, FinishRequiredPasskeyEnrollmentInput, MfaEnrollmentCompleted, PasskeyMfaCompletedInput, PasskeyMfaStarted, PasskeyRegistrationStarted, PasskeySignInStarted, RegenerateRecoveryCodesInput, RemoveMfaFactorInput, TotpEnrollmentStarted, VerifyRequiredTotpEnrollmentInput, VerifyTotpEnrollmentInput } from "./types/mfa.types.cjs";
4
+ import { AuthResult } from "./types/auth-manager.types.cjs";
5
+ import { AuthState } from "./types/state.types.cjs";
6
+ import { StorageAdapter } from "@puzzle-ui/storage";
7
+ import { SessionManager } from "@puzzle-ui/session";
8
+ export type StorageAuthMfaManagerDependencies = {
9
+ readonly storage: StorageAdapter;
10
+ readonly sessions: SessionManager;
11
+ readonly passwordHasher: PasswordHasher;
12
+ readonly rateLimitProvider: RateLimitProvider;
13
+ readonly secretProtector: AuthSecretProtector;
14
+ readonly webAuthnProvider?: WebAuthnProvider;
15
+ };
16
+ export declare class StorageAuthMfaManager implements AuthMfaGateway {
17
+ private readonly storage;
18
+ private readonly sessions;
19
+ private readonly passwordHasher;
20
+ private readonly rateLimitProvider;
21
+ private readonly secretProtector;
22
+ private readonly webAuthnProvider;
23
+ private readonly usersCollection;
24
+ private readonly credentialsCollection;
25
+ private readonly auditCollection;
26
+ private readonly factorsCollection;
27
+ private readonly challengesCollection;
28
+ private readonly recoveryCodesCollection;
29
+ private readonly rateLimitKeySecret;
30
+ private readonly challengeLifetimeSeconds;
31
+ private readonly recoveryCodeCount;
32
+ private readonly issuer;
33
+ private readonly totp;
34
+ private readonly webAuthn;
35
+ private readonly requireVerifiedEmail;
36
+ private readonly clock;
37
+ private initialized;
38
+ private initializationPromise;
39
+ constructor(dependencies: StorageAuthMfaManagerDependencies, options: AuthMfaManagerOptions);
40
+ revokeChallenges(userId: string): Promise<void>;
41
+ private validateWebAuthnConfiguration;
42
+ initialize(): Promise<void>;
43
+ private assertInitialized;
44
+ private now;
45
+ private enforceRateLimit;
46
+ private createAuditRecord;
47
+ private getAuthenticatedContext;
48
+ private verifyCurrentPassword;
49
+ private findActiveFactors;
50
+ private getPasswordSnapshot;
51
+ private createChallengeRecord;
52
+ private getChallenge;
53
+ createSignInChallenge(input: {
54
+ readonly userId: string;
55
+ readonly ipAddress: string;
56
+ }): Promise<AuthMfaPendingResult | null>;
57
+ createEnrollmentChallenge(input: {
58
+ readonly userId: string;
59
+ readonly ipAddress: string;
60
+ }): Promise<AuthMfaEnrollmentPendingResult>;
61
+ getPendingAuthState(token: string): Promise<AuthState | null>;
62
+ getAvailableFactors(challengeToken: string): Promise<readonly AuthMfaAvailableFactor[]>;
63
+ private failChallenge;
64
+ private createRecoveryCodeBundle;
65
+ private getRecoveryCodeRecord;
66
+ private createAuthenticatedResult;
67
+ /**
68
+ * Atomically consumes the challenge and the verified factor state.
69
+ *
70
+ * A session is created before the batch, but its raw token is never
71
+ * returned until the batch has successfully committed.
72
+ */
73
+ private completeChallenge;
74
+ beginRequiredTotpEnrollment(input: BeginRequiredMfaEnrollmentInput): Promise<TotpEnrollmentStarted>;
75
+ verifyRequiredTotpEnrollment(input: VerifyRequiredTotpEnrollmentInput): Promise<AuthMfaEnrollmentCompleted>;
76
+ beginRequiredPasskeyEnrollment(input: BeginRequiredMfaEnrollmentInput): Promise<PasskeyRegistrationStarted>;
77
+ finishRequiredPasskeyEnrollment(input: FinishRequiredPasskeyEnrollmentInput): Promise<AuthMfaEnrollmentCompleted>;
78
+ beginTotpEnrollment(input: BeginTotpEnrollmentInput): Promise<TotpEnrollmentStarted>;
79
+ verifyTotpEnrollment(input: VerifyTotpEnrollmentInput): Promise<MfaEnrollmentCompleted>;
80
+ verifySignInTotp(input: CompleteMfaSignInInput): Promise<AuthResult>;
81
+ verifySignInRecoveryCode(input: CompleteMfaSignInInput): Promise<AuthResult>;
82
+ hasActiveFactor(userId: string): Promise<boolean>;
83
+ listFactors(sessionToken: string): Promise<readonly AuthMfaFactor[]>;
84
+ getRecoveryCodeCount(sessionToken: string): Promise<number>;
85
+ regenerateRecoveryCodes(input: RegenerateRecoveryCodesInput): Promise<readonly string[]>;
86
+ removeFactor(input: RemoveMfaFactorInput): Promise<void>;
87
+ private getWebAuthn;
88
+ private assertWebAuthnOptions;
89
+ beginPasskeyRegistration(input: BeginPasskeyRegistrationInput): Promise<PasskeyRegistrationStarted>;
90
+ finishPasskeyRegistration(input: FinishPasskeyRegistrationInput): Promise<MfaEnrollmentCompleted>;
91
+ beginPasskeySignIn(input: BeginPasskeySignInInput): Promise<PasskeySignInStarted>;
92
+ private verifyPasskeyAssertion;
93
+ finishPasskeySignIn(input: {
94
+ readonly challengeToken: string;
95
+ readonly response: unknown;
96
+ readonly ipAddress: string;
97
+ }): Promise<AuthResult>;
98
+ beginPasskeyMfa(challengeToken: string, ipAddress: string): Promise<PasskeyMfaStarted>;
99
+ finishPasskeyMfa(input: PasskeyMfaCompletedInput): Promise<AuthResult>;
100
+ }
@@ -0,0 +1,100 @@
1
+ import { PasswordHasher, RateLimitProvider } from "./providers/provider.types.js";
2
+ import { AuthSecretProtector, WebAuthnProvider } from "./providers/mfa-provider.types.js";
3
+ import { AuthMfaAvailableFactor, AuthMfaEnrollmentCompleted, AuthMfaEnrollmentPendingResult, AuthMfaFactor, AuthMfaGateway, AuthMfaManagerOptions, AuthMfaPendingResult, BeginPasskeyRegistrationInput, BeginPasskeySignInInput, BeginRequiredMfaEnrollmentInput, BeginTotpEnrollmentInput, CompleteMfaSignInInput, FinishPasskeyRegistrationInput, FinishRequiredPasskeyEnrollmentInput, MfaEnrollmentCompleted, PasskeyMfaCompletedInput, PasskeyMfaStarted, PasskeyRegistrationStarted, PasskeySignInStarted, RegenerateRecoveryCodesInput, RemoveMfaFactorInput, TotpEnrollmentStarted, VerifyRequiredTotpEnrollmentInput, VerifyTotpEnrollmentInput } from "./types/mfa.types.js";
4
+ import { AuthResult } from "./types/auth-manager.types.js";
5
+ import { AuthState } from "./types/state.types.js";
6
+ import { StorageAdapter } from "@puzzle-ui/storage";
7
+ import { SessionManager } from "@puzzle-ui/session";
8
+ export type StorageAuthMfaManagerDependencies = {
9
+ readonly storage: StorageAdapter;
10
+ readonly sessions: SessionManager;
11
+ readonly passwordHasher: PasswordHasher;
12
+ readonly rateLimitProvider: RateLimitProvider;
13
+ readonly secretProtector: AuthSecretProtector;
14
+ readonly webAuthnProvider?: WebAuthnProvider;
15
+ };
16
+ export declare class StorageAuthMfaManager implements AuthMfaGateway {
17
+ private readonly storage;
18
+ private readonly sessions;
19
+ private readonly passwordHasher;
20
+ private readonly rateLimitProvider;
21
+ private readonly secretProtector;
22
+ private readonly webAuthnProvider;
23
+ private readonly usersCollection;
24
+ private readonly credentialsCollection;
25
+ private readonly auditCollection;
26
+ private readonly factorsCollection;
27
+ private readonly challengesCollection;
28
+ private readonly recoveryCodesCollection;
29
+ private readonly rateLimitKeySecret;
30
+ private readonly challengeLifetimeSeconds;
31
+ private readonly recoveryCodeCount;
32
+ private readonly issuer;
33
+ private readonly totp;
34
+ private readonly webAuthn;
35
+ private readonly requireVerifiedEmail;
36
+ private readonly clock;
37
+ private initialized;
38
+ private initializationPromise;
39
+ constructor(dependencies: StorageAuthMfaManagerDependencies, options: AuthMfaManagerOptions);
40
+ revokeChallenges(userId: string): Promise<void>;
41
+ private validateWebAuthnConfiguration;
42
+ initialize(): Promise<void>;
43
+ private assertInitialized;
44
+ private now;
45
+ private enforceRateLimit;
46
+ private createAuditRecord;
47
+ private getAuthenticatedContext;
48
+ private verifyCurrentPassword;
49
+ private findActiveFactors;
50
+ private getPasswordSnapshot;
51
+ private createChallengeRecord;
52
+ private getChallenge;
53
+ createSignInChallenge(input: {
54
+ readonly userId: string;
55
+ readonly ipAddress: string;
56
+ }): Promise<AuthMfaPendingResult | null>;
57
+ createEnrollmentChallenge(input: {
58
+ readonly userId: string;
59
+ readonly ipAddress: string;
60
+ }): Promise<AuthMfaEnrollmentPendingResult>;
61
+ getPendingAuthState(token: string): Promise<AuthState | null>;
62
+ getAvailableFactors(challengeToken: string): Promise<readonly AuthMfaAvailableFactor[]>;
63
+ private failChallenge;
64
+ private createRecoveryCodeBundle;
65
+ private getRecoveryCodeRecord;
66
+ private createAuthenticatedResult;
67
+ /**
68
+ * Atomically consumes the challenge and the verified factor state.
69
+ *
70
+ * A session is created before the batch, but its raw token is never
71
+ * returned until the batch has successfully committed.
72
+ */
73
+ private completeChallenge;
74
+ beginRequiredTotpEnrollment(input: BeginRequiredMfaEnrollmentInput): Promise<TotpEnrollmentStarted>;
75
+ verifyRequiredTotpEnrollment(input: VerifyRequiredTotpEnrollmentInput): Promise<AuthMfaEnrollmentCompleted>;
76
+ beginRequiredPasskeyEnrollment(input: BeginRequiredMfaEnrollmentInput): Promise<PasskeyRegistrationStarted>;
77
+ finishRequiredPasskeyEnrollment(input: FinishRequiredPasskeyEnrollmentInput): Promise<AuthMfaEnrollmentCompleted>;
78
+ beginTotpEnrollment(input: BeginTotpEnrollmentInput): Promise<TotpEnrollmentStarted>;
79
+ verifyTotpEnrollment(input: VerifyTotpEnrollmentInput): Promise<MfaEnrollmentCompleted>;
80
+ verifySignInTotp(input: CompleteMfaSignInInput): Promise<AuthResult>;
81
+ verifySignInRecoveryCode(input: CompleteMfaSignInInput): Promise<AuthResult>;
82
+ hasActiveFactor(userId: string): Promise<boolean>;
83
+ listFactors(sessionToken: string): Promise<readonly AuthMfaFactor[]>;
84
+ getRecoveryCodeCount(sessionToken: string): Promise<number>;
85
+ regenerateRecoveryCodes(input: RegenerateRecoveryCodesInput): Promise<readonly string[]>;
86
+ removeFactor(input: RemoveMfaFactorInput): Promise<void>;
87
+ private getWebAuthn;
88
+ private assertWebAuthnOptions;
89
+ beginPasskeyRegistration(input: BeginPasskeyRegistrationInput): Promise<PasskeyRegistrationStarted>;
90
+ finishPasskeyRegistration(input: FinishPasskeyRegistrationInput): Promise<MfaEnrollmentCompleted>;
91
+ beginPasskeySignIn(input: BeginPasskeySignInInput): Promise<PasskeySignInStarted>;
92
+ private verifyPasskeyAssertion;
93
+ finishPasskeySignIn(input: {
94
+ readonly challengeToken: string;
95
+ readonly response: unknown;
96
+ readonly ipAddress: string;
97
+ }): Promise<AuthResult>;
98
+ beginPasskeyMfa(challengeToken: string, ipAddress: string): Promise<PasskeyMfaStarted>;
99
+ finishPasskeyMfa(input: PasskeyMfaCompletedInput): Promise<AuthResult>;
100
+ }