Belajar 2FA Authentication - Verifikasi Code Pertama (Enrollment Confirm)
Episode 6 of 23

Belajar 2FA Authentication - Verifikasi Code Pertama (Enrollment Confirm)

Episode ini menutup alur enrollment: pengguna memasukkan 6 digit setelah memindai QR, server memverifikasinya dengan authenticator check dan toleransi window, baru mengaktifkan totpEnabled. Kalian juga melihat kapan recovery codes pertama kali ditampilkan.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

Halaman Enable 2FA sudah menampilkan QR, dan pengguna sudah memindainya. Tapi 2FA belum aktif — kode pertama belum diverifikasi. Episode 6 menutup alur enrollment dengan verifikasi code pertama: pengguna mengetik 6 digit yang tampil di Google Authenticator, dan server memastikan kode itu benar-benar cocok dengan secret yang dienkripsi di URI.

Mengapa langkah ini tidak boleh dilewati? Karena verifikasi pertama membuktikan tiga hal sekaligus: QR terbaca benar, secret sampai utuh ke aplikasi pengguna, dan jam kedua sisi cukup sinkron. Setelah itu baru aman mengaktifkan totpEnabled dan menampilkan recovery codes.

Alur Confirm Enrollment

Scan, Input, dan Verifikasi

Setelah memindai, pengguna membuka Google Authenticator, melihat 6 digit, lalu memasukkannya ke formulir konfirmasi. Server menerima kode dan membandingkannya dengan secret yang disimpan sementara:

JSEndpoint konfirmasi enrollment
const { authenticator } = require('otplib');
 
app.post('/account/enable-2fa/confirm', (req, res) => {
  const { token, secret } = req.body;
  const valid = authenticator.check(token, secret);
  if (!valid) {
    return res.status(400).json({ error: 'Kode tidak valid' });
  }
  encryptAndSaveSecret(req.user.id, secret);
  enableTotp(req.user.id);
  res.json({ ok: true });
});

Metode authenticator.check mengembalikan boolean: true jika kode cocok dengan secret dalam jendela waktu yang diizinkan, false jika salah. Setelah true, secret dienkripsi dan disimpan permanen, lalu totpEnabled diaktifkan.

Kedua operasi itu — menyimpan secret dan mengaktifkan flag — idealnya dibungkus dalam satu transaksi database agar tidak ada state di mana secret tersimpan tapi 2FA belum aktif.

Toleransi Window Waktu

Kode TOTP hanya valid dalam jendela 30 detik. Karena jam server dan perangkat bisa berbeda beberapa detik, otplib memberi toleransi melalui opsi window. Nilai 1 berarti server menerima kode untuk step sekarang, satu step sebelumnya, dan satu step sesudahnya:

Jendela toleransi window 1
[step-1] [step saat ini] [step+1]
  30dtk     30dtk          30dtk       <- masing-masing 6 digit

Default otplib adalah window 0. Atur ke 1 di produksi untuk menyerap selisih jam kecil tanpa mengorbankan keamanan: window: 1. Kalau kode kadang valid kadang tidak, jangan menaikkan window tanpa batas — periksa sinkronisasi jam lewat NTP seperti episode 2.

Istilah step merujuk pada satu jendela 30 detik. Step saat ini, step sebelumnya, dan step sesudahnya membentuk tiga kandidat kode yang dianggap valid — otplib membandingkan semuanya saat window bernilai 1. Nilai window yang lebih besar dari 2 hampir tidak pernah dibenarkan karena berarti server menerima kode yang sudah basi puluhan detik.

Warning

Setiap kegagalan verifikasi sebaiknya dicatat. Tiga sampai lima percobaan gagal berturut-turut pada langkah MFA umumnya menandakan serangan brute-force atau pengguna yang kehilangan perangkat — aktifkan cooldown atau minta verifikasi ulang. Mekanisme rate limiting dibahas penuh di episode 10.

Aktivasi totpEnabled

Hanya Setelah Kode Valid

Kunci keamanan alur ini: totpEnabled baru berubah menjadi true di dalam handler yang sama setelah authenticator.check mengembalikan true. Jangan pernah menyediakan endpoint terpisah yang mengaktifkan 2FA tanpa verifikasi — itu membuka celah pengguna (atau penyerang dengan session) mengaktifkan 2FA ke perangkatnya sendiri tanpa bukti kepemilikan secret.

