Episode ini mengubah provisioning URI menjadi QR code yang bisa dipindai Google Authenticator, merendernya di server dengan qrcode dan menampilkan hanya sekali saat enrollment, plus merancang halaman Enable 2FA dengan opsi secret manual untuk pengguna tanpa kamera.

URI otpauth:// dari episode 4 masih berupa teks panjang yang tidak nyaman diketik. Episode 5 menjembatani URI itu ke smartphone pengguna lewat QR code — format visual yang bisa dipindai dalam satu detik oleh Google Authenticator.
Kalian akan belajar merender QR di sisi server dengan package qrcode, menampilkannya hanya sekali saat enrollment, dan merancang halaman Enable 2FA yang lengkap: QR utama, opsi secret manual, dan instruksi instalasi aplikasi authenticator. Di akhir episode, pengguna bisa memindai dan akunnya muncul di Google Authenticator.
Package qrcode bisa merender QR dalam berbagai bentuk, termasuk data URL PNG yang langsung disisipkan ke tag gambar. Encode URI provisioning menjadi data URL di route enrollment:
const qrcode = require('qrcode');
app.get('/account/enable-2fa', async (req, res) => {
const secret = authenticator.generateSecret();
const uri = authenticator.keyuri(req.user.email, 'Devvnull Labs', secret);
const qr = await qrcode.toDataURL(uri, { width: 256, margin: 2 });
res.json({ qr, secret, uri });
});Data URL qr adalah string data:image/png;base64,... yang bisa dirender langsung di halaman. Metode qrcode.toDataURL menjalankan koreksi error Reed-Solomon terbaik secara default, sehingga QR tetap terbaca meski sebagian gambar tertutup.
Data URL memuat seluruh gambar PNG, jadi sengaja jangan dimasukkan ke log atau disimpan permanen — gunakan hanya sebagai respons satu kali ke halaman enrollment.
URI provisioning berisi rahasia — siapa pun yang memegangnya bisa memprovisioning akun pengguna. Karena itu QR dan secret manual hanya boleh muncul sekali, tepat saat enrollment, dan jangan pernah di-serve ulang setelah 2FA aktif. Batasi juga akses route ini hanya untuk pengguna yang totp_enabled masih false.
Package qrcode menawarkan tingkat error correction L, M, Q, dan H — semakin tinggi tingkatnya, semakin banyak pola yang bisa dipulihkan jika gambar tertutup atau rusak, namun semakin padat modul yang dihasilkan. Untuk QR yang dipindai dari layar smartphone, level M sudah memadai, sementara level H berguna untuk QR yang dicetak di media fisik.
Atur lebar gambar antara 256 hingga 320 piksel. Terlalu kecil menyulitkan pemindaian dari jarak dekat, terlalu besar tidak menambah akurasi. Contoh render dengan level error correction yang eksplisit:
const qr = await qrcode.toDataURL(uri, {
errorCorrectionLevel: 'H',
width: 320,
margin: 4,
color: {
dark: '#000000',
light: '#ffffff'
}
});Kontras hitam di atas putih adalah kombinasi paling mudah dipindai. Hindari QR dengan warna terang di atas terang atau pola latar belakang yang ramai, dan pastikan ada area tenang kosong di sekeliling QR agar kamera mengenali tepinya.
Jika QR akan dicetak, naikkan margin dan gunakan level H; jika hanya ditampilkan di layar, level M cukup untuk menyeimbangkan kepadatan dan keterbacaan.
Ukuran yang konsisten juga membantu: pertahankan 256 piksel untuk versi layar dan 512 piksel untuk versi unduh atau cetak, agar identitas visual QR tetap stabil di semua tempat kemunculannya.
Layout halaman enrollment menampilkan QR di tengah, dengan secret Base32 di bawahnya sebagai opsi manual. Pengguna yang kameranya rusak atau menolak memindai bisa mengetik secret langsung ke Google Authenticator lewat tombol "Enter a setup key". Beri tombol salin secret dan peringatan bahwa rahasia ini hanya ditampilkan sekali.
Pastikan tombol salin tidak pernah menempelkan URI lengkap ke clipboard — cukup secret saja — supaya URI yang memuat issuer dan parameter tidak tersebar tanpa sengaja.
Halaman juga harus memandu pengguna yang belum punya aplikasi: tautan ke Google Play dan App Store, langkah memindai QR, dan apa yang akan mereka lihat setelah berhasil — akun baru yang menampilkan 6 digit yang berdetak setiap 30 detik. Tulis langkah pendek dan konkret agar enrollment selesai tanpa dukungan manual.
Instruksi langkah demi langkah yang efektif berbentuk daftar pendek: buka aplikasi, tekan tombol tambah, pilih "Scan a QR code", arahkan kamera ke QR, lalu tunggu akun muncul. Setelah muncul, jangan langsung menutup halaman — langkah verifikasi pertama di episode 6 harus dilakukan saat kode masih tampil di aplikasi.
Halaman enrollment hanya bernilai kalau alur di sekitarnya benar: secret tidak bocor sebelum pemindaian, dan akun tidak aktif sebelum verifikasi. Dua subsection berikut menetapkan aturan mainnya.
Satu aturan emas: jangan aktifkan 2FA sebelum kode pertama terverifikasi. Pengguna bisa saja memindai QR yang salah, mengetik secret yang terpotong, atau aplikasinya belum selesai sinkron. Episode 6 menangani verifikasi pertama ini, tapi keputusan desainnya — QR tanpa aktivasi otomatis — sudah ditetapkan sejak halaman ini dirancang.
Secret yang ditampilkan di halaman Enable 2FA sebaiknya kedaluwarsa — misalnya 10 menit — untuk mencegah QR dibiarkan terbuka di layar dan dipindai orang lain. Setelah kedaluwarsa, pengguna memulai ulang enrollment dengan secret baru, dan secret lama dianggap tidak berlaku. Catat waktu pembuatan secret di dalam payload enrollment server.
Selain QR, tampilkan secret Base32 di bawah QR dengan tombol salin. Beberapa pengguna lebih suka mengetik manual di aplikasi authenticator yang tidak punya kamera. Simpan keputusan kecil ini dalam satu komponen agar perilakunya konsisten: sekali dirender, halaman tidak boleh menyegarkan dan menampilkan secret baru tanpa interaksi pengguna.
Halaman Enable 2FA memuat informasi sensitif. Pastikan route menuntut session yang valid, atur header seperti Content-Security-Policy yang melarang muatan script dari domain asing, dan beri peringatan visual jika halaman tidak dilayani lewat HTTPS. Hardening header ini dibahas penuh di episode 13.
Episode 5 mengubah URI provisioning menjadi QR code yang ramah pemindaian: dirender server-side dengan qrcode, ditampilkan sekali, dan dibungkus halaman Enable 2FA yang memandu pengguna dari instalasi aplikasi sampai munculnya akun pertama.
Inti yang harus dibawa pulang:
Di episode 6 selanjutnya kita akan membahas verifikasi code pertama (enrollment confirm) — memvalidasi 6 digit yang diketik pengguna terhadap secret, menerapkan toleransi window waktu, baru mengaktifkan totpEnabled, lalu menampilkan recovery codes.