What is TOTP? A short guide for developers (RFC 6238 explained)
What is TOTP (Time-based One-Time Password)? A concise RFC 6238 explanation for developers with code examples (Node, Python, Go), troubleshooting tips, and a free online TOTP tool.
tl;dr — TOTP (Time-based One-Time Password, RFC 6238) is a 6-digit code that refreshes every 30 seconds, computed from a shared Base32 secret plus the current time using HMAC-SHA1. The server and the user’s authenticator app independently produce the same code as long as their clocks agree — no network call, no state, just a shared secret and a clock.
TOTP (Time-based One-Time Password) is a simple, widely used method for generating short-lived numeric codes from a shared secret and the current time (RFC 6238). Typical use: 6-digit codes that refresh every 30 seconds. This guide explains how it works, common pitfalls, and shows quick examples in Node, Python, and Go. Try the live TOTP generator.
What is TOTP
TOTP stands for Time-based One-Time Password. It uses a shared secret (usually a Base32 string) and the current time to create short numeric codes that expire quickly — commonly every 30 seconds. TOTP is standardized by IETF RFC 6238 and is the mechanism behind most authenticator apps (Google Authenticator, Authy, Authgear TOTP Generator, etc.).
How TOTP works
- Shared secret — The server and client (authenticator) agree on a shared secret when 2FA is set up. That secret is often encoded in Base32.
- Time step — The current Unix time is divided by a timestep (typically 30 seconds) to create a moving counter.
- HMAC — The server and client compute an HMAC over the counter using a chosen hash algorithm (SHA-1, SHA-256, or SHA-512).
- Truncation — A dynamic truncation step extracts a numeric code of fixed length (commonly 6 digits).
- Validation — On login, the server computes the expected TOTP(s) and checks whether the user-provided code matches (often allowing ±1 timestep for clock skew).
In short:
TOTP = Truncate(HMAC(secret, floor(currentTime / timestep))) % 10^digits.
Important parameters
- Secret format: Base32 (alphanumeric, e.g.,
JBSWY3DPEHPK3PXP). - Timestep (period): Usually 30 seconds (RFC 6238 recommended).
- Digits: Usually 6, sometimes 8. 6 balances usability & security.
- Algorithm:
SHA-1(most compatible),SHA-256orSHA-512(stronger hashes if supported by both ends).
Quick code examples
Replace SECRET_BASE32 with your Base32 secret. These examples use standard, well-maintained libraries.
Node.js (otplib)
import { authenticator } from 'otplib';
// Server: generate a secret on enrolment
const secret = authenticator.generateSecret(); // Base32
// Client/server: generate the current code
const token = authenticator.generate(secret);
console.log(token); // e.g. "492039"
// Server: verify a code submitted by the user
const isValid = authenticator.verify({
token: '492039',
secret,
// window: 1, // optional ±1 step tolerance
});
otplib defaults to RFC 6238 standard parameters — 30-second step, 6 digits, SHA-1 — which match every major authenticator app.
Python (pyotp)
import pyotp
# Server: generate a secret on enrolment
secret = pyotp.random_base32()
# Client/server: generate the current code
totp = pyotp.TOTP(secret)
print(totp.now()) # e.g. "492039"
# Server: verify a code submitted by the user
ok = totp.verify("492039", valid_window=1) # ±1 step tolerance
pyotp also generates an otpauth:// provisioning URI (totp.provisioning_uri(name=email, issuer_name="My App")) which you can render as a QR code for users to scan with Google Authenticator.
Go (pquerna/otp)
package main
import (
"fmt"
"time"
"github.com/pquerna/otp/totp"
)
func main() {
// Server: generate a secret on enrolment
key, _ := totp.Generate(totp.GenerateOpts{
Issuer: "My App",
AccountName: "alice@example.com",
})
// Client/server: generate the current code
code, _ := totp.GenerateCode(key.Secret(), time.Now())
fmt.Println(code) // e.g. "492039"
// Server: verify a code submitted by the user
valid := totp.Validate("492039", key.Secret())
fmt.Println(valid)
}
pquerna/otp returns a *otp.Key whose URL() method produces a Google-Authenticator-compatible otpauth:// URI for QR rendering.
Common pitfalls & troubleshooting
- Clock skew — TOTP depends on accurate time. If codes “don’t match”, sync the server and authenticator clock (NTP) or allow a verification window of ±1 timestep.
- Wrong secret format — Ensure the secret is Base32 and remove spaces. If you have a QR
otpauth://URL, extractsecret=. - Algorithm/digit mismatch — Server and client must use the same
algorithm(SHA-1/256/512) anddigits(6/8). Mismatches are a very common source of failures. - Using production secrets in shared tools — Don’t store production secrets in online tools. Use local/offline generators or internal test secrets.
Security considerations
- TOTP is something you have (the shared secret) — it’s effective against remote password-only attacks but can be bypassed if the secret is stolen.
- Use TOTP as part of a multi-factor auth approach, combine with secure server-side policies (rate limiting, anomaly detection).
- Prefer SHA-256/512 if you control both sides and want a stronger HMAC than SHA-1 — but keep compatibility in mind.
When to use TOTP
- Good: human logins, admin access, developer test flows, internal tools.
- Not great: high-risk unattended API access (use client certificates, OAuth tokens, or hardware-backed keys for stronger guarantees).
- Alternative MFA methods: push-based MFA, FIDO2/WebAuthn i.e. Passkeys (phishable-resistant), hardware tokens.
How to test your integration
- Extract the shared secret from the provisioning flow (Base32).
- Generate a TOTP locally using one of the library examples above.
- Verify server-side acceptance with a small window (±1 step) for clock skew.
- Test algorithm/digits mismatch scenarios intentionally to confirm your server logs clear errors.
- Use a test-only tool to preview codes without exposing production secrets — try: Authgear TOTP Authenticator: /tools/totp-authenticator
FAQ
Q: What is the standard TOTP timestep?
A: 30 seconds (RFC 6238 recommends 30s).
Q: How many digits should I use?
A: 6 digits is standard; 8 digits adds entropy but reduces usability.
Q: Is TOTP secure?
A: TOTP is secure against many attacks when secrets are kept safe and used with additional controls (rate limits, device binding). For highest security, consider FIDO2/Passkeys where appropriate.