@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.lifecycle.manager.cjs","names":["AuthValidationError","AuthConfigurationError","StorageRateLimitProvider","initializeAuthStorage","createRateLimitKey","AuthProviderError","AuthRateLimitError","generateSecureAuthToken","generateAuthId","hashAuthToken","StorageConflictError","StorageDuplicateError","AuthAccountConflictError","AuthSessionError","AuthEmailVerificationRequiredError","AuthTokenError","normalizeEmail","normalizeDisplayName"],"sources":["../src/auth.lifecycle.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 EmailMessage,\n EmailProvider,\n PasswordHash,\n PasswordHasher,\n RateLimitProvider,\n} from \"./providers/provider.types\";\n\nimport {\n AuthAccountConflictError,\n AuthConfigurationError,\n AuthProviderError,\n AuthRateLimitError,\n AuthSessionError,\n AuthTokenError,\n AuthValidationError,\n AuthEmailVerificationRequiredError,\n} from \"./errors/auth.errors\";\n\nimport {\n AUTH_ACTION_TOKEN_COLLECTION,\n AUTH_AUDIT_COLLECTION,\n AUTH_CREDENTIAL_COLLECTION,\n AUTH_RATE_LIMIT_COLLECTION,\n AUTH_USER_COLLECTION,\n initializeAuthStorage,\n type AuthUserStorageRecord,\n} from \"./auth.storage\";\n\nimport type {\n AuthActionTokenStorageRecord,\n AuthAuditAction,\n AuthAuditEventStorageRecord,\n AuthLifecycleManagerOptions,\n AuthLifecycleRateLimitAction,\n} from \"./types/auth-lifecycle.types\";\n\nimport type { PasswordCredentialStorageRecord } from \"./types/credential.types\";\nimport type { AuthUser } from \"./types/user.types\";\n\nimport {\n generateAuthId,\n generateSecureAuthToken,\n hashAuthToken,\n createRateLimitKey,\n} from \"./utils/auth-crypto.utils\";\n\nimport { normalizeDisplayName, normalizeEmail } from \"./utils/email.utils\";\n\nimport { StorageRateLimitProvider } from \"./auth.rate-limit\";\n\nexport type StorageAuthLifecycleManagerDependencies = {\n readonly storage: StorageAdapter;\n readonly passwordHasher: PasswordHasher;\n readonly emailProvider: EmailProvider;\n readonly sessions: SessionManager;\n readonly rateLimitProvider?: RateLimitProvider;\n};\n\nexport type RequestEmailVerificationInput = {\n readonly userId: string;\n readonly verificationUrl: string;\n readonly ipAddress: string;\n};\n\nexport type RequestPasswordResetInput = {\n readonly email: string;\n readonly resetUrl: string;\n readonly ipAddress: string;\n};\n\nexport type ResetPasswordInput = {\n readonly token: string;\n readonly newPassword: string;\n};\n\nexport type ChangePasswordInput = {\n readonly sessionToken: string;\n readonly currentPassword: string;\n readonly newPassword: string;\n};\n\nexport type UpdateAuthProfileInput = {\n readonly sessionToken: string;\n readonly name?: string | null;\n readonly image?: string | null;\n};\n\nexport type RequestEmailChangeInput = {\n readonly sessionToken: string;\n readonly currentPassword: string;\n readonly email: string;\n readonly verificationUrl: string;\n readonly ipAddress: string;\n};\n\nexport type DeleteAuthAccountInput = {\n readonly sessionToken: string;\n readonly password: string;\n};\n\nexport type DeactivateAuthAccountInput = {\n readonly sessionToken: string;\n readonly password: string;\n};\n\ntype LifecycleRecord =\n | AuthActionTokenStorageRecord\n | AuthAuditEventStorageRecord\n | AuthUserStorageRecord\n | PasswordCredentialStorageRecord;\n\ntype LifecycleBatchOperation = StorageBatchOperation<LifecycleRecord>;\n\ntype AuthLifecycleUserContext = {\n readonly session: SessionRecord;\n readonly user: AuthUserStorageRecord;\n};\n\ntype AuthActionTokenResult = {\n readonly token: string;\n readonly record: AuthActionTokenStorageRecord;\n};\n\ntype RateLimitRule = {\n readonly limit: number;\n readonly windowSeconds: number;\n};\n\nconst DEFAULT_TOKEN_LIFETIMES = {\n emailVerification: 60 * 60,\n passwordReset: 60 * 60,\n emailChange: 60 * 60,\n} as const;\n\nconst DEFAULT_RATE_LIMITS = {\n emailVerification: {\n email: { limit: 3, windowSeconds: 60 * 60 },\n ip: { limit: 10, windowSeconds: 60 * 60 },\n },\n passwordReset: {\n email: { limit: 3, windowSeconds: 60 * 60 },\n ip: { limit: 10, windowSeconds: 60 * 60 },\n },\n emailChange: {\n email: { limit: 3, windowSeconds: 60 * 60 },\n ip: { limit: 10, windowSeconds: 60 * 60 },\n },\n} satisfies Record<\n AuthLifecycleRateLimitAction,\n {\n readonly email: RateLimitRule;\n readonly ip: RateLimitRule;\n }\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 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\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 validateImageUrl = (image: string | null | undefined): string | null => {\n if (image === undefined || image === null || image === \"\") {\n return null;\n }\n\n if (typeof image !== \"string\" || image.length > 2048) {\n throw new AuthValidationError(\"The profile image URL is invalid.\");\n }\n\n let url: URL;\n\n try {\n url = new URL(image);\n } catch {\n throw new AuthValidationError(\"The profile image URL is invalid.\");\n }\n\n if (url.protocol !== \"https:\" || url.username || url.password) {\n throw new AuthValidationError(\n \"Profile image URLs must use HTTPS and cannot contain credentials.\",\n );\n }\n\n return url.toString();\n};\n\nconst createTokenUrl = (baseUrl: string, token: string): string => {\n let url: URL;\n\n try {\n url = new URL(baseUrl);\n } catch {\n throw new AuthValidationError(\n \"The configured authentication callback URL is invalid.\",\n );\n }\n\n const isLocalhost =\n url.hostname === \"localhost\" ||\n url.hostname === \"127.0.0.1\" ||\n url.hostname === \"[::1]\";\n\n if (\n (url.protocol !== \"https:\" && !(isLocalhost && url.protocol === \"http:\")) ||\n url.username ||\n url.password\n ) {\n throw new AuthValidationError(\n \"Authentication callback URLs must use HTTPS.\",\n );\n }\n\n url.searchParams.set(\"token\", token);\n\n return url.toString();\n};\n\nconst validateRateLimitRule = (\n name: string,\n rule: RateLimitRule | undefined,\n): void => {\n if (\n !rule ||\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\nexport class StorageAuthLifecycleManager {\n private readonly storage: StorageAdapter;\n private readonly passwordHasher: PasswordHasher;\n private readonly emailProvider: EmailProvider;\n private readonly sessions: SessionManager;\n private readonly rateLimitProvider: RateLimitProvider;\n\n private readonly rateLimitKeySecret: string;\n private readonly usersCollection: string;\n private readonly credentialsCollection: string;\n private readonly actionTokensCollection: string;\n private readonly auditCollection: string;\n private readonly rateLimitsCollection: string;\n\n private readonly requireVerifiedEmail: boolean;\n private readonly onSessionRevocationError?: (error: unknown) => void;\n\n private readonly emailTemplates: {\n readonly emailVerification: string;\n readonly passwordReset: string;\n readonly emailChange: string;\n };\n\n private readonly emailSubjects: {\n readonly emailVerification: string;\n readonly passwordReset: string;\n readonly emailChange: string;\n };\n\n private readonly minimumPasswordLength: number;\n private readonly maximumPasswordLength: number;\n\n private readonly tokenLifetimes: typeof DEFAULT_TOKEN_LIFETIMES;\n private readonly rateLimits: typeof DEFAULT_RATE_LIMITS;\n private readonly clock: () => Date;\n private readonly onEmailDeliveryError?: (error: unknown) => void;\n\n private initialized = false;\n private initializationPromise: Promise<void> | null = null;\n\n constructor(\n dependencies: StorageAuthLifecycleManagerDependencies,\n options: AuthLifecycleManagerOptions,\n ) {\n this.storage = dependencies.storage;\n this.passwordHasher = dependencies.passwordHasher;\n this.emailProvider = dependencies.emailProvider;\n this.sessions = dependencies.sessions;\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.actionTokensCollection =\n options.actionTokensCollection ?? AUTH_ACTION_TOKEN_COLLECTION;\n\n this.auditCollection = options.auditCollection ?? AUTH_AUDIT_COLLECTION;\n\n this.rateLimitsCollection =\n options.rateLimitsCollection ?? AUTH_RATE_LIMIT_COLLECTION;\n\n this.requireVerifiedEmail = options.requireVerifiedEmail ?? false;\n this.onSessionRevocationError = options.onSessionRevocationError;\n\n this.emailTemplates = {\n emailVerification:\n options.emailTemplates?.emailVerification ?? \"auth/email-verification\",\n passwordReset:\n options.emailTemplates?.passwordReset ?? \"auth/password-reset\",\n emailChange:\n options.emailTemplates?.emailChange ?? \"auth/email-change-verification\",\n };\n\n this.emailSubjects = {\n emailVerification:\n options.emailSubjects?.emailVerification ?? \"Verify your email address\",\n passwordReset:\n options.emailSubjects?.passwordReset ?? \"Reset your password\",\n emailChange:\n options.emailSubjects?.emailChange ?? \"Confirm your new email address\",\n };\n\n this.minimumPasswordLength = options.minimumPasswordLength ?? 8;\n this.maximumPasswordLength = options.maximumPasswordLength ?? 1024;\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.tokenLifetimes = {\n emailVerification:\n options.emailVerificationTokenLifetimeSeconds ??\n DEFAULT_TOKEN_LIFETIMES.emailVerification,\n passwordReset:\n options.passwordResetTokenLifetimeSeconds ??\n DEFAULT_TOKEN_LIFETIMES.passwordReset,\n emailChange:\n options.emailChangeTokenLifetimeSeconds ??\n DEFAULT_TOKEN_LIFETIMES.emailChange,\n };\n\n for (const [name, lifetime] of Object.entries(this.tokenLifetimes)) {\n if (!Number.isFinite(lifetime) || lifetime <= 0) {\n throw new AuthConfigurationError(\n `${name} token lifetime must be positive.`,\n );\n }\n }\n\n this.rateLimits = {\n emailVerification: {\n ...DEFAULT_RATE_LIMITS.emailVerification,\n ...options.rateLimits?.emailVerification,\n },\n passwordReset: {\n ...DEFAULT_RATE_LIMITS.passwordReset,\n ...options.rateLimits?.passwordReset,\n },\n emailChange: {\n ...DEFAULT_RATE_LIMITS.emailChange,\n ...options.rateLimits?.emailChange,\n },\n };\n\n for (const [action, policy] of Object.entries(this.rateLimits)) {\n validateRateLimitRule(`${action} email rate limit`, policy.email);\n validateRateLimitRule(`${action} IP rate limit`, policy.ip);\n }\n\n this.clock = options.clock ?? (() => new Date());\n this.onEmailDeliveryError = options.onEmailDeliveryError;\n\n if (\n !this.storage.capabilities.conditionalWrites ||\n !this.storage.capabilities.atomicBatch ||\n !this.storage.capabilities.uniqueConstraints\n ) {\n throw new AuthConfigurationError(\n \"Auth lifecycle workflows require conditional writes, unique constraints and atomic batches.\",\n );\n }\n\n this.rateLimitProvider =\n dependencies.rateLimitProvider ??\n new StorageRateLimitProvider(this.storage, {\n collectionName: this.rateLimitsCollection,\n clock: this.clock,\n });\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 actionTokensCollection: this.actionTokensCollection,\n auditCollection: this.auditCollection,\n rateLimitsCollection: this.rateLimitsCollection,\n });\n\n if (this.rateLimitProvider instanceof StorageRateLimitProvider) {\n await this.rateLimitProvider.initialize();\n }\n\n this.initialized = true;\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.initialized) {\n throw new AuthConfigurationError(\n \"Auth lifecycle manager must be initialized before use.\",\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 async consumeRateLimit(\n action: AuthLifecycleRateLimitAction,\n scope: \"email\" | \"ip\",\n identifier: string,\n ): Promise<void> {\n const rule = this.rateLimits[action][scope];\n\n const key = await createRateLimitKey(\n this.rateLimitKeySecret,\n `auth:${action}:${scope}:${identifier}`,\n );\n\n let decision: Awaited<ReturnType<RateLimitProvider[\"consume\"]>>;\n\n try {\n decision = await this.rateLimitProvider.consume({\n key,\n limit: rule.limit,\n windowSeconds: 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 private async enforceRateLimits(\n action: AuthLifecycleRateLimitAction,\n email: string,\n ipAddress: string,\n ): Promise<void> {\n await this.consumeRateLimit(action, \"email\", email);\n await this.consumeRateLimit(action, \"ip\", ipAddress);\n }\n\n private async createActionToken(input: {\n readonly userId: string;\n readonly purpose: AuthActionTokenStorageRecord[\"purpose\"];\n readonly lifetimeSeconds: number;\n readonly targetEmail?: string | null;\n }): Promise<AuthActionTokenResult> {\n const now = this.now();\n const token = generateSecureAuthToken();\n\n const record: AuthActionTokenStorageRecord = {\n id: generateAuthId(\"action-token\"),\n userId: input.userId,\n purpose: input.purpose,\n tokenHash: await hashAuthToken(token),\n expiresAt: new Date(now.getTime() + input.lifetimeSeconds * 1000),\n consumedAt: null,\n targetEmail: input.targetEmail ?? null,\n createdAt: now,\n updatedAt: now,\n revision: 1,\n revokedAt: null,\n };\n\n return { token, record };\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 /**\n * All operations in a lifecycle batch must succeed.\n *\n * Storage adapters may report a missing conditional update or delete\n * through the batch result rather than by throwing an error. Treat those\n * results as conflicts so a token cannot be consumed without its related\n * account or credential mutation.\n */\n private async atomic(\n operations: readonly LifecycleBatchOperation[],\n ): Promise<void> {\n try {\n const result =\n await this.storage.atomicBatch<LifecycleRecord>(operations);\n\n for (const [index, operation] of operations.entries()) {\n const operationResult = result.results[index];\n\n if (operation.type === \"update\") {\n if (\n !operationResult ||\n operationResult.type !== \"update\" ||\n !operationResult.record\n ) {\n throw new StorageConflictError(\n \"A conditional authentication update did not match its record.\",\n );\n }\n }\n\n if (operation.type === \"delete\") {\n if (\n !operationResult ||\n operationResult.type !== \"delete\" ||\n !operationResult.deleted\n ) {\n throw new StorageConflictError(\n \"A conditional authentication delete did not match its record.\",\n );\n }\n }\n\n if (operation.type === \"insert\") {\n if (!operationResult || operationResult.type !== \"insert\") {\n throw new StorageConflictError(\n \"An authentication insert did not return its record.\",\n );\n }\n }\n }\n } catch (error) {\n if (error instanceof StorageDuplicateError) {\n throw new AuthAccountConflictError({ cause: error });\n }\n\n throw error;\n }\n }\n\n private async deliverEmail(\n message: EmailMessage,\n suppressFailure: boolean,\n ): Promise<void> {\n try {\n await this.emailProvider.send(message);\n } catch (error) {\n try {\n this.onEmailDeliveryError?.(error);\n } catch {\n // Operational logging must not change the public auth response.\n }\n\n if (!suppressFailure) {\n throw new AuthProviderError(\"Authentication email delivery failed.\", {\n cause: error,\n });\n }\n }\n }\n\n private async getSessionContext(\n token: string,\n ): Promise<AuthLifecycleUserContext> {\n if (typeof token !== \"string\" || token.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(token);\n } catch (error) {\n throw new AuthSessionError(\n \"Could not validate the authentication 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(\n \"The authentication session is no longer valid.\",\n );\n }\n\n if (this.requireVerifiedEmail && !user.emailVerifiedAt) {\n throw new AuthEmailVerificationRequiredError();\n }\n\n return { session, user };\n }\n\n private async findActionToken(\n token: string,\n purpose: AuthActionTokenStorageRecord[\"purpose\"],\n ): Promise<AuthActionTokenStorageRecord> {\n if (typeof token !== \"string\" || token.length < 32 || token.length > 256) {\n throw new AuthTokenError();\n }\n\n const tokenHash = await hashAuthToken(token);\n\n const record = await this.storage.findOne<AuthActionTokenStorageRecord>(\n this.actionTokensCollection,\n {\n where: {\n tokenHash: { operator: \"eq\", value: tokenHash },\n },\n },\n );\n\n const now = this.now();\n\n if (\n !record ||\n record.purpose !== purpose ||\n record.consumedAt !== null ||\n record.revokedAt != null ||\n record.expiresAt.getTime() <= now.getTime()\n ) {\n throw new AuthTokenError();\n }\n\n return record;\n }\n\n private async findCredential(\n userId: string,\n ): Promise<PasswordCredentialStorageRecord | null> {\n return this.storage.findOne<PasswordCredentialStorageRecord>(\n this.credentialsCollection,\n {\n where: {\n userId: { operator: \"eq\", value: userId },\n kind: { operator: \"eq\", value: \"password\" },\n },\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 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.\", { cause: error });\n }\n }\n\n private async revokeAllSessions(userId: string): Promise<void> {\n try {\n await this.sessions.revokeAll(userId);\n } catch (error) {\n try {\n this.onSessionRevocationError?.(error);\n } catch {\n // Operational logging must not change the authentication result.\n }\n\n // The storage-level sessionsInvalidatedAt check already blocks old\n // sessions. Physical revocation is cleanup, not the security boundary.\n }\n }\n\n async sendEmailVerification(\n input: RequestEmailVerificationInput,\n ): Promise<void> {\n this.assertInitialized();\n\n const ipAddress = validateIpAddress(input.ipAddress);\n\n const initialUser = await this.storage.findById<AuthUserStorageRecord>(\n this.usersCollection,\n input.userId,\n );\n\n if (!initialUser) {\n throw new AuthValidationError(\"The account does not exist.\");\n }\n\n if (initialUser.emailVerifiedAt) return;\n\n await this.enforceRateLimits(\n \"emailVerification\",\n initialUser.normalizedEmail,\n ipAddress,\n );\n\n const maximumAttempts = 5;\n\n for (let attempt = 0; attempt < maximumAttempts; attempt += 1) {\n // Always reload the user after a conflict. Another request may have\n // successfully issued a newer verification token in the meantime.\n const user = await this.storage.findById<AuthUserStorageRecord>(\n this.usersCollection,\n input.userId,\n );\n\n if (!user) {\n throw new AuthValidationError(\"The account does not exist.\");\n }\n\n if (user.emailVerifiedAt) return;\n\n const created = await this.createActionToken({\n userId: user.id,\n purpose: \"email-verification\",\n lifetimeSeconds: this.tokenLifetimes.emailVerification,\n });\n\n const outstandingTokens =\n await this.storage.findMany<AuthActionTokenStorageRecord>(\n this.actionTokensCollection,\n {\n where: {\n userId: { operator: \"eq\", value: user.id },\n purpose: { operator: \"eq\", value: \"email-verification\" },\n consumedAt: { operator: \"eq\", value: null },\n },\n },\n );\n\n const operations: LifecycleBatchOperation[] = [\n {\n // This conditional update acts as the serialization point.\n // Only one concurrent request can commit against this revision.\n type: \"update\",\n collection: this.usersCollection,\n id: user.id,\n condition: {\n expectedRevision: user.revision,\n },\n changes: {\n updatedAt: created.record.createdAt,\n },\n },\n {\n type: \"insert\",\n collection: this.actionTokensCollection,\n record: created.record,\n },\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: this.createAuditRecord(\n \"email-verification-requested\",\n user.id,\n ),\n },\n ];\n\n for (const outstanding of outstandingTokens) {\n if (outstanding.revokedAt != null) continue;\n\n operations.push({\n type: \"update\",\n collection: this.actionTokensCollection,\n id: outstanding.id,\n condition: {\n expectedRevision: outstanding.revision,\n },\n changes: {\n revokedAt: created.record.createdAt,\n },\n });\n }\n\n try {\n await this.atomic(operations);\n } catch (error) {\n if (error instanceof StorageConflictError) {\n if (attempt < maximumAttempts - 1) {\n continue;\n }\n\n throw new AuthProviderError(\n \"Could not safely issue an email verification token due to concurrent account updates.\",\n { cause: error },\n );\n }\n\n throw error;\n }\n\n const verificationUrl = createTokenUrl(\n input.verificationUrl,\n created.token,\n );\n\n await this.deliverEmail(\n {\n to: user.email,\n subject: this.emailSubjects.emailVerification,\n template: this.emailTemplates.emailVerification,\n variables: {\n url: verificationUrl,\n expiresAt: created.record.expiresAt.toISOString(),\n },\n },\n false,\n );\n\n return;\n }\n }\n\n async verifyEmail(token: string): Promise<AuthUser> {\n this.assertInitialized();\n\n const actionToken = await this.findActionToken(token, \"email-verification\");\n\n const user = await this.storage.findById<AuthUserStorageRecord>(\n this.usersCollection,\n actionToken.userId,\n );\n\n if (!user) throw new AuthTokenError();\n\n const now = this.now();\n\n try {\n await this.atomic([\n {\n type: \"update\",\n collection: this.actionTokensCollection,\n id: actionToken.id,\n condition: { expectedRevision: actionToken.revision },\n changes: { consumedAt: now },\n },\n {\n type: \"update\",\n collection: this.usersCollection,\n id: user.id,\n condition: { expectedRevision: user.revision },\n changes: {\n emailVerifiedAt: now,\n updatedAt: now,\n },\n },\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: this.createAuditRecord(\"email-verified\", user.id),\n },\n ]);\n } catch (error) {\n if (error instanceof StorageConflictError) {\n throw new AuthTokenError();\n }\n\n throw error;\n }\n\n const updated = await this.storage.findById<AuthUserStorageRecord>(\n this.usersCollection,\n user.id,\n );\n\n if (!updated) {\n throw new AuthProviderError(\n \"The verified account could not be retrieved.\",\n );\n }\n\n return toPublicUser(updated);\n }\n\n /**\n * Always resolves with the same public result, whether or not the email\n * address belongs to an account. Delivery errors are reported only through\n * onEmailDeliveryError.\n */\n async requestPasswordReset(input: RequestPasswordResetInput): Promise<void> {\n this.assertInitialized();\n\n const email = normalizeEmail(input.email);\n const ipAddress = validateIpAddress(input.ipAddress);\n\n await this.enforceRateLimits(\"passwordReset\", email, ipAddress);\n\n const user = await this.storage.findOne<AuthUserStorageRecord>(\n this.usersCollection,\n {\n where: {\n normalizedEmail: { operator: \"eq\", value: email },\n },\n },\n );\n\n if (!user) {\n await this.storage.insert<AuthAuditEventStorageRecord>(\n this.auditCollection,\n this.createAuditRecord(\"password-reset-requested\", null),\n );\n\n return;\n }\n\n const created = await this.createActionToken({\n userId: user.id,\n purpose: \"password-reset\",\n lifetimeSeconds: this.tokenLifetimes.passwordReset,\n });\n\n await this.atomic([\n {\n type: \"insert\",\n collection: this.actionTokensCollection,\n record: created.record,\n },\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: this.createAuditRecord(\"password-reset-requested\", user.id),\n },\n ]);\n\n const resetUrl = createTokenUrl(input.resetUrl, created.token);\n\n await this.deliverEmail(\n {\n to: user.email,\n subject: this.emailSubjects.passwordReset,\n template: this.emailTemplates.passwordReset,\n variables: {\n url: resetUrl,\n expiresAt: created.record.expiresAt.toISOString(),\n },\n },\n true,\n );\n }\n\n async resetPassword(input: ResetPasswordInput): Promise<void> {\n this.assertInitialized();\n\n validatePassword(\n input.newPassword,\n this.minimumPasswordLength,\n this.maximumPasswordLength,\n );\n\n const actionToken = await this.findActionToken(\n input.token,\n \"password-reset\",\n );\n\n const user = await this.storage.findById<AuthUserStorageRecord>(\n this.usersCollection,\n actionToken.userId,\n );\n\n if (!user || user.deactivatedAt != null) {\n throw new AuthTokenError();\n }\n\n const credential = await this.findCredential(actionToken.userId);\n\n if (!credential || credential.revokedAt !== null) {\n throw new AuthTokenError();\n }\n\n const passwordHash = await this.hashPassword(input.newPassword);\n const now = this.now();\n\n const otherTokens =\n await this.storage.findMany<AuthActionTokenStorageRecord>(\n this.actionTokensCollection,\n {\n where: {\n userId: { operator: \"eq\", value: actionToken.userId },\n purpose: { operator: \"eq\", value: \"password-reset\" },\n consumedAt: { operator: \"eq\", value: null },\n },\n },\n );\n\n const operations: LifecycleBatchOperation[] = [\n {\n type: \"update\",\n collection: this.actionTokensCollection,\n id: actionToken.id,\n condition: { expectedRevision: actionToken.revision },\n changes: { consumedAt: now },\n },\n {\n type: \"update\",\n collection: this.credentialsCollection,\n id: credential.id,\n condition: { expectedRevision: credential.revision },\n changes: {\n passwordHash,\n passwordUpdatedAt: now,\n updatedAt: now,\n revokedAt: null,\n },\n },\n {\n type: \"update\",\n collection: this.usersCollection,\n id: user.id,\n condition: { expectedRevision: user.revision },\n changes: {\n sessionsInvalidatedAt: now,\n updatedAt: now,\n },\n },\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: this.createAuditRecord(\n \"password-reset-completed\",\n actionToken.userId,\n ),\n },\n ];\n\n for (const other of otherTokens) {\n if (other.id === actionToken.id || other.revokedAt != null) {\n continue;\n }\n\n operations.push({\n type: \"update\",\n collection: this.actionTokensCollection,\n id: other.id,\n condition: { expectedRevision: other.revision },\n changes: { revokedAt: now },\n });\n }\n\n try {\n await this.atomic(operations);\n } catch (error) {\n if (\n error instanceof StorageConflictError ||\n error instanceof AuthAccountConflictError\n ) {\n throw new AuthTokenError();\n }\n\n throw error;\n }\n\n await this.revokeAllSessions(actionToken.userId);\n }\n\n async changePassword(input: ChangePasswordInput): Promise<void> {\n this.assertInitialized();\n\n validatePassword(\n input.newPassword,\n this.minimumPasswordLength,\n this.maximumPasswordLength,\n );\n\n const { session, user } = await this.getSessionContext(input.sessionToken);\n\n const credential = await this.findCredential(user.id);\n\n if (\n !credential ||\n credential.revokedAt !== null ||\n !(await this.verifyPassword(\n input.currentPassword,\n credential.passwordHash,\n ))\n ) {\n throw new AuthValidationError(\"The current password is incorrect.\");\n }\n\n const passwordHash = await this.hashPassword(input.newPassword);\n const now = this.now();\n\n const resetTokens =\n await this.storage.findMany<AuthActionTokenStorageRecord>(\n this.actionTokensCollection,\n {\n where: {\n userId: { operator: \"eq\", value: user.id },\n purpose: { operator: \"eq\", value: \"password-reset\" },\n consumedAt: { operator: \"eq\", value: null },\n },\n },\n );\n\n const operations: LifecycleBatchOperation[] = [\n {\n type: \"update\",\n collection: this.credentialsCollection,\n id: credential.id,\n condition: { expectedRevision: credential.revision },\n changes: {\n passwordHash,\n passwordUpdatedAt: now,\n updatedAt: now,\n },\n },\n {\n type: \"update\",\n collection: this.usersCollection,\n id: user.id,\n condition: { expectedRevision: user.revision },\n changes: {\n sessionsInvalidatedAt: now,\n updatedAt: now,\n },\n },\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: this.createAuditRecord(\"password-changed\", user.id, session.id),\n },\n ];\n\n for (const token of resetTokens) {\n if (token.revokedAt != null) continue;\n\n operations.push({\n type: \"update\",\n collection: this.actionTokensCollection,\n id: token.id,\n condition: { expectedRevision: token.revision },\n changes: { revokedAt: now },\n });\n }\n\n await this.atomic(operations);\n\n await this.revokeAllSessions(user.id);\n }\n\n async updateProfile(input: UpdateAuthProfileInput): Promise<AuthUser> {\n this.assertInitialized();\n\n const { session, user } = await this.getSessionContext(input.sessionToken);\n\n const changes: {\n name?: string | null;\n image?: string | null;\n updatedAt: Date;\n } = {\n updatedAt: this.now(),\n };\n\n if (input.name !== undefined) {\n changes.name = normalizeDisplayName(input.name);\n }\n\n if (input.image !== undefined) {\n changes.image = validateImageUrl(input.image);\n }\n\n await this.atomic([\n {\n type: \"update\",\n collection: this.usersCollection,\n id: user.id,\n condition: { expectedRevision: user.revision },\n changes,\n },\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: this.createAuditRecord(\"profile-updated\", user.id, session.id),\n },\n ]);\n\n const updated = await this.storage.findById<AuthUserStorageRecord>(\n this.usersCollection,\n user.id,\n );\n\n if (!updated) {\n throw new AuthProviderError(\n \"The updated account could not be retrieved.\",\n );\n }\n\n return toPublicUser(updated);\n }\n\n async requestEmailChange(input: RequestEmailChangeInput): Promise<void> {\n this.assertInitialized();\n\n const { session, user } = await this.getSessionContext(input.sessionToken);\n\n const credential = await this.findCredential(user.id);\n\n if (\n !credential ||\n credential.revokedAt !== null ||\n !(await this.verifyPassword(\n input.currentPassword,\n credential.passwordHash,\n ))\n ) {\n throw new AuthValidationError(\"The current password is incorrect.\");\n }\n\n const newEmail = normalizeEmail(input.email);\n\n if (newEmail === user.normalizedEmail) {\n throw new AuthValidationError(\n \"The new email address is already the current email address.\",\n );\n }\n\n const ipAddress = validateIpAddress(input.ipAddress);\n\n await this.enforceRateLimits(\"emailChange\", newEmail, ipAddress);\n\n const existing = await this.storage.findOne<AuthUserStorageRecord>(\n this.usersCollection,\n {\n where: {\n normalizedEmail: { operator: \"eq\", value: newEmail },\n },\n },\n );\n\n if (existing) {\n throw new AuthAccountConflictError();\n }\n\n const created = await this.createActionToken({\n userId: user.id,\n purpose: \"email-change\",\n targetEmail: newEmail,\n lifetimeSeconds: this.tokenLifetimes.emailChange,\n });\n\n await this.atomic([\n {\n type: \"insert\",\n collection: this.actionTokensCollection,\n record: created.record,\n },\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: this.createAuditRecord(\n \"email-change-requested\",\n user.id,\n session.id,\n ),\n },\n ]);\n\n const verificationUrl = createTokenUrl(\n input.verificationUrl,\n created.token,\n );\n\n await this.deliverEmail(\n {\n to: newEmail,\n subject: this.emailSubjects.emailChange,\n template: this.emailTemplates.emailChange,\n variables: {\n url: verificationUrl,\n expiresAt: created.record.expiresAt.toISOString(),\n },\n },\n false,\n );\n }\n\n async verifyEmailChange(token: string): Promise<AuthUser> {\n this.assertInitialized();\n\n const actionToken = await this.findActionToken(token, \"email-change\");\n\n if (!actionToken.targetEmail) {\n throw new AuthTokenError();\n }\n\n const user = await this.storage.findById<AuthUserStorageRecord>(\n this.usersCollection,\n actionToken.userId,\n );\n\n if (!user) throw new AuthTokenError();\n\n const newEmail = normalizeEmail(actionToken.targetEmail);\n const now = this.now();\n\n try {\n await this.atomic([\n {\n type: \"update\",\n collection: this.actionTokensCollection,\n id: actionToken.id,\n condition: { expectedRevision: actionToken.revision },\n changes: { consumedAt: now },\n },\n {\n type: \"update\",\n collection: this.usersCollection,\n id: user.id,\n condition: { expectedRevision: user.revision },\n changes: {\n email: newEmail,\n normalizedEmail: newEmail,\n emailVerifiedAt: now,\n sessionsInvalidatedAt: now,\n updatedAt: now,\n },\n },\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: this.createAuditRecord(\"email-changed\", user.id),\n },\n ]);\n } catch (error) {\n if (error instanceof AuthAccountConflictError) {\n throw error;\n }\n\n if (error instanceof StorageConflictError) {\n throw new AuthTokenError();\n }\n\n throw error;\n }\n\n await this.revokeAllSessions(user.id);\n\n const updated = await this.storage.findById<AuthUserStorageRecord>(\n this.usersCollection,\n user.id,\n );\n\n if (!updated) {\n throw new AuthProviderError(\n \"The updated account could not be retrieved.\",\n );\n }\n\n return toPublicUser(updated);\n }\n\n async deactivateAccount(input: DeactivateAuthAccountInput): Promise<void> {\n this.assertInitialized();\n\n const { session, user } = await this.getSessionContext(input.sessionToken);\n\n const credential = await this.findCredential(user.id);\n\n if (\n !credential ||\n credential.revokedAt !== null ||\n !(await this.verifyPassword(input.password, credential.passwordHash))\n ) {\n throw new AuthValidationError(\"The password confirmation is incorrect.\");\n }\n\n const now = this.now();\n\n await this.atomic([\n {\n type: \"update\",\n collection: this.usersCollection,\n id: user.id,\n condition: { expectedRevision: user.revision },\n changes: {\n deactivatedAt: now,\n sessionsInvalidatedAt: now,\n updatedAt: now,\n },\n },\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: this.createAuditRecord(\n \"account-deactivated\",\n user.id,\n session.id,\n ),\n },\n ]);\n\n await this.revokeAllSessions(user.id);\n }\n\n async deleteAccount(input: DeleteAuthAccountInput): Promise<void> {\n this.assertInitialized();\n\n const { session, user } = await this.getSessionContext(input.sessionToken);\n\n const credential = await this.findCredential(user.id);\n\n if (\n !credential ||\n credential.revokedAt !== null ||\n !(await this.verifyPassword(input.password, credential.passwordHash))\n ) {\n throw new AuthValidationError(\"The password confirmation is incorrect.\");\n }\n\n const credentials =\n await this.storage.findMany<PasswordCredentialStorageRecord>(\n this.credentialsCollection,\n {\n where: {\n userId: { operator: \"eq\", value: user.id },\n },\n },\n );\n\n const actionTokens =\n await this.storage.findMany<AuthActionTokenStorageRecord>(\n this.actionTokensCollection,\n {\n where: {\n userId: { operator: \"eq\", value: user.id },\n },\n },\n );\n\n const operations: LifecycleBatchOperation[] = [\n {\n type: \"delete\",\n collection: this.usersCollection,\n id: user.id,\n condition: { expectedRevision: user.revision },\n },\n {\n type: \"insert\",\n collection: this.auditCollection,\n record: this.createAuditRecord(\"account-deleted\", user.id, session.id),\n },\n ];\n\n for (const item of credentials) {\n operations.push({\n type: \"delete\",\n collection: this.credentialsCollection,\n id: item.id,\n condition: { expectedRevision: item.revision },\n });\n }\n\n for (const item of actionTokens) {\n operations.push({\n type: \"delete\",\n collection: this.actionTokensCollection,\n id: item.id,\n condition: { expectedRevision: item.revision },\n });\n }\n\n await this.atomic(operations);\n\n await this.revokeAllSessions(user.id);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { EmailProvider, PasswordHasher, RateLimitProvider } from "./providers/provider.types.cjs";
|
|
2
|
+
import { AuthLifecycleManagerOptions } from "./types/auth-lifecycle.types.cjs";
|
|
3
|
+
import { AuthUser } from "./types/user.types.cjs";
|
|
4
|
+
import { StorageAdapter } from "@puzzle-ui/storage";
|
|
5
|
+
import { SessionManager } from "@puzzle-ui/session";
|
|
6
|
+
export type StorageAuthLifecycleManagerDependencies = {
|
|
7
|
+
readonly storage: StorageAdapter;
|
|
8
|
+
readonly passwordHasher: PasswordHasher;
|
|
9
|
+
readonly emailProvider: EmailProvider;
|
|
10
|
+
readonly sessions: SessionManager;
|
|
11
|
+
readonly rateLimitProvider?: RateLimitProvider;
|
|
12
|
+
};
|
|
13
|
+
export type RequestEmailVerificationInput = {
|
|
14
|
+
readonly userId: string;
|
|
15
|
+
readonly verificationUrl: string;
|
|
16
|
+
readonly ipAddress: string;
|
|
17
|
+
};
|
|
18
|
+
export type RequestPasswordResetInput = {
|
|
19
|
+
readonly email: string;
|
|
20
|
+
readonly resetUrl: string;
|
|
21
|
+
readonly ipAddress: string;
|
|
22
|
+
};
|
|
23
|
+
export type ResetPasswordInput = {
|
|
24
|
+
readonly token: string;
|
|
25
|
+
readonly newPassword: string;
|
|
26
|
+
};
|
|
27
|
+
export type ChangePasswordInput = {
|
|
28
|
+
readonly sessionToken: string;
|
|
29
|
+
readonly currentPassword: string;
|
|
30
|
+
readonly newPassword: string;
|
|
31
|
+
};
|
|
32
|
+
export type UpdateAuthProfileInput = {
|
|
33
|
+
readonly sessionToken: string;
|
|
34
|
+
readonly name?: string | null;
|
|
35
|
+
readonly image?: string | null;
|
|
36
|
+
};
|
|
37
|
+
export type RequestEmailChangeInput = {
|
|
38
|
+
readonly sessionToken: string;
|
|
39
|
+
readonly currentPassword: string;
|
|
40
|
+
readonly email: string;
|
|
41
|
+
readonly verificationUrl: string;
|
|
42
|
+
readonly ipAddress: string;
|
|
43
|
+
};
|
|
44
|
+
export type DeleteAuthAccountInput = {
|
|
45
|
+
readonly sessionToken: string;
|
|
46
|
+
readonly password: string;
|
|
47
|
+
};
|
|
48
|
+
export type DeactivateAuthAccountInput = {
|
|
49
|
+
readonly sessionToken: string;
|
|
50
|
+
readonly password: string;
|
|
51
|
+
};
|
|
52
|
+
export declare class StorageAuthLifecycleManager {
|
|
53
|
+
private readonly storage;
|
|
54
|
+
private readonly passwordHasher;
|
|
55
|
+
private readonly emailProvider;
|
|
56
|
+
private readonly sessions;
|
|
57
|
+
private readonly rateLimitProvider;
|
|
58
|
+
private readonly rateLimitKeySecret;
|
|
59
|
+
private readonly usersCollection;
|
|
60
|
+
private readonly credentialsCollection;
|
|
61
|
+
private readonly actionTokensCollection;
|
|
62
|
+
private readonly auditCollection;
|
|
63
|
+
private readonly rateLimitsCollection;
|
|
64
|
+
private readonly requireVerifiedEmail;
|
|
65
|
+
private readonly onSessionRevocationError?;
|
|
66
|
+
private readonly emailTemplates;
|
|
67
|
+
private readonly emailSubjects;
|
|
68
|
+
private readonly minimumPasswordLength;
|
|
69
|
+
private readonly maximumPasswordLength;
|
|
70
|
+
private readonly tokenLifetimes;
|
|
71
|
+
private readonly rateLimits;
|
|
72
|
+
private readonly clock;
|
|
73
|
+
private readonly onEmailDeliveryError?;
|
|
74
|
+
private initialized;
|
|
75
|
+
private initializationPromise;
|
|
76
|
+
constructor(dependencies: StorageAuthLifecycleManagerDependencies, options: AuthLifecycleManagerOptions);
|
|
77
|
+
initialize(): Promise<void>;
|
|
78
|
+
private assertInitialized;
|
|
79
|
+
private now;
|
|
80
|
+
private consumeRateLimit;
|
|
81
|
+
private enforceRateLimits;
|
|
82
|
+
private createActionToken;
|
|
83
|
+
private createAuditRecord;
|
|
84
|
+
/**
|
|
85
|
+
* All operations in a lifecycle batch must succeed.
|
|
86
|
+
*
|
|
87
|
+
* Storage adapters may report a missing conditional update or delete
|
|
88
|
+
* through the batch result rather than by throwing an error. Treat those
|
|
89
|
+
* results as conflicts so a token cannot be consumed without its related
|
|
90
|
+
* account or credential mutation.
|
|
91
|
+
*/
|
|
92
|
+
private atomic;
|
|
93
|
+
private deliverEmail;
|
|
94
|
+
private getSessionContext;
|
|
95
|
+
private findActionToken;
|
|
96
|
+
private findCredential;
|
|
97
|
+
private verifyPassword;
|
|
98
|
+
private hashPassword;
|
|
99
|
+
private revokeAllSessions;
|
|
100
|
+
sendEmailVerification(input: RequestEmailVerificationInput): Promise<void>;
|
|
101
|
+
verifyEmail(token: string): Promise<AuthUser>;
|
|
102
|
+
/**
|
|
103
|
+
* Always resolves with the same public result, whether or not the email
|
|
104
|
+
* address belongs to an account. Delivery errors are reported only through
|
|
105
|
+
* onEmailDeliveryError.
|
|
106
|
+
*/
|
|
107
|
+
requestPasswordReset(input: RequestPasswordResetInput): Promise<void>;
|
|
108
|
+
resetPassword(input: ResetPasswordInput): Promise<void>;
|
|
109
|
+
changePassword(input: ChangePasswordInput): Promise<void>;
|
|
110
|
+
updateProfile(input: UpdateAuthProfileInput): Promise<AuthUser>;
|
|
111
|
+
requestEmailChange(input: RequestEmailChangeInput): Promise<void>;
|
|
112
|
+
verifyEmailChange(token: string): Promise<AuthUser>;
|
|
113
|
+
deactivateAccount(input: DeactivateAuthAccountInput): Promise<void>;
|
|
114
|
+
deleteAccount(input: DeleteAuthAccountInput): Promise<void>;
|
|
115
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { EmailProvider, PasswordHasher, RateLimitProvider } from "./providers/provider.types.js";
|
|
2
|
+
import { AuthLifecycleManagerOptions } from "./types/auth-lifecycle.types.js";
|
|
3
|
+
import { AuthUser } from "./types/user.types.js";
|
|
4
|
+
import { StorageAdapter } from "@puzzle-ui/storage";
|
|
5
|
+
import { SessionManager } from "@puzzle-ui/session";
|
|
6
|
+
export type StorageAuthLifecycleManagerDependencies = {
|
|
7
|
+
readonly storage: StorageAdapter;
|
|
8
|
+
readonly passwordHasher: PasswordHasher;
|
|
9
|
+
readonly emailProvider: EmailProvider;
|
|
10
|
+
readonly sessions: SessionManager;
|
|
11
|
+
readonly rateLimitProvider?: RateLimitProvider;
|
|
12
|
+
};
|
|
13
|
+
export type RequestEmailVerificationInput = {
|
|
14
|
+
readonly userId: string;
|
|
15
|
+
readonly verificationUrl: string;
|
|
16
|
+
readonly ipAddress: string;
|
|
17
|
+
};
|
|
18
|
+
export type RequestPasswordResetInput = {
|
|
19
|
+
readonly email: string;
|
|
20
|
+
readonly resetUrl: string;
|
|
21
|
+
readonly ipAddress: string;
|
|
22
|
+
};
|
|
23
|
+
export type ResetPasswordInput = {
|
|
24
|
+
readonly token: string;
|
|
25
|
+
readonly newPassword: string;
|
|
26
|
+
};
|
|
27
|
+
export type ChangePasswordInput = {
|
|
28
|
+
readonly sessionToken: string;
|
|
29
|
+
readonly currentPassword: string;
|
|
30
|
+
readonly newPassword: string;
|
|
31
|
+
};
|
|
32
|
+
export type UpdateAuthProfileInput = {
|
|
33
|
+
readonly sessionToken: string;
|
|
34
|
+
readonly name?: string | null;
|
|
35
|
+
readonly image?: string | null;
|
|
36
|
+
};
|
|
37
|
+
export type RequestEmailChangeInput = {
|
|
38
|
+
readonly sessionToken: string;
|
|
39
|
+
readonly currentPassword: string;
|
|
40
|
+
readonly email: string;
|
|
41
|
+
readonly verificationUrl: string;
|
|
42
|
+
readonly ipAddress: string;
|
|
43
|
+
};
|
|
44
|
+
export type DeleteAuthAccountInput = {
|
|
45
|
+
readonly sessionToken: string;
|
|
46
|
+
readonly password: string;
|
|
47
|
+
};
|
|
48
|
+
export type DeactivateAuthAccountInput = {
|
|
49
|
+
readonly sessionToken: string;
|
|
50
|
+
readonly password: string;
|
|
51
|
+
};
|
|
52
|
+
export declare class StorageAuthLifecycleManager {
|
|
53
|
+
private readonly storage;
|
|
54
|
+
private readonly passwordHasher;
|
|
55
|
+
private readonly emailProvider;
|
|
56
|
+
private readonly sessions;
|
|
57
|
+
private readonly rateLimitProvider;
|
|
58
|
+
private readonly rateLimitKeySecret;
|
|
59
|
+
private readonly usersCollection;
|
|
60
|
+
private readonly credentialsCollection;
|
|
61
|
+
private readonly actionTokensCollection;
|
|
62
|
+
private readonly auditCollection;
|
|
63
|
+
private readonly rateLimitsCollection;
|
|
64
|
+
private readonly requireVerifiedEmail;
|
|
65
|
+
private readonly onSessionRevocationError?;
|
|
66
|
+
private readonly emailTemplates;
|
|
67
|
+
private readonly emailSubjects;
|
|
68
|
+
private readonly minimumPasswordLength;
|
|
69
|
+
private readonly maximumPasswordLength;
|
|
70
|
+
private readonly tokenLifetimes;
|
|
71
|
+
private readonly rateLimits;
|
|
72
|
+
private readonly clock;
|
|
73
|
+
private readonly onEmailDeliveryError?;
|
|
74
|
+
private initialized;
|
|
75
|
+
private initializationPromise;
|
|
76
|
+
constructor(dependencies: StorageAuthLifecycleManagerDependencies, options: AuthLifecycleManagerOptions);
|
|
77
|
+
initialize(): Promise<void>;
|
|
78
|
+
private assertInitialized;
|
|
79
|
+
private now;
|
|
80
|
+
private consumeRateLimit;
|
|
81
|
+
private enforceRateLimits;
|
|
82
|
+
private createActionToken;
|
|
83
|
+
private createAuditRecord;
|
|
84
|
+
/**
|
|
85
|
+
* All operations in a lifecycle batch must succeed.
|
|
86
|
+
*
|
|
87
|
+
* Storage adapters may report a missing conditional update or delete
|
|
88
|
+
* through the batch result rather than by throwing an error. Treat those
|
|
89
|
+
* results as conflicts so a token cannot be consumed without its related
|
|
90
|
+
* account or credential mutation.
|
|
91
|
+
*/
|
|
92
|
+
private atomic;
|
|
93
|
+
private deliverEmail;
|
|
94
|
+
private getSessionContext;
|
|
95
|
+
private findActionToken;
|
|
96
|
+
private findCredential;
|
|
97
|
+
private verifyPassword;
|
|
98
|
+
private hashPassword;
|
|
99
|
+
private revokeAllSessions;
|
|
100
|
+
sendEmailVerification(input: RequestEmailVerificationInput): Promise<void>;
|
|
101
|
+
verifyEmail(token: string): Promise<AuthUser>;
|
|
102
|
+
/**
|
|
103
|
+
* Always resolves with the same public result, whether or not the email
|
|
104
|
+
* address belongs to an account. Delivery errors are reported only through
|
|
105
|
+
* onEmailDeliveryError.
|
|
106
|
+
*/
|
|
107
|
+
requestPasswordReset(input: RequestPasswordResetInput): Promise<void>;
|
|
108
|
+
resetPassword(input: ResetPasswordInput): Promise<void>;
|
|
109
|
+
changePassword(input: ChangePasswordInput): Promise<void>;
|
|
110
|
+
updateProfile(input: UpdateAuthProfileInput): Promise<AuthUser>;
|
|
111
|
+
requestEmailChange(input: RequestEmailChangeInput): Promise<void>;
|
|
112
|
+
verifyEmailChange(token: string): Promise<AuthUser>;
|
|
113
|
+
deactivateAccount(input: DeactivateAuthAccountInput): Promise<void>;
|
|
114
|
+
deleteAccount(input: DeleteAuthAccountInput): Promise<void>;
|
|
115
|
+
}
|