import otplib from 'otplib' const { authenticator } = otplib import { db, userIdentities, eq, and } from '~/drizzle/db' // 「该账号没开 2FA」的缓存失效入口(见 server/utils/totp-cache.ts 的说明) import { invalidateTotpCache } from '~~/server/utils/totp-cache' export default defineEventHandler(async (event) => { const user = event.context.user if (!user) { throw createError({ statusCode: 401, message: '未授权访问' }) } const body = await readBody(event) const { token, secret } = body if (!token || !secret) { throw createError({ statusCode: 400, message: '缺少验证码或密钥' }) } const isValid = authenticator.check(token, secret) if (!isValid) { throw createError({ statusCode: 400, message: '验证码错误' }) } // 检查是否已存在 const existing = await db.query.userIdentities.findFirst({ where: and(eq(userIdentities.userId, user.id), eq(userIdentities.provider, 'totp')) }) if (existing) { // 更新密钥 await db.update(userIdentities) .set({ providerUserId: secret }) .where(eq(userIdentities.id, existing.id)) } else { // 保存到数据库 await db.insert(userIdentities).values({ userId: user.id, provider: 'totp', providerUserId: secret, createdAt: new Date() }) } /* * ★ 让登录流程里那个「这个账号没开 2FA」的缓存失效。 * * login.post.ts 为了提速,会把"某账号没有 TOTP"这个结论缓存 10 分钟 * (绝大多数账号确实没开 2FA,每次登录都查库纯属浪费)。 * 但用户**刚开启 2FA** 时必须立刻让那条缓存作废, * 否则他在接下来 10 分钟内登录会被**跳过 2FA 验证** —— 那是安全问题。 */ invalidateTotpCache(user.id) return { success: true } })