Lalu Tampilkan Recovery Codes

Setelah aktivasi sukses, inilah saat yang tepat menampilkan recovery codes untuk pertama kali. Kode pemulihan harus ditampilkan sekali dan diminta pengguna menyimpannya sebelum bisa lanjut — karena setelah 2FA aktif, kehilangan authenticator tanpa recovery code berarti kunci keluar akun. Pembuatan dan pengelolaan recovery codes dibedah penuh di episode 9.

Kegagalan Berulang dan Pemulihan

Cooldown dan Lockout

Kode 6 digit ruang pencariannya kecil — hanya satu juta kemungkinan — sehingga kegagalan berulang harus direspons dengan cooldown. Strategi umum: batasi percobaan per menit, lalu tambahkan delay eksponensial setelah beberapa kegagalan. Kalau pengguna sama sekali tidak bisa masuk, arahkan ke alur recovery codes yang dibahas di episode 9.

Cooldown yang baik juga membedakan pencatatan per akun dan per IP, sehingga serangan yang terdistribusi tetap terlihat sebagai pola dari satu sudut pandang.

Pesan Error yang Membantu

Pesan error tidak boleh membocorkan status akun. "Kode tidak valid" lebih aman daripada pesan yang menyebut 2FA, karena penyerang tidak bisa membedakan apakah sebuah akun mengaktifkan 2FA atau tidak. Berikan hint tentang sinkronisasi jam hanya pada halaman yang memang memerlukan kode TOTP.

Sembunyikan juga detail window yang dipakai — penyerang tidak perlu tahu berapa banyak kode basi yang masih diterima server.

Kapan Menghapus Secret Pending

Secret yang disimpan sementara saat enrollment juga butuh batas hidup. Jika pengguna tidak pernah menyelesaikan verifikasi pertama dalam waktu tertentu, hapus secret pending dan tampilkan halaman enrollment baru. Ini menjaga kebersihan database dan mencegah QR basi tetap berlaku selamanya.

Penghapusan juga harus mencabut semua token pending yang sudah dikeluarkan untuk sesi enrollment tersebut, supaya halaman yang masih terbuka di browser tidak bisa menyelesaikan verifikasi dengan secret usang.

Kalibrasi Pikiran: Kapan Kode Ditolak

Supaya jelas, berikut situasi yang membuat verifikasi pertama gagal:

  • Kode salah ketik atau 6 digit tidak sesuai aplikasi.
  • Pengguna memindai QR yang sudah kedaluwarsa dari enrollment sebelumnya.
  • Secret yang dikirim form berbeda dari yang dirender di QR.
  • Jam perangkat sangat jauh dari waktu server melebihi window.

Untuk tiga kasus terakhir, solusi yang tepat adalah memulai ulang enrollment dengan secret baru, bukan menaikkan toleransi window. Secret baru dianggap sebagai rotasi dan diizinkan selama 2FA belum aktif.

Sebaliknya, kegagalan karena kode salah ketik cukup dibiarkan tanpa efek samping — pengguna tinggal mencoba lagi pada step berikutnya.

Penutup

Episode 6 menutup alur enrollment dengan verifikasi pertama: pengguna mengetik 6 digit, server memeriksa dengan authenticator.check dan window toleransi, baru mengaktifkan totpEnabled dan menampilkan recovery codes.

Inti yang harus dibawa pulang:

  • authenticator.check memverifikasi kode terhadap secret secara timing-safe.
  • window 1 menerima kode satu step sebelum dan sesudah step saat ini.
  • totpEnabled hanya aktif setelah kode pertama valid.
  • Jangan pisahkan aktivasi 2FA dari verifikasi kode.
  • Recovery codes pertama kali ditampilkan tepat setelah aktivasi.
  • Kode yang gagal terus-menerus berarti mulai ulang enrollment, bukan naikkan window.

Di episode 7 selanjutnya kita akan membahas menyimpan secret dengan aman — mengenkripsi secret Base32 dengan AES-256-GCM, mengelola kunci lewat environment MFA_ENCRYPTION_KEY, dan praktik logging yang tidak pernah membocorkan secret atau kode.

Belajar 2FA Authentication - Verifikasi Code Pertama (Enrollment Confirm) | Belajar 2FA Authentication