What is TOTP? A Developer's Guide to Time-Based One-Time Passwords
Understand the RFC 6238 standard under the hood, how TOTP tokens are calculated, and how to implement it securely.
Time-based One-Time Passwords (TOTP) are the foundation of modern multi-factor authentication. RFC 6238 defines the algorithm that generates those familiar 6-digit codes changing every 30 seconds. In this guide, we'll look at how the TOTP algorithm works under the hood and how developers can handle it securely.
The Math Behind TOTP
TOTP is an extension of HMAC-Based One-Time Passwords (HOTP, defined in RFC 4226). While HOTP uses an incrementing counter as the moving factor, TOTP replaces the counter with a time-step derived from the current Unix epoch time.
The core formula is:
TOTP = Truncate(HMAC-SHA-1(K, T))
Where:
- K is the shared secret key (usually shared via a QR code in Base32 encoding).
- T is an integer representing the number of time steps that have elapsed since the Unix epoch (1970-01-01 00:00:00 UTC). T = floor((CurrentTime - EpochTime) / TimeStep). The default TimeStep is 30 seconds.
Generating TOTP in Node.js
Here is a basic implementation of the TOTP token generation process using Node.js's native crypto module:
const crypto = require('crypto');
const base32 = require('thirty-two'); // to decode Base32 secret
function generateTOTP(secretBase32, timeStep = 30) {
const key = base32.decode(secretBase32);
const epoch = Math.floor(Date.now() / 1000);
const counterValue = Math.floor(epoch / timeStep);
// Buffer representing the 8-byte counter
const buffer = Buffer.alloc(8);
buffer.writeBigInt64BE(BigInt(counterValue));
// Generate HMAC-SHA1 signature
const hmac = crypto.createHmac('sha1', key);
hmac.update(buffer);
const hmacResult = hmac.digest();
// Dynamic Truncation
const offset = hmacResult[hmacResult.length - 1] & 0xf;
const binary = ((hmacResult[offset] & 0x7f) << 24) |
((hmacResult[offset + 1] & 0xff) << 16) |
((hmacResult[offset + 2] & 0xff) << 8) |
(hmacResult[offset + 3] & 0xff);
// Extract 6 digit code
const code = binary % 1000000;
return code.toString().padStart(6, '0');
}
Security Best Practices for Secret Storage
When implementing TOTP systems, the security of the shared secret key (K) is paramount. If a database compromise leaks the secret keys, attackers can generate OTP codes at will. Ensure secrets are encrypted at rest using strong AEAD ciphers like AES-256-GCM and store keys separately from user data tables.
Tired of context-switching to your phone?
Get Authium to securely autofill your 2FA codes directly inside your active browser tab. Free for up to 10 accounts.
