Every login code you've ever typed - The six digits from an authenticator app, the number texted by your bank, the code on a keychain token from 2005 - Belongs to one family: the one-time password. The acronyms get muddled because they nest inside each other. OTP is the family name, covering any password valid for a single use. HOTP and TOTP are the two standardized ways of generating those codes, and the difference between them comes down to one question: what makes the code change - A counter, or a clock?
Understanding that difference isn't trivia. It explains why authenticator codes work in airplane mode, why a code sometimes fails when your phone's clock drifts, why SMS codes stay valid for ten minutes while app codes die in 30 seconds, and why "one-time" doesn't automatically mean "phishing-proof."
OTP: the umbrella term
A one-time password is any credential designed to authenticate exactly once. That single-use property kills the classic replay attack: a static password observed once is compromised forever, but an observed OTP is worthless moments later. OTPs arrived decades before smartphones - Leslie Lamport proposed a hash-chain scheme in 1981, S/KEY implemented it in the early '90s, and banks mailed printed TAN code sheets long before apps existed.
Delivery and generation are separate questions, and conflating them causes most of the confusion. An SMS code is an OTP that a server generated randomly and delivered over the phone network. An authenticator code is an OTP that your device computed locally from a shared secret, no network needed. Both are one-time passwords; their security properties differ enormously, as our comparison of TOTP versus SMS 2FA details - The delivery channel, not the code itself, is usually the weak point.
HOTP: codes from a counter
HOTP - HMAC-based One-Time Password, standardized in RFC 4226 back in 2005 - Was the OATH industry group's first open algorithm. It needs two ingredients: a shared secret key (established once, at enrollment) and a counter that both sides track.
The recipe: feed the secret and the current counter value into HMAC-SHA-1, a keyed hash function. Out comes 20 bytes of pseudorandom data. A step called dynamic truncation selects four of those bytes, and a modulo operation reduces them to the familiar six digits. Press the button on a hardware token, the counter increments, a fresh code appears. The server runs the identical computation and compares. (If keyed hashing is new to you, our explainer on password hashing with MD5, SHA-256 and bcrypt covers the underlying machinery; you can also poke at plain hashing directly with our SHA-256 generator.)
HOTP's quirk is counter desynchronization. Press the token's button five times in a drawer and your counter runs ahead of the server's; servers cope with a "look-ahead window," checking the next several counter values. But an HOTP code has no expiry at all - It stays valid until used or superseded, which widens the window for an intercepted code to be replayed.
TOTP: codes from the clock
TOTP - Time-based One-Time Password, RFC 6238, published in 2011 - Makes one elegant substitution: replace the counter with the current time. Take the Unix timestamp, divide by a time step (30 seconds is standard), and use that number as the counter in the very same HOTP computation.
Every 30 seconds the timestep number changes, so the code changes. Both sides just need reasonably accurate clocks - No synchronization protocol, no button presses, no network. That's why authenticator apps work in airplane mode, and why a phone with a badly drifted clock generates codes the server rejects. Servers typically accept one timestep either side to absorb minor drift and typing delays.
Rule of thumb: HOTP counts, TOTP clocks. Same secret, same HMAC, same six digits - The only difference is what number goes into the hash.
TOTP's built-in expiry is its killer feature. A shoulder-surfed or intercepted code dies within a minute at most, versus HOTP's indefinite validity. That, plus zero moving parts on the user side, made TOTP the standard behind essentially every authenticator app. Every code in Google Authenticator, Authy, 2FAS, or any of the apps in our authenticator app comparison is plain RFC 6238 TOTP - Which is why they're all interchangeable. You can watch the algorithm run live with our in-browser TOTP code generator: paste in a Base32 secret and it computes the same codes your phone would, entirely client-side.
The three variants side by side
| HOTP (RFC 4226) | TOTP (RFC 6238) | Server-generated OTP (SMS/email) | |
|---|---|---|---|
| Moving factor | Event counter | Current time (30s steps) | None - Random per request |
| Code lifetime | Until used (indefinite) | ~30–90 seconds | Minutes (server policy) |
| Works offline on device | Yes | Yes | No - Needs delivery channel |
| Failure mode | Counter desync | Clock drift | Interception, SIM swap |
| Typical form | Button hardware tokens | Authenticator apps | Text messages, emails |
| Replay window | Widest | Narrowest | Middle |
What one-time codes can and can't defend
All three variants demolish the attacks that make static passwords such a liability: replay, credential stuffing with breached password lists, and the everyday disaster of password reuse. An attacker holding last year's leaked database gets nothing from an account that also demands a fresh code.
What none of them stop is real-time phishing relay. A fake login page can ask for your password and your current TOTP code, forward both to the genuine site within the 30-second window, and log in as you while you stare at a fake error message. Modern phishing kits automate this completely. The 30-second expiry narrows the attacker's window; it doesn't close it. Only origin-bound cryptography - FIDO2 hardware security keys and passkeys, where the browser proves which site is asking - Actually removes the human from the loop. NIST SP 800-63B draws exactly this line, classing OTPs as strong authenticators but reserving "phishing-resistant" for the origin-bound methods.
Notes for builders
If you're implementing codes in your own product, the standards do the heavy lifting, but the details matter: generate secrets with a cryptographically secure RNG, store them encrypted (they're symmetric secrets, not hashable like passwords), rate-limit verification aggressively (a six-digit space is only a million codes), accept exactly one adjacent timestep, and reject reuse of a just-accepted code. Provisioning uses the de facto otpauth:// URI in a QR code. Or skip the sharp edges entirely - Our 2FA API exposes enrollment, TOTP verification, and rate limiting as endpoints, so your login flow gets standards-compliant codes without you touching HMAC internals. Either way, ship recovery codes alongside enrollment from day one; locked-out users are the operational cost of OTP done carelessly.
FAQ
Is an SMS code HOTP or TOTP?
Usually neither. Most SMS and email codes are simply random numbers the server generated for that specific request and stored until you type them back - No shared secret, no HMAC, no standard. The OATH algorithms exist precisely so a device can compute codes without the server sending anything.
Why do my authenticator codes sometimes get rejected?
Almost always clock drift: TOTP depends on your device and the server agreeing what time it is. Check that the phone's date and time are set automatically; Google Authenticator even has a built-in time-sync option. The other common cause is simple typing lag - A code entered in its final seconds may expire in transit, so wait for a fresh one.
Is HOTP obsolete?
Mostly, for consumer use - TOTP's expiry and maintenance-free operation won. HOTP survives in button-press hardware tokens without clocks, some banking dongles, and YubiKey OTP slots. It remains a valid, secure standard; it just lost the deployment war to the clock.
Are six digits enough? Couldn't someone just guess the code?
The six-digit space (one million possibilities) is safe only because servers throttle attempts and codes expire - A handful of guesses per window against a million options is a hopeless attack. RFC 6238 supports eight-digit codes for the cautious. The codes' entropy is not the weak point; unthrottled verification endpoints and phishing relays are.
Do one-time codes replace my password?
In standard 2FA they supplement it: the password proves something you know, the code proves you hold the enrolled device. Some services now use OTPs as the sole login step - Closer to magic-link flows - Which trades password risks for total dependence on the code channel. For anything valuable, two distinct factors remain the stronger posture.