@cairs-npm/express-auth-2fa 0.1.0 → 0.1.2
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/dist/index.cjs +56 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -2
- package/dist/index.d.ts +43 -2
- package/dist/index.js +57 -9
- package/dist/index.js.map +1 -1
- package/package.json +11 -4
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/crypto/hash.ts","../src/index.ts","../src/config/schema.ts","../src/config/defaults.ts","../src/data/migrate.ts","../src/services/auth.service.ts","../src/middleware/errors.ts","../src/middleware/cookies.ts","../src/services/method.service.ts","../src/crypto/encrypt.ts","../src/crypto/totp.ts","../src/services/methodSecret.ts","../src/services/delivery.ts","../src/services/mfa.service.ts","../src/middleware/rateLimit.ts","../src/services/passkey.service.ts","../src/services/token.service.ts","../src/services/audit.service.ts","../src/routes/auth.routes.ts","../src/middleware/requireAuth.ts","../src/routes/account.routes.ts","../src/adapters/sms/console.ts","../src/adapters/sms/twilio.ts","../src/adapters/email/console.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport argon2 from \"argon2\";\n\nexport interface Argon2Params {\n memoryKiB: number;\n iterations: number;\n parallelism: number;\n}\n\nexport function hashPassword(plain: string, params: Argon2Params): Promise<string> {\n return argon2.hash(plain, {\n type: argon2.argon2id,\n memoryCost: params.memoryKiB,\n timeCost: params.iterations,\n parallelism: params.parallelism,\n });\n}\n\nexport function verifyPassword(hash: string, plain: string): Promise<boolean> {\n return argon2.verify(hash, plain).catch(() => false);\n}\n\n// Dummy hash used to equalize timing for unknown users (BR-1). Argon2id format.\nexport const DUMMY_HASH =\n \"$argon2id$v=19$m=65536,t=3,p=4$AAAAAAAAAAAAAAAAAAAAAA$1AkBK4sQubUqG5fSG5+0E4yP+4DpVXq3sfJv4boqK3k\";\n\nexport function sha256Hex(value: string): string {\n return crypto.createHash(\"sha256\").update(value, \"utf8\").digest(\"hex\");\n}\n\n/** Constant-time string comparison via SHA-256 (BR-8). */\nexport function timingSafeEqualStr(a: string, b: string): boolean {\n const ha = crypto.createHash(\"sha256\").update(a).digest();\n const hb = crypto.createHash(\"sha256\").update(b).digest();\n return crypto.timingSafeEqual(ha, hb);\n}\n\nexport function randomToken(): string {\n return crypto.randomBytes(32).toString(\"base64url\");\n}\n\nexport function randomSmsCode(): string {\n return crypto.randomInt(0, 1_000_000).toString().padStart(6, \"0\");\n}\n\nexport function randomId(prefix: string): string {\n return `${prefix}_${crypto.randomBytes(9).toString(\"base64url\")}`;\n}\n","import { Router } from \"express\";\nimport helmet from \"helmet\";\nimport type { AuthConfig, AuthConfigInput } from \"./config/schema.js\";\nimport { resolveConfig } from \"./config/defaults.js\";\nimport { getCollections, migrate as runMigrate } from \"./data/migrate.js\";\nimport { AuthService } from \"./services/auth.service.js\";\nimport { MfaService } from \"./services/mfa.service.js\";\nimport { MethodService } from \"./services/method.service.js\";\nimport { PasskeyService } from \"./services/passkey.service.js\";\nimport { TokenService } from \"./services/token.service.js\";\nimport { AuditService } from \"./services/audit.service.js\";\nimport { buildAuthRouter } from \"./routes/auth.routes.js\";\nimport { buildAccountRouter } from \"./routes/account.routes.js\";\nimport { errorHandler } from \"./middleware/errors.js\";\nimport type { Db } from \"mongodb\";\n\n/**\n * Create the auth collections with validators, indexes and TTLs (A.4).\n * Idempotent — safe on every boot.\n */\nexport async function migrate(db: Db, prefix = \"auth_\", auditDays = 365) {\n return runMigrate(db, prefix, auditDays);\n}\n\n/**\n * Mount API (A.3):\n * app.use(express.json());\n * app.use(createAuthRouter(config));\n */\nexport function createAuthRouter(input: AuthConfigInput): Router {\n const config: AuthConfig = resolveConfig(input);\n const cols = getCollections(config.db, config.collectionPrefix);\n\n const tokens = new TokenService(config, cols);\n const audit = new AuditService(config, cols);\n const auth = new AuthService(config, cols, tokens, audit);\n const mfa = new MfaService(config, cols, tokens, audit, auth);\n const methods = new MethodService(config, cols, tokens, audit);\n const passkeys = new PasskeyService(config, cols, audit, auth);\n\n const router = Router();\n router.use(helmet());\n router.use(buildAuthRouter(config, cols, { auth, mfa, passkeys, tokens }));\n router.use(buildAccountRouter(config, cols, { methods, passkeys, tokens }));\n router.use(errorHandler);\n\n // Expose helpers the host app may need (tests, registration glue).\n (router as unknown as { services: object }).services = { auth, mfa, methods, passkeys, tokens, audit };\n return router;\n}\n\nexport { consoleSms } from \"./adapters/sms/console.js\";\nexport { twilioSms } from \"./adapters/sms/twilio.js\";\nexport { consoleEmail } from \"./adapters/email/console.js\";\nexport { apiError, ApiErrorException, errorHandler } from \"./middleware/errors.js\";\nexport { getCollections } from \"./data/migrate.js\";\nexport { AuthService } from \"./services/auth.service.js\";\nexport type {\n AuthConfig,\n AuthConfigInput,\n AdapterUser,\n UserAdapter,\n SmsProvider,\n EmailProvider,\n SecurityEvent,\n Hooks,\n Clock,\n Logger,\n} from \"./config/schema.js\";\nexport type { Method, ApiError, Session, User, Tokens } from \"@cairs-npm/auth-2fa-contract\";\n","import { z } from \"zod\";\nimport type { Db, Document, MongoClient } from \"mongodb\";\nimport type { Request } from \"express\";\n\n\n/** App-owned user record shape returned by userAdapter. */\nexport interface AdapterUser {\n id: string;\n username?: string;\n email?: string;\n [key: string]: unknown;\n}\n\nexport interface UserAdapter {\n findByLogin: (login: string) => Promise<AdapterUser | null>;\n findById: (id: string) => Promise<AdapterUser | null>;\n}\n\nexport interface SmsProvider {\n send: (phoneE164: string, message: string) => Promise<void>;\n}\n\nexport interface EmailProvider {\n /** `message` is a ready-made plain-text body; `code` lets apps render their own template. */\n send: (input: { to: string; code: string; message: string }) => Promise<void>;\n}\n\nexport interface SecurityEvent {\n userId: string;\n type: string;\n ip?: string;\n userAgent?: string;\n at: Date;\n meta?: Record<string, unknown>;\n}\n\nexport interface Hooks {\n onSecurityEvent?: (event: SecurityEvent) => void | Promise<void>;\n}\n\nexport type Clock = () => Date;\n\nexport interface Logger {\n info: (obj: object, msg?: string) => void;\n warn: (obj: object, msg?: string) => void;\n error: (obj: object, msg?: string) => void;\n}\n\nconst limitSchema = z.object({\n /** Failed passwords before lockout (BR-2). */\n loginMaxAttempts: z.number().int().positive().default(5),\n /** Window for failed-login counting, minutes (BR-2). */\n loginWindowMin: z.number().positive().default(15),\n /** Lockout duration, minutes (BR-2, BR-9). */\n lockMinutes: z.number().positive().default(15),\n /** Min seconds between SMS sends on one challenge (BR-6). */\n smsResendSec: z.number().nonnegative().default(30),\n /** Max SMS sends per challenge (BR-6). */\n smsMaxPerChallenge: z.number().int().positive().default(5),\n /** Max SMS sends per user per hour (BR-6). */\n smsMaxPerUserHour: z.number().int().positive().default(10),\n /** Wrong codes per challenge before lockout (BR-9). */\n mfaMaxAttempts: z.number().int().positive().default(5),\n /** Max verification methods per user (BR-16). */\n maxMethods: z.number().int().positive().default(5),\n /** Per-IP login requests per minute (NFR-B4). */\n loginIpPerMin: z.number().int().positive().default(20),\n /** Per-IP MFA verify requests per minute (NFR-B4). */\n verifyIpPerMin: z.number().int().positive().default(30),\n /** Per-IP step-up requests per minute (NFR-B4). */\n stepUpIpPerMin: z.number().int().positive().default(20),\n});\n\nconst argon2Schema = z.object({\n memoryKiB: z.number().int().positive().default(65_536),\n iterations: z.number().int().positive().default(3),\n parallelism: z.number().int().positive().default(4),\n});\n\nconst cookieSchema = z.object({\n secure: z.boolean().default(true),\n domain: z.string().optional(),\n sameSite: z.enum([\"lax\", \"strict\", \"none\"]).default(\"lax\"),\n path: z.string().default(\"/\"),\n});\n\nexport const ttlSchema = z.object({\n accessTokenSec: z.number().int().positive().default(900),\n refreshTokenDays: z.number().positive().default(30),\n trustedDeviceDays: z.number().positive().default(30),\n challengeSec: z.number().int().positive().default(300),\n stepUpSec: z.number().int().positive().default(300),\n pendingMethodMin: z.number().positive().default(15),\n auditDays: z.number().positive().default(365),\n});\n\nexport const configSchema = z.object({\n db: z.custom<Db>((v) => !!v && typeof (v as Db).collection === \"function\"),\n issuer: z.string().min(1),\n /** PEM PKCS8 private key (Ed25519 or RSA). Required, min 32 bytes. */\n jwtPrivateKey: z.string().min(32),\n /** 32-byte AES key, raw string or base64. Required. */\n encryptionKey: z.string().min(32),\n keyId: z.string().default(\"v1\"),\n userAdapter: z.custom<UserAdapter>(\n (v) =>\n !!v &&\n typeof (v as UserAdapter).findByLogin === \"function\" &&\n typeof (v as UserAdapter).findById === \"function\",\n ),\n smsProvider: z.custom<SmsProvider>((v) => !!v && typeof (v as SmsProvider).send === \"function\"),\n /** Enables the always-present EMAIL method (default for new users). */\n emailProvider: z\n .custom<EmailProvider>((v) => !v || typeof (v as EmailProvider).send === \"function\")\n .optional(),\n /** MongoClient for transactions; defaults to the client behind `db`. */\n client: z.custom<MongoClient>((v) => !v || typeof (v as MongoClient).startSession === \"function\").optional(),\n logger: z.custom<Logger>().optional(),\n hooks: z\n .custom<Hooks>((v) => !v || typeof v === \"object\")\n .optional(),\n clock: z.custom<Clock>((v) => !v || typeof v === \"function\").optional(),\n collectionPrefix: z.string().min(1).default(\"auth_\"),\n /**\n * Passkeys (WebAuthn) as a second step. `rpID` is the site's domain (e.g. \"childmatch.com\",\n * \"localhost\"); `origins` are the exact browser origins allowed (e.g. \"https://app.childmatch.com\").\n * Omit to turn passkeys off.\n */\n passkeys: z\n .object({\n rpID: z.string().min(1),\n rpName: z.string().min(1),\n origins: z.array(z.string().url()).min(1),\n })\n .optional(),\n /** 2-Step Verification state for users who have not chosen one yet. */\n twoStepDefault: z.boolean().default(false),\n limits: limitSchema.default({}),\n /**\n * Set false to turn off every RATE_LIMITED check: the per-IP limits on login / code\n * verify / send / step-up and the code-send cooldown and caps. Account lockouts after\n * failed passwords or codes (LOCKED) still apply.\n */\n rateLimits: z.boolean().default(true),\n argon2: argon2Schema.default({}),\n cookies: cookieSchema.default({}),\n ttl: ttlSchema.default({}),\n /** Serve GET /auth/openapi.json (A.7 docs). */\n exposeDocs: z.boolean().default(false),\n});\n\nexport type AuthConfigInput = z.input<typeof configSchema>;\nexport type AuthConfig = z.output<typeof configSchema> & {\n clock: Clock;\n};\n\nexport type RequestMeta = Pick<Request, \"ip\" | \"headers\">;\n\nexport interface PasswordStore {\n set: (userId: string, password: string) => Promise<void>;\n}\n\nexport type { Document };\n","import { configSchema, type AuthConfig, type AuthConfigInput, type Clock } from \"./schema.js\";\n\nconst noopLogger = {\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\nconst SENSITIVE_KEYS = [\"password\", \"code\", \"secret\", \"token\", \"phone\", \"authorization\"];\n\nfunction redact(value: unknown, depth = 0): unknown {\n if (value === null || value === undefined || depth > 6) return value;\n if (typeof value === \"string\") return value;\n if (Array.isArray(value)) return value.map((v) => redact(v, depth + 1));\n if (typeof value === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = SENSITIVE_KEYS.some((s) => k.toLowerCase().includes(s)) ? \"[REDACTED]\" : redact(v, depth + 1);\n }\n return out;\n }\n return value;\n}\n\nfunction wrapLogger(logger: AuthConfig[\"logger\"]): NonNullable<AuthConfig[\"logger\"]> {\n if (!logger) return noopLogger;\n return {\n info: (o, m) => logger.info(redact(o) as object, m),\n warn: (o, m) => logger.warn(redact(o) as object, m),\n error: (o, m) => logger.error(redact(o) as object, m),\n };\n}\n\n/** Validate config at startup (NFR-B2); returns a fully-defaulted AuthConfig. */\nexport function resolveConfig(input: AuthConfigInput): AuthConfig {\n const parsed = configSchema.parse(input);\n const key = decodeEncryptionKey(parsed.encryptionKey);\n if (key.length !== 32) {\n throw new Error(\"encryptionKey must decode to exactly 32 bytes (raw 32-char string or base64)\");\n }\n const clock: Clock = parsed.clock ?? (() => new Date());\n return { ...parsed, clock, logger: wrapLogger(parsed.logger) };\n}\n\nexport function decodeEncryptionKey(raw: string): Buffer {\n const asBase64 = Buffer.from(raw, \"base64\");\n if (asBase64.length === 32 && asBase64.toString(\"base64\").replace(/=+$/, \"\") === raw.replace(/=+$/, \"\")) {\n return asBase64;\n }\n return Buffer.from(raw, \"utf8\");\n}\n","import type { Db } from \"mongodb\";\nimport type {\n ChallengeDoc,\n CredentialDoc,\n EventDoc,\n MethodDoc,\n RateLimitDoc,\n SessionDoc,\n TrustedDeviceDoc,\n} from \"./docs.js\";\n\nexport interface AuthCollections {\n credentials: import(\"mongodb\").Collection<CredentialDoc>;\n methods: import(\"mongodb\").Collection<MethodDoc>;\n challenges: import(\"mongodb\").Collection<ChallengeDoc>;\n sessions: import(\"mongodb\").Collection<SessionDoc>;\n trustedDevices: import(\"mongodb\").Collection<TrustedDeviceDoc>;\n events: import(\"mongodb\").Collection<EventDoc>;\n rateLimits: import(\"mongodb\").Collection<RateLimitDoc>;\n}\n\nexport function getCollections(db: Db, prefix = \"auth_\"): AuthCollections {\n return {\n credentials: db.collection<CredentialDoc>(`${prefix}credentials`),\n methods: db.collection<MethodDoc>(`${prefix}mfa_methods`),\n challenges: db.collection<ChallengeDoc>(`${prefix}challenges`),\n sessions: db.collection<SessionDoc>(`${prefix}sessions`),\n trustedDevices: db.collection<TrustedDeviceDoc>(`${prefix}trusted_devices`),\n events: db.collection<EventDoc>(`${prefix}events`),\n rateLimits: db.collection<RateLimitDoc>(`${prefix}rate_limits`),\n };\n}\n\nconst VALIDATORS: Record<string, object> = {\n credentials: {\n $jsonSchema: {\n bsonType: \"object\",\n required: [\"userId\", \"passwordHash\", \"twoStepEnabled\", \"updatedAt\"],\n properties: {\n userId: { bsonType: \"string\" },\n passwordHash: { bsonType: \"string\" },\n twoStepEnabled: { bsonType: \"bool\" },\n lockedUntil: { bsonType: [\"date\", \"null\"] },\n updatedAt: { bsonType: \"date\" },\n },\n },\n },\n mfa_methods: {\n $jsonSchema: {\n bsonType: \"object\",\n required: [\"userId\", \"type\", \"label\", \"isDefault\", \"status\", \"createdAt\"],\n properties: {\n userId: { bsonType: \"string\" },\n type: { enum: [\"EMAIL\", \"TOTP\", \"SMS\", \"PASSKEY\"] },\n label: { bsonType: \"string\" },\n isDefault: { bsonType: \"bool\" },\n status: { enum: [\"pending\", \"active\"] },\n createdAt: { bsonType: \"date\" },\n lastUsedAt: { bsonType: [\"date\", \"null\"] },\n },\n },\n },\n challenges: {\n $jsonSchema: {\n bsonType: \"object\",\n required: [\"_id\", \"userId\", \"purpose\", \"attempts\", \"expiresAt\"],\n properties: {\n userId: { bsonType: \"string\" },\n purpose: { enum: [\"login\", \"step_up\", \"enroll\"] },\n attempts: { bsonType: \"int\" },\n expiresAt: { bsonType: \"date\" },\n },\n },\n },\n sessions: {\n $jsonSchema: {\n bsonType: \"object\",\n required: [\"_id\", \"userId\", \"refreshTokenHash\", \"familyId\", \"expiresAt\"],\n properties: {\n userId: { bsonType: \"string\" },\n refreshTokenHash: { bsonType: \"string\" },\n familyId: { bsonType: \"string\" },\n expiresAt: { bsonType: \"date\" },\n revokedAt: { bsonType: [\"date\", \"null\"] },\n },\n },\n },\n trusted_devices: {\n $jsonSchema: {\n bsonType: \"object\",\n required: [\"userId\", \"tokenHash\", \"expiresAt\"],\n properties: {\n userId: { bsonType: \"string\" },\n tokenHash: { bsonType: \"string\" },\n expiresAt: { bsonType: \"date\" },\n },\n },\n },\n events: {\n $jsonSchema: {\n bsonType: \"object\",\n required: [\"userId\", \"type\", \"at\"],\n properties: {\n userId: { bsonType: \"string\" },\n type: { bsonType: \"string\" },\n at: { bsonType: \"date\" },\n },\n },\n },\n rate_limits: {\n $jsonSchema: {\n bsonType: \"object\",\n required: [\"_id\", \"count\", \"expiresAt\"],\n properties: {\n count: { bsonType: \"int\" },\n expiresAt: { bsonType: \"date\" },\n },\n },\n },\n};\n\n/**\n * Create the auth_* collections with JSON-schema validators, indexes and TTLs\n * (A.4). Idempotent — safe to call on every app boot.\n */\nexport async function migrate(db: Db, prefix = \"auth_\", auditDays = 365): Promise<AuthCollections> {\n for (const [suffix, validator] of Object.entries(VALIDATORS)) {\n const name = `${prefix}${suffix}`;\n const exists = await db.listCollections({ name }).hasNext();\n if (!exists) {\n await db.createCollection(name, { validator });\n } else {\n // Keep validators current on existing deployments (e.g. new method types).\n await db.command({ collMod: name, validator });\n }\n }\n\n const c = getCollections(db, prefix);\n\n await c.credentials.createIndex({ userId: 1 }, { unique: true });\n await c.methods.createIndex({ userId: 1 });\n await c.methods.createIndex(\n { userId: 1, isDefault: 1 },\n { unique: true, partialFilterExpression: { isDefault: true } },\n );\n await c.methods.createIndex(\n { userId: 1, type: 1 },\n { unique: true, partialFilterExpression: { type: \"EMAIL\" } },\n );\n await c.methods.createIndex(\n { pendingExpiresAt: 1 },\n { expireAfterSeconds: 0, partialFilterExpression: { status: \"pending\" } },\n );\n await c.challenges.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });\n await c.sessions.createIndex({ refreshTokenHash: 1 }, { unique: true });\n await c.sessions.createIndex({ userId: 1 });\n await c.sessions.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });\n await c.trustedDevices.createIndex({ tokenHash: 1 }, { unique: true });\n await c.trustedDevices.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });\n await c.events.createIndex({ userId: 1, at: -1 });\n await c.events.createIndex({ at: 1 }, { expireAfterSeconds: Math.floor(auditDays * 86400) });\n await c.rateLimits.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });\n\n return c;\n}\n","import type { Request, Response } from \"express\";\nimport type { LoginResponse, Method } from \"@cairs-npm/auth-2fa-contract\";\nimport type { AuthConfig } from \"../config/schema.js\";\nimport type { AuthCollections } from \"../data/migrate.js\";\nimport type { CredentialDoc } from \"../data/docs.js\";\nimport { DUMMY_HASH, randomId, sha256Hex, verifyPassword, randomToken } from \"../crypto/hash.js\";\nimport { apiError } from \"../middleware/errors.js\";\nimport {\n CSRF_COOKIE,\n REFRESH_COOKIE,\n TRUSTED_DEVICE_COOKIE,\n clearCookie,\n parseCookies,\n setCookie,\n} from \"../middleware/cookies.js\";\nimport { TokenService } from \"./token.service.js\";\nimport { AuditService } from \"./audit.service.js\";\nimport { toApiMethod } from \"./method.service.js\";\nimport { ensureEmailMethod } from \"./delivery.js\";\nimport { pruneUnreadableMethods } from \"./methodSecret.js\";\n\nexport interface RequestCtx {\n ip?: string;\n userAgent?: string;\n}\n\nexport function ctxOf(req: Request): RequestCtx {\n return { ip: req.ip, userAgent: req.headers[\"user-agent\"] };\n}\n\nexport class AuthService {\n constructor(\n private readonly config: AuthConfig,\n private readonly cols: AuthCollections,\n private readonly tokens: TokenService,\n private readonly audit: AuditService,\n ) {}\n\n private async getCredentials(userId: string): Promise<CredentialDoc | null> {\n return this.cols.credentials.findOne({ userId });\n }\n\n private async ensureCredentials(userId: string): Promise<CredentialDoc> {\n const now = this.config.clock();\n await this.cols.credentials.updateOne(\n { userId },\n {\n $setOnInsert: {\n userId,\n passwordHash: \"\",\n twoStepEnabled: this.config.twoStepDefault,\n failedLogins: { count: 0, windowStart: now },\n lockedUntil: null,\n updatedAt: now,\n },\n },\n { upsert: true },\n );\n return (await this.cols.credentials.findOne({ userId }))!;\n }\n\n /** Set or update the password for a user (used by apps/tests; registration is app-owned). */\n async setPassword(userId: string, password: string): Promise<void> {\n const { hashPassword } = await import(\"../crypto/hash.js\");\n const now = this.config.clock();\n const cred = await this.ensureCredentials(userId);\n await this.cols.credentials.updateOne(\n { _id: cred._id },\n { $set: { passwordHash: await hashPassword(password, this.config.argon2), updatedAt: now } },\n );\n }\n\n async login(req: Request, res: Response, input: { username: string; password: string; rememberMe: boolean }): Promise<LoginResponse> {\n const now = this.config.clock();\n const ctx = ctxOf(req);\n const user = await this.config.userAdapter.findByLogin(input.username);\n\n // Timing parity: verify against a dummy hash for unknown users (BR-1).\n const cred = user ? await this.getCredentials(user.id) : null;\n const ok = await verifyPassword(cred?.passwordHash || DUMMY_HASH, input.password);\n if (!user || !cred?.passwordHash || !ok) {\n if (user && cred) await this.registerFailedLogin(cred, now);\n throw apiError(401, \"INVALID_CREDENTIALS\", \"Invalid username or password.\");\n }\n\n // BR-2 lockout check.\n if (cred.lockedUntil && cred.lockedUntil.getTime() > now.getTime()) {\n throw apiError(423, \"LOCKED\", \"Too many attempts. Try again later.\", { retryAt: cred.lockedUntil });\n }\n await this.resetFailedLogins(cred, now);\n\n // BR-3: 2FA off, or a valid trusted device → authenticate immediately.\n const trustedOk = cred.twoStepEnabled && (await this.checkTrustedDevice(req, user.id));\n if (!cred.twoStepEnabled || trustedOk) {\n await this.audit.record(user.id, \"login_success\", ctx);\n return this.authenticate(user.id, req, res, [\"pwd\"], { rememberMe: input.rememberMe });\n }\n\n // BR-4: issue an MFA challenge.\n await ensureEmailMethod(this.config, this.cols, user.id, user.email);\n await pruneUnreadableMethods(this.config, this.cols, user.id, this.audit);\n const active = await this.cols.methods.find({ userId: user.id, status: \"active\" }).toArray();\n if (active.length === 0) {\n await this.audit.record(user.id, \"login_success\", ctx);\n return this.authenticate(user.id, req, res, [\"pwd\"], { rememberMe: input.rememberMe });\n }\n const challengeId = randomId(\"chg\");\n await this.cols.challenges.insertOne({\n _id: challengeId,\n userId: user.id,\n purpose: \"login\",\n methodId: null,\n smsCodeHash: null,\n smsSentAt: null,\n smsSendCount: 0,\n attempts: 0,\n expiresAt: new Date(now.getTime() + this.config.ttl.challengeSec * 1000),\n createdAt: now,\n });\n const methods = active.map(toApiMethod);\n // The user's own default first; without one, fall back to the account email.\n const defaultMethod =\n active.find((m) => m.isDefault) ?? active.find((m) => m.type === \"EMAIL\") ?? active[0]!;\n await this.audit.record(user.id, \"login_mfa_required\", ctx);\n return {\n status: \"MFA_REQUIRED\",\n challengeId,\n methods,\n defaultMethodId: defaultMethod._id!.toString(),\n };\n }\n\n private async registerFailedLogin(cred: CredentialDoc, now: Date): Promise<void> {\n const { loginMaxAttempts, loginWindowMin, lockMinutes } = this.config.limits;\n const windowMs = loginWindowMin * 60_000;\n const count =\n cred.failedLogins && now.getTime() - cred.failedLogins.windowStart.getTime() < windowMs\n ? cred.failedLogins.count + 1\n : 1;\n const locked = count >= loginMaxAttempts;\n await this.cols.credentials.updateOne(\n { _id: cred._id },\n {\n $set: {\n failedLogins: { count, windowStart: locked ? cred.failedLogins?.windowStart ?? now : count === 1 ? now : cred.failedLogins!.windowStart },\n ...(locked ? { lockedUntil: new Date(now.getTime() + lockMinutes * 60_000) } : {}),\n updatedAt: now,\n },\n },\n );\n if (locked) await this.audit.record(cred.userId, \"account_locked\", {});\n }\n\n private async resetFailedLogins(cred: CredentialDoc, now: Date): Promise<void> {\n await this.cols.credentials.updateOne(\n { _id: cred._id },\n { $set: { failedLogins: { count: 0, windowStart: now }, updatedAt: now } },\n );\n }\n\n private async checkTrustedDevice(req: Request, userId: string): Promise<boolean> {\n const token = parseCookies(req)[TRUSTED_DEVICE_COOKIE];\n if (!token) return false;\n const doc = await this.cols.trustedDevices.findOne({ tokenHash: sha256Hex(token), userId });\n if (!doc) return false;\n if (doc.expiresAt.getTime() <= this.config.clock().getTime()) return false;\n return true;\n }\n\n /** Issue tokens + set cookies (refresh cookie, CSRF cookie). */\n async authenticate(\n userId: string,\n req: Request,\n res: Response,\n amr: string[],\n opts: { rememberDevice?: boolean; rememberMe?: boolean; deviceLabel?: string } = {},\n ): Promise<{ status: \"AUTHENTICATED\"; user: { id: string; username?: string; email?: string }; tokens: { accessToken: string; expiresInSec: number } }> {\n const ctx = ctxOf(req);\n const bundle = await this.tokens.issueTokens(userId, amr, ctx);\n const refreshDays = opts.rememberMe ? this.config.ttl.refreshTokenDays : Math.min(this.config.ttl.refreshTokenDays, 1);\n setCookie(res, this.config, REFRESH_COOKIE, bundle.refreshToken, refreshDays * 86_400_000);\n setCookie(res, this.config, CSRF_COOKIE, randomToken(), this.config.ttl.refreshTokenDays * 86_400_000, false);\n\n if (opts.rememberDevice) {\n await this.trustDevice(userId, res, opts.deviceLabel ?? \"This device\");\n }\n\n const user = await this.config.userAdapter.findById(userId);\n return {\n status: \"AUTHENTICATED\",\n user: { id: userId, username: user?.username, email: user?.email },\n tokens: { accessToken: bundle.accessToken, expiresInSec: bundle.expiresInSec },\n };\n }\n\n async trustDevice(userId: string, res: Response, label: string): Promise<void> {\n const token = randomToken();\n const now = this.config.clock();\n await this.cols.trustedDevices.insertOne({\n userId,\n tokenHash: sha256Hex(token),\n label,\n createdAt: now,\n expiresAt: new Date(now.getTime() + this.config.ttl.trustedDeviceDays * 86_400_000),\n });\n setCookie(res, this.config, TRUSTED_DEVICE_COOKIE, token, this.config.ttl.trustedDeviceDays * 86_400_000);\n }\n\n async refresh(req: Request, res: Response): Promise<{ accessToken: string; expiresInSec: number }> {\n // CSRF double-submit on the cookie-authenticated route (NFR-B3).\n const cookies = parseCookies(req);\n const csrfCookie = cookies[CSRF_COOKIE];\n const csrfHeader = req.headers[\"x-csrf-token\"];\n if (!csrfCookie || !csrfHeader || csrfCookie !== csrfHeader) {\n throw apiError(403, \"INVALID_CREDENTIALS\", \"CSRF token mismatch.\");\n }\n const raw = cookies[REFRESH_COOKIE];\n if (!raw) throw apiError(401, \"INVALID_CREDENTIALS\", \"No refresh token.\");\n\n const bundle = await this.tokens.rotateRefreshToken(raw, ctxOf(req));\n setCookie(res, this.config, REFRESH_COOKIE, bundle.refreshToken, this.config.ttl.refreshTokenDays * 86_400_000);\n return { accessToken: bundle.accessToken, expiresInSec: bundle.expiresInSec };\n }\n\n async logout(req: Request, res: Response, sid: string): Promise<void> {\n await this.tokens.revokeSession(sid);\n clearCookie(res, this.config, REFRESH_COOKIE);\n clearCookie(res, this.config, CSRF_COOKIE);\n clearCookie(res, this.config, TRUSTED_DEVICE_COOKIE);\n const auth = (req as Request & { auth?: { sub: string } }).auth;\n if (auth) await this.audit.record(auth.sub, \"logout\", ctxOf(req));\n }\n\n async listActiveMethods(userId: string): Promise<Method[]> {\n const docs = await this.cols.methods.find({ userId, status: \"active\" }).toArray();\n return docs\n .sort((a, b) => Number(b.isDefault) - Number(a.isDefault) || a.createdAt.getTime() - b.createdAt.getTime())\n .map(toApiMethod);\n }\n}\n","import type { Request, Response, NextFunction } from \"express\";\nimport type { ApiError, ErrorCode } from \"@cairs-npm/auth-2fa-contract\";\nimport { ZodError } from \"zod\";\n\n// One error shape (NFR-B6): { error, message, attemptsLeft?, retryAt? }.\n\nexport class ApiErrorException extends Error {\n constructor(\n public readonly status: number,\n public readonly code: ErrorCode,\n message: string,\n public readonly extra?: { attemptsLeft?: number; retryAt?: string },\n ) {\n super(message);\n }\n}\n\nexport function apiError(\n status: number,\n code: ErrorCode,\n message: string,\n extra?: { attemptsLeft?: number; retryAt?: Date | string },\n): ApiErrorException {\n return new ApiErrorException(status, code, message, {\n attemptsLeft: extra?.attemptsLeft,\n retryAt: extra?.retryAt instanceof Date ? extra.retryAt.toISOString() : extra?.retryAt,\n });\n}\n\n// Express only treats a middleware with exactly 4 parameters as an error handler.\nexport function errorHandler(err: unknown, _req: Request, res: Response, _next: NextFunction): void {\n if (err instanceof ApiErrorException) {\n const body: ApiError = { error: err.code, message: err.message, ...err.extra };\n res.status(err.status).json(body);\n return;\n }\n if (err instanceof ZodError) {\n res.status(400).json({ error: \"VALIDATION_ERROR\", message: err.issues[0]?.message ?? \"The request is invalid.\" });\n return;\n }\n // No stack traces leave the server (NFR-B6); log them here instead.\n console.error(\"express-auth-2fa: unhandled error\", err);\n res.status(500).json({ error: \"VALIDATION_ERROR\", message: \"Internal error.\" });\n}\n\nexport function parseBody<T>(schema: { parse: (v: unknown) => T }, body: unknown): T {\n return schema.parse(body);\n}\n","import type { Request, Response } from \"express\";\nimport type { AuthConfig } from \"../config/schema.js\";\n\n// Minimal cookie helpers — the package must not depend on cookie-parser.\n\nexport const REFRESH_COOKIE = \"auth_refresh\";\nexport const TRUSTED_DEVICE_COOKIE = \"auth_trusted\";\nexport const CSRF_COOKIE = \"auth_csrf\";\nexport const CSRF_HEADER = \"x-csrf-token\";\n\nexport function parseCookies(req: Request): Record<string, string> {\n const header = req.headers.cookie;\n if (!header) return {};\n const out: Record<string, string> = {};\n for (const part of header.split(\";\")) {\n const eq = part.indexOf(\"=\");\n if (eq === -1) continue;\n out[part.slice(0, eq).trim()] = decodeURIComponent(part.slice(eq + 1).trim());\n }\n return out;\n}\n\nexport function setCookie(\n res: Response,\n config: AuthConfig,\n name: string,\n value: string,\n maxAgeMs: number,\n httpOnly = true,\n): void {\n res.cookie(name, value, {\n httpOnly,\n secure: config.cookies.secure,\n sameSite: config.cookies.sameSite,\n domain: config.cookies.domain,\n path: config.cookies.path,\n maxAge: maxAgeMs,\n });\n}\n\nexport function clearCookie(res: Response, config: AuthConfig, name: string): void {\n res.clearCookie(name, {\n secure: config.cookies.secure,\n sameSite: config.cookies.sameSite,\n domain: config.cookies.domain,\n path: config.cookies.path,\n });\n}\n","import { ObjectId } from \"mongodb\";\nimport { parsePhoneNumberFromString } from \"libphonenumber-js\";\nimport type { Method, MethodType } from \"@cairs-npm/auth-2fa-contract\";\nimport type { AuthConfig } from \"../config/schema.js\";\nimport type { AuthCollections } from \"../data/migrate.js\";\nimport type { MethodDoc } from \"../data/docs.js\";\nimport { encryptString } from \"../crypto/encrypt.js\";\nimport { decodeEncryptionKey } from \"../config/defaults.js\";\nimport { generateTotpSecret, buildOtpauthUri, checkTotp } from \"../crypto/totp.js\";\nimport { randomSmsCode, sha256Hex, timingSafeEqualStr } from \"../crypto/hash.js\";\nimport { apiError } from \"../middleware/errors.js\";\nimport { AuditService } from \"./audit.service.js\";\nimport { TokenService } from \"./token.service.js\";\nimport type { ClientSession, Db, MongoClient } from \"mongodb\";\nimport { ensureEmailMethod } from \"./delivery.js\";\nimport { decryptMethodSecret, pruneUnreadableMethods } from \"./methodSecret.js\";\n\nexport function toApiMethod(doc: MethodDoc): Method {\n return {\n id: doc._id!.toString(),\n type: doc.type,\n label: doc.type === \"SMS\" ? `•••• ${doc.phoneLast4 ?? \"\"}`.trim() : doc.label,\n isDefault: doc.isDefault,\n createdAt: doc.createdAt.toISOString(),\n lastUsedAt: doc.lastUsedAt ? doc.lastUsedAt.toISOString() : null,\n };\n}\n\nfunction duplicateTypeMessage(type: MethodType): string {\n return type === \"SMS\"\n ? \"You already have a phone number. Remove it before adding another.\"\n : \"You already have an authenticator app. Remove it before adding another.\";\n}\n\nexport class MethodService {\n private encKey: Buffer;\n\n constructor(\n private readonly config: AuthConfig,\n private readonly cols: AuthCollections,\n private readonly tokens: TokenService,\n private readonly audit: AuditService,\n ) {\n this.encKey = decodeEncryptionKey(config.encryptionKey);\n }\n\n private objectId(id: string): ObjectId {\n try {\n return new ObjectId(id);\n } catch {\n throw apiError(400, \"VALIDATION_ERROR\", \"Invalid id.\");\n }\n }\n\n /** BR-14 — active methods only, default first, then by createdAt. */\n async securitySummary(userId: string): Promise<{ twoStepEnabled: boolean; methods: Method[] }> {\n await ensureEmailMethod(this.config, this.cols, userId);\n await pruneUnreadableMethods(this.config, this.cols, userId, this.audit);\n const cred = await this.cols.credentials.findOne({ userId });\n const docs = await this.cols.methods.find({ userId, status: \"active\" }).toArray();\n const methods = docs\n .sort((a, b) => Number(b.isDefault) - Number(a.isDefault) || a.createdAt.getTime() - b.createdAt.getTime())\n .map(toApiMethod);\n return { twoStepEnabled: cred?.twoStepEnabled ?? this.config.twoStepDefault, methods };\n }\n\n /** BR-15 — enable needs ≥1 active method; disable needs step-up + revokes trusted devices. */\n async setTwoStep(\n userId: string,\n sid: string,\n enabled: boolean,\n stepUpToken: string | undefined,\n ): Promise<{ twoStepEnabled: boolean }> {\n const now = this.config.clock();\n if (enabled) {\n await ensureEmailMethod(this.config, this.cols, userId);\n const count = await this.cols.methods.countDocuments({ userId, status: \"active\" });\n if (count === 0) throw apiError(409, \"NO_METHODS\", \"Add a verification method before enabling 2-Step Verification.\");\n } else {\n if (!stepUpToken) throw apiError(403, \"STEP_UP_REQUIRED\", \"This action requires recent verification.\");\n await this.tokens.verifyStepUpToken(stepUpToken, userId, sid);\n await this.cols.trustedDevices.deleteMany({ userId });\n }\n await this.cols.credentials.updateOne({ userId }, { $set: { twoStepEnabled: enabled, updatedAt: now } });\n await this.audit.record(userId, enabled ? \"2fa_enabled\" : \"2fa_disabled\", {});\n return { twoStepEnabled: enabled };\n }\n\n /** BR-16 — add a pending TOTP (otpauthUri+secret once) or pending SMS (sends code). Max 5 methods. */\n async addMethod(\n userId: string,\n input: { type: MethodType; label?: string; phone?: string },\n ): Promise<{ pendingMethodId: string; otpauthUri?: string; secret?: string }> {\n if (input.type === \"EMAIL\") {\n throw apiError(400, \"VALIDATION_ERROR\", \"The email method is added automatically from the account email.\");\n }\n const now = this.config.clock();\n // One method per type (plus the built-in email): refuse a second, drop stale unconfirmed ones.\n if (await this.cols.methods.findOne({ userId, type: input.type, status: \"active\" })) {\n throw apiError(409, \"VALIDATION_ERROR\", duplicateTypeMessage(input.type));\n }\n await this.cols.methods.deleteMany({ userId, type: input.type, status: \"pending\" });\n const total = await this.cols.methods.countDocuments({ userId });\n if (total >= this.config.limits.maxMethods) {\n throw apiError(409, \"VALIDATION_ERROR\", `You can add at most ${this.config.limits.maxMethods} verification methods.`);\n }\n const pendingExpiresAt = new Date(now.getTime() + this.config.ttl.pendingMethodMin * 60_000);\n\n if (input.type === \"TOTP\") {\n const secret = generateTotpSecret();\n const user = await this.config.userAdapter.findById(userId);\n const account = user?.email ?? user?.username ?? userId;\n const doc: MethodDoc = {\n userId,\n type: \"TOTP\",\n label: input.label ?? \"Authenticator app\",\n isDefault: false,\n status: \"pending\",\n secretEnc: encryptString(secret, this.encKey, this.config.keyId),\n pendingExpiresAt,\n createdAt: now,\n lastUsedAt: null,\n };\n const { insertedId } = await this.cols.methods.insertOne(doc);\n await this.audit.record(userId, \"method_add_started\", { meta: { type: \"TOTP\" } });\n return {\n pendingMethodId: insertedId.toString(),\n otpauthUri: buildOtpauthUri(this.config.issuer, account, secret),\n secret,\n };\n }\n\n // SMS\n const parsed = input.phone ? parsePhoneNumberFromString(input.phone) : undefined;\n if (!parsed || !parsed.isValid()) throw apiError(400, \"VALIDATION_ERROR\", \"Enter a valid phone number.\");\n const e164 = parsed.number;\n const last4 = e164.slice(-4);\n const code = randomSmsCode();\n const doc: MethodDoc = {\n userId,\n type: \"SMS\",\n label: `•••• ${last4}`,\n isDefault: false,\n status: \"pending\",\n phoneE164Enc: encryptString(e164, this.encKey, this.config.keyId),\n phoneLast4: last4,\n pendingCodeHash: sha256Hex(code),\n pendingExpiresAt,\n createdAt: now,\n lastUsedAt: null,\n };\n const { insertedId } = await this.cols.methods.insertOne(doc);\n await this.config.smsProvider.send(e164, `Your verification code is ${code}`);\n await this.audit.record(userId, \"method_add_started\", { meta: { type: \"SMS\" } });\n return { pendingMethodId: insertedId.toString() };\n }\n\n /** BR-17 — confirm a pending method; first active method becomes default. */\n async confirmMethod(userId: string, methodId: string, code: string): Promise<Method> {\n const method = await this.cols.methods.findOne({ _id: this.objectId(methodId), userId, status: \"pending\" });\n if (!method) throw apiError(404, \"VALIDATION_ERROR\", \"Unknown method.\");\n const now = this.config.clock();\n\n let ok = false;\n if (method.type === \"TOTP\") {\n const secret = decryptMethodSecret(method.secretEnc!, { [this.config.keyId]: this.encKey });\n ok = checkTotp(code, secret, now.getTime()).valid;\n } else {\n ok = !!method.pendingCodeHash && timingSafeEqualStr(method.pendingCodeHash, sha256Hex(code));\n }\n if (!ok) throw apiError(401, \"INVALID_CODE\", \"That code didn't work. Try again.\");\n // A parallel add of the same type may have been confirmed first.\n if (await this.cols.methods.findOne({ userId, type: method.type, status: \"active\" })) {\n await this.cols.methods.deleteOne({ _id: method._id });\n throw apiError(409, \"VALIDATION_ERROR\", duplicateTypeMessage(method.type));\n }\n\n const activeCount = await this.cols.methods.countDocuments({ userId, status: \"active\" });\n await this.cols.methods.updateOne(\n { _id: method._id },\n {\n $set: { status: \"active\", isDefault: activeCount === 0, lastUsedAt: now },\n $unset: { pendingCodeHash: \"\", pendingExpiresAt: \"\" },\n },\n );\n await this.audit.record(userId, \"method_added\", { meta: { type: method.type } });\n return toApiMethod({ ...method, status: \"active\", isDefault: activeCount === 0, lastUsedAt: now });\n }\n\n /** BR-18 — rename (TOTP only, 1–40 chars) or set default (step-up, transaction). */\n async patchMethod(\n userId: string,\n sid: string,\n methodId: string,\n input: { label?: string; isDefault?: boolean; stepUpToken?: string },\n ): Promise<Method> {\n const method = await this.cols.methods.findOne({ _id: this.objectId(methodId), userId, status: \"active\" });\n if (!method) throw apiError(404, \"VALIDATION_ERROR\", \"Unknown method.\");\n\n if (input.isDefault === true) {\n if (!input.stepUpToken) throw apiError(403, \"STEP_UP_REQUIRED\", \"This action requires recent verification.\");\n await this.tokens.verifyStepUpToken(input.stepUpToken, userId, sid);\n await this.withTransaction(async (session) => {\n await this.cols.methods.updateMany({ userId, isDefault: true }, { $set: { isDefault: false } }, { session });\n await this.cols.methods.updateOne({ _id: method._id }, { $set: { isDefault: true } }, { session });\n });\n method.isDefault = true;\n await this.audit.record(userId, \"method_set_default\", { meta: { methodId } });\n }\n if (input.isDefault === false && method.isDefault) {\n throw apiError(400, \"VALIDATION_ERROR\", \"Set another method as default instead.\");\n }\n if (input.label !== undefined) {\n if (method.type !== \"TOTP\" && method.type !== \"PASSKEY\") {\n throw apiError(400, \"VALIDATION_ERROR\", \"Only authenticator apps and passkeys can be renamed.\");\n }\n if (input.label.length < 1 || input.label.length > 40) {\n throw apiError(400, \"VALIDATION_ERROR\", \"Label must be 1–40 characters.\");\n }\n await this.cols.methods.updateOne({ _id: method._id }, { $set: { label: input.label } });\n method.label = input.label;\n await this.audit.record(userId, \"method_renamed\", { meta: { methodId } });\n }\n return toApiMethod(method);\n }\n\n /** BR-19 — remove (step-up); LAST_METHOD guard; promote most-recently-used default. */\n async deleteMethod(userId: string, sid: string, methodId: string, stepUpToken: string | undefined): Promise<void> {\n if (!stepUpToken) throw apiError(403, \"STEP_UP_REQUIRED\", \"This action requires recent verification.\");\n await this.tokens.verifyStepUpToken(stepUpToken, userId, sid);\n\n const method = await this.cols.methods.findOne({ _id: this.objectId(methodId), userId });\n if (!method) throw apiError(404, \"VALIDATION_ERROR\", \"Unknown method.\");\n if (method.type === \"EMAIL\") throw apiError(400, \"VALIDATION_ERROR\", \"Your email verification method can't be removed.\");\n\n const cred = await this.cols.credentials.findOne({ userId });\n const activeCount = await this.cols.methods.countDocuments({ userId, status: \"active\" });\n if (cred?.twoStepEnabled && method.status === \"active\" && activeCount <= 1) {\n throw apiError(409, \"LAST_METHOD\", \"You can't remove your last verification method while 2-Step Verification is on.\");\n }\n\n await this.withTransaction(async (session) => {\n await this.cols.methods.deleteOne({ _id: method._id }, { session });\n if (method.isDefault && method.status === \"active\") {\n const next = await this.cols.methods.findOne(\n { userId, status: \"active\", _id: { $ne: method._id } },\n { sort: { lastUsedAt: -1, createdAt: -1 }, session },\n );\n if (next) await this.cols.methods.updateOne({ _id: next._id }, { $set: { isDefault: true } }, { session });\n }\n });\n await this.audit.record(userId, \"method_removed\", { meta: { methodId, type: method.type } });\n }\n\n private transactionsSupported: Promise<boolean> | null = null;\n\n /** Transactions need a replica set or mongos; a standalone server (common in dev/test) has neither. */\n private supportsTransactions(): Promise<boolean> {\n this.transactionsSupported ??= this.config.db\n .command({ hello: 1 })\n .then((h) => !!h.setName || h.msg === \"isdbgrid\")\n .catch(() => false);\n return this.transactionsSupported;\n }\n\n private async withTransaction(fn: (session?: ClientSession) => Promise<void>): Promise<void> {\n if (!(await this.supportsTransactions())) {\n // Standalone MongoDB: same writes, without atomicity.\n await fn(undefined);\n return;\n }\n // mongodb ≥5 exposes db.client; older drivers (e.g. mongoose 6 / driver 4.x) keep it on db.s.client.\n const db = this.config.db as Db & { client?: MongoClient; s?: { client?: MongoClient } };\n const client = this.config.client ?? db.client ?? db.s?.client;\n if (!client) throw new Error(\"express-auth-2fa: no MongoClient available for transactions; pass `client`.\");\n const session = client.startSession();\n try {\n await session.withTransaction(async () => fn(session));\n } finally {\n await session.endSession();\n }\n }\n}\n","import crypto from \"node:crypto\";\n\n// AES-256-GCM envelope encryption for secrets at rest (A.4 rules).\n// Format: `${keyId}.${iv}.${tag}.${ciphertext}` — all base64url.\n\nexport function encryptString(plain: string, key: Buffer, keyId: string): string {\n const iv = crypto.randomBytes(12);\n const cipher = crypto.createCipheriv(\"aes-256-gcm\", key, iv);\n const ct = Buffer.concat([cipher.update(plain, \"utf8\"), cipher.final()]);\n return [keyId, iv, cipher.getAuthTag(), ct].map((b) => (typeof b === \"string\" ? b : b.toString(\"base64url\"))).join(\".\");\n}\n\nexport function decryptString(payload: string, keys: Record<string, Buffer>): { plain: string; keyId: string } {\n const parts = payload.split(\".\");\n if (parts.length !== 4) throw new Error(\"invalid ciphertext format\");\n const [keyId, ivB64, tagB64, ctB64] = parts as [string, string, string, string];\n const key = keys[keyId];\n if (!key) throw new Error(`unknown keyId: ${keyId}`);\n const decipher = crypto.createDecipheriv(\"aes-256-gcm\", key, Buffer.from(ivB64, \"base64url\"));\n decipher.setAuthTag(Buffer.from(tagB64, \"base64url\"));\n const plain = Buffer.concat([decipher.update(Buffer.from(ctB64, \"base64url\")), decipher.final()]);\n return { plain: plain.toString(\"utf8\"), keyId };\n}\n","import { authenticator } from \"otplib\";\n\n// RFC 6238 TOTP: SHA-1, 6 digits, 30s step, ±1 step window (BR-7).\n// verifyWithStep also returns the matched time-step so callers can enforce\n// the lastUsedStep replay guard.\n\nfunction atEpoch(epochMs: number) {\n const a = authenticator.clone();\n a.options = { digits: 6, step: 30, window: 0, epoch: epochMs };\n return a;\n}\n\nexport function generateTotpSecret(): string {\n // 20-byte (160-bit) secret per BR-16.\n return authenticator.generateSecret(20);\n}\n\nexport function buildOtpauthUri(issuer: string, account: string, secret: string): string {\n return authenticator.keyuri(account, issuer, secret);\n}\n\nexport function totpStepAt(epochMs: number): number {\n return Math.floor(epochMs / 30_000);\n}\n\nexport interface TotpCheck {\n valid: boolean;\n /** Time-step the code matched (base-10 counter), or null. */\n step: number | null;\n}\n\nexport function checkTotp(code: string, secret: string, nowMs: number): TotpCheck {\n for (const offset of [-1, 0, 1]) {\n const epoch = nowMs + offset * 30_000;\n if (atEpoch(epoch).verify({ token: code, secret })) {\n return { valid: true, step: totpStepAt(epoch) };\n }\n }\n return { valid: false, step: null };\n}\n\nexport function generateTotpCode(secret: string, epochMs: number): string {\n return atEpoch(epochMs).generate(secret);\n}\n","import { decryptString } from \"../crypto/encrypt.js\";\nimport { apiError } from \"../middleware/errors.js\";\nimport { decodeEncryptionKey } from \"../config/defaults.js\";\nimport type { AuthConfig } from \"../config/schema.js\";\nimport type { AuthCollections } from \"../data/migrate.js\";\nimport type { AuditService } from \"./audit.service.js\";\n\n/**\n * Decrypts a method's stored phone / TOTP secret. A payload sealed with a key the\n * server no longer has (e.g. a rotated or regenerated encryption key) can never be\n * read again, so tell the user to re-add the method instead of failing with a 500.\n */\nexport function decryptMethodSecret(payload: string, keys: Record<string, Buffer>): string {\n try {\n return decryptString(payload, keys).plain;\n } catch {\n throw apiError(\n 409,\n \"VALIDATION_ERROR\",\n \"This verification method can no longer be used. Choose another method, or remove it and add it again.\",\n );\n }\n}\n\n/**\n * Deletes a user's phone / authenticator methods whose secret can no longer be decrypted\n * (sealed with a key the server lost), so sign-in never offers a method that can't work.\n * If the default was among them, the email method (else the most recently used) takes over.\n * Returns how many methods were removed.\n */\nexport async function pruneUnreadableMethods(\n config: AuthConfig,\n cols: AuthCollections,\n userId: string,\n audit?: AuditService,\n): Promise<number> {\n const keys = { [config.keyId]: decodeEncryptionKey(config.encryptionKey) };\n const methods = await cols.methods.find({ userId, type: { $in: [\"SMS\", \"TOTP\"] } }).toArray();\n const unreadable = methods.filter((m) => {\n const sealed = m.type === \"SMS\" ? m.phoneE164Enc : m.secretEnc;\n if (!sealed) return true;\n try {\n decryptString(sealed, keys);\n return false;\n } catch {\n return true;\n }\n });\n if (unreadable.length === 0) return 0;\n\n await cols.methods.deleteMany({ _id: { $in: unreadable.map((m) => m._id!) } });\n if (!(await cols.methods.findOne({ userId, status: \"active\", isDefault: true }))) {\n const next =\n (await cols.methods.findOne({ userId, status: \"active\", type: \"EMAIL\" })) ??\n (await cols.methods.findOne({ userId, status: \"active\" }, { sort: { lastUsedAt: -1, createdAt: -1 } }));\n if (next) await cols.methods.updateOne({ _id: next._id }, { $set: { isDefault: true } });\n }\n for (const m of unreadable) {\n await audit?.record(userId, \"method_removed\", { meta: { methodId: m._id!.toString(), type: m.type, reason: \"unreadable\" } });\n }\n return unreadable.length;\n}\n","import type { AuthConfig } from \"../config/schema.js\";\nimport type { AuthCollections } from \"../data/migrate.js\";\nimport type { MethodDoc } from \"../data/docs.js\";\nimport { apiError } from \"../middleware/errors.js\";\nimport { decryptMethodSecret } from \"./methodSecret.js\";\n\n/** \"subrat@example.com\" → \"s•••@example.com\". */\nexport function maskEmail(email: string): string {\n const at = email.lastIndexOf(\"@\");\n if (at <= 0) return \"•••\";\n return `${email[0]}•••${email.slice(at)}`;\n}\n\nfunction isDuplicateKey(e: unknown): boolean {\n return !!e && typeof e === \"object\" && (e as { code?: number }).code === 11000;\n}\n\n/**\n * Every user with an email address gets an active EMAIL method. It becomes the\n * default unless the user already has one. No-op when no emailProvider is set.\n */\nexport async function ensureEmailMethod(\n config: AuthConfig,\n cols: AuthCollections,\n userId: string,\n email?: string,\n): Promise<void> {\n if (!config.emailProvider) return;\n const address = email ?? (await config.userAdapter.findById(userId))?.email;\n if (!address || !address.includes(\"@\")) return;\n const label = maskEmail(address);\n\n const existing = await cols.methods.findOne({ userId, type: \"EMAIL\" });\n if (existing) {\n if (existing.label !== label) await cols.methods.updateOne({ _id: existing._id }, { $set: { label } });\n return;\n }\n\n const hasDefault = (await cols.methods.countDocuments({ userId, status: \"active\", isDefault: true })) > 0;\n const doc: MethodDoc = {\n userId,\n type: \"EMAIL\",\n label,\n isDefault: !hasDefault,\n status: \"active\",\n createdAt: config.clock(),\n lastUsedAt: null,\n };\n try {\n await cols.methods.insertOne(doc);\n } catch (e) {\n if (!isDuplicateKey(e)) throw e;\n // Lost a race: either the EMAIL method or another default was created concurrently.\n if (await cols.methods.findOne({ userId, type: \"EMAIL\" })) return;\n try {\n await cols.methods.insertOne({ ...doc, _id: undefined, isDefault: false });\n } catch (e2) {\n if (!isDuplicateKey(e2)) throw e2;\n }\n }\n}\n\n/** Send a one-time code over the method's channel; returns the masked destination. */\nexport async function deliverCode(\n config: AuthConfig,\n encKey: Buffer,\n method: MethodDoc,\n code: string,\n): Promise<string> {\n const message = `Your verification code is ${code}`;\n if (method.type === \"SMS\") {\n if (!method.phoneE164Enc) throw apiError(400, \"VALIDATION_ERROR\", \"Unknown SMS method.\");\n const phone = decryptMethodSecret(method.phoneE164Enc, { [config.keyId]: encKey });\n await config.smsProvider.send(phone, message);\n return `•••• ${method.phoneLast4 ?? \"\"}`.trim();\n }\n if (method.type === \"EMAIL\") {\n const email = (await config.userAdapter.findById(method.userId))?.email;\n if (!config.emailProvider || !email) throw apiError(400, \"VALIDATION_ERROR\", \"Email verification is not available.\");\n await config.emailProvider.send({ to: email, code, message });\n return maskEmail(email);\n }\n throw apiError(400, \"VALIDATION_ERROR\", \"This method does not send codes.\");\n}\n","import { ObjectId } from \"mongodb\";\nimport type { Request, Response } from \"express\";\nimport type { AuthConfig } from \"../config/schema.js\";\nimport type { AuthCollections } from \"../data/migrate.js\";\nimport type { ChallengeDoc, MethodDoc } from \"../data/docs.js\";\nimport { checkTotp } from \"../crypto/totp.js\";\nimport { decodeEncryptionKey } from \"../config/defaults.js\";\nimport {\n randomSmsCode,\n sha256Hex,\n timingSafeEqualStr,\n randomId,\n} from \"../crypto/hash.js\";\nimport { apiError } from \"../middleware/errors.js\";\nimport { consumeUserQuota } from \"../middleware/rateLimit.js\";\nimport { TokenService } from \"./token.service.js\";\nimport { AuditService } from \"./audit.service.js\";\nimport { AuthService, ctxOf } from \"./auth.service.js\";\nimport { deliverCode } from \"./delivery.js\";\nimport { decryptMethodSecret } from \"./methodSecret.js\";\n\nexport class MfaService {\n private encKey: Buffer;\n\n constructor(\n private readonly config: AuthConfig,\n private readonly cols: AuthCollections,\n private readonly tokens: TokenService,\n private readonly audit: AuditService,\n private readonly auth: AuthService,\n ) {\n this.encKey = decodeEncryptionKey(config.encryptionKey);\n }\n\n private async getChallenge(challengeId: string, purpose: ChallengeDoc[\"purpose\"]): Promise<ChallengeDoc> {\n const ch = await this.cols.challenges.findOne({ _id: challengeId, purpose });\n if (!ch || ch.expiresAt.getTime() <= this.config.clock().getTime()) {\n throw apiError(410, \"CHALLENGE_EXPIRED\", \"This verification challenge has expired. Sign in again.\");\n }\n return ch;\n }\n\n /** BR-5/BR-6 — send (or resend) an SMS or email code for a challenge. */\n async sendCode(challengeId: string, methodId: string): Promise<{ sentTo: string; resendAfterSec: number }> {\n const ch = await this.getChallenge(challengeId, \"login\");\n const now = this.config.clock();\n const limits = this.config.limits;\n\n const method = await this.cols.methods.findOne({\n _id: this.objectId(methodId),\n userId: ch.userId,\n type: { $in: [\"SMS\", \"EMAIL\"] },\n status: \"active\",\n });\n if (!method) throw apiError(400, \"VALIDATION_ERROR\", \"Unknown method.\");\n\n // BR-6: resend cooldown + caps (unless the app turned rate limits off).\n const limited = this.config.rateLimits;\n if (limited && ch.smsSentAt && now.getTime() - ch.smsSentAt.getTime() < limits.smsResendSec * 1000) {\n throw apiError(429, \"RATE_LIMITED\", \"Please wait before requesting another code.\", {\n retryAt: new Date(ch.smsSentAt.getTime() + limits.smsResendSec * 1000),\n });\n }\n if (limited && ch.smsSendCount >= limits.smsMaxPerChallenge) {\n throw apiError(429, \"RATE_LIMITED\", \"Too many codes sent for this challenge.\", { retryAt: ch.expiresAt });\n }\n const quota = limited\n ? await consumeUserQuota(this.cols, this.config, `${method.type.toLowerCase()}_send`, ch.userId, limits.smsMaxPerUserHour, 3_600_000)\n : { allowed: true as const, retryAt: undefined };\n if (!quota.allowed) {\n throw apiError(429, \"RATE_LIMITED\", \"Too many codes sent. Try again later.\", { retryAt: quota.retryAt });\n }\n\n const code = randomSmsCode();\n await this.cols.challenges.updateOne(\n { _id: challengeId },\n { $set: { methodId, smsCodeHash: sha256Hex(code), smsSentAt: now }, $inc: { smsSendCount: 1 } },\n );\n\n const sentTo = await deliverCode(this.config, this.encKey, method, code);\n return { sentTo, resendAfterSec: limited ? limits.smsResendSec : 0 };\n }\n\n /** BR-7..BR-10 — verify a 6-digit code against a login challenge. */\n async verify(\n req: Request,\n res: Response,\n input: { challengeId: string; methodId: string; code: string; rememberDevice: boolean },\n ): Promise<void> {\n const ch = await this.getChallenge(input.challengeId, \"login\");\n const now = this.config.clock();\n const method = await this.cols.methods.findOne({ _id: this.objectId(input.methodId), userId: ch.userId, status: \"active\" });\n if (!method) throw apiError(400, \"VALIDATION_ERROR\", \"Unknown method.\");\n if (method.type === \"PASSKEY\") throw apiError(400, \"VALIDATION_ERROR\", \"Use your passkey to verify.\");\n\n const ok = await this.checkCode(ch, method, input.code, now);\n if (!ok) {\n const attempts = ch.attempts + 1;\n const attemptsLeft = this.config.limits.mfaMaxAttempts - attempts;\n if (attemptsLeft <= 0) {\n await this.cols.challenges.deleteOne({ _id: ch._id });\n const lockedUntil = new Date(now.getTime() + this.config.limits.lockMinutes * 60_000);\n await this.cols.credentials.updateOne({ userId: ch.userId }, { $set: { lockedUntil, updatedAt: now } });\n await this.audit.record(ch.userId, \"mfa_locked\", ctxOf(req));\n throw apiError(423, \"LOCKED\", \"Too many attempts. Try again later.\", { retryAt: lockedUntil });\n }\n await this.cols.challenges.updateOne({ _id: ch._id }, { $set: { attempts } });\n await this.audit.record(ch.userId, \"mfa_failed\", ctxOf(req));\n throw apiError(401, \"INVALID_CODE\", \"That code didn't work. Try again.\", { attemptsLeft });\n }\n\n await this.cols.challenges.deleteOne({ _id: ch._id });\n await this.cols.methods.updateOne({ _id: method._id }, { $set: { lastUsedAt: now } });\n await this.audit.record(ch.userId, \"login_success\", ctxOf(req));\n const user = await this.config.userAdapter.findById(ch.userId);\n const ua = req.headers[\"user-agent\"];\n const result = await this.auth.authenticate(ch.userId, req, res, [\"pwd\", \"otp\"], {\n rememberDevice: input.rememberDevice,\n deviceLabel: typeof ua === \"string\" ? ua.slice(0, 80) : undefined,\n });\n res.json({ ...result, user: result.user ?? { id: ch.userId, username: user?.username, email: user?.email } });\n }\n\n private async checkCode(ch: ChallengeDoc, method: MethodDoc, code: string, now: Date): Promise<boolean> {\n if (method.type === \"TOTP\") {\n if (!method.secretEnc) return false;\n const secret = decryptMethodSecret(method.secretEnc, { [this.config.keyId]: this.encKey });\n const check = checkTotp(code, secret, now.getTime());\n // BR-7 replay guard: reject a step already consumed.\n if (check.valid && check.step !== null && check.step > (method.lastUsedStep ?? -1)) {\n await this.cols.methods.updateOne({ _id: method._id }, { $set: { lastUsedStep: check.step } });\n return true;\n }\n return false;\n }\n // SMS / EMAIL — compare hashes in constant time; a used code is deleted (BR-8).\n const valid = !!ch.smsCodeHash && timingSafeEqualStr(ch.smsCodeHash, sha256Hex(code));\n if (valid) await this.cols.challenges.updateOne({ _id: ch._id }, { $set: { smsCodeHash: null } });\n return valid;\n }\n\n /** BR-13 — step-up: verify a fresh TOTP/SMS/email code, return a 5-min stepUpToken. */\n async stepUp(\n req: Request,\n input: { methodId: string; code?: string },\n auth: { sub: string; sid: string },\n ): Promise<{ stepUpToken: string; expiresInSec: number } | { challengeId: string; sentTo: string; resendAfterSec: number }> {\n const method = await this.cols.methods.findOne({ _id: this.objectId(input.methodId), userId: auth.sub, status: \"active\" });\n if (!method) throw apiError(400, \"VALIDATION_ERROR\", \"Unknown method.\");\n if (method.type === \"PASSKEY\") throw apiError(400, \"VALIDATION_ERROR\", \"Choose a method that sends or shows a code.\");\n const now = this.config.clock();\n\n if (method.type !== \"TOTP\" && !input.code) {\n // First pass: create a step_up challenge and send a code.\n const challengeId = randomId(\"chg\");\n const code = randomSmsCode();\n await this.cols.challenges.insertOne({\n _id: challengeId,\n userId: auth.sub,\n purpose: \"step_up\",\n methodId: input.methodId,\n smsCodeHash: sha256Hex(code),\n smsSentAt: now,\n smsSendCount: 1,\n attempts: 0,\n expiresAt: new Date(now.getTime() + this.config.ttl.challengeSec * 1000),\n createdAt: now,\n });\n const sentTo = await deliverCode(this.config, this.encKey, method, code);\n return { challengeId, sentTo, resendAfterSec: this.config.rateLimits ? this.config.limits.smsResendSec : 0 };\n }\n if (!input.code) throw apiError(400, \"VALIDATION_ERROR\", \"code is required.\");\n\n let ok = false;\n if (method.type === \"TOTP\") {\n const secret = decryptMethodSecret(method.secretEnc!, { [this.config.keyId]: this.encKey });\n const check = checkTotp(input.code, secret, now.getTime());\n if (check.valid && check.step !== null && check.step > (method.lastUsedStep ?? -1)) {\n ok = true;\n await this.cols.methods.updateOne({ _id: method._id }, { $set: { lastUsedStep: check.step } });\n }\n } else {\n const ch = await this.cols.challenges.findOne(\n { userId: auth.sub, purpose: \"step_up\", methodId: input.methodId },\n { sort: { createdAt: -1 } },\n );\n if (ch && ch.expiresAt.getTime() > now.getTime() && ch.smsCodeHash && timingSafeEqualStr(ch.smsCodeHash, sha256Hex(input.code))) {\n ok = true;\n await this.cols.challenges.deleteOne({ _id: ch._id });\n }\n }\n if (!ok) {\n await this.audit.record(auth.sub, \"step_up_failed\", ctxOf(req));\n throw apiError(401, \"INVALID_CODE\", \"That code didn't work. Try again.\");\n }\n\n await this.cols.methods.updateOne({ _id: method._id }, { $set: { lastUsedAt: now } });\n await this.audit.record(auth.sub, \"step_up_success\", ctxOf(req));\n const { token, expiresInSec } = await this.tokens.signStepUpToken(auth.sub, auth.sid);\n return { stepUpToken: token, expiresInSec };\n }\n\n private objectId(id: string): ObjectId {\n try {\n return new ObjectId(id);\n } catch {\n throw apiError(400, \"VALIDATION_ERROR\", \"Invalid id.\");\n }\n }\n}\n","import type { NextFunction, Request, Response } from \"express\";\nimport type { AuthCollections } from \"../data/migrate.js\";\nimport type { AuthConfig } from \"../config/schema.js\";\nimport { apiError } from \"./errors.js\";\n\n// MongoDB-backed fixed-window rate limiting (NFR-B4, NFR-B10 — stateless).\n\nexport function rateLimit(\n cols: AuthCollections,\n config: AuthConfig,\n bucket: string,\n max: number,\n windowMs: number,\n) {\n return async (req: Request, _res: Response, next: NextFunction): Promise<void> => {\n if (!config.rateLimits) {\n next();\n return;\n }\n const now = config.clock().getTime();\n const slot = Math.floor(now / windowMs);\n const key = `${bucket}:${req.ip ?? \"unknown\"}:${slot}`;\n const expiresAt = new Date((slot + 1) * windowMs + 60_000);\n\n try {\n const doc = await cols.rateLimits.findOneAndUpdate(\n { _id: key },\n { $inc: { count: 1 }, $setOnInsert: { expiresAt } },\n { upsert: true, returnDocument: \"after\" },\n );\n const count = doc?.count ?? 1;\n if (count > max) {\n next(\n apiError(429, \"RATE_LIMITED\", \"Too many requests. Try again later.\", {\n retryAt: new Date((slot + 1) * windowMs),\n }),\n );\n return;\n }\n next();\n } catch {\n next(); // fail-open: rate limiting must not take the app down\n }\n };\n}\n\n/** Per-user counter — used for the per-hour SMS cap (BR-6). */\nexport async function consumeUserQuota(\n cols: AuthCollections,\n config: AuthConfig,\n bucket: string,\n userId: string,\n max: number,\n windowMs: number,\n): Promise<{ allowed: boolean; retryAt: Date }> {\n const now = config.clock().getTime();\n const slot = Math.floor(now / windowMs);\n const key = `${bucket}:u:${userId}:${slot}`;\n const retryAt = new Date((slot + 1) * windowMs);\n const doc = await cols.rateLimits.findOneAndUpdate(\n { _id: key },\n { $inc: { count: 1 }, $setOnInsert: { expiresAt: new Date((slot + 1) * windowMs + 60_000) } },\n { upsert: true, returnDocument: \"after\" },\n );\n return { allowed: (doc?.count ?? 1) <= max, retryAt };\n}\n","import type { Request, Response } from \"express\";\nimport {\n generateAuthenticationOptions,\n generateRegistrationOptions,\n verifyAuthenticationResponse,\n verifyRegistrationResponse,\n type AuthenticationResponseJSON,\n type AuthenticatorTransport,\n type RegistrationResponseJSON,\n} from \"@simplewebauthn/server\";\nimport type { Method } from \"@cairs-npm/auth-2fa-contract\";\nimport type { AuthConfig } from \"../config/schema.js\";\nimport type { AuthCollections } from \"../data/migrate.js\";\nimport type { ChallengeDoc, MethodDoc } from \"../data/docs.js\";\nimport { randomId } from \"../crypto/hash.js\";\nimport { apiError } from \"../middleware/errors.js\";\nimport { AuditService } from \"./audit.service.js\";\nimport { AuthService, ctxOf } from \"./auth.service.js\";\nimport { toApiMethod } from \"./method.service.js\";\n\n// Passkeys (WebAuthn) as a verification method: registered from Security while signed in,\n// then used on the code step of sign-in instead of an emailed / texted / app code.\nexport class PasskeyService {\n constructor(\n private readonly config: AuthConfig,\n private readonly cols: AuthCollections,\n private readonly audit: AuditService,\n private readonly auth: AuthService,\n ) {}\n\n private rp(): NonNullable<AuthConfig[\"passkeys\"]> {\n if (!this.config.passkeys) throw apiError(400, \"VALIDATION_ERROR\", \"Passkeys are not enabled.\");\n return this.config.passkeys;\n }\n\n private activePasskeys(userId: string): Promise<MethodDoc[]> {\n return this.cols.methods.find({ userId, type: \"PASSKEY\", status: \"active\" }).toArray();\n }\n\n private descriptor(m: MethodDoc): { id: string; transports?: AuthenticatorTransport[] } {\n return { id: m.credentialId!, transports: m.transports as AuthenticatorTransport[] | undefined };\n }\n\n private async liveChallenge(filter: Partial<ChallengeDoc>): Promise<ChallengeDoc> {\n const ch = await this.cols.challenges.findOne(filter);\n if (!ch || ch.expiresAt.getTime() <= this.config.clock().getTime()) {\n throw apiError(410, \"CHALLENGE_EXPIRED\", \"This verification challenge has expired. Sign in again.\");\n }\n return ch;\n }\n\n /** Step 1 of adding a passkey: WebAuthn creation options for the signed-in user. */\n async registrationOptions(userId: string): Promise<Record<string, unknown>> {\n const rp = this.rp();\n const now = this.config.clock();\n if ((await this.cols.methods.countDocuments({ userId })) >= this.config.limits.maxMethods) {\n throw apiError(409, \"VALIDATION_ERROR\", `You can add at most ${this.config.limits.maxMethods} verification methods.`);\n }\n const user = await this.config.userAdapter.findById(userId);\n const name = user?.email ?? user?.username ?? userId;\n const options = await generateRegistrationOptions({\n rpName: rp.rpName,\n rpID: rp.rpID,\n userName: name,\n userDisplayName: name,\n userID: new TextEncoder().encode(userId),\n attestationType: \"none\",\n excludeCredentials: (await this.activePasskeys(userId)).map((m) => this.descriptor(m)),\n authenticatorSelection: { residentKey: \"preferred\", userVerification: \"preferred\" },\n });\n // One enrollment in flight per user; a restart replaces it.\n await this.cols.challenges.deleteMany({ userId, purpose: \"enroll\" });\n await this.cols.challenges.insertOne({\n _id: randomId(\"chg\"),\n userId,\n purpose: \"enroll\",\n methodId: null,\n smsCodeHash: null,\n smsSentAt: null,\n smsSendCount: 0,\n attempts: 0,\n webauthnChallenge: options.challenge,\n expiresAt: new Date(now.getTime() + this.config.ttl.challengeSec * 1000),\n createdAt: now,\n });\n return options as unknown as Record<string, unknown>;\n }\n\n /** Step 2 of adding a passkey: verify the browser's attestation and store the credential. */\n async register(userId: string, input: { label?: string; response: Record<string, unknown> }): Promise<Method> {\n const rp = this.rp();\n const ch = await this.liveChallenge({ userId, purpose: \"enroll\" });\n let verification;\n try {\n verification = await verifyRegistrationResponse({\n response: input.response as unknown as RegistrationResponseJSON,\n expectedChallenge: ch.webauthnChallenge ?? \"\",\n expectedOrigin: rp.origins,\n expectedRPID: rp.rpID,\n requireUserVerification: false,\n });\n } catch {\n verification = null;\n }\n if (!verification?.verified || !verification.registrationInfo) {\n throw apiError(400, \"VALIDATION_ERROR\", \"We couldn't add that passkey. Try again.\");\n }\n await this.cols.challenges.deleteOne({ _id: ch._id });\n\n const { credential } = verification.registrationInfo;\n if (await this.cols.methods.findOne({ type: \"PASSKEY\", credentialId: credential.id })) {\n throw apiError(409, \"VALIDATION_ERROR\", \"That passkey is already added.\");\n }\n const now = this.config.clock();\n const hasDefault = !!(await this.cols.methods.findOne({ userId, status: \"active\", isDefault: true }));\n const doc: MethodDoc = {\n userId,\n type: \"PASSKEY\",\n label: input.label?.trim() || \"Passkey\",\n isDefault: !hasDefault,\n status: \"active\",\n credentialId: credential.id,\n publicKey: Buffer.from(credential.publicKey).toString(\"base64url\"),\n counter: credential.counter,\n transports: credential.transports,\n createdAt: now,\n lastUsedAt: null,\n };\n const { insertedId } = await this.cols.methods.insertOne(doc);\n await this.audit.record(userId, \"method_added\", { meta: { type: \"PASSKEY\" } });\n return toApiMethod({ ...doc, _id: insertedId });\n }\n\n /** Sign-in code step: WebAuthn request options for the user behind a login challenge. */\n async loginOptions(challengeId: string): Promise<Record<string, unknown>> {\n const rp = this.rp();\n const ch = await this.liveChallenge({ _id: challengeId, purpose: \"login\" });\n const passkeys = await this.activePasskeys(ch.userId);\n if (passkeys.length === 0) throw apiError(400, \"VALIDATION_ERROR\", \"There's no passkey on this account.\");\n const options = await generateAuthenticationOptions({\n rpID: rp.rpID,\n allowCredentials: passkeys.map((m) => this.descriptor(m)),\n userVerification: \"preferred\",\n });\n await this.cols.challenges.updateOne({ _id: ch._id }, { $set: { webauthnChallenge: options.challenge } });\n return options as unknown as Record<string, unknown>;\n }\n\n /** Sign-in code step: verify the passkey assertion and finish signing in. */\n async loginVerify(\n req: Request,\n res: Response,\n input: { challengeId: string; response: Record<string, unknown>; rememberDevice: boolean },\n ): Promise<void> {\n const rp = this.rp();\n const ch = await this.liveChallenge({ _id: input.challengeId, purpose: \"login\" });\n if (!ch.webauthnChallenge) throw apiError(400, \"VALIDATION_ERROR\", \"Start the passkey check again.\");\n const response = input.response as unknown as AuthenticationResponseJSON;\n const method = await this.cols.methods.findOne({\n userId: ch.userId,\n type: \"PASSKEY\",\n status: \"active\",\n credentialId: typeof response.id === \"string\" ? response.id : \"\",\n });\n if (!method) throw apiError(401, \"INVALID_CODE\", \"That passkey isn't on this account.\");\n\n let verification;\n try {\n verification = await verifyAuthenticationResponse({\n response,\n expectedChallenge: ch.webauthnChallenge,\n expectedOrigin: rp.origins,\n expectedRPID: rp.rpID,\n credential: {\n id: method.credentialId!,\n publicKey: Buffer.from(method.publicKey!, \"base64url\"),\n counter: method.counter ?? 0,\n transports: method.transports as AuthenticatorTransport[] | undefined,\n },\n requireUserVerification: false,\n });\n } catch {\n verification = null;\n }\n if (!verification?.verified) {\n await this.audit.record(ch.userId, \"mfa_failed\", ctxOf(req));\n throw apiError(401, \"INVALID_CODE\", \"We couldn't verify your passkey. Try again.\");\n }\n\n const now = this.config.clock();\n await this.cols.challenges.deleteOne({ _id: ch._id });\n await this.cols.methods.updateOne(\n { _id: method._id },\n { $set: { counter: verification.authenticationInfo.newCounter, lastUsedAt: now } },\n );\n await this.audit.record(ch.userId, \"login_success\", ctxOf(req));\n const ua = req.headers[\"user-agent\"];\n const result = await this.auth.authenticate(ch.userId, req, res, [\"pwd\", \"hwk\"], {\n rememberDevice: input.rememberDevice,\n deviceLabel: typeof ua === \"string\" ? ua.slice(0, 80) : undefined,\n });\n res.json(result);\n }\n}\n","import crypto from \"node:crypto\";\nimport { SignJWT, jwtVerify, importPKCS8, importSPKI } from \"jose\";\nimport type { AuthConfig } from \"../config/schema.js\";\nimport type { AuthCollections } from \"../data/migrate.js\";\nimport type { SessionDoc } from \"../data/docs.js\";\nimport { randomId, randomToken, sha256Hex } from \"../crypto/hash.js\";\nimport { apiError } from \"../middleware/errors.js\";\n\nexport interface TokenBundle {\n accessToken: string;\n expiresInSec: number;\n refreshToken: string; // raw, goes in the httpOnly cookie\n sessionId: string;\n}\n\nexport class TokenService {\n private alg: \"EdDSA\" | \"RS256\";\n private privateKeyPromise: Promise<crypto.KeyObject | import(\"jose\").KeyLike>;\n private publicKeyPromise: Promise<crypto.KeyObject | import(\"jose\").KeyLike>;\n\n constructor(\n private readonly config: AuthConfig,\n private readonly cols: AuthCollections,\n ) {\n const keyObj = crypto.createPrivateKey(config.jwtPrivateKey);\n this.alg = keyObj.asymmetricKeyType === \"ed25519\" ? \"EdDSA\" : \"RS256\";\n this.privateKeyPromise = importPKCS8(config.jwtPrivateKey, this.alg);\n this.publicKeyPromise = importSPKI(\n crypto.createPublicKey(keyObj).export({ type: \"spki\", format: \"pem\" }).toString(),\n this.alg,\n );\n }\n\n async signAccessToken(userId: string, sid: string, amr: string[]): Promise<string> {\n const key = await this.privateKeyPromise;\n return new SignJWT({ sid, amr })\n .setProtectedHeader({ alg: this.alg })\n .setIssuer(this.config.issuer)\n .setSubject(userId)\n .setIssuedAt()\n .setExpirationTime(`${this.config.ttl.accessTokenSec}s`)\n .sign(key);\n }\n\n async verifyAccessToken(token: string): Promise<{ sub: string; sid: string; amr: string[] }> {\n try {\n const { payload } = await jwtVerify(token, await this.publicKeyPromise, {\n issuer: this.config.issuer,\n });\n return { sub: payload.sub as string, sid: payload.sid as string, amr: (payload.amr as string[]) ?? [] };\n } catch {\n throw apiError(401, \"INVALID_CREDENTIALS\", \"Invalid or expired access token.\");\n }\n }\n\n /** Create a new session + refresh token; returns tokens for the client. */\n async issueTokens(\n userId: string,\n amr: string[],\n meta: { userAgent?: string; ip?: string },\n familyId?: string,\n ): Promise<TokenBundle> {\n const now = this.config.clock();\n const sid = randomId(\"ses\");\n const refreshToken = randomToken();\n const expiresAt = new Date(now.getTime() + this.config.ttl.refreshTokenDays * 86_400_000);\n\n const doc: SessionDoc = {\n _id: sid,\n userId,\n refreshTokenHash: sha256Hex(refreshToken),\n familyId: familyId ?? randomId(\"fam\"),\n rotatedFrom: null,\n userAgent: meta.userAgent,\n ip: meta.ip,\n createdAt: now,\n expiresAt,\n revokedAt: null,\n };\n await this.cols.sessions.insertOne(doc);\n\n return {\n accessToken: await this.signAccessToken(userId, sid, amr),\n expiresInSec: this.config.ttl.accessTokenSec,\n refreshToken,\n sessionId: sid,\n };\n }\n\n /**\n * Rotate a refresh token (BR-11). Reuse of a rotated token revokes the whole\n * family. Old hashes are retained in `usedHashes` so reuse is detectable.\n */\n async rotateRefreshToken(\n rawToken: string,\n meta: { userAgent?: string; ip?: string },\n ): Promise<TokenBundle> {\n const hash = sha256Hex(rawToken);\n const now = this.config.clock();\n const session = await this.cols.sessions.findOne({\n $or: [{ refreshTokenHash: hash }, { usedHashes: hash } as never],\n });\n\n if (!session) {\n throw apiError(401, \"INVALID_CREDENTIALS\", \"Invalid refresh token.\");\n }\n if (session.refreshTokenHash !== hash) {\n // Rotated-token reuse → revoke the whole family.\n await this.cols.sessions.updateMany(\n { familyId: session.familyId, revokedAt: null },\n { $set: { revokedAt: now } },\n );\n throw apiError(401, \"INVALID_CREDENTIALS\", \"Refresh token reuse detected; session revoked.\");\n }\n if (session.revokedAt || session.expiresAt.getTime() <= now.getTime()) {\n throw apiError(401, \"INVALID_CREDENTIALS\", \"Session expired.\");\n }\n\n const newToken = randomToken();\n await this.cols.sessions.updateOne(\n { _id: session._id, refreshTokenHash: hash },\n {\n $set: {\n refreshTokenHash: sha256Hex(newToken),\n userAgent: meta.userAgent,\n ip: meta.ip,\n },\n $push: { usedHashes: { $each: [hash], $slice: -200 } } as never,\n },\n );\n\n return {\n accessToken: await this.signAccessToken(session.userId, session._id, [\"pwd\", \"otp\"]),\n expiresInSec: this.config.ttl.accessTokenSec,\n refreshToken: newToken,\n sessionId: session._id,\n };\n }\n\n async revokeSession(sid: string): Promise<void> {\n await this.cols.sessions.updateOne({ _id: sid }, { $set: { revokedAt: this.config.clock() } });\n }\n\n /** 5-minute step-up JWT bound to sub+sid (BR-13). */\n async signStepUpToken(userId: string, sid: string): Promise<{ token: string; expiresInSec: number }> {\n const key = await this.privateKeyPromise;\n const token = await new SignJWT({ sid, purpose: \"step_up\" })\n .setProtectedHeader({ alg: this.alg })\n .setIssuer(this.config.issuer)\n .setSubject(userId)\n .setIssuedAt()\n .setExpirationTime(`${this.config.ttl.stepUpSec}s`)\n .sign(key);\n return { token, expiresInSec: this.config.ttl.stepUpSec };\n }\n\n async verifyStepUpToken(token: string, userId: string, sid: string): Promise<void> {\n try {\n const { payload } = await jwtVerify(token, await this.publicKeyPromise, {\n issuer: this.config.issuer,\n });\n if (payload.purpose !== \"step_up\" || payload.sub !== userId || payload.sid !== sid) {\n throw new Error(\"mismatch\");\n }\n } catch {\n throw apiError(403, \"STEP_UP_REQUIRED\", \"This action requires recent verification.\");\n }\n }\n}\n","import type { AuthCollections } from \"../data/migrate.js\";\nimport type { AuthConfig, SecurityEvent } from \"../config/schema.js\";\n\n// BR-20: every security change writes an auth_events entry and calls\n// hooks.onSecurityEvent(event).\n\nexport class AuditService {\n constructor(\n private readonly config: AuthConfig,\n private readonly cols: AuthCollections,\n ) {}\n\n async record(\n userId: string,\n type: string,\n meta: { ip?: string; userAgent?: string; meta?: Record<string, unknown> } = {},\n ): Promise<void> {\n const event: SecurityEvent = {\n userId,\n type,\n ip: meta.ip,\n userAgent: meta.userAgent,\n at: this.config.clock(),\n meta: meta.meta,\n };\n await this.cols.events.insertOne(event as never);\n try {\n await this.config.hooks?.onSecurityEvent?.(event);\n } catch {\n // Hooks must never break a security flow.\n }\n }\n}\n","import { Router } from \"express\";\nimport {\n loginRequestSchema,\n mfaSendRequestSchema,\n mfaVerifyRequestSchema,\n passkeyLoginOptionsRequestSchema,\n passkeyLoginVerifyRequestSchema,\n stepUpRequestSchema,\n} from \"@cairs-npm/auth-2fa-contract\";\nimport type { AuthConfig } from \"../config/schema.js\";\nimport type { AuthCollections } from \"../data/migrate.js\";\nimport { AuthService } from \"../services/auth.service.js\";\nimport { MfaService } from \"../services/mfa.service.js\";\nimport { PasskeyService } from \"../services/passkey.service.js\";\nimport { TokenService } from \"../services/token.service.js\";\nimport { rateLimit } from \"../middleware/rateLimit.js\";\nimport { requireAuth } from \"../middleware/requireAuth.js\";\nimport { parseBody } from \"../middleware/errors.js\";\n\nconst wrap =\n (fn: (req: import(\"express\").Request, res: import(\"express\").Response) => Promise<void | unknown>) =>\n (req: import(\"express\").Request, res: import(\"express\").Response, next: import(\"express\").NextFunction) =>\n fn(req, res).then((r) => (r === undefined ? undefined : res.json(r))).catch(next);\n\nexport interface Services {\n auth: AuthService;\n mfa: MfaService;\n passkeys: PasskeyService;\n tokens: TokenService;\n}\n\nexport function buildAuthRouter(config: AuthConfig, cols: AuthCollections, services: Services): Router {\n const router = Router();\n const { loginIpPerMin, verifyIpPerMin, stepUpIpPerMin } = config.limits;\n\n router.post(\"/auth/login\", rateLimit(cols, config, \"login_ip\", loginIpPerMin, 60_000), wrap(async (req, res) => {\n const input = parseBody(loginRequestSchema, req.body);\n return services.auth.login(req, res, input);\n }));\n\n router.post(\"/auth/mfa/send\", rateLimit(cols, config, \"mfa_send_ip\", verifyIpPerMin, 60_000), wrap(async (req) => {\n const input = parseBody(mfaSendRequestSchema, req.body);\n return services.mfa.sendCode(input.challengeId, input.methodId);\n }));\n\n router.post(\"/auth/mfa/verify\", rateLimit(cols, config, \"mfa_verify_ip\", verifyIpPerMin, 60_000), async (req, res, next) => {\n try {\n const input = parseBody(mfaVerifyRequestSchema, req.body);\n await services.mfa.verify(req, res, input);\n } catch (err) {\n next(err);\n }\n });\n\n // Passkey as the code step: options for the challenge, then the browser's assertion.\n router.post(\"/auth/passkey/options\", rateLimit(cols, config, \"mfa_verify_ip\", verifyIpPerMin, 60_000), wrap(async (req) => {\n const input = parseBody(passkeyLoginOptionsRequestSchema, req.body);\n return services.passkeys.loginOptions(input.challengeId);\n }));\n\n router.post(\"/auth/passkey/verify\", rateLimit(cols, config, \"mfa_verify_ip\", verifyIpPerMin, 60_000), async (req, res, next) => {\n try {\n const input = parseBody(passkeyLoginVerifyRequestSchema, req.body);\n await services.passkeys.loginVerify(req, res, input);\n } catch (err) {\n next(err);\n }\n });\n\n router.post(\"/auth/refresh\", wrap(async (req, res) => services.auth.refresh(req, res)));\n\n router.post(\"/auth/logout\", requireAuth(services.tokens), wrap(async (req, res) => {\n await services.auth.logout(req, res, req.auth!.sid);\n res.status(204).end();\n return undefined;\n }));\n\n router.post(\"/auth/step-up\", requireAuth(services.tokens), rateLimit(cols, config, \"step_up_ip\", stepUpIpPerMin, 60_000), wrap(async (req) => {\n const input = parseBody(stepUpRequestSchema.extend({ code: stepUpRequestSchema.shape.code.optional() }), req.body);\n return services.mfa.stepUp(req, input, req.auth!);\n }));\n\n if (config.exposeDocs) {\n router.get(\"/auth/openapi.json\", (_req, res) => {\n res.json(buildOpenApiDoc());\n });\n }\n\n return router;\n}\n\nfunction buildOpenApiDoc(): object {\n return {\n openapi: \"3.1.0\",\n info: { title: \"express-auth-2fa\", version: \"0.1.0\" },\n paths: {\n \"/auth/login\": { post: { summary: \"Password sign-in; returns AUTHENTICATED or MFA_REQUIRED\" } },\n \"/auth/mfa/send\": { post: { summary: \"Send/resend an SMS code for a challenge\" } },\n \"/auth/mfa/verify\": { post: { summary: \"Verify a 6-digit TOTP or SMS code\" } },\n \"/auth/refresh\": { post: { summary: \"Rotate refresh cookie; returns a new access token\" } },\n \"/auth/logout\": { post: { summary: \"Revoke the session and clear cookies\" } },\n \"/auth/step-up\": { post: { summary: \"Verify a fresh code; returns a 5-min stepUpToken\" } },\n \"/account/security\": { get: { summary: \"2FA status + active methods\" } },\n \"/account/security/two-step\": { put: { summary: \"Enable/disable 2-Step Verification\" } },\n \"/account/security/methods\": { post: { summary: \"Add a pending TOTP or SMS method\" } },\n \"/account/security/methods/{id}/confirm\": { post: { summary: \"Confirm a pending method with a code\" } },\n \"/account/security/methods/{id}\": {\n patch: { summary: \"Rename or set-default a method\" },\n delete: { summary: \"Remove a method\" },\n },\n },\n };\n}\n","import type { NextFunction, Request, Response } from \"express\";\nimport type { TokenService } from \"../services/token.service.js\";\nimport { apiError } from \"./errors.js\";\n\ndeclare module \"express-serve-static-core\" {\n interface Request {\n auth?: { sub: string; sid: string; amr: string[] };\n }\n}\n\n/** Bearer-token auth for account routes. */\nexport function requireAuth(tokens: TokenService) {\n return async (req: Request, _res: Response, next: NextFunction): Promise<void> => {\n const header = req.headers.authorization ?? \"\";\n const token = header.startsWith(\"Bearer \") ? header.slice(7) : null;\n if (!token) {\n next(apiError(401, \"INVALID_CREDENTIALS\", \"Missing access token.\"));\n return;\n }\n try {\n req.auth = await tokens.verifyAccessToken(token);\n next();\n } catch (err) {\n next(err);\n }\n };\n}\n","import { Router } from \"express\";\nimport {\n addMethodRequestSchema,\n confirmMethodRequestSchema,\n patchMethodRequestSchema,\n deleteMethodRequestSchema,\n twoStepRequestSchema,\n passkeyRegisterRequestSchema,\n} from \"@cairs-npm/auth-2fa-contract\";\nimport type { AuthConfig } from \"../config/schema.js\";\nimport type { AuthCollections } from \"../data/migrate.js\";\nimport { MethodService } from \"../services/method.service.js\";\nimport { PasskeyService } from \"../services/passkey.service.js\";\nimport { TokenService } from \"../services/token.service.js\";\nimport { requireAuth } from \"../middleware/requireAuth.js\";\nimport { parseBody } from \"../middleware/errors.js\";\n\nconst wrap =\n (fn: (req: import(\"express\").Request, res: import(\"express\").Response) => Promise<void | unknown>) =>\n (req: import(\"express\").Request, res: import(\"express\").Response, next: import(\"express\").NextFunction) =>\n fn(req, res).then((r) => (r === undefined ? undefined : res.json(r))).catch(next);\n\nexport function buildAccountRouter(\n _config: AuthConfig,\n _cols: AuthCollections,\n services: { methods: MethodService; passkeys: PasskeyService; tokens: TokenService },\n): Router {\n const router = Router();\n const auth = requireAuth(services.tokens);\n\n router.get(\"/account/security\", auth, wrap(async (req) => {\n return services.methods.securitySummary(req.auth!.sub);\n }));\n\n router.put(\"/account/security/two-step\", auth, wrap(async (req) => {\n const input = parseBody(twoStepRequestSchema, req.body);\n return services.methods.setTwoStep(req.auth!.sub, req.auth!.sid, input.enabled, input.stepUpToken);\n }));\n\n router.post(\"/account/security/methods\", auth, wrap(async (req) => {\n const input = parseBody(addMethodRequestSchema, req.body);\n return services.methods.addMethod(req.auth!.sub, input);\n }));\n\n router.post(\"/account/security/passkeys/options\", auth, wrap(async (req) => {\n return services.passkeys.registrationOptions(req.auth!.sub);\n }));\n\n router.post(\"/account/security/passkeys\", auth, wrap(async (req) => {\n const input = parseBody(passkeyRegisterRequestSchema, req.body);\n return services.passkeys.register(req.auth!.sub, input);\n }));\n\n router.post(\"/account/security/methods/:id/confirm\", auth, wrap(async (req) => {\n const input = parseBody(confirmMethodRequestSchema, req.body);\n return services.methods.confirmMethod(req.auth!.sub, req.params.id!, input.code);\n }));\n\n router.patch(\"/account/security/methods/:id\", auth, wrap(async (req) => {\n const input = parseBody(patchMethodRequestSchema, req.body);\n return services.methods.patchMethod(req.auth!.sub, req.auth!.sid, req.params.id!, input);\n }));\n\n router.delete(\"/account/security/methods/:id\", auth, wrap(async (req, res) => {\n const input = parseBody(deleteMethodRequestSchema, req.body);\n await services.methods.deleteMethod(req.auth!.sub, req.auth!.sid, req.params.id!, input.stepUpToken);\n res.status(204).end();\n return undefined;\n }));\n\n return router;\n}\n","import type { SmsProvider, Logger } from \"../../config/schema.js\";\n\n/** Dev/test provider — logs the message instead of sending a real SMS. */\nexport function consoleSms(logger?: Logger): SmsProvider {\n return {\n send: async (phoneE164: string, message: string) => {\n const masked = `•••• ${phoneE164.slice(-4)}`;\n (logger ?? console).info({ to: masked }, message);\n },\n };\n}\n","import type { SmsProvider } from \"../../config/schema.js\";\n\nexport interface TwilioSmsOptions {\n /** Account SID (AC…). */\n sid: string;\n /** Auth token for the account. */\n token: string;\n /** Sender number in E.164 (or a Messaging Service SID, MG…). */\n from: string;\n /** Twilio REST base URL; defaults to https://api.twilio.com/2010-04-01. */\n baseUrl?: string;\n /** Optional pre-built client (for tests / custom transports). */\n client?: { messages: { create: (args: { to: string; from: string; body: string }) => Promise<unknown> } };\n}\n\n/**\n * Twilio SMS provider over Twilio's REST API (Messages resource), so apps need no\n * extra dependency — pass the account settings they already have.\n */\nexport function twilioSms(opts: TwilioSmsOptions): SmsProvider {\n const baseUrl = (opts.baseUrl ?? \"https://api.twilio.com/2010-04-01\").replace(/\\/+$/, \"\");\n const auth = `Basic ${Buffer.from(`${opts.sid}:${opts.token}`).toString(\"base64\")}`;\n\n return {\n send: async (phoneE164, message) => {\n if (opts.client) {\n await opts.client.messages.create({ to: phoneE164, from: opts.from, body: message });\n return;\n }\n const form = new URLSearchParams({ To: phoneE164, Body: message });\n form.set(opts.from.startsWith(\"MG\") ? \"MessagingServiceSid\" : \"From\", opts.from);\n const res = await fetch(`${baseUrl}/Accounts/${encodeURIComponent(opts.sid)}/Messages.json`, {\n method: \"POST\",\n headers: { Authorization: auth, \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: form.toString(),\n });\n if (!res.ok) {\n const detail = (await res.json().catch(() => null)) as { code?: number; message?: string } | null;\n throw new Error(`twilio: SMS not sent (${res.status}${detail?.code ? ` ${detail.code}` : \"\"}): ${detail?.message ?? res.statusText}`);\n }\n },\n };\n}\n","import type { EmailProvider, Logger } from \"../../config/schema.js\";\nimport { maskEmail } from \"../../services/delivery.js\";\n\n/** Dev/test provider — logs the message instead of sending a real email. */\nexport function consoleEmail(logger?: Logger): EmailProvider {\n return {\n send: async ({ to, message }) => {\n (logger ?? console).info({ to: maskEmail(to) }, message);\n },\n };\n}\n"],"mappings":"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","names":["argon2","crypto","migrate","import_express","import_zod","import_node_crypto","crypto","doc","insertedId","hashPassword","import_mongodb","import_node_crypto","crypto","import_express","import_auth_2fa_contract","wrap","migrate","helmet"]}
|
|
1
|
+
{"version":3,"sources":["../src/crypto/hash.ts","../src/index.ts","../src/config/schema.ts","../src/config/defaults.ts","../src/data/migrate.ts","../src/services/auth.service.ts","../src/middleware/errors.ts","../src/middleware/cookies.ts","../src/services/method.service.ts","../src/crypto/encrypt.ts","../src/crypto/totp.ts","../src/services/methodSecret.ts","../src/services/delivery.ts","../src/services/mfa.service.ts","../src/middleware/rateLimit.ts","../src/services/passkey.service.ts","../src/services/token.service.ts","../src/services/audit.service.ts","../src/routes/auth.routes.ts","../src/middleware/requireAuth.ts","../src/routes/account.routes.ts","../src/adapters/sms/console.ts","../src/adapters/sms/twilio.ts","../src/adapters/email/console.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport argon2 from \"argon2\";\n\nexport interface Argon2Params {\n memoryKiB: number;\n iterations: number;\n parallelism: number;\n}\n\nexport function hashPassword(plain: string, params: Argon2Params): Promise<string> {\n return argon2.hash(plain, {\n type: argon2.argon2id,\n memoryCost: params.memoryKiB,\n timeCost: params.iterations,\n parallelism: params.parallelism,\n });\n}\n\nexport function verifyPassword(hash: string, plain: string): Promise<boolean> {\n return argon2.verify(hash, plain).catch(() => false);\n}\n\n// Dummy hash used to equalize timing for unknown users (BR-1). Argon2id format.\nexport const DUMMY_HASH =\n \"$argon2id$v=19$m=65536,t=3,p=4$AAAAAAAAAAAAAAAAAAAAAA$1AkBK4sQubUqG5fSG5+0E4yP+4DpVXq3sfJv4boqK3k\";\n\nexport function sha256Hex(value: string): string {\n return crypto.createHash(\"sha256\").update(value, \"utf8\").digest(\"hex\");\n}\n\n/** Constant-time string comparison via SHA-256 (BR-8). */\nexport function timingSafeEqualStr(a: string, b: string): boolean {\n const ha = crypto.createHash(\"sha256\").update(a).digest();\n const hb = crypto.createHash(\"sha256\").update(b).digest();\n return crypto.timingSafeEqual(ha, hb);\n}\n\nexport function randomToken(): string {\n return crypto.randomBytes(32).toString(\"base64url\");\n}\n\nexport function randomSmsCode(): string {\n return crypto.randomInt(0, 1_000_000).toString().padStart(6, \"0\");\n}\n\nexport function randomId(prefix: string): string {\n return `${prefix}_${crypto.randomBytes(9).toString(\"base64url\")}`;\n}\n","import { Router } from \"express\";\nimport helmet from \"helmet\";\nimport type { AuthConfig, AuthConfigInput } from \"./config/schema.js\";\nimport { resolveConfig } from \"./config/defaults.js\";\nimport { getCollections, migrate as runMigrate } from \"./data/migrate.js\";\nimport { AuthService } from \"./services/auth.service.js\";\nimport { MfaService } from \"./services/mfa.service.js\";\nimport { MethodService } from \"./services/method.service.js\";\nimport { PasskeyService } from \"./services/passkey.service.js\";\nimport { TokenService } from \"./services/token.service.js\";\nimport { AuditService } from \"./services/audit.service.js\";\nimport { buildAuthRouter } from \"./routes/auth.routes.js\";\nimport { buildAccountRouter } from \"./routes/account.routes.js\";\nimport { errorHandler } from \"./middleware/errors.js\";\nimport type { Db } from \"mongodb\";\n\n/**\n * Create the auth collections with validators, indexes and TTLs (A.4).\n * Idempotent — safe on every boot.\n */\nexport async function migrate(db: Db, prefix = \"auth_\", auditDays = 365) {\n return runMigrate(db, prefix, auditDays);\n}\n\n/**\n * Mount API (A.3):\n * app.use(express.json());\n * app.use(createAuthRouter(config));\n */\nexport function createAuthRouter(input: AuthConfigInput): Router {\n const config: AuthConfig = resolveConfig(input);\n const cols = getCollections(config.db, config.collectionPrefix);\n\n const tokens = new TokenService(config, cols);\n const audit = new AuditService(config, cols);\n const auth = new AuthService(config, cols, tokens, audit);\n const mfa = new MfaService(config, cols, tokens, audit, auth);\n const methods = new MethodService(config, cols, tokens, audit);\n const passkeys = new PasskeyService(config, cols, audit, auth);\n\n const router = Router();\n router.use(helmet());\n router.use(buildAuthRouter(config, cols, { auth, mfa, passkeys, tokens }));\n router.use(buildAccountRouter(config, cols, { methods, passkeys, tokens }));\n router.use(errorHandler);\n\n // Expose helpers the host app may need (tests, registration glue).\n (router as unknown as { services: object }).services = { auth, mfa, methods, passkeys, tokens, audit };\n return router;\n}\n\nexport { consoleSms } from \"./adapters/sms/console.js\";\nexport { twilioSms } from \"./adapters/sms/twilio.js\";\nexport { consoleEmail } from \"./adapters/email/console.js\";\nexport { apiError, ApiErrorException, errorHandler } from \"./middleware/errors.js\";\nexport { getCollections } from \"./data/migrate.js\";\nexport { AuthService } from \"./services/auth.service.js\";\nexport type {\n AuthConfig,\n AuthConfigInput,\n AdapterUser,\n UserAdapter,\n SmsProvider,\n EmailProvider,\n SecurityEvent,\n Hooks,\n Clock,\n Logger,\n} from \"./config/schema.js\";\nexport type { Method, ApiError, Session, User, Tokens } from \"@cairs-npm/auth-2fa-contract\";\n","import { z } from \"zod\";\nimport type { Db, Document, MongoClient } from \"mongodb\";\nimport type { Request } from \"express\";\n\n\n/** App-owned user record shape returned by userAdapter. */\nexport interface AdapterUser {\n id: string;\n username?: string;\n email?: string;\n [key: string]: unknown;\n}\n\nexport interface UserAdapter {\n /** `ctx` is only set by passwordless sign-in (/auth/login/start), e.g. { type: \"badge\", passwordless: true }. */\n findByLogin: (login: string, ctx?: { type?: string; passwordless?: boolean }) => Promise<AdapterUser | null>;\n findById: (id: string) => Promise<AdapterUser | null>;\n}\n\nexport interface SmsProvider {\n send: (phoneE164: string, message: string) => Promise<void>;\n}\n\nexport interface EmailProvider {\n /** `message` is a ready-made plain-text body; `code` lets apps render their own template. */\n send: (input: { to: string; code: string; message: string }) => Promise<void>;\n}\n\nexport interface SecurityEvent {\n userId: string;\n type: string;\n ip?: string;\n userAgent?: string;\n at: Date;\n meta?: Record<string, unknown>;\n}\n\nexport interface Hooks {\n onSecurityEvent?: (event: SecurityEvent) => void | Promise<void>;\n}\n\nexport type Clock = () => Date;\n\nexport interface Logger {\n info: (obj: object, msg?: string) => void;\n warn: (obj: object, msg?: string) => void;\n error: (obj: object, msg?: string) => void;\n}\n\nconst limitSchema = z.object({\n /** Failed passwords before lockout (BR-2). */\n loginMaxAttempts: z.number().int().positive().default(5),\n /** Window for failed-login counting, minutes (BR-2). */\n loginWindowMin: z.number().positive().default(15),\n /** Lockout duration, minutes (BR-2, BR-9). */\n lockMinutes: z.number().positive().default(15),\n /** Min seconds between SMS sends on one challenge (BR-6). */\n smsResendSec: z.number().nonnegative().default(30),\n /** Max SMS sends per challenge (BR-6). */\n smsMaxPerChallenge: z.number().int().positive().default(5),\n /** Max SMS sends per user per hour (BR-6). */\n smsMaxPerUserHour: z.number().int().positive().default(10),\n /** Wrong codes per challenge before lockout (BR-9). */\n mfaMaxAttempts: z.number().int().positive().default(5),\n /** Max verification methods per user (BR-16). */\n maxMethods: z.number().int().positive().default(5),\n /** Per-IP login requests per minute (NFR-B4). */\n loginIpPerMin: z.number().int().positive().default(20),\n /** Per-IP MFA verify requests per minute (NFR-B4). */\n verifyIpPerMin: z.number().int().positive().default(30),\n /** Per-IP step-up requests per minute (NFR-B4). */\n stepUpIpPerMin: z.number().int().positive().default(20),\n});\n\nconst argon2Schema = z.object({\n memoryKiB: z.number().int().positive().default(65_536),\n iterations: z.number().int().positive().default(3),\n parallelism: z.number().int().positive().default(4),\n});\n\nconst cookieSchema = z.object({\n secure: z.boolean().default(true),\n domain: z.string().optional(),\n sameSite: z.enum([\"lax\", \"strict\", \"none\"]).default(\"lax\"),\n path: z.string().default(\"/\"),\n});\n\nexport const ttlSchema = z.object({\n accessTokenSec: z.number().int().positive().default(900),\n refreshTokenDays: z.number().positive().default(30),\n trustedDeviceDays: z.number().positive().default(30),\n challengeSec: z.number().int().positive().default(300),\n stepUpSec: z.number().int().positive().default(300),\n pendingMethodMin: z.number().positive().default(15),\n auditDays: z.number().positive().default(365),\n});\n\nexport const configSchema = z.object({\n db: z.custom<Db>((v) => !!v && typeof (v as Db).collection === \"function\"),\n issuer: z.string().min(1),\n /** PEM PKCS8 private key (Ed25519 or RSA). Required, min 32 bytes. */\n jwtPrivateKey: z.string().min(32),\n /** 32-byte AES key, raw string or base64. Required. */\n encryptionKey: z.string().min(32),\n keyId: z.string().default(\"v1\"),\n userAdapter: z.custom<UserAdapter>(\n (v) =>\n !!v &&\n typeof (v as UserAdapter).findByLogin === \"function\" &&\n typeof (v as UserAdapter).findById === \"function\",\n ),\n smsProvider: z.custom<SmsProvider>((v) => !!v && typeof (v as SmsProvider).send === \"function\"),\n /** Enables the always-present EMAIL method (default for new users). */\n emailProvider: z\n .custom<EmailProvider>((v) => !v || typeof (v as EmailProvider).send === \"function\")\n .optional(),\n /** MongoClient for transactions; defaults to the client behind `db`. */\n client: z.custom<MongoClient>((v) => !v || typeof (v as MongoClient).startSession === \"function\").optional(),\n logger: z.custom<Logger>().optional(),\n hooks: z\n .custom<Hooks>((v) => !v || typeof v === \"object\")\n .optional(),\n clock: z.custom<Clock>((v) => !v || typeof v === \"function\").optional(),\n collectionPrefix: z.string().min(1).default(\"auth_\"),\n /**\n * Passkeys (WebAuthn) as a second step. `rpID` is the site's domain (e.g. \"childmatch.com\",\n * \"localhost\"); `origins` are the exact browser origins allowed (e.g. \"https://app.childmatch.com\").\n * Omit to turn passkeys off.\n */\n passkeys: z\n .object({\n rpID: z.string().min(1),\n rpName: z.string().min(1),\n origins: z.array(z.string().url()).min(1),\n })\n .optional(),\n /**\n * Allow POST /auth/login/start: sign in with the identifier and a verification method only\n * (no password). Single-factor — enable only for apps that already sign in by code. Pass a\n * function to allow it per request, e.g. only for `type: \"badge\"`.\n */\n passwordlessLogin: z\n .union([\n z.boolean(),\n z.custom<(input: { username: string; type?: string }) => boolean | Promise<boolean>>(\n (v) => typeof v === \"function\",\n ),\n ])\n .default(false),\n /** 2-Step Verification state for users who have not chosen one yet. */\n twoStepDefault: z.boolean().default(false),\n limits: limitSchema.default({}),\n /**\n * Set false to turn off every RATE_LIMITED check: the per-IP limits on login / code\n * verify / send / step-up and the code-send cooldown and caps. Account lockouts after\n * failed passwords or codes (LOCKED) still apply.\n */\n rateLimits: z.boolean().default(true),\n argon2: argon2Schema.default({}),\n cookies: cookieSchema.default({}),\n ttl: ttlSchema.default({}),\n /** Serve GET /auth/openapi.json (A.7 docs). */\n exposeDocs: z.boolean().default(false),\n});\n\nexport type AuthConfigInput = z.input<typeof configSchema>;\nexport type AuthConfig = z.output<typeof configSchema> & {\n clock: Clock;\n};\n\nexport type RequestMeta = Pick<Request, \"ip\" | \"headers\">;\n\nexport interface PasswordStore {\n set: (userId: string, password: string) => Promise<void>;\n}\n\nexport type { Document };\n","import { configSchema, type AuthConfig, type AuthConfigInput, type Clock } from \"./schema.js\";\n\nconst noopLogger = {\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\nconst SENSITIVE_KEYS = [\"password\", \"code\", \"secret\", \"token\", \"phone\", \"authorization\"];\n\nfunction redact(value: unknown, depth = 0): unknown {\n if (value === null || value === undefined || depth > 6) return value;\n if (typeof value === \"string\") return value;\n if (Array.isArray(value)) return value.map((v) => redact(v, depth + 1));\n if (typeof value === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = SENSITIVE_KEYS.some((s) => k.toLowerCase().includes(s)) ? \"[REDACTED]\" : redact(v, depth + 1);\n }\n return out;\n }\n return value;\n}\n\nfunction wrapLogger(logger: AuthConfig[\"logger\"]): NonNullable<AuthConfig[\"logger\"]> {\n if (!logger) return noopLogger;\n return {\n info: (o, m) => logger.info(redact(o) as object, m),\n warn: (o, m) => logger.warn(redact(o) as object, m),\n error: (o, m) => logger.error(redact(o) as object, m),\n };\n}\n\n/** Validate config at startup (NFR-B2); returns a fully-defaulted AuthConfig. */\nexport function resolveConfig(input: AuthConfigInput): AuthConfig {\n const parsed = configSchema.parse(input);\n const key = decodeEncryptionKey(parsed.encryptionKey);\n if (key.length !== 32) {\n throw new Error(\"encryptionKey must decode to exactly 32 bytes (raw 32-char string or base64)\");\n }\n const clock: Clock = parsed.clock ?? (() => new Date());\n return { ...parsed, clock, logger: wrapLogger(parsed.logger) };\n}\n\nexport function decodeEncryptionKey(raw: string): Buffer {\n const asBase64 = Buffer.from(raw, \"base64\");\n if (asBase64.length === 32 && asBase64.toString(\"base64\").replace(/=+$/, \"\") === raw.replace(/=+$/, \"\")) {\n return asBase64;\n }\n return Buffer.from(raw, \"utf8\");\n}\n","import type { Db } from \"mongodb\";\nimport type {\n ChallengeDoc,\n CredentialDoc,\n EventDoc,\n MethodDoc,\n RateLimitDoc,\n SessionDoc,\n TrustedDeviceDoc,\n} from \"./docs.js\";\n\nexport interface AuthCollections {\n credentials: import(\"mongodb\").Collection<CredentialDoc>;\n methods: import(\"mongodb\").Collection<MethodDoc>;\n challenges: import(\"mongodb\").Collection<ChallengeDoc>;\n sessions: import(\"mongodb\").Collection<SessionDoc>;\n trustedDevices: import(\"mongodb\").Collection<TrustedDeviceDoc>;\n events: import(\"mongodb\").Collection<EventDoc>;\n rateLimits: import(\"mongodb\").Collection<RateLimitDoc>;\n}\n\nexport function getCollections(db: Db, prefix = \"auth_\"): AuthCollections {\n return {\n credentials: db.collection<CredentialDoc>(`${prefix}credentials`),\n methods: db.collection<MethodDoc>(`${prefix}mfa_methods`),\n challenges: db.collection<ChallengeDoc>(`${prefix}challenges`),\n sessions: db.collection<SessionDoc>(`${prefix}sessions`),\n trustedDevices: db.collection<TrustedDeviceDoc>(`${prefix}trusted_devices`),\n events: db.collection<EventDoc>(`${prefix}events`),\n rateLimits: db.collection<RateLimitDoc>(`${prefix}rate_limits`),\n };\n}\n\nconst VALIDATORS: Record<string, object> = {\n credentials: {\n $jsonSchema: {\n bsonType: \"object\",\n required: [\"userId\", \"passwordHash\", \"twoStepEnabled\", \"updatedAt\"],\n properties: {\n userId: { bsonType: \"string\" },\n passwordHash: { bsonType: \"string\" },\n twoStepEnabled: { bsonType: \"bool\" },\n lockedUntil: { bsonType: [\"date\", \"null\"] },\n updatedAt: { bsonType: \"date\" },\n },\n },\n },\n mfa_methods: {\n $jsonSchema: {\n bsonType: \"object\",\n required: [\"userId\", \"type\", \"label\", \"isDefault\", \"status\", \"createdAt\"],\n properties: {\n userId: { bsonType: \"string\" },\n type: { enum: [\"EMAIL\", \"TOTP\", \"SMS\", \"PASSKEY\"] },\n label: { bsonType: \"string\" },\n isDefault: { bsonType: \"bool\" },\n status: { enum: [\"pending\", \"active\"] },\n createdAt: { bsonType: \"date\" },\n lastUsedAt: { bsonType: [\"date\", \"null\"] },\n },\n },\n },\n challenges: {\n $jsonSchema: {\n bsonType: \"object\",\n required: [\"_id\", \"userId\", \"purpose\", \"attempts\", \"expiresAt\"],\n properties: {\n userId: { bsonType: \"string\" },\n purpose: { enum: [\"login\", \"step_up\", \"enroll\"] },\n attempts: { bsonType: \"int\" },\n expiresAt: { bsonType: \"date\" },\n },\n },\n },\n sessions: {\n $jsonSchema: {\n bsonType: \"object\",\n required: [\"_id\", \"userId\", \"refreshTokenHash\", \"familyId\", \"expiresAt\"],\n properties: {\n userId: { bsonType: \"string\" },\n refreshTokenHash: { bsonType: \"string\" },\n familyId: { bsonType: \"string\" },\n expiresAt: { bsonType: \"date\" },\n revokedAt: { bsonType: [\"date\", \"null\"] },\n },\n },\n },\n trusted_devices: {\n $jsonSchema: {\n bsonType: \"object\",\n required: [\"userId\", \"tokenHash\", \"expiresAt\"],\n properties: {\n userId: { bsonType: \"string\" },\n tokenHash: { bsonType: \"string\" },\n expiresAt: { bsonType: \"date\" },\n },\n },\n },\n events: {\n $jsonSchema: {\n bsonType: \"object\",\n required: [\"userId\", \"type\", \"at\"],\n properties: {\n userId: { bsonType: \"string\" },\n type: { bsonType: \"string\" },\n at: { bsonType: \"date\" },\n },\n },\n },\n rate_limits: {\n $jsonSchema: {\n bsonType: \"object\",\n required: [\"_id\", \"count\", \"expiresAt\"],\n properties: {\n count: { bsonType: \"int\" },\n expiresAt: { bsonType: \"date\" },\n },\n },\n },\n};\n\n/**\n * Create the auth_* collections with JSON-schema validators, indexes and TTLs\n * (A.4). Idempotent — safe to call on every app boot.\n */\nexport async function migrate(db: Db, prefix = \"auth_\", auditDays = 365): Promise<AuthCollections> {\n for (const [suffix, validator] of Object.entries(VALIDATORS)) {\n const name = `${prefix}${suffix}`;\n const exists = await db.listCollections({ name }).hasNext();\n if (!exists) {\n await db.createCollection(name, { validator });\n } else {\n // Keep validators current on existing deployments (e.g. new method types).\n await db.command({ collMod: name, validator });\n }\n }\n\n const c = getCollections(db, prefix);\n\n await c.credentials.createIndex({ userId: 1 }, { unique: true });\n await c.methods.createIndex({ userId: 1 });\n await c.methods.createIndex(\n { userId: 1, isDefault: 1 },\n { unique: true, partialFilterExpression: { isDefault: true } },\n );\n await c.methods.createIndex(\n { userId: 1, type: 1 },\n { unique: true, partialFilterExpression: { type: \"EMAIL\" } },\n );\n await c.methods.createIndex(\n { pendingExpiresAt: 1 },\n { expireAfterSeconds: 0, partialFilterExpression: { status: \"pending\" } },\n );\n await c.challenges.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });\n await c.sessions.createIndex({ refreshTokenHash: 1 }, { unique: true });\n await c.sessions.createIndex({ userId: 1 });\n await c.sessions.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });\n await c.trustedDevices.createIndex({ tokenHash: 1 }, { unique: true });\n await c.trustedDevices.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });\n await c.events.createIndex({ userId: 1, at: -1 });\n await c.events.createIndex({ at: 1 }, { expireAfterSeconds: Math.floor(auditDays * 86400) });\n await c.rateLimits.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });\n\n return c;\n}\n","import type { Request, Response } from \"express\";\nimport type { LoginMfaRequired, LoginResponse, Method } from \"@cairs-npm/auth-2fa-contract\";\nimport type { AuthConfig } from \"../config/schema.js\";\nimport type { AuthCollections } from \"../data/migrate.js\";\nimport type { CredentialDoc } from \"../data/docs.js\";\nimport { DUMMY_HASH, randomId, sha256Hex, verifyPassword, randomToken } from \"../crypto/hash.js\";\nimport { apiError } from \"../middleware/errors.js\";\nimport {\n CSRF_COOKIE,\n REFRESH_COOKIE,\n TRUSTED_DEVICE_COOKIE,\n clearCookie,\n parseCookies,\n setCookie,\n} from \"../middleware/cookies.js\";\nimport { TokenService } from \"./token.service.js\";\nimport { AuditService } from \"./audit.service.js\";\nimport { toApiMethod } from \"./method.service.js\";\nimport { ensureEmailMethod } from \"./delivery.js\";\nimport { pruneUnreadableMethods } from \"./methodSecret.js\";\n\nexport interface RequestCtx {\n ip?: string;\n userAgent?: string;\n}\n\nexport function ctxOf(req: Request): RequestCtx {\n return { ip: req.ip, userAgent: req.headers[\"user-agent\"] };\n}\n\nexport class AuthService {\n constructor(\n private readonly config: AuthConfig,\n private readonly cols: AuthCollections,\n private readonly tokens: TokenService,\n private readonly audit: AuditService,\n ) {}\n\n private async getCredentials(userId: string): Promise<CredentialDoc | null> {\n return this.cols.credentials.findOne({ userId });\n }\n\n private async ensureCredentials(userId: string): Promise<CredentialDoc> {\n const now = this.config.clock();\n await this.cols.credentials.updateOne(\n { userId },\n {\n $setOnInsert: {\n userId,\n passwordHash: \"\",\n twoStepEnabled: this.config.twoStepDefault,\n failedLogins: { count: 0, windowStart: now },\n lockedUntil: null,\n updatedAt: now,\n },\n },\n { upsert: true },\n );\n return (await this.cols.credentials.findOne({ userId }))!;\n }\n\n /** Set or update the password for a user (used by apps/tests; registration is app-owned). */\n async setPassword(userId: string, password: string): Promise<void> {\n const { hashPassword } = await import(\"../crypto/hash.js\");\n const now = this.config.clock();\n const cred = await this.ensureCredentials(userId);\n await this.cols.credentials.updateOne(\n { _id: cred._id },\n { $set: { passwordHash: await hashPassword(password, this.config.argon2), updatedAt: now } },\n );\n }\n\n async login(req: Request, res: Response, input: { username: string; password: string; rememberMe: boolean }): Promise<LoginResponse> {\n const now = this.config.clock();\n const ctx = ctxOf(req);\n const user = await this.config.userAdapter.findByLogin(input.username);\n\n // Timing parity: verify against a dummy hash for unknown users (BR-1).\n const cred = user ? await this.getCredentials(user.id) : null;\n const ok = await verifyPassword(cred?.passwordHash || DUMMY_HASH, input.password);\n if (!user || !cred?.passwordHash || !ok) {\n if (user && cred) await this.registerFailedLogin(cred, now);\n throw apiError(401, \"INVALID_CREDENTIALS\", \"Invalid username or password.\");\n }\n\n // BR-2 lockout check.\n if (cred.lockedUntil && cred.lockedUntil.getTime() > now.getTime()) {\n throw apiError(423, \"LOCKED\", \"Too many attempts. Try again later.\", { retryAt: cred.lockedUntil });\n }\n await this.resetFailedLogins(cred, now);\n\n // BR-3: 2FA off, or a valid trusted device → authenticate immediately.\n const trustedOk = cred.twoStepEnabled && (await this.checkTrustedDevice(req, user.id));\n if (!cred.twoStepEnabled || trustedOk) {\n await this.audit.record(user.id, \"login_success\", ctx);\n return this.authenticate(user.id, req, res, [\"pwd\"], { rememberMe: input.rememberMe });\n }\n\n // BR-4: issue an MFA challenge.\n const challenge = await this.issueLoginChallenge(user, ctx, now, false);\n if (!challenge) {\n await this.audit.record(user.id, \"login_success\", ctx);\n return this.authenticate(user.id, req, res, [\"pwd\"], { rememberMe: input.rememberMe });\n }\n return challenge;\n }\n\n /**\n * Passwordless sign-in (config.passwordlessLogin): the identifier alone starts the same\n * verification step as a password sign-in. Always needs a method — there's no password to\n * fall back on.\n */\n async loginStart(req: Request, input: { username: string; type?: string }): Promise<LoginMfaRequired> {\n const allow = this.config.passwordlessLogin;\n const allowed = typeof allow === \"function\" ? await allow({ username: input.username, type: input.type }) : allow;\n if (!allowed) throw apiError(404, \"VALIDATION_ERROR\", \"Not found.\");\n const now = this.config.clock();\n const ctx = ctxOf(req);\n const user = await this.config.userAdapter.findByLogin(input.username, { type: input.type, passwordless: true });\n if (!user) throw apiError(404, \"INVALID_CREDENTIALS\", \"We couldn't find an account with that email.\");\n const cred = await this.getCredentials(user.id);\n if (cred?.lockedUntil && cred.lockedUntil.getTime() > now.getTime()) {\n throw apiError(423, \"LOCKED\", \"Too many attempts. Try again later.\", { retryAt: cred.lockedUntil });\n }\n const challenge = await this.issueLoginChallenge(user, ctx, now, true, input.type);\n if (!challenge) {\n throw apiError(409, \"NO_METHODS\", \"We can't send you a code: this account has no email or verification method.\");\n }\n return challenge;\n }\n\n /** Creates a login challenge over the user's active methods; null when there are none. */\n private async issueLoginChallenge(\n user: { id: string; email?: string },\n ctx: ReturnType<typeof ctxOf>,\n now: Date,\n passwordless: boolean,\n clientType?: string,\n ): Promise<LoginMfaRequired | null> {\n await ensureEmailMethod(this.config, this.cols, user.id, user.email);\n await pruneUnreadableMethods(this.config, this.cols, user.id, this.audit);\n const active = await this.cols.methods.find({ userId: user.id, status: \"active\" }).toArray();\n if (active.length === 0) return null;\n const challengeId = randomId(\"chg\");\n await this.cols.challenges.insertOne({\n _id: challengeId,\n userId: user.id,\n purpose: \"login\",\n methodId: null,\n smsCodeHash: null,\n smsSentAt: null,\n smsSendCount: 0,\n attempts: 0,\n ...(passwordless ? { passwordless: true } : {}),\n ...(clientType ? { clientType } : {}),\n expiresAt: new Date(now.getTime() + this.config.ttl.challengeSec * 1000),\n createdAt: now,\n });\n const methods = active.map(toApiMethod);\n // The user's own default first; without one, fall back to the account email.\n const defaultMethod =\n active.find((m) => m.isDefault) ?? active.find((m) => m.type === \"EMAIL\") ?? active[0]!;\n await this.audit.record(user.id, \"login_mfa_required\", ctx);\n return {\n status: \"MFA_REQUIRED\",\n challengeId,\n methods,\n defaultMethodId: defaultMethod._id!.toString(),\n };\n }\n\n private async registerFailedLogin(cred: CredentialDoc, now: Date): Promise<void> {\n const { loginMaxAttempts, loginWindowMin, lockMinutes } = this.config.limits;\n const windowMs = loginWindowMin * 60_000;\n const count =\n cred.failedLogins && now.getTime() - cred.failedLogins.windowStart.getTime() < windowMs\n ? cred.failedLogins.count + 1\n : 1;\n const locked = count >= loginMaxAttempts;\n await this.cols.credentials.updateOne(\n { _id: cred._id },\n {\n $set: {\n failedLogins: { count, windowStart: locked ? cred.failedLogins?.windowStart ?? now : count === 1 ? now : cred.failedLogins!.windowStart },\n ...(locked ? { lockedUntil: new Date(now.getTime() + lockMinutes * 60_000) } : {}),\n updatedAt: now,\n },\n },\n );\n if (locked) await this.audit.record(cred.userId, \"account_locked\", {});\n }\n\n private async resetFailedLogins(cred: CredentialDoc, now: Date): Promise<void> {\n await this.cols.credentials.updateOne(\n { _id: cred._id },\n { $set: { failedLogins: { count: 0, windowStart: now }, updatedAt: now } },\n );\n }\n\n private async checkTrustedDevice(req: Request, userId: string): Promise<boolean> {\n const token = parseCookies(req)[TRUSTED_DEVICE_COOKIE];\n if (!token) return false;\n const doc = await this.cols.trustedDevices.findOne({ tokenHash: sha256Hex(token), userId });\n if (!doc) return false;\n if (doc.expiresAt.getTime() <= this.config.clock().getTime()) return false;\n return true;\n }\n\n /** Issue tokens + set cookies (refresh cookie, CSRF cookie). */\n async authenticate(\n userId: string,\n req: Request,\n res: Response,\n amr: string[],\n opts: { rememberDevice?: boolean; rememberMe?: boolean; deviceLabel?: string } = {},\n ): Promise<{ status: \"AUTHENTICATED\"; user: { id: string; username?: string; email?: string }; tokens: { accessToken: string; expiresInSec: number; csrfToken?: string } }> {\n const ctx = ctxOf(req);\n const bundle = await this.tokens.issueTokens(userId, amr, ctx);\n const refreshDays = opts.rememberMe ? this.config.ttl.refreshTokenDays : Math.min(this.config.ttl.refreshTokenDays, 1);\n setCookie(res, this.config, REFRESH_COOKIE, bundle.refreshToken, refreshDays * 86_400_000);\n // The CSRF value also goes in the body: a UI on another host (app.x.com vs api.x.com) can't\n // read the API's cookie, so the client keeps this copy for the x-csrf-token header.\n const csrfToken = randomToken();\n setCookie(res, this.config, CSRF_COOKIE, csrfToken, this.config.ttl.refreshTokenDays * 86_400_000, false);\n\n if (opts.rememberDevice) {\n await this.trustDevice(userId, res, opts.deviceLabel ?? \"This device\");\n }\n\n const user = await this.config.userAdapter.findById(userId);\n return {\n status: \"AUTHENTICATED\",\n user: { id: userId, username: user?.username, email: user?.email },\n tokens: { accessToken: bundle.accessToken, expiresInSec: bundle.expiresInSec, csrfToken },\n };\n }\n\n async trustDevice(userId: string, res: Response, label: string): Promise<void> {\n const token = randomToken();\n const now = this.config.clock();\n await this.cols.trustedDevices.insertOne({\n userId,\n tokenHash: sha256Hex(token),\n label,\n createdAt: now,\n expiresAt: new Date(now.getTime() + this.config.ttl.trustedDeviceDays * 86_400_000),\n });\n setCookie(res, this.config, TRUSTED_DEVICE_COOKIE, token, this.config.ttl.trustedDeviceDays * 86_400_000);\n }\n\n async refresh(req: Request, res: Response): Promise<{ accessToken: string; expiresInSec: number; csrfToken?: string }> {\n // CSRF double-submit on the cookie-authenticated route (NFR-B3).\n const cookies = parseCookies(req);\n const csrfCookie = cookies[CSRF_COOKIE];\n const csrfHeader = req.headers[\"x-csrf-token\"];\n if (!csrfCookie || !csrfHeader || csrfCookie !== csrfHeader) {\n throw apiError(403, \"INVALID_CREDENTIALS\", \"CSRF token mismatch.\");\n }\n const raw = cookies[REFRESH_COOKIE];\n if (!raw) throw apiError(401, \"INVALID_CREDENTIALS\", \"No refresh token.\");\n\n const bundle = await this.tokens.rotateRefreshToken(raw, ctxOf(req));\n setCookie(res, this.config, REFRESH_COOKIE, bundle.refreshToken, this.config.ttl.refreshTokenDays * 86_400_000);\n return { accessToken: bundle.accessToken, expiresInSec: bundle.expiresInSec, csrfToken: csrfCookie };\n }\n\n async logout(req: Request, res: Response, sid: string): Promise<void> {\n await this.tokens.revokeSession(sid);\n clearCookie(res, this.config, REFRESH_COOKIE);\n clearCookie(res, this.config, CSRF_COOKIE);\n clearCookie(res, this.config, TRUSTED_DEVICE_COOKIE);\n const auth = (req as Request & { auth?: { sub: string } }).auth;\n if (auth) await this.audit.record(auth.sub, \"logout\", ctxOf(req));\n }\n\n async listActiveMethods(userId: string): Promise<Method[]> {\n const docs = await this.cols.methods.find({ userId, status: \"active\" }).toArray();\n return docs\n .sort((a, b) => Number(b.isDefault) - Number(a.isDefault) || a.createdAt.getTime() - b.createdAt.getTime())\n .map(toApiMethod);\n }\n}\n","import type { Request, Response, NextFunction } from \"express\";\nimport type { ApiError, ErrorCode } from \"@cairs-npm/auth-2fa-contract\";\nimport { ZodError } from \"zod\";\n\n// One error shape (NFR-B6): { error, message, attemptsLeft?, retryAt? }.\n\nexport class ApiErrorException extends Error {\n constructor(\n public readonly status: number,\n public readonly code: ErrorCode,\n message: string,\n public readonly extra?: { attemptsLeft?: number; retryAt?: string },\n ) {\n super(message);\n }\n}\n\nexport function apiError(\n status: number,\n code: ErrorCode,\n message: string,\n extra?: { attemptsLeft?: number; retryAt?: Date | string },\n): ApiErrorException {\n return new ApiErrorException(status, code, message, {\n attemptsLeft: extra?.attemptsLeft,\n retryAt: extra?.retryAt instanceof Date ? extra.retryAt.toISOString() : extra?.retryAt,\n });\n}\n\n// Express only treats a middleware with exactly 4 parameters as an error handler.\nexport function errorHandler(err: unknown, _req: Request, res: Response, _next: NextFunction): void {\n if (err instanceof ApiErrorException) {\n const body: ApiError = { error: err.code, message: err.message, ...err.extra };\n res.status(err.status).json(body);\n return;\n }\n if (err instanceof ZodError) {\n res.status(400).json({ error: \"VALIDATION_ERROR\", message: err.issues[0]?.message ?? \"The request is invalid.\" });\n return;\n }\n // No stack traces leave the server (NFR-B6); log them here instead.\n console.error(\"express-auth-2fa: unhandled error\", err);\n res.status(500).json({ error: \"VALIDATION_ERROR\", message: \"Internal error.\" });\n}\n\nexport function parseBody<T>(schema: { parse: (v: unknown) => T }, body: unknown): T {\n return schema.parse(body);\n}\n","import type { Request, Response } from \"express\";\nimport type { AuthConfig } from \"../config/schema.js\";\n\n// Minimal cookie helpers — the package must not depend on cookie-parser.\n\nexport const REFRESH_COOKIE = \"auth_refresh\";\nexport const TRUSTED_DEVICE_COOKIE = \"auth_trusted\";\nexport const CSRF_COOKIE = \"auth_csrf\";\nexport const CSRF_HEADER = \"x-csrf-token\";\n\nexport function parseCookies(req: Request): Record<string, string> {\n const header = req.headers.cookie;\n if (!header) return {};\n const out: Record<string, string> = {};\n for (const part of header.split(\";\")) {\n const eq = part.indexOf(\"=\");\n if (eq === -1) continue;\n out[part.slice(0, eq).trim()] = decodeURIComponent(part.slice(eq + 1).trim());\n }\n return out;\n}\n\nexport function setCookie(\n res: Response,\n config: AuthConfig,\n name: string,\n value: string,\n maxAgeMs: number,\n httpOnly = true,\n): void {\n res.cookie(name, value, {\n httpOnly,\n secure: config.cookies.secure,\n sameSite: config.cookies.sameSite,\n domain: config.cookies.domain,\n path: config.cookies.path,\n maxAge: maxAgeMs,\n });\n}\n\nexport function clearCookie(res: Response, config: AuthConfig, name: string): void {\n res.clearCookie(name, {\n secure: config.cookies.secure,\n sameSite: config.cookies.sameSite,\n domain: config.cookies.domain,\n path: config.cookies.path,\n });\n}\n","import { ObjectId } from \"mongodb\";\nimport { parsePhoneNumberFromString } from \"libphonenumber-js\";\nimport type { Method, MethodType } from \"@cairs-npm/auth-2fa-contract\";\nimport type { AuthConfig } from \"../config/schema.js\";\nimport type { AuthCollections } from \"../data/migrate.js\";\nimport type { MethodDoc } from \"../data/docs.js\";\nimport { encryptString } from \"../crypto/encrypt.js\";\nimport { decodeEncryptionKey } from \"../config/defaults.js\";\nimport { generateTotpSecret, buildOtpauthUri, checkTotp } from \"../crypto/totp.js\";\nimport { randomSmsCode, sha256Hex, timingSafeEqualStr } from \"../crypto/hash.js\";\nimport { apiError } from \"../middleware/errors.js\";\nimport { AuditService } from \"./audit.service.js\";\nimport { TokenService } from \"./token.service.js\";\nimport type { ClientSession, Db, MongoClient } from \"mongodb\";\nimport { ensureEmailMethod } from \"./delivery.js\";\nimport { decryptMethodSecret, pruneUnreadableMethods } from \"./methodSecret.js\";\n\nexport function toApiMethod(doc: MethodDoc): Method {\n return {\n id: doc._id!.toString(),\n type: doc.type,\n label: doc.type === \"SMS\" ? `•••• ${doc.phoneLast4 ?? \"\"}`.trim() : doc.label,\n isDefault: doc.isDefault,\n createdAt: doc.createdAt.toISOString(),\n lastUsedAt: doc.lastUsedAt ? doc.lastUsedAt.toISOString() : null,\n };\n}\n\nfunction duplicateTypeMessage(type: MethodType): string {\n return type === \"SMS\"\n ? \"You already have a phone number. Remove it before adding another.\"\n : \"You already have an authenticator app. Remove it before adding another.\";\n}\n\nexport class MethodService {\n private encKey: Buffer;\n\n constructor(\n private readonly config: AuthConfig,\n private readonly cols: AuthCollections,\n private readonly tokens: TokenService,\n private readonly audit: AuditService,\n ) {\n this.encKey = decodeEncryptionKey(config.encryptionKey);\n }\n\n private objectId(id: string): ObjectId {\n try {\n return new ObjectId(id);\n } catch {\n throw apiError(400, \"VALIDATION_ERROR\", \"Invalid id.\");\n }\n }\n\n /** BR-14 — active methods only, default first, then by createdAt. */\n async securitySummary(userId: string): Promise<{ twoStepEnabled: boolean; methods: Method[] }> {\n await ensureEmailMethod(this.config, this.cols, userId);\n await pruneUnreadableMethods(this.config, this.cols, userId, this.audit);\n const cred = await this.cols.credentials.findOne({ userId });\n const docs = await this.cols.methods.find({ userId, status: \"active\" }).toArray();\n const methods = docs\n .sort((a, b) => Number(b.isDefault) - Number(a.isDefault) || a.createdAt.getTime() - b.createdAt.getTime())\n .map(toApiMethod);\n return { twoStepEnabled: cred?.twoStepEnabled ?? this.config.twoStepDefault, methods };\n }\n\n /** BR-15 — enable needs ≥1 active method; disable needs step-up + revokes trusted devices. */\n async setTwoStep(\n userId: string,\n sid: string,\n enabled: boolean,\n stepUpToken: string | undefined,\n ): Promise<{ twoStepEnabled: boolean }> {\n const now = this.config.clock();\n if (enabled) {\n await ensureEmailMethod(this.config, this.cols, userId);\n const count = await this.cols.methods.countDocuments({ userId, status: \"active\" });\n if (count === 0) throw apiError(409, \"NO_METHODS\", \"Add a verification method before enabling 2-Step Verification.\");\n } else {\n if (!stepUpToken) throw apiError(403, \"STEP_UP_REQUIRED\", \"This action requires recent verification.\");\n await this.tokens.verifyStepUpToken(stepUpToken, userId, sid);\n await this.cols.trustedDevices.deleteMany({ userId });\n }\n await this.cols.credentials.updateOne({ userId }, { $set: { twoStepEnabled: enabled, updatedAt: now } });\n await this.audit.record(userId, enabled ? \"2fa_enabled\" : \"2fa_disabled\", {});\n return { twoStepEnabled: enabled };\n }\n\n /** BR-16 — add a pending TOTP (otpauthUri+secret once) or pending SMS (sends code). Max 5 methods. */\n async addMethod(\n userId: string,\n input: { type: MethodType; label?: string; phone?: string },\n ): Promise<{ pendingMethodId: string; otpauthUri?: string; secret?: string }> {\n if (input.type === \"EMAIL\") {\n throw apiError(400, \"VALIDATION_ERROR\", \"The email method is added automatically from the account email.\");\n }\n const now = this.config.clock();\n // One method per type (plus the built-in email): refuse a second, drop stale unconfirmed ones.\n if (await this.cols.methods.findOne({ userId, type: input.type, status: \"active\" })) {\n throw apiError(409, \"VALIDATION_ERROR\", duplicateTypeMessage(input.type));\n }\n await this.cols.methods.deleteMany({ userId, type: input.type, status: \"pending\" });\n const total = await this.cols.methods.countDocuments({ userId });\n if (total >= this.config.limits.maxMethods) {\n throw apiError(409, \"VALIDATION_ERROR\", `You can add at most ${this.config.limits.maxMethods} verification methods.`);\n }\n const pendingExpiresAt = new Date(now.getTime() + this.config.ttl.pendingMethodMin * 60_000);\n\n if (input.type === \"TOTP\") {\n const secret = generateTotpSecret();\n const user = await this.config.userAdapter.findById(userId);\n const account = user?.email ?? user?.username ?? userId;\n const doc: MethodDoc = {\n userId,\n type: \"TOTP\",\n label: input.label ?? \"Authenticator app\",\n isDefault: false,\n status: \"pending\",\n secretEnc: encryptString(secret, this.encKey, this.config.keyId),\n pendingExpiresAt,\n createdAt: now,\n lastUsedAt: null,\n };\n const { insertedId } = await this.cols.methods.insertOne(doc);\n await this.audit.record(userId, \"method_add_started\", { meta: { type: \"TOTP\" } });\n return {\n pendingMethodId: insertedId.toString(),\n otpauthUri: buildOtpauthUri(this.config.issuer, account, secret),\n secret,\n };\n }\n\n // SMS\n const parsed = input.phone ? parsePhoneNumberFromString(input.phone) : undefined;\n if (!parsed || !parsed.isValid()) throw apiError(400, \"VALIDATION_ERROR\", \"Enter a valid phone number.\");\n const e164 = parsed.number;\n const last4 = e164.slice(-4);\n const code = randomSmsCode();\n const doc: MethodDoc = {\n userId,\n type: \"SMS\",\n label: `•••• ${last4}`,\n isDefault: false,\n status: \"pending\",\n phoneE164Enc: encryptString(e164, this.encKey, this.config.keyId),\n phoneLast4: last4,\n pendingCodeHash: sha256Hex(code),\n pendingExpiresAt,\n createdAt: now,\n lastUsedAt: null,\n };\n const { insertedId } = await this.cols.methods.insertOne(doc);\n await this.config.smsProvider.send(e164, `Your verification code is ${code}`);\n await this.audit.record(userId, \"method_add_started\", { meta: { type: \"SMS\" } });\n return { pendingMethodId: insertedId.toString() };\n }\n\n /** BR-17 — confirm a pending method; first active method becomes default. */\n async confirmMethod(userId: string, methodId: string, code: string): Promise<Method> {\n const method = await this.cols.methods.findOne({ _id: this.objectId(methodId), userId, status: \"pending\" });\n if (!method) throw apiError(404, \"VALIDATION_ERROR\", \"Unknown method.\");\n const now = this.config.clock();\n\n let ok = false;\n if (method.type === \"TOTP\") {\n const secret = decryptMethodSecret(method.secretEnc!, { [this.config.keyId]: this.encKey });\n ok = checkTotp(code, secret, now.getTime()).valid;\n } else {\n ok = !!method.pendingCodeHash && timingSafeEqualStr(method.pendingCodeHash, sha256Hex(code));\n }\n if (!ok) throw apiError(401, \"INVALID_CODE\", \"That code didn't work. Try again.\");\n // A parallel add of the same type may have been confirmed first.\n if (await this.cols.methods.findOne({ userId, type: method.type, status: \"active\" })) {\n await this.cols.methods.deleteOne({ _id: method._id });\n throw apiError(409, \"VALIDATION_ERROR\", duplicateTypeMessage(method.type));\n }\n\n const activeCount = await this.cols.methods.countDocuments({ userId, status: \"active\" });\n await this.cols.methods.updateOne(\n { _id: method._id },\n {\n $set: { status: \"active\", isDefault: activeCount === 0, lastUsedAt: now },\n $unset: { pendingCodeHash: \"\", pendingExpiresAt: \"\" },\n },\n );\n await this.audit.record(userId, \"method_added\", { meta: { type: method.type } });\n return toApiMethod({ ...method, status: \"active\", isDefault: activeCount === 0, lastUsedAt: now });\n }\n\n /** BR-18 — rename (TOTP only, 1–40 chars) or set default (step-up, transaction). */\n async patchMethod(\n userId: string,\n sid: string,\n methodId: string,\n input: { label?: string; isDefault?: boolean; stepUpToken?: string },\n ): Promise<Method> {\n const method = await this.cols.methods.findOne({ _id: this.objectId(methodId), userId, status: \"active\" });\n if (!method) throw apiError(404, \"VALIDATION_ERROR\", \"Unknown method.\");\n\n if (input.isDefault === true) {\n if (!input.stepUpToken) throw apiError(403, \"STEP_UP_REQUIRED\", \"This action requires recent verification.\");\n await this.tokens.verifyStepUpToken(input.stepUpToken, userId, sid);\n await this.withTransaction(async (session) => {\n await this.cols.methods.updateMany({ userId, isDefault: true }, { $set: { isDefault: false } }, { session });\n await this.cols.methods.updateOne({ _id: method._id }, { $set: { isDefault: true } }, { session });\n });\n method.isDefault = true;\n await this.audit.record(userId, \"method_set_default\", { meta: { methodId } });\n }\n if (input.isDefault === false && method.isDefault) {\n throw apiError(400, \"VALIDATION_ERROR\", \"Set another method as default instead.\");\n }\n if (input.label !== undefined) {\n if (method.type !== \"TOTP\" && method.type !== \"PASSKEY\") {\n throw apiError(400, \"VALIDATION_ERROR\", \"Only authenticator apps and passkeys can be renamed.\");\n }\n if (input.label.length < 1 || input.label.length > 40) {\n throw apiError(400, \"VALIDATION_ERROR\", \"Label must be 1–40 characters.\");\n }\n await this.cols.methods.updateOne({ _id: method._id }, { $set: { label: input.label } });\n method.label = input.label;\n await this.audit.record(userId, \"method_renamed\", { meta: { methodId } });\n }\n return toApiMethod(method);\n }\n\n /** BR-19 — remove (step-up); LAST_METHOD guard; promote most-recently-used default. */\n async deleteMethod(userId: string, sid: string, methodId: string, stepUpToken: string | undefined): Promise<void> {\n if (!stepUpToken) throw apiError(403, \"STEP_UP_REQUIRED\", \"This action requires recent verification.\");\n await this.tokens.verifyStepUpToken(stepUpToken, userId, sid);\n\n const method = await this.cols.methods.findOne({ _id: this.objectId(methodId), userId });\n if (!method) throw apiError(404, \"VALIDATION_ERROR\", \"Unknown method.\");\n if (method.type === \"EMAIL\") throw apiError(400, \"VALIDATION_ERROR\", \"Your email verification method can't be removed.\");\n\n const cred = await this.cols.credentials.findOne({ userId });\n const activeCount = await this.cols.methods.countDocuments({ userId, status: \"active\" });\n if (cred?.twoStepEnabled && method.status === \"active\" && activeCount <= 1) {\n throw apiError(409, \"LAST_METHOD\", \"You can't remove your last verification method while 2-Step Verification is on.\");\n }\n\n await this.withTransaction(async (session) => {\n await this.cols.methods.deleteOne({ _id: method._id }, { session });\n if (method.isDefault && method.status === \"active\") {\n const next = await this.cols.methods.findOne(\n { userId, status: \"active\", _id: { $ne: method._id } },\n { sort: { lastUsedAt: -1, createdAt: -1 }, session },\n );\n if (next) await this.cols.methods.updateOne({ _id: next._id }, { $set: { isDefault: true } }, { session });\n }\n });\n await this.audit.record(userId, \"method_removed\", { meta: { methodId, type: method.type } });\n }\n\n private transactionsSupported: Promise<boolean> | null = null;\n\n /** Transactions need a replica set or mongos; a standalone server (common in dev/test) has neither. */\n private supportsTransactions(): Promise<boolean> {\n this.transactionsSupported ??= this.config.db\n .command({ hello: 1 })\n .then((h) => !!h.setName || h.msg === \"isdbgrid\")\n .catch(() => false);\n return this.transactionsSupported;\n }\n\n private async withTransaction(fn: (session?: ClientSession) => Promise<void>): Promise<void> {\n if (!(await this.supportsTransactions())) {\n // Standalone MongoDB: same writes, without atomicity.\n await fn(undefined);\n return;\n }\n // mongodb ≥5 exposes db.client; older drivers (e.g. mongoose 6 / driver 4.x) keep it on db.s.client.\n const db = this.config.db as Db & { client?: MongoClient; s?: { client?: MongoClient } };\n const client = this.config.client ?? db.client ?? db.s?.client;\n if (!client) throw new Error(\"express-auth-2fa: no MongoClient available for transactions; pass `client`.\");\n const session = client.startSession();\n try {\n await session.withTransaction(async () => fn(session));\n } finally {\n await session.endSession();\n }\n }\n}\n","import crypto from \"node:crypto\";\n\n// AES-256-GCM envelope encryption for secrets at rest (A.4 rules).\n// Format: `${keyId}.${iv}.${tag}.${ciphertext}` — all base64url.\n\nexport function encryptString(plain: string, key: Buffer, keyId: string): string {\n const iv = crypto.randomBytes(12);\n const cipher = crypto.createCipheriv(\"aes-256-gcm\", key, iv);\n const ct = Buffer.concat([cipher.update(plain, \"utf8\"), cipher.final()]);\n return [keyId, iv, cipher.getAuthTag(), ct].map((b) => (typeof b === \"string\" ? b : b.toString(\"base64url\"))).join(\".\");\n}\n\nexport function decryptString(payload: string, keys: Record<string, Buffer>): { plain: string; keyId: string } {\n const parts = payload.split(\".\");\n if (parts.length !== 4) throw new Error(\"invalid ciphertext format\");\n const [keyId, ivB64, tagB64, ctB64] = parts as [string, string, string, string];\n const key = keys[keyId];\n if (!key) throw new Error(`unknown keyId: ${keyId}`);\n const decipher = crypto.createDecipheriv(\"aes-256-gcm\", key, Buffer.from(ivB64, \"base64url\"));\n decipher.setAuthTag(Buffer.from(tagB64, \"base64url\"));\n const plain = Buffer.concat([decipher.update(Buffer.from(ctB64, \"base64url\")), decipher.final()]);\n return { plain: plain.toString(\"utf8\"), keyId };\n}\n","import { authenticator } from \"otplib\";\n\n// RFC 6238 TOTP: SHA-1, 6 digits, 30s step, ±1 step window (BR-7).\n// verifyWithStep also returns the matched time-step so callers can enforce\n// the lastUsedStep replay guard.\n\nfunction atEpoch(epochMs: number) {\n const a = authenticator.clone();\n a.options = { digits: 6, step: 30, window: 0, epoch: epochMs };\n return a;\n}\n\nexport function generateTotpSecret(): string {\n // 20-byte (160-bit) secret per BR-16.\n return authenticator.generateSecret(20);\n}\n\nexport function buildOtpauthUri(issuer: string, account: string, secret: string): string {\n return authenticator.keyuri(account, issuer, secret);\n}\n\nexport function totpStepAt(epochMs: number): number {\n return Math.floor(epochMs / 30_000);\n}\n\nexport interface TotpCheck {\n valid: boolean;\n /** Time-step the code matched (base-10 counter), or null. */\n step: number | null;\n}\n\nexport function checkTotp(code: string, secret: string, nowMs: number): TotpCheck {\n for (const offset of [-1, 0, 1]) {\n const epoch = nowMs + offset * 30_000;\n if (atEpoch(epoch).verify({ token: code, secret })) {\n return { valid: true, step: totpStepAt(epoch) };\n }\n }\n return { valid: false, step: null };\n}\n\nexport function generateTotpCode(secret: string, epochMs: number): string {\n return atEpoch(epochMs).generate(secret);\n}\n","import { decryptString } from \"../crypto/encrypt.js\";\nimport { apiError } from \"../middleware/errors.js\";\nimport { decodeEncryptionKey } from \"../config/defaults.js\";\nimport type { AuthConfig } from \"../config/schema.js\";\nimport type { AuthCollections } from \"../data/migrate.js\";\nimport type { AuditService } from \"./audit.service.js\";\n\n/**\n * Decrypts a method's stored phone / TOTP secret. A payload sealed with a key the\n * server no longer has (e.g. a rotated or regenerated encryption key) can never be\n * read again, so tell the user to re-add the method instead of failing with a 500.\n */\nexport function decryptMethodSecret(payload: string, keys: Record<string, Buffer>): string {\n try {\n return decryptString(payload, keys).plain;\n } catch {\n throw apiError(\n 409,\n \"VALIDATION_ERROR\",\n \"This verification method can no longer be used. Choose another method, or remove it and add it again.\",\n );\n }\n}\n\n/**\n * Deletes a user's phone / authenticator methods whose secret can no longer be decrypted\n * (sealed with a key the server lost), so sign-in never offers a method that can't work.\n * If the default was among them, the email method (else the most recently used) takes over.\n * Returns how many methods were removed.\n */\nexport async function pruneUnreadableMethods(\n config: AuthConfig,\n cols: AuthCollections,\n userId: string,\n audit?: AuditService,\n): Promise<number> {\n const keys = { [config.keyId]: decodeEncryptionKey(config.encryptionKey) };\n const methods = await cols.methods.find({ userId, type: { $in: [\"SMS\", \"TOTP\"] } }).toArray();\n const unreadable = methods.filter((m) => {\n const sealed = m.type === \"SMS\" ? m.phoneE164Enc : m.secretEnc;\n if (!sealed) return true;\n try {\n decryptString(sealed, keys);\n return false;\n } catch {\n return true;\n }\n });\n if (unreadable.length === 0) return 0;\n\n await cols.methods.deleteMany({ _id: { $in: unreadable.map((m) => m._id!) } });\n if (!(await cols.methods.findOne({ userId, status: \"active\", isDefault: true }))) {\n const next =\n (await cols.methods.findOne({ userId, status: \"active\", type: \"EMAIL\" })) ??\n (await cols.methods.findOne({ userId, status: \"active\" }, { sort: { lastUsedAt: -1, createdAt: -1 } }));\n if (next) await cols.methods.updateOne({ _id: next._id }, { $set: { isDefault: true } });\n }\n for (const m of unreadable) {\n await audit?.record(userId, \"method_removed\", { meta: { methodId: m._id!.toString(), type: m.type, reason: \"unreadable\" } });\n }\n return unreadable.length;\n}\n","import type { AuthConfig } from \"../config/schema.js\";\nimport type { AuthCollections } from \"../data/migrate.js\";\nimport type { MethodDoc } from \"../data/docs.js\";\nimport { apiError } from \"../middleware/errors.js\";\nimport { decryptMethodSecret } from \"./methodSecret.js\";\n\n/** \"subrat@example.com\" → \"s•••@example.com\". */\nexport function maskEmail(email: string): string {\n const at = email.lastIndexOf(\"@\");\n if (at <= 0) return \"•••\";\n return `${email[0]}•••${email.slice(at)}`;\n}\n\nfunction isDuplicateKey(e: unknown): boolean {\n return !!e && typeof e === \"object\" && (e as { code?: number }).code === 11000;\n}\n\n/**\n * Every user with an email address gets an active EMAIL method. It becomes the\n * default unless the user already has one. No-op when no emailProvider is set.\n */\nexport async function ensureEmailMethod(\n config: AuthConfig,\n cols: AuthCollections,\n userId: string,\n email?: string,\n): Promise<void> {\n if (!config.emailProvider) return;\n const address = email ?? (await config.userAdapter.findById(userId))?.email;\n if (!address || !address.includes(\"@\")) return;\n const label = maskEmail(address);\n\n const existing = await cols.methods.findOne({ userId, type: \"EMAIL\" });\n if (existing) {\n if (existing.label !== label) await cols.methods.updateOne({ _id: existing._id }, { $set: { label } });\n return;\n }\n\n const hasDefault = (await cols.methods.countDocuments({ userId, status: \"active\", isDefault: true })) > 0;\n const doc: MethodDoc = {\n userId,\n type: \"EMAIL\",\n label,\n isDefault: !hasDefault,\n status: \"active\",\n createdAt: config.clock(),\n lastUsedAt: null,\n };\n try {\n await cols.methods.insertOne(doc);\n } catch (e) {\n if (!isDuplicateKey(e)) throw e;\n // Lost a race: either the EMAIL method or another default was created concurrently.\n if (await cols.methods.findOne({ userId, type: \"EMAIL\" })) return;\n try {\n await cols.methods.insertOne({ ...doc, _id: undefined, isDefault: false });\n } catch (e2) {\n if (!isDuplicateKey(e2)) throw e2;\n }\n }\n}\n\n/** Send a one-time code over the method's channel; returns the masked destination. */\nexport async function deliverCode(\n config: AuthConfig,\n encKey: Buffer,\n method: MethodDoc,\n code: string,\n): Promise<string> {\n const message = `Your verification code is ${code}`;\n if (method.type === \"SMS\") {\n if (!method.phoneE164Enc) throw apiError(400, \"VALIDATION_ERROR\", \"Unknown SMS method.\");\n const phone = decryptMethodSecret(method.phoneE164Enc, { [config.keyId]: encKey });\n await config.smsProvider.send(phone, message);\n return `•••• ${method.phoneLast4 ?? \"\"}`.trim();\n }\n if (method.type === \"EMAIL\") {\n const email = (await config.userAdapter.findById(method.userId))?.email;\n if (!config.emailProvider || !email) throw apiError(400, \"VALIDATION_ERROR\", \"Email verification is not available.\");\n await config.emailProvider.send({ to: email, code, message });\n return maskEmail(email);\n }\n throw apiError(400, \"VALIDATION_ERROR\", \"This method does not send codes.\");\n}\n","import { ObjectId } from \"mongodb\";\nimport type { Request, Response } from \"express\";\nimport type { AuthConfig } from \"../config/schema.js\";\nimport type { AuthCollections } from \"../data/migrate.js\";\nimport type { ChallengeDoc, MethodDoc } from \"../data/docs.js\";\nimport { checkTotp } from \"../crypto/totp.js\";\nimport { decodeEncryptionKey } from \"../config/defaults.js\";\nimport {\n randomSmsCode,\n sha256Hex,\n timingSafeEqualStr,\n randomId,\n} from \"../crypto/hash.js\";\nimport { apiError } from \"../middleware/errors.js\";\nimport { consumeUserQuota } from \"../middleware/rateLimit.js\";\nimport { TokenService } from \"./token.service.js\";\nimport { AuditService } from \"./audit.service.js\";\nimport { AuthService, ctxOf } from \"./auth.service.js\";\nimport { deliverCode } from \"./delivery.js\";\nimport { decryptMethodSecret } from \"./methodSecret.js\";\n\nexport class MfaService {\n private encKey: Buffer;\n\n constructor(\n private readonly config: AuthConfig,\n private readonly cols: AuthCollections,\n private readonly tokens: TokenService,\n private readonly audit: AuditService,\n private readonly auth: AuthService,\n ) {\n this.encKey = decodeEncryptionKey(config.encryptionKey);\n }\n\n private async getChallenge(challengeId: string, purpose: ChallengeDoc[\"purpose\"]): Promise<ChallengeDoc> {\n const ch = await this.cols.challenges.findOne({ _id: challengeId, purpose });\n if (!ch || ch.expiresAt.getTime() <= this.config.clock().getTime()) {\n throw apiError(410, \"CHALLENGE_EXPIRED\", \"This verification challenge has expired. Sign in again.\");\n }\n return ch;\n }\n\n /** BR-5/BR-6 — send (or resend) an SMS or email code for a challenge. */\n async sendCode(challengeId: string, methodId: string): Promise<{ sentTo: string; resendAfterSec: number }> {\n const ch = await this.getChallenge(challengeId, \"login\");\n const now = this.config.clock();\n const limits = this.config.limits;\n\n const method = await this.cols.methods.findOne({\n _id: this.objectId(methodId),\n userId: ch.userId,\n type: { $in: [\"SMS\", \"EMAIL\"] },\n status: \"active\",\n });\n if (!method) throw apiError(400, \"VALIDATION_ERROR\", \"Unknown method.\");\n\n // BR-6: resend cooldown + caps (unless the app turned rate limits off).\n const limited = this.config.rateLimits;\n if (limited && ch.smsSentAt && now.getTime() - ch.smsSentAt.getTime() < limits.smsResendSec * 1000) {\n throw apiError(429, \"RATE_LIMITED\", \"Please wait before requesting another code.\", {\n retryAt: new Date(ch.smsSentAt.getTime() + limits.smsResendSec * 1000),\n });\n }\n if (limited && ch.smsSendCount >= limits.smsMaxPerChallenge) {\n throw apiError(429, \"RATE_LIMITED\", \"Too many codes sent for this challenge.\", { retryAt: ch.expiresAt });\n }\n const quota = limited\n ? await consumeUserQuota(this.cols, this.config, `${method.type.toLowerCase()}_send`, ch.userId, limits.smsMaxPerUserHour, 3_600_000)\n : { allowed: true as const, retryAt: undefined };\n if (!quota.allowed) {\n throw apiError(429, \"RATE_LIMITED\", \"Too many codes sent. Try again later.\", { retryAt: quota.retryAt });\n }\n\n const code = randomSmsCode();\n await this.cols.challenges.updateOne(\n { _id: challengeId },\n { $set: { methodId, smsCodeHash: sha256Hex(code), smsSentAt: now }, $inc: { smsSendCount: 1 } },\n );\n\n const sentTo = await deliverCode(this.config, this.encKey, method, code);\n return { sentTo, resendAfterSec: limited ? limits.smsResendSec : 0 };\n }\n\n /** BR-7..BR-10 — verify a 6-digit code against a login challenge. */\n async verify(\n req: Request,\n res: Response,\n input: { challengeId: string; methodId: string; code: string; rememberDevice: boolean },\n ): Promise<void> {\n const ch = await this.getChallenge(input.challengeId, \"login\");\n const now = this.config.clock();\n const method = await this.cols.methods.findOne({ _id: this.objectId(input.methodId), userId: ch.userId, status: \"active\" });\n if (!method) throw apiError(400, \"VALIDATION_ERROR\", \"Unknown method.\");\n if (method.type === \"PASSKEY\") throw apiError(400, \"VALIDATION_ERROR\", \"Use your passkey to verify.\");\n\n const ok = await this.checkCode(ch, method, input.code, now);\n if (!ok) {\n const attempts = ch.attempts + 1;\n const attemptsLeft = this.config.limits.mfaMaxAttempts - attempts;\n if (attemptsLeft <= 0) {\n await this.cols.challenges.deleteOne({ _id: ch._id });\n const lockedUntil = new Date(now.getTime() + this.config.limits.lockMinutes * 60_000);\n await this.cols.credentials.updateOne({ userId: ch.userId }, { $set: { lockedUntil, updatedAt: now } });\n await this.audit.record(ch.userId, \"mfa_locked\", ctxOf(req));\n throw apiError(423, \"LOCKED\", \"Too many attempts. Try again later.\", { retryAt: lockedUntil });\n }\n await this.cols.challenges.updateOne({ _id: ch._id }, { $set: { attempts } });\n await this.audit.record(ch.userId, \"mfa_failed\", ctxOf(req));\n throw apiError(401, \"INVALID_CODE\", \"That code didn't work. Try again.\", { attemptsLeft });\n }\n\n await this.cols.challenges.deleteOne({ _id: ch._id });\n await this.cols.methods.updateOne({ _id: method._id }, { $set: { lastUsedAt: now } });\n await this.audit.record(ch.userId, \"login_success\", ctxOf(req));\n const user = await this.config.userAdapter.findById(ch.userId);\n const ua = req.headers[\"user-agent\"];\n const result = await this.auth.authenticate(ch.userId, req, res, ch.passwordless ? [\"otp\"] : [\"pwd\", \"otp\"], {\n rememberDevice: input.rememberDevice,\n deviceLabel: typeof ua === \"string\" ? ua.slice(0, 80) : undefined,\n });\n res.json({ ...result, user: result.user ?? { id: ch.userId, username: user?.username, email: user?.email } });\n }\n\n private async checkCode(ch: ChallengeDoc, method: MethodDoc, code: string, now: Date): Promise<boolean> {\n if (method.type === \"TOTP\") {\n if (!method.secretEnc) return false;\n const secret = decryptMethodSecret(method.secretEnc, { [this.config.keyId]: this.encKey });\n const check = checkTotp(code, secret, now.getTime());\n // BR-7 replay guard: reject a step already consumed.\n if (check.valid && check.step !== null && check.step > (method.lastUsedStep ?? -1)) {\n await this.cols.methods.updateOne({ _id: method._id }, { $set: { lastUsedStep: check.step } });\n return true;\n }\n return false;\n }\n // SMS / EMAIL — compare hashes in constant time; a used code is deleted (BR-8).\n const valid = !!ch.smsCodeHash && timingSafeEqualStr(ch.smsCodeHash, sha256Hex(code));\n if (valid) await this.cols.challenges.updateOne({ _id: ch._id }, { $set: { smsCodeHash: null } });\n return valid;\n }\n\n /** BR-13 — step-up: verify a fresh TOTP/SMS/email code, return a 5-min stepUpToken. */\n async stepUp(\n req: Request,\n input: { methodId: string; code?: string },\n auth: { sub: string; sid: string },\n ): Promise<{ stepUpToken: string; expiresInSec: number } | { challengeId: string; sentTo: string; resendAfterSec: number }> {\n const method = await this.cols.methods.findOne({ _id: this.objectId(input.methodId), userId: auth.sub, status: \"active\" });\n if (!method) throw apiError(400, \"VALIDATION_ERROR\", \"Unknown method.\");\n if (method.type === \"PASSKEY\") throw apiError(400, \"VALIDATION_ERROR\", \"Choose a method that sends or shows a code.\");\n const now = this.config.clock();\n\n if (method.type !== \"TOTP\" && !input.code) {\n // First pass: create a step_up challenge and send a code.\n const challengeId = randomId(\"chg\");\n const code = randomSmsCode();\n await this.cols.challenges.insertOne({\n _id: challengeId,\n userId: auth.sub,\n purpose: \"step_up\",\n methodId: input.methodId,\n smsCodeHash: sha256Hex(code),\n smsSentAt: now,\n smsSendCount: 1,\n attempts: 0,\n expiresAt: new Date(now.getTime() + this.config.ttl.challengeSec * 1000),\n createdAt: now,\n });\n const sentTo = await deliverCode(this.config, this.encKey, method, code);\n return { challengeId, sentTo, resendAfterSec: this.config.rateLimits ? this.config.limits.smsResendSec : 0 };\n }\n if (!input.code) throw apiError(400, \"VALIDATION_ERROR\", \"code is required.\");\n\n let ok = false;\n if (method.type === \"TOTP\") {\n const secret = decryptMethodSecret(method.secretEnc!, { [this.config.keyId]: this.encKey });\n const check = checkTotp(input.code, secret, now.getTime());\n if (check.valid && check.step !== null && check.step > (method.lastUsedStep ?? -1)) {\n ok = true;\n await this.cols.methods.updateOne({ _id: method._id }, { $set: { lastUsedStep: check.step } });\n }\n } else {\n const ch = await this.cols.challenges.findOne(\n { userId: auth.sub, purpose: \"step_up\", methodId: input.methodId },\n { sort: { createdAt: -1 } },\n );\n if (ch && ch.expiresAt.getTime() > now.getTime() && ch.smsCodeHash && timingSafeEqualStr(ch.smsCodeHash, sha256Hex(input.code))) {\n ok = true;\n await this.cols.challenges.deleteOne({ _id: ch._id });\n }\n }\n if (!ok) {\n await this.audit.record(auth.sub, \"step_up_failed\", ctxOf(req));\n throw apiError(401, \"INVALID_CODE\", \"That code didn't work. Try again.\");\n }\n\n await this.cols.methods.updateOne({ _id: method._id }, { $set: { lastUsedAt: now } });\n await this.audit.record(auth.sub, \"step_up_success\", ctxOf(req));\n const { token, expiresInSec } = await this.tokens.signStepUpToken(auth.sub, auth.sid);\n return { stepUpToken: token, expiresInSec };\n }\n\n private objectId(id: string): ObjectId {\n try {\n return new ObjectId(id);\n } catch {\n throw apiError(400, \"VALIDATION_ERROR\", \"Invalid id.\");\n }\n }\n}\n","import type { NextFunction, Request, Response } from \"express\";\nimport type { AuthCollections } from \"../data/migrate.js\";\nimport type { AuthConfig } from \"../config/schema.js\";\nimport { apiError } from \"./errors.js\";\n\n// MongoDB-backed fixed-window rate limiting (NFR-B4, NFR-B10 — stateless).\n\nexport function rateLimit(\n cols: AuthCollections,\n config: AuthConfig,\n bucket: string,\n max: number,\n windowMs: number,\n) {\n return async (req: Request, _res: Response, next: NextFunction): Promise<void> => {\n if (!config.rateLimits) {\n next();\n return;\n }\n const now = config.clock().getTime();\n const slot = Math.floor(now / windowMs);\n const key = `${bucket}:${req.ip ?? \"unknown\"}:${slot}`;\n const expiresAt = new Date((slot + 1) * windowMs + 60_000);\n\n try {\n const doc = await cols.rateLimits.findOneAndUpdate(\n { _id: key },\n { $inc: { count: 1 }, $setOnInsert: { expiresAt } },\n { upsert: true, returnDocument: \"after\" },\n );\n const count = doc?.count ?? 1;\n if (count > max) {\n next(\n apiError(429, \"RATE_LIMITED\", \"Too many requests. Try again later.\", {\n retryAt: new Date((slot + 1) * windowMs),\n }),\n );\n return;\n }\n next();\n } catch {\n next(); // fail-open: rate limiting must not take the app down\n }\n };\n}\n\n/** Per-user counter — used for the per-hour SMS cap (BR-6). */\nexport async function consumeUserQuota(\n cols: AuthCollections,\n config: AuthConfig,\n bucket: string,\n userId: string,\n max: number,\n windowMs: number,\n): Promise<{ allowed: boolean; retryAt: Date }> {\n const now = config.clock().getTime();\n const slot = Math.floor(now / windowMs);\n const key = `${bucket}:u:${userId}:${slot}`;\n const retryAt = new Date((slot + 1) * windowMs);\n const doc = await cols.rateLimits.findOneAndUpdate(\n { _id: key },\n { $inc: { count: 1 }, $setOnInsert: { expiresAt: new Date((slot + 1) * windowMs + 60_000) } },\n { upsert: true, returnDocument: \"after\" },\n );\n return { allowed: (doc?.count ?? 1) <= max, retryAt };\n}\n","import type { Request, Response } from \"express\";\nimport {\n generateAuthenticationOptions,\n generateRegistrationOptions,\n verifyAuthenticationResponse,\n verifyRegistrationResponse,\n type AuthenticationResponseJSON,\n type AuthenticatorTransport,\n type RegistrationResponseJSON,\n} from \"@simplewebauthn/server\";\nimport type { Method } from \"@cairs-npm/auth-2fa-contract\";\nimport type { AuthConfig } from \"../config/schema.js\";\nimport type { AuthCollections } from \"../data/migrate.js\";\nimport type { ChallengeDoc, MethodDoc } from \"../data/docs.js\";\nimport { randomId } from \"../crypto/hash.js\";\nimport { apiError } from \"../middleware/errors.js\";\nimport { AuditService } from \"./audit.service.js\";\nimport { AuthService, ctxOf } from \"./auth.service.js\";\nimport { toApiMethod } from \"./method.service.js\";\n\n// Passkeys (WebAuthn) as a verification method: registered from Security while signed in,\n// then used on the code step of sign-in instead of an emailed / texted / app code.\nexport class PasskeyService {\n constructor(\n private readonly config: AuthConfig,\n private readonly cols: AuthCollections,\n private readonly audit: AuditService,\n private readonly auth: AuthService,\n ) {}\n\n private rp(): NonNullable<AuthConfig[\"passkeys\"]> {\n if (!this.config.passkeys) throw apiError(400, \"VALIDATION_ERROR\", \"Passkeys are not enabled.\");\n return this.config.passkeys;\n }\n\n private activePasskeys(userId: string): Promise<MethodDoc[]> {\n return this.cols.methods.find({ userId, type: \"PASSKEY\", status: \"active\" }).toArray();\n }\n\n private descriptor(m: MethodDoc): { id: string; transports?: AuthenticatorTransport[] } {\n return { id: m.credentialId!, transports: m.transports as AuthenticatorTransport[] | undefined };\n }\n\n private async liveChallenge(filter: Partial<ChallengeDoc>): Promise<ChallengeDoc> {\n const ch = await this.cols.challenges.findOne(filter);\n if (!ch || ch.expiresAt.getTime() <= this.config.clock().getTime()) {\n throw apiError(410, \"CHALLENGE_EXPIRED\", \"This verification challenge has expired. Sign in again.\");\n }\n return ch;\n }\n\n /** Step 1 of adding a passkey: WebAuthn creation options for the signed-in user. */\n async registrationOptions(userId: string): Promise<Record<string, unknown>> {\n const rp = this.rp();\n const now = this.config.clock();\n if ((await this.cols.methods.countDocuments({ userId })) >= this.config.limits.maxMethods) {\n throw apiError(409, \"VALIDATION_ERROR\", `You can add at most ${this.config.limits.maxMethods} verification methods.`);\n }\n const user = await this.config.userAdapter.findById(userId);\n const name = user?.email ?? user?.username ?? userId;\n const options = await generateRegistrationOptions({\n rpName: rp.rpName,\n rpID: rp.rpID,\n userName: name,\n userDisplayName: name,\n userID: new TextEncoder().encode(userId),\n attestationType: \"none\",\n excludeCredentials: (await this.activePasskeys(userId)).map((m) => this.descriptor(m)),\n authenticatorSelection: { residentKey: \"preferred\", userVerification: \"preferred\" },\n });\n // One enrollment in flight per user; a restart replaces it.\n await this.cols.challenges.deleteMany({ userId, purpose: \"enroll\" });\n await this.cols.challenges.insertOne({\n _id: randomId(\"chg\"),\n userId,\n purpose: \"enroll\",\n methodId: null,\n smsCodeHash: null,\n smsSentAt: null,\n smsSendCount: 0,\n attempts: 0,\n webauthnChallenge: options.challenge,\n expiresAt: new Date(now.getTime() + this.config.ttl.challengeSec * 1000),\n createdAt: now,\n });\n return options as unknown as Record<string, unknown>;\n }\n\n /** Step 2 of adding a passkey: verify the browser's attestation and store the credential. */\n async register(userId: string, input: { label?: string; response: Record<string, unknown> }): Promise<Method> {\n const rp = this.rp();\n const ch = await this.liveChallenge({ userId, purpose: \"enroll\" });\n let verification;\n try {\n verification = await verifyRegistrationResponse({\n response: input.response as unknown as RegistrationResponseJSON,\n expectedChallenge: ch.webauthnChallenge ?? \"\",\n expectedOrigin: rp.origins,\n expectedRPID: rp.rpID,\n requireUserVerification: false,\n });\n } catch {\n verification = null;\n }\n if (!verification?.verified || !verification.registrationInfo) {\n throw apiError(400, \"VALIDATION_ERROR\", \"We couldn't add that passkey. Try again.\");\n }\n await this.cols.challenges.deleteOne({ _id: ch._id });\n\n const { credential } = verification.registrationInfo;\n if (await this.cols.methods.findOne({ type: \"PASSKEY\", credentialId: credential.id })) {\n throw apiError(409, \"VALIDATION_ERROR\", \"That passkey is already added.\");\n }\n const now = this.config.clock();\n const hasDefault = !!(await this.cols.methods.findOne({ userId, status: \"active\", isDefault: true }));\n const doc: MethodDoc = {\n userId,\n type: \"PASSKEY\",\n label: input.label?.trim() || \"Passkey\",\n isDefault: !hasDefault,\n status: \"active\",\n credentialId: credential.id,\n publicKey: Buffer.from(credential.publicKey).toString(\"base64url\"),\n counter: credential.counter,\n transports: credential.transports,\n createdAt: now,\n lastUsedAt: null,\n };\n const { insertedId } = await this.cols.methods.insertOne(doc);\n await this.audit.record(userId, \"method_added\", { meta: { type: \"PASSKEY\" } });\n return toApiMethod({ ...doc, _id: insertedId });\n }\n\n /** Sign-in code step: WebAuthn request options for the user behind a login challenge. */\n async loginOptions(challengeId: string): Promise<Record<string, unknown>> {\n const rp = this.rp();\n const ch = await this.liveChallenge({ _id: challengeId, purpose: \"login\" });\n const passkeys = await this.activePasskeys(ch.userId);\n if (passkeys.length === 0) throw apiError(400, \"VALIDATION_ERROR\", \"There's no passkey on this account.\");\n const options = await generateAuthenticationOptions({\n rpID: rp.rpID,\n allowCredentials: passkeys.map((m) => this.descriptor(m)),\n userVerification: \"preferred\",\n });\n await this.cols.challenges.updateOne({ _id: ch._id }, { $set: { webauthnChallenge: options.challenge } });\n return options as unknown as Record<string, unknown>;\n }\n\n /** Sign-in code step: verify the passkey assertion and finish signing in. */\n async loginVerify(\n req: Request,\n res: Response,\n input: { challengeId: string; response: Record<string, unknown>; rememberDevice: boolean },\n ): Promise<void> {\n const rp = this.rp();\n const ch = await this.liveChallenge({ _id: input.challengeId, purpose: \"login\" });\n if (!ch.webauthnChallenge) throw apiError(400, \"VALIDATION_ERROR\", \"Start the passkey check again.\");\n const response = input.response as unknown as AuthenticationResponseJSON;\n const method = await this.cols.methods.findOne({\n userId: ch.userId,\n type: \"PASSKEY\",\n status: \"active\",\n credentialId: typeof response.id === \"string\" ? response.id : \"\",\n });\n if (!method) throw apiError(401, \"INVALID_CODE\", \"That passkey isn't on this account.\");\n\n let verification;\n try {\n verification = await verifyAuthenticationResponse({\n response,\n expectedChallenge: ch.webauthnChallenge,\n expectedOrigin: rp.origins,\n expectedRPID: rp.rpID,\n credential: {\n id: method.credentialId!,\n publicKey: Buffer.from(method.publicKey!, \"base64url\"),\n counter: method.counter ?? 0,\n transports: method.transports as AuthenticatorTransport[] | undefined,\n },\n requireUserVerification: false,\n });\n } catch {\n verification = null;\n }\n if (!verification?.verified) {\n await this.audit.record(ch.userId, \"mfa_failed\", ctxOf(req));\n throw apiError(401, \"INVALID_CODE\", \"We couldn't verify your passkey. Try again.\");\n }\n\n const now = this.config.clock();\n await this.cols.challenges.deleteOne({ _id: ch._id });\n await this.cols.methods.updateOne(\n { _id: method._id },\n { $set: { counter: verification.authenticationInfo.newCounter, lastUsedAt: now } },\n );\n await this.audit.record(ch.userId, \"login_success\", ctxOf(req));\n const ua = req.headers[\"user-agent\"];\n const result = await this.auth.authenticate(ch.userId, req, res, ch.passwordless ? [\"hwk\"] : [\"pwd\", \"hwk\"], {\n rememberDevice: input.rememberDevice,\n deviceLabel: typeof ua === \"string\" ? ua.slice(0, 80) : undefined,\n });\n res.json(result);\n }\n}\n","import crypto from \"node:crypto\";\nimport { SignJWT, jwtVerify, importPKCS8, importSPKI } from \"jose\";\nimport type { AuthConfig } from \"../config/schema.js\";\nimport type { AuthCollections } from \"../data/migrate.js\";\nimport type { SessionDoc } from \"../data/docs.js\";\nimport { randomId, randomToken, sha256Hex } from \"../crypto/hash.js\";\nimport { apiError } from \"../middleware/errors.js\";\n\nexport interface TokenBundle {\n accessToken: string;\n expiresInSec: number;\n refreshToken: string; // raw, goes in the httpOnly cookie\n sessionId: string;\n}\n\nexport class TokenService {\n private alg: \"EdDSA\" | \"RS256\";\n private privateKeyPromise: Promise<crypto.KeyObject | import(\"jose\").KeyLike>;\n private publicKeyPromise: Promise<crypto.KeyObject | import(\"jose\").KeyLike>;\n\n constructor(\n private readonly config: AuthConfig,\n private readonly cols: AuthCollections,\n ) {\n const keyObj = crypto.createPrivateKey(config.jwtPrivateKey);\n this.alg = keyObj.asymmetricKeyType === \"ed25519\" ? \"EdDSA\" : \"RS256\";\n this.privateKeyPromise = importPKCS8(config.jwtPrivateKey, this.alg);\n this.publicKeyPromise = importSPKI(\n crypto.createPublicKey(keyObj).export({ type: \"spki\", format: \"pem\" }).toString(),\n this.alg,\n );\n }\n\n async signAccessToken(userId: string, sid: string, amr: string[]): Promise<string> {\n const key = await this.privateKeyPromise;\n return new SignJWT({ sid, amr })\n .setProtectedHeader({ alg: this.alg })\n .setIssuer(this.config.issuer)\n .setSubject(userId)\n .setIssuedAt()\n .setExpirationTime(`${this.config.ttl.accessTokenSec}s`)\n .sign(key);\n }\n\n async verifyAccessToken(token: string): Promise<{ sub: string; sid: string; amr: string[] }> {\n try {\n const { payload } = await jwtVerify(token, await this.publicKeyPromise, {\n issuer: this.config.issuer,\n });\n return { sub: payload.sub as string, sid: payload.sid as string, amr: (payload.amr as string[]) ?? [] };\n } catch {\n throw apiError(401, \"INVALID_CREDENTIALS\", \"Invalid or expired access token.\");\n }\n }\n\n /** Create a new session + refresh token; returns tokens for the client. */\n async issueTokens(\n userId: string,\n amr: string[],\n meta: { userAgent?: string; ip?: string },\n familyId?: string,\n ): Promise<TokenBundle> {\n const now = this.config.clock();\n const sid = randomId(\"ses\");\n const refreshToken = randomToken();\n const expiresAt = new Date(now.getTime() + this.config.ttl.refreshTokenDays * 86_400_000);\n\n const doc: SessionDoc = {\n _id: sid,\n userId,\n refreshTokenHash: sha256Hex(refreshToken),\n familyId: familyId ?? randomId(\"fam\"),\n rotatedFrom: null,\n userAgent: meta.userAgent,\n ip: meta.ip,\n createdAt: now,\n expiresAt,\n revokedAt: null,\n };\n await this.cols.sessions.insertOne(doc);\n\n return {\n accessToken: await this.signAccessToken(userId, sid, amr),\n expiresInSec: this.config.ttl.accessTokenSec,\n refreshToken,\n sessionId: sid,\n };\n }\n\n /**\n * Rotate a refresh token (BR-11). Reuse of a rotated token revokes the whole\n * family. Old hashes are retained in `usedHashes` so reuse is detectable.\n */\n async rotateRefreshToken(\n rawToken: string,\n meta: { userAgent?: string; ip?: string },\n ): Promise<TokenBundle> {\n const hash = sha256Hex(rawToken);\n const now = this.config.clock();\n const session = await this.cols.sessions.findOne({\n $or: [{ refreshTokenHash: hash }, { usedHashes: hash } as never],\n });\n\n if (!session) {\n throw apiError(401, \"INVALID_CREDENTIALS\", \"Invalid refresh token.\");\n }\n if (session.refreshTokenHash !== hash) {\n // Rotated-token reuse → revoke the whole family.\n await this.cols.sessions.updateMany(\n { familyId: session.familyId, revokedAt: null },\n { $set: { revokedAt: now } },\n );\n throw apiError(401, \"INVALID_CREDENTIALS\", \"Refresh token reuse detected; session revoked.\");\n }\n if (session.revokedAt || session.expiresAt.getTime() <= now.getTime()) {\n throw apiError(401, \"INVALID_CREDENTIALS\", \"Session expired.\");\n }\n\n const newToken = randomToken();\n await this.cols.sessions.updateOne(\n { _id: session._id, refreshTokenHash: hash },\n {\n $set: {\n refreshTokenHash: sha256Hex(newToken),\n userAgent: meta.userAgent,\n ip: meta.ip,\n },\n $push: { usedHashes: { $each: [hash], $slice: -200 } } as never,\n },\n );\n\n return {\n accessToken: await this.signAccessToken(session.userId, session._id, [\"pwd\", \"otp\"]),\n expiresInSec: this.config.ttl.accessTokenSec,\n refreshToken: newToken,\n sessionId: session._id,\n };\n }\n\n async revokeSession(sid: string): Promise<void> {\n await this.cols.sessions.updateOne({ _id: sid }, { $set: { revokedAt: this.config.clock() } });\n }\n\n /** 5-minute step-up JWT bound to sub+sid (BR-13). */\n async signStepUpToken(userId: string, sid: string): Promise<{ token: string; expiresInSec: number }> {\n const key = await this.privateKeyPromise;\n const token = await new SignJWT({ sid, purpose: \"step_up\" })\n .setProtectedHeader({ alg: this.alg })\n .setIssuer(this.config.issuer)\n .setSubject(userId)\n .setIssuedAt()\n .setExpirationTime(`${this.config.ttl.stepUpSec}s`)\n .sign(key);\n return { token, expiresInSec: this.config.ttl.stepUpSec };\n }\n\n async verifyStepUpToken(token: string, userId: string, sid: string): Promise<void> {\n try {\n const { payload } = await jwtVerify(token, await this.publicKeyPromise, {\n issuer: this.config.issuer,\n });\n if (payload.purpose !== \"step_up\" || payload.sub !== userId || payload.sid !== sid) {\n throw new Error(\"mismatch\");\n }\n } catch {\n throw apiError(403, \"STEP_UP_REQUIRED\", \"This action requires recent verification.\");\n }\n }\n}\n","import type { AuthCollections } from \"../data/migrate.js\";\nimport type { AuthConfig, SecurityEvent } from \"../config/schema.js\";\n\n// BR-20: every security change writes an auth_events entry and calls\n// hooks.onSecurityEvent(event).\n\nexport class AuditService {\n constructor(\n private readonly config: AuthConfig,\n private readonly cols: AuthCollections,\n ) {}\n\n async record(\n userId: string,\n type: string,\n meta: { ip?: string; userAgent?: string; meta?: Record<string, unknown> } = {},\n ): Promise<void> {\n const event: SecurityEvent = {\n userId,\n type,\n ip: meta.ip,\n userAgent: meta.userAgent,\n at: this.config.clock(),\n meta: meta.meta,\n };\n await this.cols.events.insertOne(event as never);\n try {\n await this.config.hooks?.onSecurityEvent?.(event);\n } catch {\n // Hooks must never break a security flow.\n }\n }\n}\n","import { Router } from \"express\";\nimport {\n loginRequestSchema,\n loginStartRequestSchema,\n mfaSendRequestSchema,\n mfaVerifyRequestSchema,\n passkeyLoginOptionsRequestSchema,\n passkeyLoginVerifyRequestSchema,\n stepUpRequestSchema,\n} from \"@cairs-npm/auth-2fa-contract\";\nimport type { AuthConfig } from \"../config/schema.js\";\nimport type { AuthCollections } from \"../data/migrate.js\";\nimport { AuthService } from \"../services/auth.service.js\";\nimport { MfaService } from \"../services/mfa.service.js\";\nimport { PasskeyService } from \"../services/passkey.service.js\";\nimport { TokenService } from \"../services/token.service.js\";\nimport { rateLimit } from \"../middleware/rateLimit.js\";\nimport { requireAuth } from \"../middleware/requireAuth.js\";\nimport { parseBody } from \"../middleware/errors.js\";\n\nconst wrap =\n (fn: (req: import(\"express\").Request, res: import(\"express\").Response) => Promise<void | unknown>) =>\n (req: import(\"express\").Request, res: import(\"express\").Response, next: import(\"express\").NextFunction) =>\n fn(req, res).then((r) => (r === undefined ? undefined : res.json(r))).catch(next);\n\nexport interface Services {\n auth: AuthService;\n mfa: MfaService;\n passkeys: PasskeyService;\n tokens: TokenService;\n}\n\nexport function buildAuthRouter(config: AuthConfig, cols: AuthCollections, services: Services): Router {\n const router = Router();\n const { loginIpPerMin, verifyIpPerMin, stepUpIpPerMin } = config.limits;\n\n router.post(\"/auth/login\", rateLimit(cols, config, \"login_ip\", loginIpPerMin, 60_000), wrap(async (req, res) => {\n const input = parseBody(loginRequestSchema, req.body);\n return services.auth.login(req, res, input);\n }));\n\n // Passwordless sign-in (opt-in): 404 unless config.passwordlessLogin.\n router.post(\"/auth/login/start\", rateLimit(cols, config, \"login_ip\", loginIpPerMin, 60_000), wrap(async (req) => {\n const input = parseBody(loginStartRequestSchema, req.body);\n return services.auth.loginStart(req, input);\n }));\n\n router.post(\"/auth/mfa/send\", rateLimit(cols, config, \"mfa_send_ip\", verifyIpPerMin, 60_000), wrap(async (req) => {\n const input = parseBody(mfaSendRequestSchema, req.body);\n return services.mfa.sendCode(input.challengeId, input.methodId);\n }));\n\n router.post(\"/auth/mfa/verify\", rateLimit(cols, config, \"mfa_verify_ip\", verifyIpPerMin, 60_000), async (req, res, next) => {\n try {\n const input = parseBody(mfaVerifyRequestSchema, req.body);\n await services.mfa.verify(req, res, input);\n } catch (err) {\n next(err);\n }\n });\n\n // Passkey as the code step: options for the challenge, then the browser's assertion.\n router.post(\"/auth/passkey/options\", rateLimit(cols, config, \"mfa_verify_ip\", verifyIpPerMin, 60_000), wrap(async (req) => {\n const input = parseBody(passkeyLoginOptionsRequestSchema, req.body);\n return services.passkeys.loginOptions(input.challengeId);\n }));\n\n router.post(\"/auth/passkey/verify\", rateLimit(cols, config, \"mfa_verify_ip\", verifyIpPerMin, 60_000), async (req, res, next) => {\n try {\n const input = parseBody(passkeyLoginVerifyRequestSchema, req.body);\n await services.passkeys.loginVerify(req, res, input);\n } catch (err) {\n next(err);\n }\n });\n\n router.post(\"/auth/refresh\", wrap(async (req, res) => services.auth.refresh(req, res)));\n\n router.post(\"/auth/logout\", requireAuth(services.tokens), wrap(async (req, res) => {\n await services.auth.logout(req, res, req.auth!.sid);\n res.status(204).end();\n return undefined;\n }));\n\n router.post(\"/auth/step-up\", requireAuth(services.tokens), rateLimit(cols, config, \"step_up_ip\", stepUpIpPerMin, 60_000), wrap(async (req) => {\n const input = parseBody(stepUpRequestSchema.extend({ code: stepUpRequestSchema.shape.code.optional() }), req.body);\n return services.mfa.stepUp(req, input, req.auth!);\n }));\n\n if (config.exposeDocs) {\n router.get(\"/auth/openapi.json\", (_req, res) => {\n res.json(buildOpenApiDoc());\n });\n }\n\n return router;\n}\n\nfunction buildOpenApiDoc(): object {\n return {\n openapi: \"3.1.0\",\n info: { title: \"express-auth-2fa\", version: \"0.1.0\" },\n paths: {\n \"/auth/login\": { post: { summary: \"Password sign-in; returns AUTHENTICATED or MFA_REQUIRED\" } },\n \"/auth/mfa/send\": { post: { summary: \"Send/resend an SMS code for a challenge\" } },\n \"/auth/mfa/verify\": { post: { summary: \"Verify a 6-digit TOTP or SMS code\" } },\n \"/auth/refresh\": { post: { summary: \"Rotate refresh cookie; returns a new access token\" } },\n \"/auth/logout\": { post: { summary: \"Revoke the session and clear cookies\" } },\n \"/auth/step-up\": { post: { summary: \"Verify a fresh code; returns a 5-min stepUpToken\" } },\n \"/account/security\": { get: { summary: \"2FA status + active methods\" } },\n \"/account/security/two-step\": { put: { summary: \"Enable/disable 2-Step Verification\" } },\n \"/account/security/methods\": { post: { summary: \"Add a pending TOTP or SMS method\" } },\n \"/account/security/methods/{id}/confirm\": { post: { summary: \"Confirm a pending method with a code\" } },\n \"/account/security/methods/{id}\": {\n patch: { summary: \"Rename or set-default a method\" },\n delete: { summary: \"Remove a method\" },\n },\n },\n };\n}\n","import type { NextFunction, Request, Response } from \"express\";\nimport type { TokenService } from \"../services/token.service.js\";\nimport { apiError } from \"./errors.js\";\n\ndeclare module \"express-serve-static-core\" {\n interface Request {\n auth?: { sub: string; sid: string; amr: string[] };\n }\n}\n\n/** Bearer-token auth for account routes. */\nexport function requireAuth(tokens: TokenService) {\n return async (req: Request, _res: Response, next: NextFunction): Promise<void> => {\n const header = req.headers.authorization ?? \"\";\n const token = header.startsWith(\"Bearer \") ? header.slice(7) : null;\n if (!token) {\n next(apiError(401, \"INVALID_CREDENTIALS\", \"Missing access token.\"));\n return;\n }\n try {\n req.auth = await tokens.verifyAccessToken(token);\n next();\n } catch (err) {\n next(err);\n }\n };\n}\n","import { Router } from \"express\";\nimport {\n addMethodRequestSchema,\n confirmMethodRequestSchema,\n patchMethodRequestSchema,\n deleteMethodRequestSchema,\n twoStepRequestSchema,\n passkeyRegisterRequestSchema,\n} from \"@cairs-npm/auth-2fa-contract\";\nimport type { AuthConfig } from \"../config/schema.js\";\nimport type { AuthCollections } from \"../data/migrate.js\";\nimport { MethodService } from \"../services/method.service.js\";\nimport { PasskeyService } from \"../services/passkey.service.js\";\nimport { TokenService } from \"../services/token.service.js\";\nimport { requireAuth } from \"../middleware/requireAuth.js\";\nimport { parseBody } from \"../middleware/errors.js\";\n\nconst wrap =\n (fn: (req: import(\"express\").Request, res: import(\"express\").Response) => Promise<void | unknown>) =>\n (req: import(\"express\").Request, res: import(\"express\").Response, next: import(\"express\").NextFunction) =>\n fn(req, res).then((r) => (r === undefined ? undefined : res.json(r))).catch(next);\n\nexport function buildAccountRouter(\n _config: AuthConfig,\n _cols: AuthCollections,\n services: { methods: MethodService; passkeys: PasskeyService; tokens: TokenService },\n): Router {\n const router = Router();\n const auth = requireAuth(services.tokens);\n\n router.get(\"/account/security\", auth, wrap(async (req) => {\n return services.methods.securitySummary(req.auth!.sub);\n }));\n\n router.put(\"/account/security/two-step\", auth, wrap(async (req) => {\n const input = parseBody(twoStepRequestSchema, req.body);\n return services.methods.setTwoStep(req.auth!.sub, req.auth!.sid, input.enabled, input.stepUpToken);\n }));\n\n router.post(\"/account/security/methods\", auth, wrap(async (req) => {\n const input = parseBody(addMethodRequestSchema, req.body);\n return services.methods.addMethod(req.auth!.sub, input);\n }));\n\n router.post(\"/account/security/passkeys/options\", auth, wrap(async (req) => {\n return services.passkeys.registrationOptions(req.auth!.sub);\n }));\n\n router.post(\"/account/security/passkeys\", auth, wrap(async (req) => {\n const input = parseBody(passkeyRegisterRequestSchema, req.body);\n return services.passkeys.register(req.auth!.sub, input);\n }));\n\n router.post(\"/account/security/methods/:id/confirm\", auth, wrap(async (req) => {\n const input = parseBody(confirmMethodRequestSchema, req.body);\n return services.methods.confirmMethod(req.auth!.sub, req.params.id!, input.code);\n }));\n\n router.patch(\"/account/security/methods/:id\", auth, wrap(async (req) => {\n const input = parseBody(patchMethodRequestSchema, req.body);\n return services.methods.patchMethod(req.auth!.sub, req.auth!.sid, req.params.id!, input);\n }));\n\n router.delete(\"/account/security/methods/:id\", auth, wrap(async (req, res) => {\n const input = parseBody(deleteMethodRequestSchema, req.body);\n await services.methods.deleteMethod(req.auth!.sub, req.auth!.sid, req.params.id!, input.stepUpToken);\n res.status(204).end();\n return undefined;\n }));\n\n return router;\n}\n","import type { SmsProvider, Logger } from \"../../config/schema.js\";\n\n/** Dev/test provider — logs the message instead of sending a real SMS. */\nexport function consoleSms(logger?: Logger): SmsProvider {\n return {\n send: async (phoneE164: string, message: string) => {\n const masked = `•••• ${phoneE164.slice(-4)}`;\n (logger ?? console).info({ to: masked }, message);\n },\n };\n}\n","import type { SmsProvider } from \"../../config/schema.js\";\n\nexport interface TwilioSmsOptions {\n /** Account SID (AC…). */\n sid: string;\n /** Auth token for the account. */\n token: string;\n /** Sender number in E.164 (or a Messaging Service SID, MG…). */\n from: string;\n /** Twilio REST base URL; defaults to https://api.twilio.com/2010-04-01. */\n baseUrl?: string;\n /** Optional pre-built client (for tests / custom transports). */\n client?: { messages: { create: (args: { to: string; from: string; body: string }) => Promise<unknown> } };\n}\n\n/**\n * Twilio SMS provider over Twilio's REST API (Messages resource), so apps need no\n * extra dependency — pass the account settings they already have.\n */\nexport function twilioSms(opts: TwilioSmsOptions): SmsProvider {\n const baseUrl = (opts.baseUrl ?? \"https://api.twilio.com/2010-04-01\").replace(/\\/+$/, \"\");\n const auth = `Basic ${Buffer.from(`${opts.sid}:${opts.token}`).toString(\"base64\")}`;\n\n return {\n send: async (phoneE164, message) => {\n if (opts.client) {\n await opts.client.messages.create({ to: phoneE164, from: opts.from, body: message });\n return;\n }\n const form = new URLSearchParams({ To: phoneE164, Body: message });\n form.set(opts.from.startsWith(\"MG\") ? \"MessagingServiceSid\" : \"From\", opts.from);\n const res = await fetch(`${baseUrl}/Accounts/${encodeURIComponent(opts.sid)}/Messages.json`, {\n method: \"POST\",\n headers: { Authorization: auth, \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: form.toString(),\n });\n if (!res.ok) {\n const detail = (await res.json().catch(() => null)) as { code?: number; message?: string } | null;\n throw new Error(`twilio: SMS not sent (${res.status}${detail?.code ? ` ${detail.code}` : \"\"}): ${detail?.message ?? res.statusText}`);\n }\n },\n };\n}\n","import type { EmailProvider, Logger } from \"../../config/schema.js\";\nimport { maskEmail } from \"../../services/delivery.js\";\n\n/** Dev/test provider — logs the message instead of sending a real email. */\nexport function consoleEmail(logger?: Logger): EmailProvider {\n return {\n send: async ({ to, message }) => {\n (logger ?? console).info({ to: maskEmail(to) }, message);\n },\n };\n}\n"],"mappings":"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","names":["argon2","crypto","migrate","import_express","import_zod","import_node_crypto","crypto","doc","insertedId","hashPassword","import_mongodb","import_node_crypto","crypto","import_express","import_auth_2fa_contract","wrap","migrate","helmet"]}
|