TOTP (Time-based One-Time Password, RFC 6238) is the six-digit codes from Google Authenticator, Authy, 1Password, Bitwarden, and similar apps. Every 30 seconds the code changes; both the user's app and the platform compute the same code from a shared secret and the current time.
TOTP is weaker than WebAuthn (the codes can be phished if the user is tricked into typing them on a malicious site), but it is dramatically better than no MFA, and it works on any device with an authenticator app — including users whose devices do not support WebAuthn.
Enrolment
Section titled “Enrolment”const { startMfaEnrolment } = useIntelliAuth()
const enrolment = await startMfaEnrolment('totp')// enrolment.kind === 'pending'// enrolment.secret — the base32 shared secret// enrolment.qr_code — a data URL for the QR// enrolment.uri — the otpauth:// URI behind the QRYour UI shows the QR code. The user scans it with their authenticator app — Google Authenticator, Authy, 1Password, the OS keychain, etc. The app stores the shared secret and starts generating codes.
To complete enrolment, the user types in the current code from their app:
const result = await enrolment.complete({ code: '123456' })if (result.kind === 'success') { // Enrolment is done. The user record now has a TOTP factor.}The platform verifies the code (allowing a one-step window of clock drift, ±30 seconds). On success, the secret is stored against the user; the QR / secret / pending state is discarded.
QR code etiquette
Section titled “QR code etiquette”The QR encodes an otpauth:// URI. The IntelliAuth SDK returns both the data-URL QR (ready to render in an <img>) and the raw URI (so you can render the QR yourself if you want a specific size or styling).
Show the secret in text form beneath the QR. Some users cannot scan a QR — they need to type the secret into the app manually. Group it in chunks of 4 characters for readability:
JBSW Y3DP EHPK 3PXPAuthentication
Section titled “Authentication”When MFA is required, the platform returns a flow_id and the available factors. If the user picks TOTP:
const { completeMfaStep } = useIntelliAuth()
const result = await completeMfaStep(flowId, 'totp', { code: '123456' })The platform verifies and returns the session.
Backup codes
Section titled “Backup codes”When the user enrolls TOTP, prompt them to also generate backup codes — a list of one-shot codes they can use if they lose access to their authenticator. The platform supports backup codes natively:
const { regenerateBackupCodes } = useIntelliAuth()const codes = await regenerateBackupCodes()// codes is an array of strings; show them once, never againShow the codes once. The user copies them somewhere safe (password manager, printout). Treat them like an emergency credential. Each code is single-use; once consumed it's gone from the user's account.
The platform always returns the full fresh set when this is called — calling it again invalidates the previous set. Tell the user this is what will happen, or they'll be surprised when their saved-off codes stop working.
When TOTP fails
Section titled “When TOTP fails”| Symptom | Cause | Fix |
|---|---|---|
| Code is correct but rejected | Clock drift on the user's device | Most apps let users sync time; suggest that |
| Code never works | Wrong secret or wrong user | Re-enrol; QR may have been mis-scanned |
| Code accepted but session fails | Different problem; not TOTP | Check the audit log |
The platform allows ±30 seconds of clock drift by default. Wider tolerance reduces security; tighter would frustrate users on devices with bad clocks.
Replay protection
Section titled “Replay protection”The platform records the last successfully consumed code for each user. A code cannot be used twice — once accepted, it is burned. If the user tries to authenticate again in the same 30-second window, they wait for the next code.
Compared to SMS
Section titled “Compared to SMS”TOTP and SMS solve the same UX problem (one-time code as a second factor), but TOTP is meaningfully stronger:
- TOTP — the secret never leaves the user's device.
- SMS — the code is sent over a network channel that has been compromised more than once (SIM-swap, SS7 attacks).
Prefer TOTP. SMS is a fallback for users whose devices cannot run an authenticator app, not a primary option.