Episode ini memperkuat sesi dan token: session jangka pendek dengan rotasi id, logout menyeluruh di semua perangkat, opsi trusted device dengan cookie terpisah, serta JWT dengan audience dan expiry yang berhubungan dengan flag 2FA saat dikeluarkan.

2FA menambah lapisan di gerbang masuk, tapi pekerjaannya belum selesai setelah pintu terbuka. Episode 15 membahas apa yang terjadi sesudah login: session yang berumur pendek, rotasi id saat autentikasi, logout yang benar-benar menyeluruh, opsi trusted device, dan JWT yang mencatat status 2FA.
Semua ini menentukan berapa lama sebuah sesi curian masih berharga. Session yang panjang tanpa rotasi mengubah satu kode curian menjadi akses permanen — justru menghancurkan nilai 2FA yang baru dibangun. Kalian akan menutup masalah itu dari sisi sesi dan token.
Session panjang memberi kenyamanan, tapi juga memperbesar jendela penyalahgunaan jika cookie bocor. Batasi masa hidup session menjadi 15-30 menit untuk operasi sensitif, dengan aktivitas yang memperpanjang sesi secara bertahap. Jangan pernah menjadikan 2FA permanen lewat session yang tidak berakhir.
Setiap kali tingkat autentikasi naik — khususnya setelah verifikasi MFA — ganti session id. Rotasi memastikan cookie lama yang mungkin sudah dicegat tidak lagi terikat dengan sesi baru:
app.post('/login/mfa', async (req, res) => {
// ... verifikasi kode TOTP ...
await new Promise((resolve) => req.session.regenerate(resolve));
req.session.userId = user.id;
res.json({ ok: true });
});req.session.regenerate mengeluarkan session id baru dan membuang data session lama. Panggil setelah kode TOTP valid — ini adalah batas yang paling logis untuk memotong hubungan dengan cookie lama.
Rotasi yang dilakukan pada session yang belum penuh — misalnya saat pendingMfa — harus dibedakan: jangan sampai rotasi menghapus status pending yang masih dibutuhkan alur login.
Logout hanya satu perangkat tidak cukup ketika session bocor. Sediakan opsi "logout semua perangkat" yang menghapus seluruh session pengguna dari store session. Di express-session dengan store database:
node -e "require('express-session'); const s = new (require('connect-sqlite3')(require('express-session')))(); s.destroyAllForUser('550e8400-e29b-41d4-a716-446655440000');"Praktik produksi memakai query terstruktur pada tabel session dengan kolom user_id, menghapus semua baris milik pengguna. Pasangkan fitur ini ke halaman keamanan akun, dan otomatiskan ketika pengguna merotasi secret atau memakai recovery code.
Logout menyeluruh juga menjadi bagian alur ketika secret 2FA berubah — session yang dibuat dengan 2FA lama seharusnya tidak bertahan setelah 2FA dirotasi.
Opsi "ingat perangkat ini selama 30 hari" menyimpan cookie khusus yang ditandatangani server, terpisah dari session. Saat pengguna kembali dengan cookie trusted device, server bisa melewati tantangan TOTP — tapi hanya setelah password benar:
const jwt = require('jsonwebtoken');
function issueTrustedDevice(userId) {
return jwt.sign(
{ sub: userId, purpose: 'trusted-device' },
process.env.SESSION_SECRET,
{ expiresIn: '30d' }
);
}Token purpose: 'trusted-device' membedakan cookie ini dari token lainnya, sehingga trusted device tidak bisa disalahgunakan untuk hal di luar izinnya.
Trusted device hanya boleh menghilangkan langkah TOTP, tidak pernah menggantikan password. Jangan terapkan trusted device pada operasi sensitif seperti disable 2FA atau reset secret — operasi itu selalu menuntut kode penuh. Dan ingat: password saja tidak cukup untuk device baru; cookie trusted yang valid tetap menuntut password.
Simpan daftar trusted device sebagai hash dari token, bukan token mentah, supaya kebocoran tabel tidak langsung memberi cookie valid. Cabut akses device dari halaman keamanan satu per satu.
Ketika API memakai JWT, klaim harus ditetapkan dengan ketat: aud membatasi audience, exp membatasi umur. Token yang tidak memiliki aud bisa dipakai ulang di konteks yang tidak seharusnya:
const token = jwt.sign(
{ sub: user.id, mfa: user.totp_enabled, role: user.role },
process.env.JWT_SECRET,
{ audience: 'devvnull-api', expiresIn: '15m', issuer: 'devvnull-auth' }
);Klaim mfa: user.totp_enabled memberi tahu resource server apakah pengguna melewati MFA saat token dikeluarkan. Ini penting untuk otorisasi berlapis: endpoint yang sensitif bisa menuntut token dengan mfa: true.
Token penuh hanya boleh dikeluarkan setelah MFA selesai. Jika flow JWT dipakai, pendekatan dua langkah seperti session: token sementara dengan cakupan terbatas untuk langkah pertama, token penuh setelah kode TOTP valid. Jangan pernah menerbitkan token akses penuh di /login ketika pengguna punya 2FA.
Jika resource server memakai library verifikasi token, pastikan ia memvalidasi audience dan issuer di setiap request — bukan hanya tanda tangan.
Episode 15 memperkuat apa yang terjadi setelah login: session pendek dengan rotasi id, logout menyeluruh, trusted device dengan cookie bertanda tangan yang tidak menggantikan password, dan JWT dengan audience, expiry, serta flag 2FA.
Inti yang harus dibawa pulang:
Di episode 16 selanjutnya kita akan membahas testing: RFC test vectors dan E2E real device — memvalidasi TOTP dengan vektor resmi RFC 6238, memakai fake timers, dan menguji alur lengkap dengan Google Authenticator sungguhan.