5 Common TOTP Mistakes Developers Make (and How to Fix them)

TOTP codes not working? See 5 common mistakes developers make — clock drift, Base32 secrets, RFC 6238 parameter mismatches — and how to fix them. Includes a free online TOTP Authenticator tool.

 min. read
August 28, 2025

TOTP (time-based one-time password) codes seem simple—6 digits, refresh every 30 seconds—but implementation details can break logins fast. If you’re seeing “invalid TOTP code” errors, one of these five issues is likely the culprit. Below we explain the symptoms, why they happen, and how to fix them quickly. You can also test each fix live with our TOTP Authenticator tool (supports SHA-1/256/512, 6–8 digits, and 30-second periods).

Mistake 1 — Clock drift (server and authenticator out of sync)

Symptoms: Codes fail intermittently; users say “it works sometimes.” QA can reproduce locally but not on CI/containers.

Why it happens: TOTP derives a code from the current time. Even a 30–60s skew between server and client can invalidate a code.

How to fix:

  • Sync time with NTP (Network Time Protocol).
  • Allow a verification window (e.g., ±1 step) during validation so minor drift doesn’t lock users out.
  • Log both server time and received code to spot systematic drift.

Mistake 2 — Wrong secret format (Base32 vs hex, casing, padding)

Why it happens: Provisioning often shares the secret in Base32. Some developers accidentally treat it as hex/ASCII or strip padding/whitespace incorrectly.

How to fix:

  • Ensure the stored secret is Base32-decoded correctly.
  • From an otpauth:// URI, extract the secret= parameter as Base32. See the Key URI Format spec by Google

Mistake 3 — RFC 6238 parameter mismatch (digits, period, algorithm)

Why it happens: TOTP has configurable parameters. If the server uses 8 digits but the client expects 6, or the server uses SHA-256 while the app uses SHA-1, codes won’t align.

How to fix:

  • Standard defaults: 6 digits, 30-second period, SHA-1.
  • Read the official RFC 6238 spec (IETF) for parameter details.

Mistake 4 — Wrong provisioning data (otpauth URI / QR issues)

Why it happens: The otpauth://totp/... provisioning URI encodes the label, issuer, secret, algorithm, digits, and period. Typos or truncation cause authenticator apps to save incorrect parameters.

How to fix:

Mistake 5 — Naïve verification logic (no window, replay, or rate limiting)

Why it happens: Servers that check only the current time step can reject valid codes if the user types near a boundary. Without replay checks and rate limiting, attackers can brute-force.

How to fix:

  • Verify across a small window (±1 step).
  • Add replay protection and rate limiting. For code libraries, check out PyOTP (Python) or otplib (Node.js) for robust implementations.

Bonus Checklist: Quick Fix for “Invalid TOTP Code”

  • ✅ Clocks are NTP-synced
  • ✅ Secret is Base32
  • ✅ Parameters match RFC 6238
  • ✅ URI encodes same parameters
  • ✅ Verification window & rate limiting enabled

CTA

Need to isolate the problem quickly? Test your secret and parameters with the Authgear TOTP Authenticator and confirm codes before going live.

Preferences

Privacy is important to us, so you have the option of disabling certain types of storage that may not be necessary for the basic functioning of the website. Blocking categories may impact your experience on the website.

Accept all cookies

These items are required to enable basic website functionality.

Always active

These items are used to deliver advertising that is more relevant to you and your interests.

These items allow the website to remember choices you make (such as your user name, language, or the region you are in) and provide enhanced, more personal features.

These items help the website operator understand how its website performs, how visitors interact with the site, and whether there may be technical issues.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.