Episode ini mengelola siklus hidup 2FA: mematikan fitur dengan re-autentikasi password dan kode, merotasi secret saat perangkat dicurigai bocor, serta menghapus recovery codes. Kalian juga mempelajari transisi status disabled, pending, dan enabled.

Fitur 2FA bukan benda mati — ia dilahirkan, dirawat, dan suatu saat perlu dimatikan atau diganti. Episode 11 membahas sisi manajemen ini: menonaktifkan 2FA, merotasi secret, dan menghapus recovery codes. Ketiganya termasuk operasi paling sensitif di akun pengguna.
Mengapa sensitif? Karena salah satu skenario penyerang adalah mengambil alih 2FA — mematikan fitur milik pengguna dan memasangnya ke perangkat sendiri. Karena itu setiap transisi status harus dituntut bukti identitas yang kuat. Kalian akan membangun alur yang benar dan memahami state machine yang menopangnya.
Mematikan 2FA adalah gerbang yang sangat berbahaya: sekali nonaktif, akun kembali hanya bergantung pada password. Karena itu server wajib meminta bukti identitas ulang — minimal password dan kode TOTP saat itu:
app.post('/account/disable-2fa', requireAuth, async (req, res) => {
const validPass = await bcrypt.compare(req.body.password, req.user.password_hash);
const secret = decryptSecret(req.user.totp_secret_encrypted);
const validCode = authenticator.check(req.body.token, secret);
if (!validPass || !validCode) {
return res.status(400).json({ error: 'Verifikasi ulang gagal' });
}
await users.update({
where: { id: req.user.id },
data: { totp_secret_encrypted: null, totp_enabled: false }
});
await recoveryCodes.deleteMany({ where: { userId: req.user.id } });
res.json({ ok: true });
});Handler disable-2fa menuntut dua bukti: password yang benar dan kode TOTP yang valid saat ini. Keduanya harus lolos sebelum secret dihapus — penyerang dengan session curian saja tidak cukup untuk melepas 2FA.
Menonaktifkan 2FA juga menghapus recovery codes dalam satu operasi. Menyisakan kode lama saat fitur dinonaktifkan meninggalkan jalur darurat yang tidak jelas statusnya — hapus semuanya dan mulai bersih.
Rotasi secret dijalankan ketika pengguna merasa perangkatnya hilang, dicuri, atau 2FA terasa aneh. Rotasi mengganti secret lama dengan yang baru, memaksa Google Authenticator di perangkat lama tidak lagi menghasilkan kode yang cocok:
app.post('/account/rotate-2fa', requireAuth, async (req, res) => {
const validPass = await bcrypt.compare(req.body.password, req.user.password_hash);
const validCode = authenticator.check(req.body.token, decryptSecret(req.user.totp_secret_encrypted));
if (!validPass || !validCode) {
return res.status(400).json({ error: 'Verifikasi ulang gagal' });
}
const newSecret = authenticator.generateSecret();
await users.update({
where: { id: req.user.id },
data: { totp_secret_encrypted: encryptSecret(newSecret), totp_enabled: false }
});
await recoveryCodes.deleteMany({ where: { userId: req.user.id } });
res.json({ secret: newSecret });
});Rotasi kembali ke status pending: secret baru disimpan terenkripsi, totp_enabled direset ke false, dan pengguna harus menyelesaikan enrollment ulang seperti episode 6. Perangkat lama otomatis berhenti berfungsi.
Satu keputusan penting: rotasi tidak boleh menuntut kode TOTP lama yang sudah tidak bisa dihasilkan perangkat yang hilang. Tuntut password (dan recovery code bila perlu), tapi jangan meminta bukti yang secara teknis sudah tidak bisa dipenuhi pemilik sah.
Siklus hidup 2FA paling mudah dipahami sebagai state machine tiga status:
disabled -> pending -> enabled
^ |
|______ disable ________|Setiap transisi punya prasyaratnya sendiri. disabled ke pending hanya butuh session; pending ke enabled wajib kode pertama valid; enabled kembali ke disabled wajib re-autentikasi. Rotasi adalah jalur enabled menuju pending dengan secret baru.
Setiap transisi status harus meninggalkan jejak audit: mfa_enabled, mfa_disabled, mfa_rotated, dan mfa_recovery_used. Tanpa jejak ini, tim keamanan tidak bisa menjawab pertanyaan dasar seperti siapa yang mematikan 2FA dan kapan. Episode 20 akan membahas observability ini secara utuh.
Saat 2FA dinonaktifkan atau dirotasi, kirim notifikasi ke kanal lain — email atau push notification. Akun yang tiba-tiba melepas 2FA tanpa sepengetahuan pemilik adalah sinyal kuat akun telah dibajak, dan peringatan memberi pengguna kesempatan bereaksi.
Episode 11 melengkapi manajemen 2FA: disable yang menuntut re-autentikasi, rotasi yang mengganti secret dan mengembalikan status ke pending, serta state machine disabled, pending, dan enabled yang menjadi bahasa bersama seluruh fitur.
Inti yang harus dibawa pulang:
Di episode 12 selanjutnya kita akan membahas skema database dan data management — struktur tabel users dan recovery_codes dengan indeks dan constraint, migrasi database, serta kebijakan backup yang menjaga data 2FA tetap utuh.