Your Authenticator App Doesn't Need the Internet, So How Does It Know the Code?
Open Google Authenticator.
No Wi-Fi.
Turn off mobile data.
Wait for the number to change.
It still works.
A new six-digit code appears every 30 seconds.
No request was sent to Google.
No request was received from the website.
So...
Where the hell is the code coming from?
The answer is surprisingly elegant.
Your phone and the server already share a secret.
And they use the current time to independently calculate the same answer.
There Is No Server Sending You The Code
The first important thing to understand is that your authenticator app isn't receiving the OTP.
There's no request like this:
Authenticator App
|
| "Give me a code"
↓
Server
|
| "847291"
↓
Authenticator AppInstead, both sides independently calculate it.
Shared Secret
|
┌────────┴────────┐
↓ ↓
Authenticator Server
| |
Current Time Current Time
| |
↓ ↓
847291 847291They don't need to communicate.
They just need to have the same inputs.
The Secret Is Created When You Set It Up
When you enable two-factor authentication on a website, you'll usually see a QR code.
You scan it with your authenticator app.
That QR code isn't simply saying:
"Use this website."
It contains configuration information, most importantly a shared secret.
Conceptually:
Website
|
| Generate secret
↓
Shared Secret
|
├──────────────→ Authenticator App
|
└──────────────→ ServerYour authenticator stores that secret locally.
The server stores its copy.
Now both sides have the same secret.
The Magic Is Actually TOTP
Most authenticator apps use something called:
TOTP — Time-based One-Time Password.
The idea is beautifully simple.
Take:
Shared Secret
+
Current TimeFeed them into a cryptographic algorithm.
Get a number.
That's your OTP.
Conceptually:
TOTP(secret, current_time)
↓
847291Wait 30 seconds.
The time changes.
The output changes.
TOTP(secret, next_time)
↓
193482No internet required.
But What Exactly Is The Time?
The phone doesn't literally use:
4:32:17 PMTOTP converts time into fixed intervals called time steps.
A common configuration uses a 30-second interval.
Conceptually:
12:00:00 ───────── 12:00:29
↓
Code A
12:00:30 ───────── 12:00:59
↓
Code B
12:01:00 ───────── 12:01:29
↓
Code CBoth the phone and server calculate which time window they're currently in.
Then they generate the same code.
Where Does Cryptography Come In?
The secret isn't simply combined with the time like:
secret + time = 123456That would be terrible security.
Instead, TOTP is built on HMAC, a cryptographic construction that combines a secret key with a message.
A simplified version looks like:
Secret
|
▼
Time Counter ──────→ HMAC
|
▼
Cryptographic Output
|
▼
Convert To Digits
|
▼
847291The actual standard is defined by RFC 6238.
Why Can't Someone Just Guess The Code?
They can try.
That's the point of the system.
A typical OTP is only six digits:
000000 → 999999That's one million possibilities.
But the code changes rapidly.
If a website also limits login attempts...
Brute-forcing becomes impractical.
Wrong
Wrong
Wrong
Too many attempts
Account temporarily blockedThe OTP isn't supposed to be the only security mechanism.
It's an additional factor.
The Really Cool Part
Imagine your phone is completely offline.
Wi-Fi: OFF
Mobile Data: OFF
Airplane Mode: ONThe authenticator can still generate:
847291Why?
Because everything necessary to generate it is already on your phone.
Stored Secret
+
Current Time
↓
Cryptographic Function
↓
OTPThe internet is irrelevant to the generation process.
Then How Does The Website Verify It?
This is where the internet comes back.
You enter:
847291The website receives it.
Your Phone
847291
↓ Internet
WebsiteThe server already has the same secret.
It calculates:
TOTP(secret, current_time)Suppose it also gets:
847291The server compares:
Your Code
=
Server's Code
847291
=
847291
✓You're authenticated.
What If Your Phone Is 20 Seconds Ahead?
This is an interesting problem.
The phone and server clocks aren't guaranteed to be perfectly synchronized.
So servers typically allow a small amount of clock drift.
For example, they might check the current time window and nearby windows.
Conceptually:
Previous Window
Current Window
Next WindowThis gives the system some tolerance without making the OTP valid indefinitely.
Why Does It Stop Working After 30 Seconds?
Because the time input changed.
Suppose:
Time Window 1000
↓
847291Thirty seconds later:
Time Window 1001
↓
193482The old code is no longer the expected code.
That's why it's called a one-time password.
The same code isn't meant to remain useful forever.
What If Someone Takes A Screenshot?
A screenshot of the current OTP isn't particularly useful for long.
Suppose someone sees:
847291They now have a very short window to use it.
After the time step changes:
847291
↓
INVALIDOf course, an attacker who gets your authenticator's secret key is in a completely different situation.
The secret is the thing that must be protected.
The QR Code Is More Important Than It Looks
When you first configure an authenticator, the QR code effectively transfers the secret from the service to your authenticator.
After that...
The QR code isn't needed every 30 seconds.
The app has already stored the secret.
That's why the app can keep generating codes offline.
Setup
Website
↓
QR Code
↓
Authenticator
↓
Secret Stored
Later
Secret + Time
↓
OTPWhy This Is Such A Nice Piece Of Engineering
There's something elegant about TOTP.
The phone doesn't need to ask the server:
"What code should I show?"
And the server doesn't need to constantly send codes to millions of phones.
Both sides already possess the same secret.
They independently calculate the answer.
That means the system is:
- Stateless from the server's perspective for code generation
- Extremely lightweight
- Offline-capable on the authenticator side
- Easy to scale
- Based on well-understood cryptographic primitives
Imagine having 100 million users.
A push-based system might require massive amounts of infrastructure to continuously communicate with those devices.
TOTP doesn't need that.
The computation happens locally.
The Whole System In One Diagram
ONE-TIME SETUP
│
▼
┌─────────────┐
│ Website │
└──────┬──────┘
│
Shared Secret
│
▼
┌─────────────┐
│ Authenticator│
└─────────────┘
EVERY 30 SECONDS
┌──────────────────────────┐
│ │
▼ ▼
Authenticator Server
│ │
Shared Secret Shared Secret
+ +
Current Time Current Time
│ │
▼ ▼
HMAC HMAC
│ │
▼ ▼
847291 847291
│ │
└──────────┬───────────────┘
│
Compare
│
▼
SUCCESSTOTP vs SMS OTP
This also explains why authenticator-based 2FA is generally preferable to SMS OTP in many threat models.
With SMS:
Website
↓
Telecom Network
↓
Mobile Network
↓
Your PhoneThe code has to travel through external infrastructure.
With TOTP:
Secret
+
Time
↓
Local Calculation
↓
CodeThere is no delivery step.
The server only needs to verify what you entered.
One Important Detail
TOTP doesn't make you invincible.
If someone steals your password and gains access to your authenticator secret, they can potentially generate valid codes.
And phishing attacks can still trick users into entering legitimate OTPs into a fake website.
That's one reason modern authentication systems increasingly use passkeys and WebAuthn, which can provide stronger phishing resistance.
But TOTP remains incredibly useful because it's simple, widely supported, inexpensive, and doesn't require an internet connection to generate the code.
Final Thoughts
The next time you're sitting somewhere with absolutely no signal and your authenticator generates a fresh six-digit number...
Remember what's happening.
Your phone isn't contacting Google.
It isn't contacting Microsoft.
It isn't asking your bank.
It already knows the secret.
It knows the time.
And both your phone and the server independently arrive at the same answer.
No message needs to travel.
No code needs to be sent.
Just:
Shared Secret
+
Time
↓
Cryptography
↓
One-Time PasswordSometimes the coolest distributed systems are the ones where the two systems don't need to talk to each other at all.