Security6 min read

TOTP Explained: How Time-Based One-Time Passwords Work

Understand how TOTP (Time-Based One-Time Passwords) generate 6-digit 2FA codes, the cryptography behind them, how to implement TOTP, and how to generate test codes online.

Try the free online tool mentioned in this guide:TOTP / 2FA Code Generator

What is TOTP?

TOTP (Time-Based One-Time Password) is the algorithm behind authenticator apps like Google Authenticator, Authy, and 1Password. It generates a new 6-digit code every 30 seconds using a shared secret and the current time.

TOTP is defined in RFC 6238 and is built on top of HOTP (HMAC-Based One-Time Password, RFC 4226). It is a cornerstone of modern two-factor authentication (2FA) — even if an attacker captures your TOTP code, it expires in 30 seconds and is useless afterward.

How TOTP works: step by step

1. Setup: The server generates a random secret key (typically 20 bytes, Base32-encoded). This secret is shared with the user via a QR code or manual entry into an authenticator app.

2. Time step: TOTP divides Unix time into 30-second windows: T = floor(currentTime / 30).

3. HMAC: Compute HMAC-SHA1(secret, T). The secret is the key; T is the 8-byte big-endian message.

4. Dynamic truncation: Take the last byte of the HMAC, use its low 4 bits as an offset, and read 4 bytes starting at that offset.

5. Code: Apply a bitmask and compute result mod 10^6 to get a 6-digit code.

Both the authenticator app and the server perform this calculation independently. If they arrive at the same code (within a 1-window tolerance), authentication succeeds.

javascript
// TOTP in Node.js (simplified)
import { createHmac } from "crypto"

function generateTOTP(secret: string, digits = 6, period = 30): string {
  const key = Buffer.from(secret, "base32")
  const counter = Math.floor(Date.now() / 1000 / period)

  // Counter as 8-byte big-endian buffer
  const msg = Buffer.alloc(8)
  msg.writeBigInt64BE(BigInt(counter))

  const hmac = createHmac("sha1", key).update(msg).digest()

  // Dynamic truncation
  const offset = hmac[hmac.length - 1] & 0x0f
  const code = (hmac.readUInt32BE(offset) & 0x7fffffff) % 10 ** digits

  return code.toString().padStart(digits, "0")
}

The QR code and secret key

When you scan a QR code to add an account to an authenticator app, the QR code encodes a otpauth:// URI:

otpauth://totp/MyApp:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=MyApp&algorithm=SHA1&digits=6&period=30

The secret is a Base32-encoded random byte string. The issuer and account name are display labels only — they do not affect code generation. The secret is what must be kept secure: anyone with the secret can generate valid TOTP codes.

text
# Example otpauth URI
otpauth://totp/GitHub:alice@example.com?secret=JBSWY3DPEHPK3PXP&issuer=GitHub

# Fields:
# type:   totp (time-based)
# label:  GitHub:alice@example.com (display only)
# secret: JBSWY3DPEHPK3PXP (Base32-encoded shared secret)
# issuer: GitHub (display only)

TOTP vs HOTP: what is the difference?

HOTP (HMAC-Based OTP): uses a counter instead of time. The counter increments with each use. Codes do not expire — they are only valid once. Requires synchronizing the counter between server and client, which causes drift issues.

TOTP (Time-Based OTP): uses the current time divided into 30-second windows instead of a counter. Codes expire automatically. More widely adopted because it does not require counter synchronization — just clock synchronization (NTP handles this automatically).

Security considerations

Protect the secret: the shared secret is equivalent to a password. Store it encrypted at rest. Never log it.

Clock drift: TOTP servers typically accept codes from the current window and one window on either side (±30 seconds) to tolerate minor clock drift.

Backup codes: always provide backup codes when setting up TOTP. If a user loses their authenticator device, backup codes are the recovery path.

Phishing: TOTP does not protect against real-time phishing (attacker proxies the code immediately). Passkeys (WebAuthn) are phishing-resistant; TOTP is not.

SMS 2FA vs TOTP: SMS-based 2FA is weaker than TOTP because SMS can be intercepted via SIM swapping. Prefer TOTP authenticator apps over SMS.

Frequently asked questions

Why do TOTP codes expire after 30 seconds?

The 30-second window is a design choice from RFC 6238 that balances usability (enough time to read and type the code) with security (short enough that intercepted codes are quickly useless).

Can I use TOTP for my own application?

Yes. Libraries exist for all major languages (speakeasy for Node.js, pyotp for Python, otplib for TypeScript). Generate a secret, show it as a QR code, verify codes server-side. The MyDevTools TOTP Generator can generate test codes from a secret for development and debugging.

What happens if I lose my TOTP device?

You need backup codes (which you should have saved during setup) or an account recovery process. This is why providing and storing backup codes is essential when implementing TOTP-based 2FA.

Is TOTP the same as the codes from Google Authenticator?

Yes. Google Authenticator, Authy, Microsoft Authenticator, and similar apps all implement RFC 6238 TOTP. The codes are interchangeable — you can use any compliant authenticator app with any TOTP-supporting service.

Try TOTP / 2FA Code Generator for free

Paste a Base32 authenticator secret and see the current six-digit RFC 6238 TOTP code refresh every 30 seconds. SHA-1, 30-second step—ideal for testing MFA and sign-in flows. Runs entirely in your browser. No install, no account required to try it.