Back to Blog Security

No Internet, No SMS — How Does Your Authenticator App Know the Right OTP?

Jun 18, 2026 9 min read Srikanth Badavath

Reading…
 TOTP · 2FA · Cryptography
No internet. No SMS. So how does your app know the right code?
Your phone and Google's server generate the exact same 6-digit code without ever talking to each other in that moment. Here is the surprisingly elegant math behind it.
------
30s
Live TOTP Demo

1 The Puzzle

Every time you log into Gmail with 2FA, your phone shows a 6-digit code that Google's server somehow already knows — without your phone and Google ever talking to each other in that moment. How is that possible? The server isn't texting you the code. The phone isn't phoning home to fetch it. Yet they both display — and agree on — the exact same number. And 30 seconds later, they both agree on a completely different one.

This is not magic. It is a beautifully simple piece of cryptography called TOTP — Time-Based One-Time Password. Once you understand it, you will never look at that little spinning countdown the same way again.

2 What Is 2FA?

Two-Factor Authentication (2FA) is the idea that logging in should require two separate proofs of identity — not just one. The classic analogy is a bank safe: the manager has one key, and the owner has another. Neither can open it alone. Both keys must be present.

For your online accounts, the two factors are typically:

Even if an attacker steals your password, they cannot log in without your phone. Here is how these two locks work together:

Factor 1
Something you know
Password
+
Factor 2
Something you have
Your Phone
Result
Both verified
Access Granted
OTP vs TOTP: OTP just means "one-time password" — any code that can only be used once. TOTP (Time-Based OTP) is a specific standard (RFC 6238) that makes the code expire after a time window, which is what Google Authenticator, Authy, and most modern 2FA apps use.

3 The Setup — Scanning That QR Code

Here is the key insight that most people miss: the magic happens once, during setup. When you enable 2FA on any service, the first thing that happens is this: the server generates a random secret key — a string of random bytes — and shows it to you as a QR code. You scan it. Now both your phone and the server hold that same secret. That is the only time any secret travels anywhere.

You scan the QR code once
Your phone (Google Authenticator) SECRET
Google's server SECRET
Secret never travels again after setup

Think of it like two identical clocks that were synchronized once, then sealed in separate rooms. They never communicate again, but they always show the same time because they were set together at the start. The secret key is that initial synchronization.

4 The Formula

Every 30 seconds, both your phone and the server independently run the exact same calculation:

TOTP  =  HMAC-SHA1(secret_key,  time_window)
secret_key — the thing you scanned once
×
time_window = floor(now / 30)
+
HMAC-SHA1 — the mixing function
Your OTP this window
548 291

Let us break down each ingredient:

secret_key — A random string of bytes (shown as Base32 text like JBSWY3DPEHPK3PXP) that was in the QR code. Both your phone and the server have it. It never changes.

time_window — This is just the current Unix timestamp (seconds since January 1, 1970) divided by 30 and rounded down. Right now, that number is the same on your phone and every Google server everywhere in the world — because clocks agree. This is the secret sauce: time is a shared secret that nobody needs to communicate.

HMAC-SHA1 — HMAC stands for "Hash-based Message Authentication Code." SHA1 is a hashing algorithm. Together, HMAC-SHA1 takes the secret key and the time window, mixes them in a mathematically one-way function, and produces a long number. The last 6 digits (trimmed to 6 digits by a process called "dynamic truncation") become your OTP.

5 The 30-Second Window

Time is divided into 30-second blocks. Each block gets a completely different code, because the time_window input to the formula changes each time. Scroll the timeline below to see how each window maps to a different OTP:

Time windows — each block = 30 seconds

The server is forgiving by about one window in either direction (so ±30 seconds of clock drift is fine). Beyond that, the codes simply won't match — which is by design. An OTP from 90 seconds ago is completely useless.

Why 30 seconds? It is a balance between security and usability. Too short (5 seconds) and users keep failing to type fast enough. Too long (5 minutes) and a stolen OTP has a long attack window. 30 seconds is the RFC 6238 standard and works well for human reaction times.

6 Live Demo — Phone and Server, In Sync

The real magic: watch your phone and Google's server both independently compute the same code, at the same time, without ever communicating. Both panels below update every second, driven only by your clock:

Real-time TOTP synchronization
Both sides run the same formula independently — and always agree
📱 Your Phone
HMAC-SHA1(   "JBSWY3DP..."   ... ) = ------
------
--s
Same time: window
#--
🖥️ Google's Server
HMAC-SHA1(   "JBSWY3DP..."   ... ) = ------
------
--s

7 How the Server Verifies

When you type your OTP and click "Sign in," here is exactly what happens on the server side in milliseconds:

1
You type the code and submit the form
The 6-digit code travels from your browser to Google's server over HTTPS. This code is safe to transmit — it expires in 30 seconds anyway and can never be reused.
2
Server computes its own TOTP independently
The server looks up your account's secret key from its own database, grabs the current time window, and runs: HMAC-SHA1(your_secret, floor(now/30)). It does this without your phone being involved at all.
3
If the codes match — access granted
The server compares what you typed with what it computed. If they match, it marks the code as "used" (so it cannot be replayed) and lets you in. If they don't match, access is denied — possibly the window just flipped.

8 Why It Is Secure

TOTP has three separate security properties that make it robust against common attacks:

