Skip to main content
Technology

How TOTP Works - The 6-Digit Code Behind Google Authenticator

What TOTP Is - One-Time Codes That Change Every 30 Seconds

Time-based One-Time Password (TOTP) is the algorithm behind Google Authenticator, Authy, Microsoft Authenticator, and 1Password. The server and the user's device share a secret key, and both compute a 6-digit number by combining that key with the current time. The number changes every 30 seconds, and successful login requires the user to type whatever digits are valid in the current window. RFC 6238 standardized this in 2011, ensuring vendor-neutral interoperability.

TOTP's biggest practical advantage is that it works without any network connectivity once the secret is shared. The server does not need to send anything to the device, and the device does not need to query the server. This makes TOTP cheaper to operate at scale than SMS-based one-time passwords, while sidestepping the SIM-swapping attacks that plague SMS authentication.

HOTP - The Counter-Based Predecessor

TOTP is built on top of HMAC-based One-Time Password (HOTP), defined in RFC 4226. HOTP uses a counter and a secret key, hashing them with HMAC-SHA-1 and extracting a 6-digit number from the result. Each successful login increments the counter on both sides, so the next code is different. HOTP works without any time source, which makes it suitable for offline hardware tokens with no clock.

The downside of HOTP is counter desynchronization. If a user opens the authenticator app multiple times without using the codes, the device's counter advances while the server's stays put. The server must therefore look ahead a few values to find a match, which adds operational complexity. TOTP solves this elegantly by replacing the counter with the current time divided into 30-second buckets.

HOTP against TOTP - where the counter comes from
AspectHOTPTOTP
SpecificationRFC 4226RFC 6238 (standardized in 2011)
What the counter isA code counter. The device increments it each time it generates a code; the server increments it on each successful loginUnix time divided by 30. It advances once every 30 seconds
Clock requiredNo - works on hardware tokens that have no clock at allYes - both the device and the server must know the correct time
How it falls out of syncGenerating codes without using them advances only the device's counterClock drift on the device or on the server
RecoveryThe server looks ahead a few counter values to find a matchRe-synchronize the clock

Hashing the shared secret with HMAC-SHA-1 and extracting six digits is identical in both schemes. The only difference is whether the counter handed to HMAC is built from a count of logins or from the clock, so TOTP reuses the HOTP computation unchanged.

The TOTP Algorithm - 30-Second Time Steps

TOTP computes the counter as the current Unix timestamp divided by 30 (integer division). For example, at Unix time 1716000000 (May 18, 2024 at 02:40 UTC), the counter is 57200000. That counter is fed into the HOTP algorithm with the shared secret, producing a 6-digit number. Thirty seconds later, the counter becomes 57200001 and a completely different number is generated.

The 30-second step size from RFC 6238 balances usability and security. Too short and users cannot type the code in time; too long and brute-force attempts have a wider window to succeed. Servers commonly accept the previous and next 30-second windows as well, giving roughly 90 seconds of total tolerance. Different services tune this slightly: Steam Guard uses a 30-second step but with custom tweaks, while some enterprise tokens use 60 seconds.

The five steps that produce six digits - from shared secret and clock to a number
  1. 1

    Exchange the shared secret once - The QR code shown when you turn on two-factor authentication is an otpauth URI carrying a Base32 secret. After that, the secret never travels over the network again.

  2. 2

    Divide Unix time by 30 - The quotient is the counter. At Unix time 1716000000 (May 18, 2024 at 02:40 UTC) the counter is 57200000.

  3. 3

    Hash the counter and the secret with HMAC-SHA-1 - The output is a 20-byte digest. This step is exactly the same as in HOTP.

  4. 4

    Cut four bytes out of the digest - The low four bits of the final byte point at the offset to read from, and the top bit of those four bytes is dropped to leave a 31-bit positive integer.

  5. 5

    Show the remainder modulo 1000000 as six digits - Missing places are padded with zeros. Thirty seconds later the counter is 57200001 and an entirely different six digits appear.

The server walks the same five steps itself and compares its own six digits with the ones you typed. Most implementations also accept one step either side (about 90 seconds in total), and that tolerance is what absorbs small clock differences between device and server. The otpauth URI also defines a digits parameter that can request eight digits, though client support for anything other than six is limited.

The Secret Inside the QR Code - The otpauth URI

When you enable two-factor authentication, the QR code on screen encodes an otpauth:// URI containing the shared secret. A typical URI looks like otpauth://totp/Example:alice@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Example. The secret is Base32-encoded (Base32 is used because it avoids characters that look alike in display fonts). Authenticator apps parse this URI and store the secret locally.

Anyone who photographs the QR code can clone the OTP generator, so the setup screen should be displayed only once and never during a screen-sharing session. Most services pair TOTP setup with a set of one-time backup codes. Each backup code is a single-use long random string that bypasses TOTP, useful when a phone is lost. Storing backup codes in a separate password manager or printed in a safe place is the standard practice.

Time Drift - The Most Common Cause of Failure

When TOTP authentication fails, the cause is usually that the server and device disagree on the current time. Servers typically synchronize to UTC via NTP, but a phone with manually adjusted time, or one that just returned from international travel with confused settings, can drift by a minute or more. Google Authenticator used to offer a "Time correction" setting in the app, but it was removed in version 7.0; the app now simply uses the time setting of the operating system. On the device side, the fix is to leave automatic time synchronization enabled in the OS.

Server-side time drift is just as risky. Containers running without proper NTP sync can drift tens of seconds, especially in nested virtualization. Drift on the server is particularly awkward because it is not limited to a few unlucky users - it surfaces as every user being locked out at once. For systems implementing TOTP, regular NTP sync and alerting on time-sync failures are non-negotiable operational requirements.

Implementation Notes - SHA-1 to SHA-256, Replay Protection

TOTP defaults to HMAC-SHA-1, which RFC 6238 considers adequate for OTP use cases. Newer implementations may select SHA-256 or SHA-512 via the algorithm parameter in the otpauth URI. Client compatibility is the catch: Google Authenticator ignores the algorithm parameter altogether and treats the secret as SHA-1, so most services stay on SHA-1 for interoperability. For greenfield implementations targeting modern authenticator apps only, SHA-256 is a reasonable upgrade.

Servers must also prevent replay attacks. If two login attempts arrive within the same 30-second window, only the first should succeed; otherwise, an attacker who captured a code could reuse it. Storing the most recent successfully used counter value per user and rejecting any equal or smaller value is the canonical defense. Six digits is the standard length, and while the digits parameter allows eight to be requested, client support is limited enough that few services adopt it.

XB!LINE

Was this article helpful?

Related Articles