TOTP Generator ยท 5 min read
The 30-Second Window: Why TOTP Codes Work the Way They Do
Why TOTP codes refresh every 30 seconds, why servers usually accept the previous code, and how clock drift, time zones, and replay protection shape the design.
Every authenticator app โ Google Authenticator, Authy, 1Password โ shows a 6-digit code that expires every 30 seconds. The number isn't arbitrary. It's the result of a tradeoff between attacker brute-force budget, real-world clock drift, and human typing speed. Once you understand the constraints, the design choices in RFC 6238 stop looking magic and start looking obvious.
What the Code Actually Is
A TOTP code is the last six digits of HMAC-SHA1(secret, T), where T is the current Unix time divided by 30. The shared secret was set up when you scanned the QR code; both your phone and the server have a copy. Both compute the same HMAC and truncate to six digits. As long as your phone's clock and the server's clock both round to the same 30-second slot, the codes match.
Why Time Instead of a Counter
TOTP's predecessor, HOTP (RFC 4226), used a counter: every time you generated a code, the counter ticked up by one. The problem was synchronisation. If your authenticator generated three codes you didn't use (you opened the app, looked, closed it), your counter was three ahead of the server's. Servers had to sweep a window of upcoming counter values to find a match, and the window had to be small enough that an attacker couldn't exhaust it.
Time fixes this implicitly. Both sides agree what time it is (more or less), so there's no drift to track. The cost is that the "counter" now ticks even when nobody is using it โ which means an attacker has a fixed budget of 30 seconds per code to brute-force the 1-in-a-million guess.
Why 30 Seconds, Not 60 or 10?
RFC 6238 calls 30 seconds the recommended default but allows other intervals. The number is a balance:
- Too short (10s): Users frequently fail to type the code before it rolls over. Clock drift becomes a bigger fraction of the window.
- Too long (5 min): An attacker who shoulder-surfs or intercepts a code has too long to use it. The brute-force budget grows linearly with window length.
- 30 seconds: Long enough for a human to read, type, and submit. Short enough that an attacker who steals the code in transit has only a small replay window.
At 30 seconds with 6 digits (1,000,000 possible codes), an online brute-force attacker gets roughly 30 attempts per second per code if they have unlimited bandwidth โ which is why every TOTP server should rate-limit failed attempts. Five wrong codes in a row, lock the account for a minute. That single rule turns the math from "eventually crackable" to "astronomically improbable."
The ยฑ1 Window Trick
In practice, servers don't accept only the current code. They accept the previous one and sometimes the next one too โ usually a window of ยฑ1 step (ยฑ30 seconds). The reason is clock drift. Phones and servers don't synchronise perfectly: a phone's clock might be 15 seconds behind the server's, and a code generated "now" on the phone might land in the previous slot on the server.
Accepting ยฑ1 step gives you 90 seconds of effective tolerance for a 30-second nominal window โ enough to absorb most realistic clock skew and most human typing latency. It also triples the attacker's brute-force budget per code, which is why rate-limiting matters so much.
Time Zones Don't Matter (And Why That's Worth Stating)
The time used in TOTP is Unix time โ seconds since 1970-01-01 UTC. It's the same number everywhere on Earth at the same instant. A phone in Tokyo and a server in Virginia both see the same Unix time, even though their wall clocks read 14 hours apart. Time zones are a display concern; TOTP doesn't care.
What does break TOTP is a wrong system clock. If your phone's "automatic time" is off and you're manually setting it five minutes fast, every code will fail. The fix is always the same: enable network-synced time. NTP (RFC 5905) keeps most consumer devices within a few hundred milliseconds of true time, well inside the ยฑ1-step tolerance.
Why Codes Don't Repeat (Within Practical Limits)
The output is six digits, so there are only a million possible codes. Eventually, by pigeonhole, codes repeat. With a fresh time slot every 30 seconds, you'd expect a duplicate within a few months purely by chance. That's fine โ what matters is that adjacent codes (within an attacker's window) aren't predictable from each other. Because the input to HMAC changes by exactly one each step and HMAC outputs are pseudorandom, knowing one code tells you nothing useful about the next.
The Replay Defence
A used code should not be accepted twice, even within its 30-second window. NIST SP 800-63B requires this explicitly. Implementations enforce it by storing the last successfully-used time step per user and rejecting any code from that step or earlier. Without this rule, an attacker who shoulder-surfs a code has the rest of the window to use it; with it, the window collapses to zero the moment the legitimate user logs in.
The Whole Design in One Sentence
TOTP is HOTP with the counter replaced by "current time, rounded to a window large enough to absorb clock drift and small enough to limit replay." Every other detail โ the ยฑ1 acceptance, the rate limit, the replay rule, the Unix-time choice โ falls out of that one tradeoff. Thirty seconds was the right answer in 2011 and it's still the right answer in 2026.
References
- M'Raihi, D., Machani, S., Pei, M., & Rydell, J. (2011). RFC 6238: TOTP: Time-Based One-Time Password Algorithm. Internet Engineering Task Force.
- M'Raihi, D., Bellare, M., Hoornaert, F., Naccache, D., & Ranen, O. (2005). RFC 4226: HOTP: An HMAC-Based One-Time Password Algorithm. Internet Engineering Task Force.
- Mills, D. L. (2010). RFC 5905: Network Time Protocol Version 4. Internet Engineering Task Force.
- National Institute of Standards and Technology. (2017). NIST Special Publication 800-63B: Digital Identity Guidelines.