TOTP (Time-Based One-Time Password) is a one-time password algorithm that uses the current time as a variable. It is commonly used in two-factor authentication (2FA) systems. TOTP generates a numeric code that changes every 30 seconds and is based on a shared secret between the client and the server.
- Defined in RFC 6238
- Based on HOTP (HMAC-based One-Time Password, RFC 4226)
- Shared Secret: A Base32-encoded key is shared between the server and the client (user).
- Time Counter: Unix time is divided by a fixed interval (usually 30 seconds) to get a moving counter.
counter = floor(current_unix_time / time_step)
- HMAC Generation: HMAC-SHA1 is applied using the shared secret and the counter.
- Dynamic Truncation: Extracts a 4-byte dynamic binary code from the HMAC result.
- Modulo Operation: Converts to a 6-digit code using modulo:
OTP = (truncated_value) % 10^6
TOTP secrets can be encoded into a URL and represented as a QR code for use in authenticator apps.
otpauth://totp/{ISSUER}:{ACCOUNT}?secret={SECRET}&issuer={ISSUER}&digits=6&period=30
otpauth://totp/DevApp:[email protected]?secret=JBSWY3DPEHPK3PXP&issuer=DevApp
- issuer: Name of the service or app (URL-safe)
- account: Typically user's email or username
- secret: Shared secret (Base32-encoded)
- digits: Number of digits in the TOTP (default is 6)
- period: Time step duration in seconds (default is 30)
- Google Authenticator
- Microsoft Authenticator
- Authy
- 1Password
- FreeOTP
- Aegis (Android)
- Server generates a shared secret and encodes it in a TOTP URI.
- User scans the QR code containing the URI using an authenticator app.
- App generates a new TOTP code every 30 seconds.
- User enters the code during authentication.
- Server verifies the TOTP code using the shared secret and current time.
import pyotp
import qrcode
totp = pyotp.TOTP("JBSWY3DPEHPK3PXP")
uri = totp.provisioning_uri(name="[email protected]", issuer_name="DevApp")
# Generate QR code image
img = qrcode.make(uri)
img.save("totp_qr.png")
qrencode -o totp.png "otpauth://totp/DevApp:[email protected]?secret=JBSWY3DPEHPK3PXP&issuer=DevApp"
import pyotp
totp = pyotp.TOTP("JBSWY3DPEHPK3PXP")
if totp.verify("123456"):
print("β
Token is valid")
else:
print("β Invalid token")
const speakeasy = require("speakeasy");
const verified = speakeasy.totp.verify({
secret: "JBSWY3DPEHPK3PXP",
encoding: "base32",
token: "123456",
window: 1 // allows +/- 1 time step
});
console.log(verified ? "β
Token is valid" : "β Invalid token");
To tolerate slight clock differences between server and client, a window parameter is used:
- window = 1 allows one time-step before and after the current time.
Examples:
pyotp.TOTP(...).verify(token, valid_window=1)
speakeasy.totp.verify({ ..., window: 1 })
Even though Google Authenticator shows a 30-second countdown, the same TOTP code may still be accepted by the server after the timer resets. This is due to a concept called time window (or skew tolerance).
TOTP tokens are generated based on time intervals:
TOTP = HMAC(secret, floor(current_unix_time / 30))
But due to potential clock skew between the client (user device) and the server, most implementations allow a small time window around the current 30-second step:
T - 1
β previous time step (up to 30 seconds before)T
β current time step (30 seconds)T + 1
β next time step (up to 30 seconds after)
A code may be valid for up to 90 seconds if the server allows a Β±1
window.
speakeasy.totp.verify({
secret: user.secret,
encoding: 'base32',
token: '123456',
window: 1 // Allows current, previous, and next time steps
});
If you want to make tokens valid for only 30 seconds:
window: 0
But this is not recommended in real-world apps due to:
- Minor time drift between server and client
- Network delay
Scenario | Recommended window |
---|---|
Production Login Flow | 1 (Β±30s tolerance) |
High-Security Operation | 0 (strict match) |
Debugging | 1 or higher |
To ensure better accuracy, synchronize your server clock using NTP (Network Time Protocol) services. If needed, compare client and server timestamps during debugging.
- π Store shared secrets securely (use KMS, encrypted storage)
β οΈ Never log TOTP secrets or OTP codes- π« Don't transmit secrets in clear text
- π Use secure time synchronization on both server and client
- π Rotate secrets when a device is lost or reset
- π Implement retry limits and lockout policies
- β Always enforce HTTPS for QR provisioning and verification endpoints
Tool | Purpose |
---|---|
pyotp |
TOTP generation/verification (Python) |
speakeasy |
TOTP/HOTP support (Node.js) |
qrcode |
Generate QR codes in Python |
qrencode |
Generate QR codes via CLI |
Aegis/Authy | Mobile apps for testing TOTP |
Yes β TOTP backup codes are strongly recommended, even though they are not part of the TOTP standard itself.
TOTP is tied to a shared secret stored in an authenticator app. If you:
- Lose your phone
- Reset or uninstall the app
- Upgrade devices without exporting TOTP secrets
π You lose access to your account unless:
- You exported the secret key beforehand
- OR you have backup codes
- One-time use codes
- Typically 8β12 characters long
- Provided when setting up TOTP
- Can be used as a fallback when you canβt access your authenticator app
Your backup codes:
1. 4KJF-2D8A
2. XZPR-7A9T
3. 9QWE-LM4Z
...
(Each code can be used only once)
- User sets up TOTP 2FA
- Server generates a list of backup codes
- User saves these codes securely
- If TOTP is unavailable:
- User selects βUse backup codeβ
- Enters one of the unused codes
- The code is validated and marked as used
Practice | Recommendation |
---|---|
Generate only once (or reset) | β Yes |
Allow regeneration (invalidate old) | β Yes |
Store as hashes (not plaintext) | β Yes |
Limit attempts per code/login | β Yes |
Display/download securely | β Yes |
Recovery Method | Security Level | Usability |
---|---|---|
Backup Codes | β High | β Easy |
SMS fallback | β Low | β Easy |
Email-based recovery | β Medium | β Medium |
Recovery key (manual) | β High | |
Device-based recovery | β High | β Medium |
While not mandatory in TOTP specs, backup codes are a critical recovery tool to prevent users from getting locked out of their accounts. They also reduce helpdesk load and improve user experience in secure environments.