How do 2FA codes work? TOTP explained
By the Novaz team · Updated October 11, 2026 · Novaz steps checked against version 1.0.3
An authenticator app and a website each calculate the same 6-digit number from two things they both have: a secret key they shared once, and the current time. Nothing is sent between them when you sign in. That's why the code works with no signal, and why it changes every 30 seconds. The method is called TOTP, for time-based one-time password, and it's an open standard (RFC 6238).
Ingredient 1: the setup key
When you turn on 2FA, the website makes a random secret and shows it to you as a QR code, with a text version called the setup key. Your app saves it. From then on, the website and your app both have the same key, and nobody else should.
The QR code is just a link in a standard format, otpauth://totp/…, holding the key, the service name, your account name, and optionally the settings described below. Our 2FA QR code decoder shows what's inside one.
Ingredient 2: the time, counted in steps
Both sides take the current time as seconds since 1 January 1970 (Unix time) and divide it by 30, dropping the remainder. The result is a whole number that goes up by one every 30 seconds. RFC 6238 calls it T, and recommends "a default time-step size of 30 seconds".
Mixing them into a code
The app runs the key and T through HMAC, a cryptographic function, usually with SHA-1. Then it cuts the result down to a short number, using a method defined in the HOTP standard (RFC 4226, section 5.3):
- The last 4 bits of the HMAC result pick a position in it.
- The app takes 31 bits from that position, as a number.
- It keeps the last 6 digits of that number. Some services use 8.
Change the key or the time step and you get a completely different code. You can't work backwards from a code to the key.
Try it: a live TOTP code
Calculated in your browser from an example key. Don't paste a real setup key here.
Current code
…
Previous: …
Next: …
- Unix time
- …
- Time step T = ⌊time ÷ 30⌋
- …
- Seconds left
- …
Why the website accepts it
The website does the same calculation with its copy of the key and its own clock. If the numbers match, you're in. Two details make this work in practice:
- A little slack for delay. A code you typed at the end of a step might arrive in the next one. RFC 6238 recommends that "at most one time step is allowed as the network delay", so many websites also accept the code just before the current one.
- Each code works once. The standard says a server "MUST NOT accept the second attempt" of a code it has already accepted. NIST's US guidelines say the same: verifiers "SHALL accept a given OTP only once while it is valid" (NIST SP 800-63B-4).
That's also why a wrong clock breaks codes. If your iPhone is a minute off, it's calculating the code for a different time step from the website's. See Authenticator code not working?
Settings a service can change
The standards allow some variation, and a QR code can carry it:
| Setting | Usual value | Also allowed |
|---|---|---|
| Hash | SHA-1 | SHA-256, SHA-512 (RFC 6238, section 1.2) |
| Digits | 6 | 7 or 8 (RFC 4226, section 5.3) |
| Interval | 30 seconds | Other lengths |
If a service uses something unusual, add it by scanning the QR code rather than typing the key, so your app gets the settings too.
TOTP and HOTP
HOTP, the older standard, uses a counter that goes up each time you ask for a code, instead of the time. TOTP is HOTP with the time step as the counter: "TOTP = HOTP(K, T)". Most websites use TOTP today.
What TOTP doesn't protect against
Because you type the code yourself, a convincing fake sign-in page can ask for it and use it straight away. NIST says it plainly: "OTP authentication is not phishing-resistant". Passkeys and security keys are. More in Authenticator app vs SMS.
In Authenticator App - Novaz
Novaz makes standard TOTP codes on your iPhone:
- From a QR code: SHA-1, SHA-256 or SHA-512; 6 to 8 digits; intervals from 1 to 300 seconds.
- From a key typed by hand: always 6 digits, 30 seconds and SHA-1.
It doesn't make HOTP codes. It also checks your iPhone's clock, and warns you when it's 15 seconds or more off.