9 min readBy the DependsiT team

What TOTP is and how those six-digit codes work

A plain-language guide to TOTP: how time-based one-time passwords are generated, what an otpauth link contains, and why the codes work with no internet.

Cover card for What TOTP is and how those six-digit codes work

Scan a QR code once, and from that moment your phone shows a six-digit code that changes every thirty seconds. Bank logins, email accounts, game platforms, git hosts: the same pattern is everywhere, and in almost every case the code you are typing is a TOTP.

TOTP is the quiet workhorse of two-factor authentication: an open standard since 2011, implemented by every serious authenticator, owned by no vendor. This post explains what TOTP is, how the codes are generated, what the QR code actually contains, and why it all works with no network connection. For the broader picture, start with our guide to what 2FA is and why it matters; this one goes deep on the codes themselves.

What TOTP is

TOTP stands for Time-based One-Time Password. If you came here asking what TOTP is, the short answer: two parties that share one secret each compute the same short code from that secret plus the current time, so typing the code proves you hold the secret without the secret ever being sent.

The specification is RFC 6238, published by the Internet Engineering Task Force in May 2011. Being an open standard matters: because the algorithm is public and unowned, a secret issued by one service works in any app that supports TOTP. Generate codes in one authenticator today, export the entries, continue in a different app tomorrow — nothing is tied to a vendor or a phone number.

A TOTP is one member of the OTP family — one-time passwords. Its counter-based cousin HOTP is covered below. When a login page offers "authenticator app" as a second factor, TOTP is what runs underneath.

How the codes are generated

Everything rests on one shared secret, established at setup. When you enable TOTP on a website, the site generates a random secret — at least 128 bits, with 160 recommended — encodes it in base32 so a human could retype it, and delivers it to your app as a QR code or a text link beginning otpauth://. From then on:

  1. Your app and the server both hold the same secret.
  2. Both divide the current Unix time by 30 and discard the remainder. The result is a counter that advances every 30 seconds.
  3. Both run that counter through an HMAC — a keyed hash, RFC 2104, by default with SHA-1 — using the shared secret as the key.
  4. Both take a fixed slice of the hash output and truncate it to six digits.
  5. You read the code TOTP produced and type it in. The server, holding the same secret and reading the same clock, computed the same number.

No code is ever transmitted in advance. The server verifies by recomputing, which means your application can generate TOTP codes in airplane mode: the code and the secret never travel together, and the secret never travels again after setup.

Each code is valid for its 30-second window plus whatever grace the server allows, and an accepted code is rejected on reuse. An attacker who glimpses one code over your shoulder has seconds, at best, to replay it.

Anatomy of an otpauth link

The QR code you scan at setup is not a picture of the secret. It is a URL in a compact de facto format carrying everything an authenticator needs. Here is a complete example, in the style of Google's Key URI Format documentation:

otpauth://totp/Acme:alice%40example.com?secret=JBSWY3DPEHPK3PXP&issuer=Acme&algorithm=SHA1&digits=6&period=30

Piece by piece:

  • Scheme and type: otpauth://totp/ for time-based codes, otpauth://hotp/ for counter-based ones.
  • Label: Acme:alice@example.com — the entry's name, by convention the issuer, a colon, and the account name.
  • secret: the base32-encoded key itself. The only required parameter, and the only part that must stay private.
  • issuer: the TOTP issuer — the service the entry belongs to. A well-formed link carries it twice, in the label prefix and as this parameter. The "Acme" your app shows above the moving digits came from here.
  • algorithm: the HMAC variant — SHA1 by default, with SHA256 and SHA512 as options.
  • digits: the code length, six by default; seven and eight are valid.
  • period: the window length in seconds, 30 by default.

The defaults are so universal that many links ship only the secret and the issuer. The full parameter set is why one TOTP app can serve every service: unusual configurations are data, not code.

HOTP, the counter-based cousin

TOTP builds on HOTP, the HMAC-based One-Time Password algorithm of RFC 4226, published in 2005. The two are identical except for the counter: TOTP feeds the HMAC a counter derived from the clock; HOTP feeds it a counter that increments by one each time a code is requested.

HOTP still has a niche: devices without a dependable clock, and hardware tokens where a button press reveals the next code. Its weakness is the flip side. A code does not expire on its own — it stays valid until the counter moves past it — so the server must track a look-ahead window, and every code requested without being used widens the gap.

TOTP won for most services because time is a cleaner shared counter than "number of codes requested". Both sides already have clocks, nothing needs resynchronizing after an unused code, and a stolen code ages out in seconds.

Why 30 seconds

The 30-second window is a compromise, and RFC 6238 recommends it as the default. Shorter, and people cannot read and type the code before it changes; a method that fails one attempt in three trains people to give up. Longer, and every intercepted code stays replayable that much longer. Most servers accept the current window and one or two neighbors, buying typing time and clock tolerance. A code is dead within about a minute of appearing on your screen, usually sooner.

Clock drift and time sync

TOTP has exactly one runtime dependency: your device's clock must agree with the server's. It does not need to be accurate, just in agreement. A phone offline for weeks, or with automatic time off, drifts until its counter lands a window or two away, and every code is rejected.

The fix is nearly always the same toggle: enable automatic date and time in your phone's settings, let it sync, and try again. That resolves most "my codes suddenly stopped working" reports. Drift of a few seconds is harmless thanks to neighbor-window acceptance; drift past a minute is fatal.

TOTP versus SMS codes

SMS-based 2FA texts a code to your phone number. TOTP computes the code on your device. The differences compound:

  • No phone number on file. SMS ties your security to an identifier your carrier owns, and carriers can be socially engineered.
  • No SIM swap. The most practical attack on SMS 2FA is moving your number to the attacker's SIM. TOTP has no equivalent: there is nothing at the carrier to move.
  • No network. SMS needs cell reception; TOTP generation is local math.
  • No per-login cost for the service, one reason TOTP is offered everywhere for free.

SMS is still far better than no second factor. Given the choice, the app-based code wins every time.

When hardware keys are better

TOTP has one honest weakness: the code can be phished. A convincing fake login page can relay your password and the current code in real time, because a TOTP is just a number you type, blind to where it is being typed. FIDO2 hardware keys and passkeys are origin-bound: the browser cryptographically checks the site, and a fake domain cannot receive the credential at all.

For the few accounts where that threat is real — email, banking, cryptocurrency — a hardware key is the stronger choice. A later post in this series covers them in depth; until then, app-based TOTP everywhere, hardware keys where the stakes justify them.

Trying TOTP safely right now

You need neither an account nor a service to watch TOTP work. The best-known TOTP example is the secret JBSWY3DPEHPK3PXP, the illustration used in Google's Key URI Format documentation. Paste it into the guest generator on our homepage and a live six-digit code appears instantly, counting down each 30-second window.

The generator runs entirely in your browser. Paste a base32 secret, an otpauth link, a QR code image, or an entire Google Authenticator export, and codes appear with zero network requests and nothing stored — reload the page and everything is gone. It is the same math your bank's login page uses.

When you want to keep entries instead of pasting them, create a free account. The vault is zero-knowledge: each secret is encrypted in your browser before upload — PBKDF2 salted derivation, HKDF key separation, AES-256-GCM per item — so the server holds only ciphertext, with Argon2id protecting login. The features page has the full list: TOTP, HOTP, and Steam Guard support; per-key algorithm, digits, period, and counter; and an installable PWA that keeps working fully offline on iPhone, Android, Windows, and Mac. The free tier holds 100 folders and 100 entries; Yearly is $24 and Lifetime is $99.

Frequently asked questions

Is TOTP safe?

Against the attacks that do the most damage — credential stuffing, password breaches, offline guessing — yes, decisively. The caveats: anyone who extracts your secret generates codes as you, real-time phishing can relay a code as you type it, and malware can read codes off your screen. Those are arguments for hardware keys on critical accounts, not against TOTP.

Does TOTP need internet?

No. Generation is a local computation over the secret and the clock, so an app with TOTP entries works in airplane mode indefinitely. A connection matters only for enrolling, and for syncing a vault if you keep one. Our generator and the installed app compute codes entirely offline.

Can one app hold many accounts?

Yes — dozens of them. Each entry is just an issuer, a label, and its own secret, all independent, which is why the issuer field exists. One authenticator covering email, banking, work, and gaming is the normal setup.

What is an otpauth link?

The de facto standard URI, otpauth://type/label?parameters, that encodes an OTP entry: issuer, account name, secret, and the algorithm, digits, and period options. The QR code at setup is this link rendered as an image, and pasting the text form does the same job as scanning.

Why did my codes suddenly stop working?

Almost always clock drift. Turn on automatic date and time, then try again. If not: the entries never made it across a phone migration, the service reset its secret, or you are reading the wrong entry for a different issuer. The codes themselves do not wear out.

Keep reading