@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.
- package/README.md +301 -2
- package/dist/LICENSE +21 -0
- package/dist/auth.lifecycle.manager.cjs +835 -0
- package/dist/auth.lifecycle.manager.cjs.map +1 -0
- package/dist/auth.lifecycle.manager.d.cts +115 -0
- package/dist/auth.lifecycle.manager.d.ts +115 -0
- package/dist/auth.lifecycle.manager.js +835 -0
- package/dist/auth.lifecycle.manager.js.map +1 -0
- package/dist/auth.manager.cjs +454 -0
- package/dist/auth.manager.cjs.map +1 -0
- package/dist/auth.manager.d.cts +54 -0
- package/dist/auth.manager.d.ts +54 -0
- package/dist/auth.manager.js +454 -0
- package/dist/auth.manager.js.map +1 -0
- package/dist/auth.mfa.manager.cjs +1488 -0
- package/dist/auth.mfa.manager.cjs.map +1 -0
- package/dist/auth.mfa.manager.d.cts +100 -0
- package/dist/auth.mfa.manager.d.ts +100 -0
- package/dist/auth.mfa.manager.js +1488 -0
- package/dist/auth.mfa.manager.js.map +1 -0
- package/dist/auth.rate-limit.cjs +78 -0
- package/dist/auth.rate-limit.cjs.map +1 -0
- package/dist/auth.rate-limit.d.cts +24 -0
- package/dist/auth.rate-limit.d.ts +24 -0
- package/dist/auth.rate-limit.js +78 -0
- package/dist/auth.rate-limit.js.map +1 -0
- package/dist/auth.step-up.manager.cjs +269 -0
- package/dist/auth.step-up.manager.cjs.map +1 -0
- package/dist/auth.step-up.manager.d.cts +74 -0
- package/dist/auth.step-up.manager.d.ts +74 -0
- package/dist/auth.step-up.manager.js +268 -0
- package/dist/auth.step-up.manager.js.map +1 -0
- package/dist/auth.storage.cjs +164 -0
- package/dist/auth.storage.cjs.map +1 -0
- package/dist/auth.storage.d.cts +29 -0
- package/dist/auth.storage.d.ts +29 -0
- package/dist/auth.storage.js +148 -0
- package/dist/auth.storage.js.map +1 -0
- package/dist/errors/auth.errors.cjs +101 -0
- package/dist/errors/auth.errors.cjs.map +1 -0
- package/dist/errors/auth.errors.d.cts +62 -0
- package/dist/errors/auth.errors.d.ts +62 -0
- package/dist/errors/auth.errors.js +90 -0
- package/dist/errors/auth.errors.js.map +1 -0
- package/dist/index.cjs +54 -0
- package/dist/index.d.cts +21 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +13 -0
- package/dist/providers/mfa-provider.types.d.cts +66 -0
- package/dist/providers/mfa-provider.types.d.ts +66 -0
- package/dist/providers/provider.types.d.cts +34 -0
- package/dist/providers/provider.types.d.ts +34 -0
- package/dist/providers/simplewebauthn-provider.cjs +185 -0
- package/dist/providers/simplewebauthn-provider.cjs.map +1 -0
- package/dist/providers/simplewebauthn-provider.d.cts +35 -0
- package/dist/providers/simplewebauthn-provider.d.ts +35 -0
- package/dist/providers/simplewebauthn-provider.js +185 -0
- package/dist/providers/simplewebauthn-provider.js.map +1 -0
- package/dist/providers/webcrypto-secret-protector.cjs +118 -0
- package/dist/providers/webcrypto-secret-protector.cjs.map +1 -0
- package/dist/providers/webcrypto-secret-protector.d.cts +14 -0
- package/dist/providers/webcrypto-secret-protector.d.ts +14 -0
- package/dist/providers/webcrypto-secret-protector.js +118 -0
- package/dist/providers/webcrypto-secret-protector.js.map +1 -0
- package/dist/types/auth-lifecycle.types.d.cts +53 -0
- package/dist/types/auth-lifecycle.types.d.ts +53 -0
- package/dist/types/auth-manager.types.d.cts +106 -0
- package/dist/types/auth-manager.types.d.ts +106 -0
- package/dist/types/credential.types.d.cts +9 -0
- package/dist/types/credential.types.d.ts +9 -0
- package/dist/types/mfa.types.d.cts +244 -0
- package/dist/types/mfa.types.d.ts +244 -0
- package/dist/types/state.types.d.cts +33 -0
- package/dist/types/state.types.d.ts +33 -0
- package/dist/types/user.types.d.cts +9 -0
- package/dist/types/user.types.d.ts +9 -0
- package/dist/utils/auth-crypto.utils.cjs +38 -0
- package/dist/utils/auth-crypto.utils.cjs.map +1 -0
- package/dist/utils/auth-crypto.utils.d.cts +4 -0
- package/dist/utils/auth-crypto.utils.d.ts +4 -0
- package/dist/utils/auth-crypto.utils.js +35 -0
- package/dist/utils/auth-crypto.utils.js.map +1 -0
- package/dist/utils/email.utils.cjs +20 -0
- package/dist/utils/email.utils.cjs.map +1 -0
- package/dist/utils/email.utils.d.cts +2 -0
- package/dist/utils/email.utils.d.ts +2 -0
- package/dist/utils/email.utils.js +19 -0
- package/dist/utils/email.utils.js.map +1 -0
- package/dist/utils/totp.utils.cjs +145 -0
- package/dist/utils/totp.utils.cjs.map +1 -0
- package/dist/utils/totp.utils.d.cts +26 -0
- package/dist/utils/totp.utils.d.ts +26 -0
- package/dist/utils/totp.utils.js +140 -0
- package/dist/utils/totp.utils.js.map +1 -0
- package/package.json +45 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.manager.js","names":[],"sources":["../src/auth.manager.ts"],"sourcesContent":["import {\n StorageDuplicateError,\n type StorageAdapter,\n type StorageBatchOperation,\n} from \"@puzzle-ui/storage\";\n\nimport type { SessionRecord, SessionManager } from \"@puzzle-ui/session\";\n\nimport type {\n PasswordHash,\n PasswordHasher,\n RateLimitProvider,\n} from \"./providers/provider.types\";\n\nimport type {\n AuthManager,\n AuthManagerOptions,\n AuthRegisterInput,\n AuthSignInInput,\n AuthSignInResult,\n AuthSession,\n AuthRateLimitPolicies,\n AuthRateLimitRule,\n AuthMfaPolicy,\n AuthRegisterResult,\n} from \"./types/auth-manager.types\";\n\nimport type {\n AuthAuditAction,\n AuthAuditEventStorageRecord,\n} from \"./types/auth-lifecycle.types\";\n\nimport type { AuthState } from \"./types/state.types\";\nimport type { AuthUser } from \"./types/user.types\";\nimport type { PasswordCredentialStorageRecord } from \"./types/credential.types\";\n\nimport {\n AUTH_AUDIT_COLLECTION,\n AUTH_CREDENTIAL_COLLECTION,\n AUTH_USER_COLLECTION,\n initializeAuthStorage,\n type AuthUserStorageRecord,\n} from \"./auth.storage\";\n\nimport {\n AuthAccountConflictError,\n AuthConfigurationError,\n AuthEmailVerificationRequiredError,\n AuthInvalidCredentialsError,\n AuthProviderError,\n AuthRateLimitError,\n AuthSessionError,\n AuthValidationError,\n} from \"./errors/auth.errors\";\n\nimport { normalizeDisplayName, normalizeEmail } from \"./utils/email.utils\";\n\nimport { createRateLimitKey, generateAuthId } from \"./utils/auth-crypto.utils\";\n\nimport type { AuthMfaGateway } from \"./types/mfa.types\";\n\nexport type StorageAuthManagerDependencies = {\n readonly storage: StorageAdapter;\n readonly passwordHasher: PasswordHasher;\n readonly rateLimitProvider: RateLimitProvider;\n readonly sessions: SessionManager;\n readonly mfa?: AuthMfaGateway;\n};\n\nconst DEFAULT_RATE_LIMITS: AuthRateLimitPolicies = {\n registration: {\n email: { limit: 5, windowSeconds: 3600 },\n ip: { limit: 10, windowSeconds: 3600 },\n },\n signIn: {\n email: { limit: 10, windowSeconds: 900 },\n ip: { limit: 30, windowSeconds: 900 },\n },\n};\n\nconst DEFAULT_MINIMUM_PASSWORD_LENGTH = 8;\nconst DEFAULT_MAXIMUM_PASSWORD_LENGTH = 1024;\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 validateRateLimitRule = (name: string, rule: AuthRateLimitRule): void => {\n if (\n !Number.isInteger(rule.limit) ||\n rule.limit < 1 ||\n !Number.isFinite(rule.windowSeconds) ||\n rule.windowSeconds <= 0\n ) {\n throw new AuthConfigurationError(\n `${name} must have a positive integer limit and a positive window.`,\n );\n }\n};\n\nconst validatePassword = (\n password: string,\n minimumLength: number,\n maximumLength: number,\n): void => {\n if (typeof password !== \"string\") {\n throw new AuthValidationError(\"A password is required.\");\n }\n\n const length = Array.from(password).length;\n const byteLength = new TextEncoder().encode(password).byteLength;\n\n if (length < minimumLength) {\n throw new AuthValidationError(\n `Password must contain at least ${minimumLength} characters.`,\n );\n }\n\n if (length > maximumLength || byteLength > 4096) {\n throw new AuthValidationError(\n `Password must contain no more than ${maximumLength} characters.`,\n );\n }\n};\n\nconst validateSignInPassword = (\n password: string,\n maximumLength: number,\n): void => {\n if (typeof password !== \"string\" || password.length === 0) {\n throw new AuthValidationError(\"A password is required.\");\n }\n\n if (\n Array.from(password).length > maximumLength ||\n new TextEncoder().encode(password).byteLength > 4096\n ) {\n throw new AuthValidationError(\"The supplied password is too long.\");\n }\n};\n\nconst validateIpAddress = (ipAddress: string): string => {\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 return ipAddress.trim();\n};\n\nexport class StorageAuthManager implements AuthManager {\n private readonly storage: StorageAdapter;\n private readonly passwordHasher: PasswordHasher;\n private readonly rateLimitProvider: RateLimitProvider;\n private readonly sessions: SessionManager;\n private readonly mfa?: AuthMfaGateway;\n private readonly mfaPolicy: AuthMfaPolicy;\n\n private readonly usersCollection: string;\n private readonly credentialsCollection: string;\n private readonly auditCollection: string;\n\n private readonly minimumPasswordLength: number;\n private readonly maximumPasswordLength: number;\n\n private readonly rateLimitPolicies: AuthRateLimitPolicies;\n private readonly rateLimitKeySecret: string;\n private readonly requireVerifiedEmail: boolean;\n private readonly clock: () => Date;\n\n private initializationPromise: Promise<void> | null = null;\n private dummyPasswordHash: PasswordHash | null = null;\n\n constructor(\n dependencies: StorageAuthManagerDependencies,\n options: AuthManagerOptions,\n ) {\n this.storage = dependencies.storage;\n this.passwordHasher = dependencies.passwordHasher;\n this.rateLimitProvider = dependencies.rateLimitProvider;\n this.sessions = dependencies.sessions;\n this.mfa = dependencies.mfa;\n this.mfaPolicy = options.mfaPolicy ?? {};\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 this.requireVerifiedEmail = options.requireVerifiedEmail ?? false;\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.minimumPasswordLength =\n options.minimumPasswordLength ?? DEFAULT_MINIMUM_PASSWORD_LENGTH;\n\n this.maximumPasswordLength =\n options.maximumPasswordLength ?? DEFAULT_MAXIMUM_PASSWORD_LENGTH;\n\n if (\n !Number.isInteger(this.minimumPasswordLength) ||\n !Number.isInteger(this.maximumPasswordLength) ||\n this.minimumPasswordLength < 1 ||\n this.maximumPasswordLength < this.minimumPasswordLength\n ) {\n throw new AuthConfigurationError(\n \"Password length configuration is invalid.\",\n );\n }\n\n this.rateLimitPolicies = {\n registration: {\n ...DEFAULT_RATE_LIMITS.registration,\n ...options.rateLimits?.registration,\n },\n signIn: {\n ...DEFAULT_RATE_LIMITS.signIn,\n ...options.rateLimits?.signIn,\n },\n };\n\n validateRateLimitRule(\n \"Registration email rate limit\",\n this.rateLimitPolicies.registration.email,\n );\n\n validateRateLimitRule(\n \"Registration IP rate limit\",\n this.rateLimitPolicies.registration.ip,\n );\n\n validateRateLimitRule(\n \"Sign-in email rate limit\",\n this.rateLimitPolicies.signIn.email,\n );\n\n validateRateLimitRule(\n \"Sign-in IP rate limit\",\n this.rateLimitPolicies.signIn.ip,\n );\n\n this.clock = options.clock ?? (() => new Date());\n }\n\n async initialize(): Promise<void> {\n if (!this.initializationPromise) {\n const initialization = (async () => {\n await initializeAuthStorage(this.storage, {\n usersCollection: this.usersCollection,\n credentialsCollection: this.credentialsCollection,\n auditCollection: this.auditCollection,\n });\n\n // MFA storage must be initialized before authentication is exposed.\n // An initialization failure therefore fails closed.\n await this.mfa?.initialize();\n\n this.dummyPasswordHash = await this.hashPassword(\n generateAuthId(\"auth-dummy\"),\n );\n })();\n\n this.initializationPromise = initialization.catch((error) => {\n this.initializationPromise = null;\n throw error;\n });\n }\n\n return this.initializationPromise;\n }\n\n private assertInitialized(): void {\n if (!this.dummyPasswordHash) {\n throw new AuthConfigurationError(\n \"AuthManager must be initialized before it can be used.\",\n );\n }\n }\n\n private now(): Date {\n const now = this.clock();\n\n if (!(now instanceof Date) || !Number.isFinite(now.getTime())) {\n throw new AuthConfigurationError(\n \"The configured clock returned an invalid date.\",\n );\n }\n\n return now;\n }\n\n private createAuditRecord(\n action: AuthAuditAction,\n userId: string | null,\n sessionId: string | null = null,\n metadata?: AuthAuditEventStorageRecord[\"metadata\"],\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 recordAudit(\n action: AuthAuditAction,\n userId: string | null,\n sessionId: string | null = null,\n metadata?: AuthAuditEventStorageRecord[\"metadata\"],\n ): Promise<void> {\n try {\n await this.storage.insert<AuthAuditEventStorageRecord>(\n this.auditCollection,\n this.createAuditRecord(action, userId, sessionId, metadata),\n );\n } catch (error) {\n throw new AuthProviderError(\n \"Could not record the authentication audit event.\",\n { cause: error },\n );\n }\n }\n\n private async hashPassword(password: string): Promise<PasswordHash> {\n try {\n const result = await this.passwordHasher.hash(password);\n\n if (\n !result ||\n result.algorithm !== \"argon2id\" ||\n typeof result.hash !== \"string\" ||\n result.hash.length === 0\n ) {\n throw new Error(\"PasswordHasher returned an invalid Argon2id hash.\");\n }\n\n return result;\n } catch (error) {\n throw new AuthProviderError(\"Password hashing failed.\", {\n cause: error,\n });\n }\n }\n\n private async verifyPassword(\n password: string,\n stored: PasswordHash,\n ): Promise<boolean> {\n try {\n return await this.passwordHasher.verify(password, stored);\n } catch (error) {\n throw new AuthProviderError(\"Password verification failed.\", {\n cause: error,\n });\n }\n }\n\n private async enforceRateLimits(\n action: \"registration\" | \"signIn\",\n email: string,\n ipAddress: string,\n ): Promise<void> {\n const policy = this.rateLimitPolicies[action];\n\n const checks = [\n {\n scope: \"email\",\n identifier: email,\n rule: policy.email,\n },\n {\n scope: \"ip\",\n identifier: ipAddress,\n rule: policy.ip,\n },\n ] as const;\n\n for (const check of checks) {\n const key = await createRateLimitKey(\n this.rateLimitKeySecret,\n `auth:${action}:${check.scope}:${check.identifier}`,\n );\n\n let decision: Awaited<ReturnType<RateLimitProvider[\"consume\"]>>;\n\n try {\n decision = await this.rateLimitProvider.consume({\n key,\n limit: check.rule.limit,\n windowSeconds: check.rule.windowSeconds,\n });\n } catch (error) {\n throw new AuthProviderError(\n \"The authentication rate limiter is unavailable.\",\n { cause: error },\n );\n }\n\n if (\n !decision ||\n typeof decision.allowed !== \"boolean\" ||\n !(decision.resetAt instanceof Date) ||\n !Number.isFinite(decision.resetAt.getTime())\n ) {\n throw new AuthProviderError(\n \"The rate limiter returned an invalid response.\",\n );\n }\n\n if (!decision.allowed) {\n throw new AuthRateLimitError(decision.resetAt);\n }\n }\n }\n\n private async findUserByEmail(\n normalizedEmail: string,\n ): Promise<AuthUserStorageRecord | null> {\n return this.storage.findOne<AuthUserStorageRecord>(this.usersCollection, {\n where: {\n normalizedEmail: {\n operator: \"eq\",\n value: normalizedEmail,\n },\n },\n });\n }\n\n private isSessionValidForUser(\n session: SessionRecord,\n user: AuthUserStorageRecord,\n ): boolean {\n if (user.deactivatedAt != null) return false;\n\n if (\n user.sessionsInvalidatedAt &&\n session.createdAt.getTime() <= user.sessionsInvalidatedAt.getTime()\n ) {\n return false;\n }\n\n return true;\n }\n\n private async isMfaRequired(user: AuthUser): Promise<boolean> {\n if (this.mfaPolicy.required === true) {\n return true;\n }\n\n if (this.mfaPolicy.requiredFor) {\n return Boolean(await this.mfaPolicy.requiredFor(user));\n }\n\n return false;\n }\n\n private async hasSatisfiedMfaPolicy(user: AuthUser): Promise<boolean> {\n const required = await this.isMfaRequired(user);\n\n if (!required) {\n return true;\n }\n\n if (!this.mfa) {\n throw new AuthConfigurationError(\n \"MFA is required by policy, but no MFA manager is configured.\",\n );\n }\n\n return this.mfa.hasActiveFactor(user.id);\n }\n\n private async createAuthenticatedSession(\n userId: string,\n metadata?: Readonly<Record<string, unknown>>,\n ): Promise<{\n readonly session: AuthSession;\n readonly token: string;\n }> {\n try {\n const result = await this.sessions.create({\n userId,\n kind: \"authenticated\",\n metadata,\n });\n\n if (\n !result.token ||\n result.session.userId !== userId ||\n result.session.kind !== \"authenticated\"\n ) {\n throw new Error(\n \"SessionManager returned an invalid authenticated session.\",\n );\n }\n\n return {\n session: toPublicSession(result.session),\n token: result.token,\n };\n } catch (error) {\n if (error instanceof AuthSessionError) {\n throw error;\n }\n\n throw new AuthSessionError(\"Could not create an authenticated session.\", {\n cause: error,\n });\n }\n }\n\n async register(input: AuthRegisterInput): Promise<AuthRegisterResult> {\n this.assertInitialized();\n\n const normalizedEmail = normalizeEmail(input.email);\n const name = normalizeDisplayName(input.name);\n const ipAddress = validateIpAddress(input.ipAddress);\n\n validatePassword(\n input.password,\n this.minimumPasswordLength,\n this.maximumPasswordLength,\n );\n\n await this.enforceRateLimits(\"registration\", normalizedEmail, ipAddress);\n\n const passwordHash = await this.hashPassword(input.password);\n const now = this.now();\n\n const userId = generateAuthId(\"user\");\n const credentialId = generateAuthId(\"credential\");\n\n const userRecord: AuthUserStorageRecord = {\n id: userId,\n email: normalizedEmail,\n normalizedEmail,\n emailVerifiedAt: null,\n name,\n image: null,\n deactivatedAt: null,\n sessionsInvalidatedAt: null,\n createdAt: now,\n updatedAt: now,\n revision: 1,\n };\n\n const credentialRecord: PasswordCredentialStorageRecord = {\n id: credentialId,\n userId,\n kind: \"password\",\n passwordHash,\n passwordUpdatedAt: now,\n createdAt: now,\n updatedAt: now,\n revokedAt: null,\n revision: 1,\n };\n\n const publicUser = toPublicUser(userRecord);\n const mfaRequired = await this.isMfaRequired(publicUser);\n\n if (mfaRequired && !this.mfa) {\n throw new AuthConfigurationError(\n \"MFA is required by policy, but no MFA manager is configured.\",\n );\n }\n\n const auditRecord = this.createAuditRecord(\"account-registered\", userId);\n\n const operations: StorageBatchOperation<\n | AuthUserStorageRecord\n | PasswordCredentialStorageRecord\n | AuthAuditEventStorageRecord\n >[] = [\n {\n type: \"insert\",\n collection: this.usersCollection,\n record: userRecord,\n },\n {\n type: \"insert\",\n collection: this.credentialsCollection,\n record: credentialRecord,\n },\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: auditRecord,\n },\n ];\n\n try {\n await this.storage.atomicBatch(operations);\n } catch (error) {\n if (error instanceof StorageDuplicateError) {\n throw new AuthAccountConflictError({ cause: error });\n }\n\n throw error;\n }\n\n if (mfaRequired) {\n // No authenticated session is created here.\n // The user must complete their first MFA enrollment first.\n return this.mfa!.createEnrollmentChallenge({\n userId,\n ipAddress,\n });\n }\n\n const authenticated = await this.createAuthenticatedSession(\n userId,\n input.sessionMetadata,\n );\n\n return {\n user: publicUser,\n session: authenticated.session,\n token: authenticated.token,\n };\n }\n\n async signIn(input: AuthSignInInput): Promise<AuthSignInResult> {\n this.assertInitialized();\n\n const normalizedEmail = normalizeEmail(input.email);\n const ipAddress = validateIpAddress(input.ipAddress);\n\n validateSignInPassword(input.password, this.maximumPasswordLength);\n\n await this.enforceRateLimits(\"signIn\", normalizedEmail, ipAddress);\n\n const userRecord = await this.findUserByEmail(normalizedEmail);\n\n const credential = userRecord\n ? await this.storage.findOne<PasswordCredentialStorageRecord>(\n this.credentialsCollection,\n {\n where: {\n userId: {\n operator: \"eq\",\n value: userRecord.id,\n },\n kind: {\n operator: \"eq\",\n value: \"password\",\n },\n },\n },\n )\n : null;\n\n const activeCredential =\n credential && credential.revokedAt === null ? credential : null;\n\n const passwordHash =\n activeCredential?.passwordHash ?? this.dummyPasswordHash!;\n\n const passwordMatches = await this.verifyPassword(\n input.password,\n passwordHash,\n );\n\n if (!userRecord || !activeCredential || !passwordMatches) {\n await this.recordAudit(\"sign-in-failed\", userRecord?.id ?? null, null, {\n reason: \"invalid-credentials\",\n });\n\n throw new AuthInvalidCredentialsError();\n }\n\n if (userRecord.deactivatedAt != null) {\n await this.recordAudit(\"sign-in-failed\", userRecord.id, null, {\n reason: \"account-deactivated\",\n });\n\n throw new AuthInvalidCredentialsError();\n }\n\n if (this.requireVerifiedEmail && !userRecord.emailVerifiedAt) {\n await this.recordAudit(\"sign-in-failed\", userRecord.id, null, {\n reason: \"email-not-verified\",\n });\n\n throw new AuthEmailVerificationRequiredError();\n }\n\n // CRITICAL:\n // When active factors exist, stop here.\n // Do not create a normal authenticated session yet.\n // When active factors exist, the user must verify one before\n // an authenticated session can be created.\n const publicUser = toPublicUser(userRecord);\n const mfaRequired = await this.isMfaRequired(publicUser);\n\n const mfaPending = await this.mfa?.createSignInChallenge({\n userId: userRecord.id,\n ipAddress,\n });\n\n if (mfaPending) {\n return mfaPending;\n }\n\n if (mfaRequired) {\n if (!this.mfa) {\n throw new AuthConfigurationError(\n \"MFA is required by policy, but no MFA manager is configured.\",\n );\n }\n\n // The password was verified, but no active MFA factor exists.\n // Return an enrollment challenge instead of creating a session.\n return this.mfa.createEnrollmentChallenge({\n userId: userRecord.id,\n ipAddress,\n });\n }\n\n const authenticated = await this.createAuthenticatedSession(\n userRecord.id,\n input.sessionMetadata,\n );\n\n try {\n await this.recordAudit(\n \"sign-in\",\n userRecord.id,\n authenticated.session.id,\n );\n } catch (error) {\n try {\n await this.sessions.revoke(authenticated.session.id);\n } catch (revokeError) {\n throw new AuthSessionError(\n \"Could not revoke a session after an audit failure.\",\n { cause: revokeError },\n );\n }\n\n throw error;\n }\n\n return {\n user: toPublicUser(userRecord),\n session: authenticated.session,\n token: authenticated.token,\n };\n }\n\n private async validateSession(token: string): Promise<SessionRecord | null> {\n if (typeof token !== \"string\" || token.length === 0) {\n return null;\n }\n\n try {\n return await this.sessions.validate(token);\n } catch (error) {\n throw new AuthSessionError(\n \"Could not validate the authentication session.\",\n { cause: error },\n );\n }\n }\n\n async getCurrentUser(token: string): Promise<AuthUser | null> {\n this.assertInitialized();\n\n const session = await this.validateSession(token);\n\n if (!session || session.kind !== \"authenticated\" || !session.userId) {\n return null;\n }\n\n const user = await this.storage.findById<AuthUserStorageRecord>(\n this.usersCollection,\n session.userId,\n );\n\n if (!user) return null;\n\n if (!this.isSessionValidForUser(session, user)) return null;\n\n if (this.requireVerifiedEmail && !user.emailVerifiedAt) {\n return null;\n }\n\n const publicUser = toPublicUser(user);\n\n if (!(await this.hasSatisfiedMfaPolicy(publicUser))) {\n return null;\n }\n\n return publicUser;\n }\n\n async getAuthState(token: string): Promise<AuthState> {\n this.assertInitialized();\n\n // A temporary MFA token is never treated as an authenticated session.\n const pendingMfa = await this.mfa?.getPendingAuthState(token);\n\n if (pendingMfa) {\n return pendingMfa;\n }\n\n const session = await this.validateSession(token);\n\n if (!session || session.kind !== \"authenticated\" || !session.userId) {\n return { status: \"unauthenticated\" };\n }\n\n const user = await this.storage.findById<AuthUserStorageRecord>(\n this.usersCollection,\n session.userId,\n );\n\n if (!user) {\n return { status: \"unauthenticated\" };\n }\n\n if (!this.isSessionValidForUser(session, user)) {\n return { status: \"unauthenticated\" };\n }\n\n if (this.requireVerifiedEmail && !user.emailVerifiedAt) {\n return { status: \"unauthenticated\" };\n }\n\n const publicUser = toPublicUser(user);\n\n if (!(await this.hasSatisfiedMfaPolicy(publicUser))) {\n return {\n status: \"mfa-enrollment-required\",\n userId: user.id,\n sessionId: session.id,\n };\n }\n\n return {\n status: \"authenticated\",\n userId: user.id,\n sessionId: session.id,\n };\n }\n\n async signOut(token: string): Promise<void> {\n this.assertInitialized();\n\n const session = await this.validateSession(token);\n\n if (!session) return;\n\n try {\n await this.sessions.revoke(session.id);\n } catch (error) {\n throw new AuthSessionError(\n \"Could not revoke the authentication session.\",\n { cause: error },\n );\n }\n\n await this.recordAudit(\"sign-out\", session.userId, session.id);\n }\n\n async signOutEverywhere(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 try {\n await this.sessions.revokeAll(userId);\n } catch (error) {\n throw new AuthSessionError(\"Could not revoke the user's sessions.\", {\n cause: error,\n });\n }\n\n try {\n await this.mfa?.revokeChallenges(userId);\n } catch (error) {\n throw new AuthProviderError(\"Could not revoke pending MFA challenges.\", {\n cause: error,\n });\n }\n\n await this.recordAudit(\"sign-out-everywhere\", userId);\n }\n}\n"],"mappings":"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"}
|