Expires in 30 seconds
Even if an attacker intercepts your OTP mid-transmission, it is useless after 30 seconds. Replay attacks — where someone reuses a captured code — are impossible by design.
🔐
Secret never travels
After the initial QR scan, the secret key never leaves your device or the server. There is nothing to intercept. Even a man-in-the-middle attack on your login cannot expose the underlying key.
🎲
1-in-1,000,000 odds
A 6-digit code has 1,000,000 possible values. An attacker gets at most a handful of guesses per 30-second window before the account locks. The math simply does not work in their favour.

9 End-to-End TOTP Flow

Here is the complete lifecycle — from enabling 2FA to getting access granted — as a single connected flowchart:

flowchart TD A[User enables 2FA] --> B[Server generates secret key] B --> C[Secret shown as QR code] C --> D[User scans QR code] D --> E[Secret stored on phone AND server] E --> F[User tries to log in] F --> G[Enters username + password] G --> H[Server asks for OTP] H --> I[Phone calculates TOTP] H --> J[Server calculates TOTP] I --> K[TOTP = HMAC-SHA1 secret + time_window] J --> L[TOTP = HMAC-SHA1 secret + time_window] K --> M{Do they match?} L --> M M -->|Yes| N[Access Granted] M -->|No| O[Access Denied]
Figure: Complete TOTP authentication flow — setup, login, and verification

10 The Code (For Developers)

If you are a developer, here is how trivially easy TOTP is to implement — because the heavy lifting is done by well-audited libraries:

Python
# pip install pyotp import pyotp import time # The secret you share with the user (Base32-encoded) secret = "JBSWY3DPEHPK3PXP" # Create a TOTP object totp = pyotp.TOTP(secret) # Generate current OTP (same as what user's app shows) current_otp = totp.now() print(current_otp) # e.g. "548291" # Verify a code the user typed (with ±1 window tolerance) user_typed = "548291" if totp.verify(user_typed): print("Access granted") else: print("Invalid OTP") # Generate a provisioning URI (for the QR code) uri = totp.provisioning_uri( "user@example.com", issuer_name="MyApp" ) # Encode uri as QR code and show to user during setup
JavaScript
// npm install otpauth import * as OTPAuth from 'otpauth'; // Create a TOTP instance const totp = new OTPAuth.TOTP({ issuer: 'MyApp', label: 'user@example.com', secret: OTPAuth.Secret.fromBase32('JBSWY3DPEHPK3PXP'), digits: 6, period: 30, }); // Generate current code const code = totp.generate(); // Verify a code (returns delta or null) const delta = totp.validate({ token: '548291', window: 1 }); if (delta !== null) { console.log('Valid OTP, window delta:', delta); } else { console.log('Invalid OTP'); } // Get the provisioning URI for the QR code image const uri = totp.toString(); // Pass to a QR code library: qrcode.toDataURL(uri)
Security note for devs: Never implement HMAC-SHA1 yourself for TOTP. Always use a well-audited library (pyotp, otpauth, speakeasy). The spec details in RFC 6238 (TOTP) and RFC 4226 (HOTP) are subtle, and a custom implementation is a security liability. The real code above is literally all you need.

11 Frequently Asked Questions

TOTP depends entirely on both sides agreeing on the current time. If your phone clock drifts, the time_window values diverge and the codes stop matching. Most authenticator apps handle up to ±30 seconds of drift (one window in either direction). If your clock is significantly wrong, enabling automatic time sync in your phone settings (Settings → Date & Time → Set Automatically) fixes it immediately. Google Authenticator also has a "Time Correction for Codes" feature that compensates for drift by comparing against Google's servers periodically — without revealing your secret.

Technically, yes — but it is much harder to exploit than stealing a password. If an attacker intercepts your OTP via a man-in-the-middle attack (e.g., a fake login page that relays your credentials in real-time), they could use it within the 30-second window. This is called a real-time phishing attack. TOTP does not defend against this. For that level of protection, you need hardware security keys (FIDO2/WebAuthn, like a YubiKey), which are phishing-resistant by design. But for the vast majority of threats — account stuffing, credential dumps, remote attacks — TOTP is extremely effective.

RFC 6238, published in 2011 by the IETF (the body that standardizes internet protocols), specifies 30 seconds as the default window. It is a deliberate human-factors decision: user studies found that most people take between 5 and 25 seconds to type a 6-digit code after seeing it. A 30-second window gives comfortable headroom. 60-second windows are allowed by the spec, and some services use them, but 30 seconds is the default that Google Authenticator, Authy, and most implementations adopted. The shorter the window, the smaller the replay-attack surface, so 30 seconds became the sweet spot.

No — SMS OTP is significantly weaker. The code travels over the phone network, which is vulnerable to SIM-swapping attacks (where an attacker convinces your carrier to port your number to their SIM), SS7 protocol vulnerabilities (which can intercept SMS messages at the network level), and real-time phishing. TOTP codes are generated entirely on your device and never transmitted from your phone to any network. The NIST (National Institute of Standards and Technology) deprecated SMS as a second factor in 2016, and most security researchers recommend switching to an authenticator app (TOTP) or a hardware key. That said, SMS OTP is infinitely better than no 2FA at all — so if a service only offers SMS, use it.

Curious how other protocols protect you?

TOTP is one piece of the modern security stack. The same elegant cryptographic thinking shows up everywhere — in the TLS handshake that secures every HTTPS connection, in end-to-end encryption, in digital signatures. Keep reading: