In one sentence
TOTP is an algorithm that generates a constantly changing, temporary numeric code from a shared secret and the current time, used as a second factor for logging in.
The problem it solves
Let's be real: passwords are a necessary evil. We forget them, we reuse them, and attackers are depressingly good at stealing them through phishing, database breaches, and good old-fashioned guessing. Relying on a password alone is like leaving your front door locked but putting the key under the doormat.
This is where Multi-Factor Authentication (MFA), and its most common flavor, Two-Factor Authentication (2FA), comes in. The idea is to prove your identity with more than just something you know (a password). You also need something you have (like your phone) or something you are (like your fingerprint).
Early 2FA often meant getting a code via SMS. This was better than nothing, but it's prone to "SIM swapping" attacks, where a scammer convinces your cell provider to transfer your number to their phone. Suddenly, they're getting your 2FA codes. Not great.
Enter the app-based authenticator. Instead of relying on the shaky security of the phone network, an app on your device can generate codes locally. But how does your Google Authenticator app know how to generate a code for your GitHub account? And how does your Microsoft Authenticator know how to talk to your Dropbox?
They all speak the same open-standard language: TOTP, the Time-Based One-Time Password. TOTP is the unsung hero that decouples your second factor from any single company. It’s an open, interoperable standard that lets any compliant app work with any compliant service. It’s the evolution of an earlier standard called HOTP (which was based on a simple counter), but by using time as the moving part, TOTP became way more user-friendly.
How it works under the hood
TOTP seems like magic. You scan a QR code once, and for the rest of time, your phone and a server halfway across the world can agree on the same six-digit number every 30 seconds, without ever talking to each other again. It's not magic, it's just some seriously clever crypto.
Let's break down the recipe.
The Shared Secret: The Key to the Kingdom
Everything starts with a secret. When you enable 2FA on a service, the server generates a random, secret key (e.g., 160 bits of pure randomness). This secret is the one and only thing that both you and the server need to know. The whole security of the system rests on keeping it secret.
Of course, a string of 160 random bits isn't very user-friendly. So for us humans, it's encoded into a format called Base32. Unlike its cousin Base64, Base32 uses a smaller alphabet that's case-insensitive and avoids ambiguous characters like 1 and l, or 0 and O. This makes it possible (though still a pain) to type in manually if you can't scan the QR code.
A typical Base32 secret looks like this: JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP
The most common way to get this secret into your authenticator app is via a QR code that encodes a special URI:
otpauth://totp/FlowingDev:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=FlowingDev
Your app scans this, saves the issuer (FlowingDev), the account name (user@example.com), and most importantly, the secret. The handshake is complete.
The Moving Part: Time
The "T" in TOTP stands for Time-Based. This is the crucial ingredient that makes the code change. But the system doesn't use the exact time down to the millisecond—that would be impossible to sync. Instead, it uses a discrete time step.
The formula is simple:
TimeStep = floor(CurrentUnixTime / 30)
- CurrentUnixTime: The number of seconds that have passed since January 1st, 1970 (the Unix Epoch).
- / 30: The time step is almost universally 30 seconds.
This calculation results in an integer that only changes every 30 seconds. At 10:00:00 AM, the TimeStep is some value T. At 10:00:29 AM, it's still T. But at 10:00:30 AM, it increments to T+1. This TimeStep is the "message" we're going to authenticate.
The Crypto Magic: HMAC-SHA1
Now we combine the static secret with the moving time step. This is done using a cryptographic primitive called HMAC (Hash-based Message Authentication Code).
HMAC_Result = HMAC-SHA1(SecretKey, TimeStep)
Think of HMAC as a way to create a secure signature. It takes our secret key and the data we want to sign (the TimeStep) and mashes them together using a hash function—in this case, the venerable SHA-1.
The output is a 20-byte (160-bit) hash. It's a long, hexadecimal string that's completely unique to that specific secret and that specific time step. Change one bit of the secret or wait 30 seconds for the time step to change, and you'll get a wildly different hash.
While SHA-1 has known weaknesses for other purposes like signing certificates, it's still considered perfectly safe when used inside the HMAC construction. Many modern implementations are moving to HMAC-SHA256 or HMAC-SHA512 for good measure.
The Truncation: Getting to Six Digits
A 20-byte hex string is not a user-friendly six-digit code. The final step is to "dynamically truncate" this hash into a number we can actually use.
- Find the Offset: Look at the very last 4 bits (nibble) of the 20-byte HMAC result. This gives us a number between 0 and 15. This is our
offset. - Grab 4 Bytes: Starting at that
offsetposition in the 20-byte hash, grab the next 4 bytes (32 bits). - Clear the Top Bit: Take this 32-bit chunk and do a bitwise AND with
0x7FFFFFFF. This effectively gets rid of the very first bit, which solves some potential signed vs. unsigned integer problems and ensures the result is always positive. - Modulo Time! Now you have a big 31-bit integer. To get it down to 6 digits, you take it modulo 1,000,000 (
10^6). - Pad with Zeros: The result is your code! If the number is, say,
4521, you pad it with leading zeros to make it004521.
That's it! Both your phone and the server run this exact same calculation. When you enter the code, the server compares its result with yours. To account for slight clock drift between your phone and the server, the server will typically check the code for the current time step, the one before, and the one after. If any of them match, you're in.
Real-world stories
The GitHub Account Takeover That Wasn't
A developer, deep in the zone, gets an email that looks exactly like it's from GitHub, warning of a security issue. In a moment of distraction, they click the link, land on a pixel-perfect clone of the GitHub login page, and enter their username and password. The attacker now has their credentials. The phisher immediately tries to log into the real GitHub. They enter the stolen password... and are met with a prompt: "Please enter your two-factor authentication code." The attacker is completely stuck. They don't have the developer's phone and have no way to generate the TOTP code. The developer, meanwhile, gets a real email from GitHub about a new login, realizes their mistake, and promptly changes their password.
Lesson: TOTP acts as a critical firewall. It turns a potentially catastrophic password breach into a harmless, failed login attempt.
The Offline Access Emergency
A network engineer is on a flight to a conference. Thirty minutes after takeoff, they need to check on a critical production system. There's no in-flight Wi-Fi and zero cell service. An SMS or push-based 2FA would be useless. But their dashboard is secured with TOTP. They open their laptop, pull up the login page, open the authenticator app on their phone, and type in the six-digit code. The app doesn't need any internet connection; its math is self-contained, relying only on the shared secret and the phone's internal clock. They log in and resolve the issue before the flight attendants even start the beverage service.
Lesson: TOTP's ability to work completely offline makes it incredibly robust and reliable for critical access, no matter where you are.
The Cross-Platform Migration Headache... Solved
A long-time Android user decides to switch to an iPhone. They open Google Authenticator and are horrified to find there's no simple "sync to my new phone" button. They have over 30 accounts set up, and the thought of going through the 2FA-reset process for every single one is a nightmare. But then they remember their personal security policy: for every new service, they've been saving the initial QR code as a screenshot in their encrypted password manager. They install a new authenticator app on their iPhone, open their password manager, and one by one, re-scan the saved QR codes. In ten minutes, all 30 accounts are migrated and working perfectly.
Lesson: The TOTP secret (as a Base32 string or QR code) is the real key. Being independent of any single authenticator app by securely backing up your secrets gives you true ownership and portability.
Common mistakes and traps
- Treating the secret casually. The QR code or the Base32 string you're given at setup is the secret key. If an attacker gets it, they can generate valid codes forever. Don't screenshot it and leave it on your desktop, don't email it to yourself, and don't store it in a plain text file. Treat it like a master password.
- Forgetting to save recovery codes. When you enable 2FA, nearly every service will provide a set of one-time-use recovery codes. PRINT THEM. Save them in a safe place (a real safe, or at a minimum, your password manager). If you lose your phone, these codes are your only way back into your account without a painful support process.
- Assuming your authenticator app is backed up. Many simple authenticator apps (like the original Google Authenticator) store secrets only on that one device. If the phone is lost or wiped, the secrets are gone forever. Use an app that offers secure, encrypted cloud sync (like Authy or 1Password) or be disciplined about backing up your secrets manually.
- Ignoring clock drift. TOTP is time-based. If your phone's clock is off by more than a minute or two from the real time, the codes it generates will be rejected. Always set your phone and computer to "Set time automatically." It's almost always the culprit when valid codes suddenly stop working.
Why it belongs on your radar
If you're a developer building any system that has user accounts, you should be implementing MFA, and TOTP is the best-in-class, open-standard option for app-based 2FA. It's more secure than SMS and more open than proprietary push-notification systems. Understanding how to generate a secret, create an otpauth:// URI, and validate an incoming token is a fundamental skill for building secure applications.
Even if you never implement a TOTP system yourself, you are a user of them on platforms like AWS, Cloudflare, GitHub, and npm. Knowing how the magic works under the hood empowers you to use it more securely. It helps you understand the importance of protecting the initial secret, saving your recovery codes, and choosing the right authenticator app for your needs. It demystifies the little six-digit number that stands between your data and the bad guys.
Go deeper
- RFC 6238: Time-Based One-Time Password Algorithm — The official specification for TOTP.
- RFC 4226: HMAC-Based One-Time Password Algorithm — The specification for HOTP, the foundation upon which TOTP is built.
- RFC 4648: The Base16, Base32, and Base64 Data Encodings — The spec defining the Base32 encoding used for sharing secrets.
- Key URI Format · google/google-authenticator Wiki — The de-facto standard for the
otpauth://URI used in QR codes. - Time-based One-time Password - Wikipedia — A high-level and historical overview of the algorithm.