// SEC3-d Backfill: bestehende Klartext-TOTP-Secrets (Identity + PlatformAdmin) verschlüsseln. // Idempotent — bereits verschlüsselte (enc:v1:) werden übersprungen. Optional: der Code // akzeptiert Alt-Klartext ohnehin weiter; dieses Skript verschlüsselt ihn aktiv. // Lauf (mit gesetztem DATABASE_URL + AUTH_SECRET): npx tsx scripts/encrypt-mfa-secrets.ts import { PrismaClient } from "@prisma/client"; import { PrismaPg } from "@prisma/adapter-pg"; import { encryptSecret, isEncrypted } from "../src/server/secret-crypto"; const prisma = new PrismaClient({ adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }) }); async function main() { let users = 0; // Option C: MFA-Secrets leben an der globalen Identity. for (const u of await prisma.identity.findMany({ where: { mfaSecret: { not: null } }, select: { id: true, mfaSecret: true } })) { if (u.mfaSecret && !isEncrypted(u.mfaSecret)) { await prisma.identity.update({ where: { id: u.id }, data: { mfaSecret: encryptSecret(u.mfaSecret) } }); users++; } } let admins = 0; for (const a of await prisma.platformAdmin.findMany({ where: { mfaSecret: { not: null } }, select: { id: true, mfaSecret: true } })) { if (a.mfaSecret && !isEncrypted(a.mfaSecret)) { await prisma.platformAdmin.update({ where: { id: a.id }, data: { mfaSecret: encryptSecret(a.mfaSecret) } }); admins++; } } console.log(`Verschlüsselt: ${users} Nutzer-Secret(s), ${admins} Plattform-Admin-Secret(s).`); } main() .catch((e) => { console.error("Backfill fehlgeschlagen:", e.message); process.exit(1); }) .finally(() => prisma.$disconnect());