Episode ini membedah anatomi kode TOTP: shared secret Base32, counter waktu epoch dibagi 30 detik, HMAC-SHA1, dan truncation menjadi 6 digit. Kalian juga mempelajari arsitektur end-to-end dari generate secret, QR code, verifikasi pertama, sampai login dua langkah, termasuk pentingnya sinkronisasi jam.

Sebelum menulis kode, kalian harus memahami apa yang sebenarnya terjadi di balik 6 digit yang berubah setiap 30 detik. Episode 2 membedah TOTP dari dalam: shared secret, counter waktu, HMAC-SHA1, truncation, dan sinkronisasi jam. Pemahaman ini membedakan kalian dari developer yang sekadar menyalin pustaka.
Setelah memahami anatomi kode, kita akan menelusuri arsitektur utama fitur 2FA end-to-end — dari server membuat secret, merender QR code, sampai verifikasi login dua langkah. Kalian juga akan melihat langsung kode TOTP yang dihitung dengan otplib, dan mengapa jam yang akurat adalah syarat mutlak TOTP.
TOTP dimulai dari shared secret — nilai acak yang hanya diketahui server dan perangkat pengguna. Secret ini dikodekan dalam Base32 (RFC 3548), sehingga hanya memakai huruf A-Z dan angka 2-7. Bentuk Base32 inilah yang tampak di Google Authenticator saat kalian memasukkan secret secara manual, misalnya JBSWY3DPEHPK3PXP.
Base32 membuat secret mudah diketik manusia tanpa karakter yang mudah tertukar, dan bisa di-encode ke dalam URI otpauth://. Secret harus unik per pengguna dan jangan pernah dipakai ulang antar akun — episode 4 akan membahas pembuatannya secara detail.
TOTP adalah HOTP dengan counter yang berasal dari waktu. Counter dihitung sebagai hasil bagi pembulatan ke bawah epoch UNIX (detik sejak 1 Januari 1970) dengan periode. Dengan periode 30 detik, nilai counter berganti setiap 30 detik — inilah yang membuat kode tampak berdetak.
Perhitungan sederhananya:
counter = floor(epoch_seconds / 30)Karena server dan client memakai waktu nyata, keduanya menghasilkan counter yang sama pada jendela waktu yang sama. Di sinilah sinkronisasi jam menjadi krusial — TOTP gagal total jika salah satu sisi telat puluhan detik.
Kode TOTP dihitung dengan fungsi HMAC-SHA1 yang menerima secret (setelah dekode Base32) dan counter sebagai pesan. Hasilnya adalah 20 byte, lalu dynamic truncation mengambil 4 byte dari posisi yang ditentukan oleh 4 bit terakhir hash, memotong 31 bit, dan mengambil modulus 1.000.000. Hasil akhirnya adalah 6 digit — dijamin kode selalu berbentuk angka 6 digit seperti 123456.
Rantai lengkapnya: secret ditambah waktu, lalu HMAC-SHA1, truncate, modulo 1.000.000, dan hasilnya 6 digit.
RFC 6238 tidak membatasi TOTP pada HMAC-SHA1. Parameter algorithm bisa diganti menjadi SHA256 atau SHA512, dan period bisa diubah dari 30 detik default. otplib membaca parameter dari URI provisioning sehingga kode selalu cocok dengan apa yang dikonfigurasi aplikasi authenticator.
Kompatibilitas terbaik tetap di HMAC-SHA1 dengan 6 digit dan periode 30 detik — semua aplikasi mendukung kombinasi ini. Anggap variasi algoritma sebagai fitur lanjutan untuk kebutuhan khusus, bukan pilihan default.
Implementasi di atas sudah diuji ribuan kali di dalam otplib. Kalian cukup memakai API authenticator:
node -e "
const { authenticator } = require('otplib');
const secret = 'JBSWY3DPEHPK3PXP';
const token = authenticator.generate(secret);
console.log('TOTP saat ini:', token);
console.log('Counter step:', Math.floor(Date.now() / 1000 / 30));
"Jalankan perintah itu dua kali dalam jarak 30 detik untuk melihat kode berubah. Metode authenticator.generate menerima secret Base32 dan mengembalikan 6 digit — persis yang dihasilkan Google Authenticator untuk secret yang sama. Episode 4 dan 6 akan memakai API ini untuk enrollment dan verifikasi.
Tip
otplib menerima opsi global melalui authenticator.options = { step: 30, window: 1 }. Parameter algorithm, digits, dan period pada URI provisioning juga dibaca otomatis oleh authenticator.generate, sehingga hasilnya selalu konsisten dengan aplikasi authenticator.
Seluruh series dibangun di atas satu arsitektur utama. Urutannya:
Episode 4 sampai 7 membedah setiap langkah ini satu per satu.
Setelah enrollment, login berubah menjadi dua langkah:
pendingMfa.Arsitektur ini mencegah pengguna mengakses protected route sebelum menyelesaikan tantangan 2FA. Implementasinya akan dibahas penuh di episode 8.
TOTP sangat peka terhadap waktu. Standar RFC 6238 menyarankan server mengizinkan toleransi satu step di setiap sisi — total jendela tiga step — untuk mengakomodasi selisih jam perangkat. Di server produksi, gunakan NTP dan jangan andalkan waktu server yang mengambang:
sudo timedatectl set-ntp true
timedatectl statusOutput timedatectl status menampilkan baris "System clock synchronized: yes" ketika NTP aktif. Episode 6 akan memakai toleransi step ini saat verifikasi pertama, dan episode 16 mengujinya dengan clock skew buatan.
Episode 2 membedah inti TOTP: secret Base32 sebagai rahasia bersama, counter dari epoch dibagi 30 detik, HMAC-SHA1 dengan dynamic truncation menjadi 6 digit, dan arsitektur enrollment serta login dua langkah yang menopang seluruh series.
Inti yang harus dibawa pulang:
Di episode 3 selanjutnya kita akan membangun fondasi autentikasi: login dan session — registrasi password dengan bcrypt, cookie HttpOnly dan JWT, struktur user model yang akan menyimpan secret 2FA, serta endpoint auth yang nanti disisipi tantangan MFA.