v3.1 Authenticator App (TOTP) - Complete Flow Guide
← Back to Releases

Authenticator App (TOTP) - Complete Flow Guide

TOTP is an opt-in second factor that uses a 6-digit code from Google Authenticator, Authy, or any other TOTP-compatible app. SMS MFA must already be active on the account before TOTP can be enabled. Twilio Verify manages the underlying factor and validates every code we send it; our portal only orchestrates the UI and persists the device link.

v3.1 3 flows Twilio Verify
Prerequisite

The user must already have SMS MFA enabled (a verified phone number + MFA toggled on) before the Authenticator App option appears. If SMS MFA is not active, the TOTP card on the profile shows as locked.

1

Enrollment Flow (Profile)

Logged-in user enables an authenticator app from the Account Settings page. The portal asks Twilio Verify to create a new TOTP factor, renders the QR code, and locks the device in only after Twilio confirms the very first 6-digit code.

Account Settings Click "Enable" Twilio creates Factor Render QR (30s) Scan in Auth App Enter 6-digit code Twilio verified? TOTP Active ✓
Twilio Verify owns the TOTP secret. Our portal stores only the Factor SID and a confirmed flag - never the shared secret.
01
Step 1

Open Account Settings

User navigates to the profile/account settings page from the top-right menu.

Page
Authenticated
Account Settings - TOTP card
What the user sees

The Authenticator App card sits under the phone number section. If SMS MFA is already active, the card shows a pink "Not set up" pill and a black "Enable →" button. If SMS MFA is not active, the card is rendered in a locked / amber state and the button is disabled.

Visible only when MFA is enabled Locked card if SMS MFA off
02
Step 2

Click "Enable" - Factor created

Portal POSTs to generate-totp-factor/. Server asks Twilio to create a new TOTP factor for this user.

Twilio call
Twilio Verify
Enable clicked
Server side

The view initiate_totp_enrollment verifies the user has MFA enabled and that no confirmed TOTP device already exists, then calls OTPDeliveryService.create_totp_factor(). Twilio returns a Factor SID and a binding URI (otpauth://totp/...). The portal rewrites the issuer + label on the URI so the user's app shows "SAFHIR" and their email.

Why a factor instead of a static secret?

Twilio rotates and validates every code. Our DB only persists the Factor SID + a confirmed=False row until the user proves they scanned the QR.

03
Step 3

QR code rendered (30-second window)

The binding URI is rendered client-side as a QR code. A 30-second countdown blurs the QR to limit shoulder-surfing.

User action
Scan in app
QR code rendered
What the user sees

A QR code, the message "QR code hides in 30s", and a 6-digit code field. After 30 seconds the QR is blurred and a "Reveal QR Code" button appears. The user opens Google Authenticator / Authy / Microsoft Authenticator and scans the code.

Auto-blur after 30s Reveal button restarts the timer Twilio factor expires ~10 minutes after creation
04
Step 4

Enter 6-digit code + Confirm

Portal POSTs to confirm-totp-enrollment/ with the code. Server forwards it to Twilio.

Verification
Twilio Verify
Enter 6-digit code
Server side

The view validates the input is six digits, reads TOTP_ENROLLMENT_FACTOR_SID from the session, then calls OTPDeliveryService.confirm_totp_enrollment(). Twilio returns status=verified only when the code matches. On success the portal sets confirmed=True on the TOTPDevice row and stamps the session with the TOTP backend so the MFA middleware accepts the user on the very next request.

What if the code is wrong?

Server returns 400 with a user-friendly "Invalid code. Please try again." The session factor SID is preserved, so the user can re-enter without restarting.

05
Step 5

Card flips to "Active"

The enrollment card is replaced inline with a green "Authenticator App Active" pill plus a red "Remove" button - no page reload required.

Done
TOTP active
Authenticator App Active
Enrollment complete

From now on the user can pick TOTP at the login modal. SMS MFA also remains available as a backup.

2

Login with TOTP

A returning user enters their username + password. If the credentials are valid and the user has a confirmed TOTP device, they can pick "TOTP" at the verification modal instead of waiting for an SMS code.

Login Page Credentials valid Choose method TOTP button Enter 6-digit code Twilio approved? Dashboard ✓
01
Step 1

Username + password

Standard credential check. On success the user enters a pre-auth state - logged in conceptually, but not yet permitted past MFA.

Page
Login
Login page
Server side

The login view stores the user's id in PRE_AUTH_USER_ID and any post-login redirect in NEXT_AFTER_LOGIN. It also pre-computes has_totp server-side so the TOTP button is rendered enabled only for users with a confirmed device.

02
Step 2

Choose verification method

A small modal asks "Choose Verification Method" with SMS / TOTP buttons.

Modal
User action
Choose verification method
What the user sees

Two buttons. The TOTP button is enabled only because has_totp was true on the server. Selecting TOTP skips the "Send OTP" step entirely - codes come from the user's authenticator app, nothing is sent over SMS.

No SMS sent for TOTP Cannot bypass via cookies - server gates the button
03
Step 3

Enter 6-digit code

Portal POSTs { totp_code } to validate-totp-code/. IP-based rate limiting is applied.

Twilio call
Twilio Verify
Enter 6-digit code (login)
Server side

validate_totp_code looks up the user's confirmed TOTPDevice, then calls OTPDeliveryService.verify_totp_challenge(). Twilio creates a Challenge with the code as auth_payload and returns approved or denied.

Rate-limited per IP No code is ever stored - Twilio validates
04
Step 4

Login completed

Server calls login(), pops the pre-auth keys and stamps the MFA session with the TOTP backend.

Done
Authenticated
Dashboard after login
Browser redirects

Client receives { success: true, redirect_url } and navigates to the originally requested page (or /). The session now passes the MFA middleware.

3

Removal Flow (Profile)

User deregisters their authenticator app. The portal asks Twilio to delete the factor, removes the device row locally, and re-stamps the session with the user's SMS device so they keep working without re-login.

Account Settings Click "Remove" Confirm prompt Twilio deletes Factor Session → SMS Falls back to SMS ✓
01
Step 1

Click "Remove"

An inline confirmation prompt appears - no separate modal.

User action
Confirm intent
Active card with Remove button
What the user sees

The active green card switches to a red-bordered "Remove authenticator app?" panel with two buttons: "Yes, remove it" and "Cancel". Cancel restores the active state without any server call.

02
Step 2

Confirm - call Twilio

POST to remove-totp-device/. Portal calls OTPDeliveryService.delete_totp_factor().

Twilio call
Twilio Verify
Remove confirmation prompt
Server side

Twilio returns success or 20404 (factor already gone). Both are treated as success and the local TOTPDevice row is deleted. Any other Twilio error returns 503 and the DB record is preserved so the user can retry safely.

Twilio 20404 = treat as success Other Twilio error = no local delete
03
Step 3

Session re-stamped with SMS

The LOGIN_TOTP_VERIFIED flag is cleared and the MFA session is re-stamped with the user's SMS device.

Session
Continuity
Removing - Twilio call
Why re-stamp?

Without this the MFA middleware would see mfa_device=None on the very next request and try to start a new SMS challenge inline - producing a broken "Enter code" page in the middle of the profile save. Re-stamping keeps the user logged in seamlessly.

04
Step 4

Card flips back to "Not set up"

User can re-enroll later by clicking Enable again.

Done
Removed
Card back to Not set up
Account stays protected

SMS MFA continues to gate the account. The user remains logged in and can choose to re-enroll TOTP at any time.