Introduction

TOTP (Time-based One-Time Password) generates 6-digit codes that change every 30 seconds based on the current time. If the user's device clock drifts from the server clock -- due to device time settings, battery removal, or timezone changes -- the generated codes will not match the server's expected value, causing MFA authentication to fail.

Symptoms

  • User enters correct TOTP code but authentication is rejected
  • MFA works after resyncing the authenticator app but fails again later
  • Users report MFA failures after traveling across timezones
  • Server logs show TOTP code mismatch for valid-looking codes
  • Error message: Invalid MFA code. Please check your device time and try again.

Common Causes

  • User device clock not synchronized with NTP (manual time setting)
  • Device battery removed, causing the real-time clock to reset
  • Timezone change on the device without automatic time update
  • Server TOTP window tolerance too narrow (only checking current time step)
  • Authenticator app cache stale after device timezone change

Step-by-Step Fix

  1. 1.Verify the user device time is synchronized: Check the device clock.
  2. 2.`
  3. 3.# On the user's device:
  4. 4.# iOS: Settings > General > Date & Time > Set Automatically = ON
  5. 5.# Android: Settings > System > Date & Time > Automatic date & time = ON
  6. 6.# Compare device time with: https://time.is/
  7. 7.`
  8. 8.Resync the authenticator app: Force a time correction in the app.
  9. 9.`
  10. 10.# Google Authenticator:
  11. 11.# Settings > Time correction for codes > Sync now

# Microsoft Authenticator: # Settings > Automatic time correction > Enable

# Authy: # Settings > Time correction > Sync now ```

  1. 1.Increase server TOTP window tolerance: Allow for clock drift.
  2. 2.```python
  3. 3.# Python pyotp example with window tolerance
  4. 4.import pyotp
  5. 5.totp = pyotp.TOTP(secret)
  6. 6.# Check current and adjacent time steps (window of 1 = +/- 30 seconds)
  7. 7.# Window of 2 = +/- 60 seconds
  8. 8.is_valid = totp.verify(user_code, valid_window=2)
  9. 9.`
  10. 10.Re-provision the TOTP secret if resync does not help: Generate a new QR code.
  11. 11.`
  12. 12.# In the application:
  13. 13.# 1. Disable MFA for the user
  14. 14.# 2. Re-enable MFA and display a new QR code
  15. 15.# 3. User scans the new QR code with their authenticator app
  16. 16.# 4. Verify the new code works
  17. 17.`
  18. 18.Implement backup authentication methods: Provide fallback options.
  19. 19.`
  20. 20.# Offer alternative MFA methods:
  21. 21.# - SMS-based OTP (less secure but available)
  22. 22.# - Email-based OTP
  23. 23.# - Recovery codes (one-time use)
  24. 24.# - Hardware security keys (FIDO2/WebAuthn)
  25. 25.`

Prevention

  • Configure server TOTP tolerance window of at least 2 (plus/minus 60 seconds)
  • Provide clear instructions for users to enable automatic time sync on their devices
  • Offer multiple MFA methods so users have fallback options
  • Monitor MFA failure rates and alert on sudden increases that may indicate time drift
  • Implement adaptive MFA that allows recovery codes when TOTP repeatedly fails
  • Educate users about the importance of device time synchronization for MFA