diagram.mmd — sequence
Two Factor Authentication sequence diagram

Two-factor authentication (2FA) adds a second verification step to the login process, requiring users to prove both something they know (their password) and something they have (a physical device or app). This dramatically reduces account takeover risk: even if a password is compromised in a data breach, an attacker cannot log in without also controlling the user's second factor.

The most common second factor for developer-focused apps is TOTP (Time-based One-Time Password), defined in RFC 6238. During enrollment, the server generates a shared secret and presents it to the user as a QR code. The user scans it with an authenticator app (Google Authenticator, Authy, 1Password). Both the server and the app independently compute a 6-digit code by running HMAC-SHA1 over the shared secret and the current 30-second time window. Because both sides use the same secret and the same timestamp, they produce the same code without any network communication.

The login flow is a two-step process. First, the user submits their username and password. If valid, rather than issuing a session or token immediately, the server transitions the user to a partial authentication state — a temporary, limited session that permits only the 2FA verification endpoint. The server asks for the TOTP code.

The user opens their authenticator app, reads the 6-digit code, and submits it. The server computes the expected TOTP for the current time window (and optionally the previous window to handle clock drift), compares it to the submitted code, and checks that this code hasn't been used before (preventing replay attacks within the same 30-second window). If valid, the server upgrades the partial session to a full authenticated session.

Recovery codes — a set of pre-generated single-use backup codes — should always be provided at enrollment time to prevent lockout if the user loses their authenticator device. For passwordless alternatives, see Magic Link Login.

Free online editor
Edit this diagram in Graphlet
Fork, modify, and export to SVG or PNG. No sign-up required.
Open in Graphlet →

Frequently asked questions

Two-factor authentication adds a second verification step to login, requiring users to prove both something they know (their password) and something they have (an authenticator app or device). This means a stolen password alone is not sufficient to compromise an account — the attacker also needs physical access to the second factor.
During enrollment, the server generates a shared secret and presents it as a QR code for the user to scan with an authenticator app. Both the server and the app independently compute a 6-digit code by running HMAC-SHA1 over the shared secret and the current 30-second time window. Because both sides use the same inputs, they produce the same code with no network communication required.
After a user submits valid credentials, the server does not immediately grant full access. Instead it creates a temporary partial session that permits only the 2FA verification endpoint. This prevents an attacker who steals the session from bypassing the second factor. Full session access is only granted after the TOTP code is successfully validated.
Provide pre-generated single-use recovery codes at enrollment time. When a user cannot access their authenticator, they can enter a recovery code to authenticate and regain access. Recovery codes should be treated as highly sensitive credentials — store only hashed versions server-side and advise users to keep them in a password manager or secure physical location.
mermaid
sequenceDiagram participant User participant Client as Browser participant Server participant AuthApp as Authenticator App User->>Client: Submit username and password Client->>Server: POST /auth/login with credentials Server-->>Server: Verify password hash alt Password invalid Server-->>Client: 401 Unauthorized else Password valid, 2FA enabled Server-->>Server: Create partial session (2FA pending) Server-->>Client: 200 - prompt for 2FA code Client-->>User: Show 2FA code input User->>AuthApp: Open authenticator app AuthApp-->>AuthApp: Compute TOTP from shared secret + current time AuthApp-->>User: Display 6-digit code User->>Client: Enter 6-digit TOTP code Client->>Server: POST /auth/verify-2fa with code Server-->>Server: Compute expected TOTP Server-->>Server: Check code not already used (anti-replay) alt Code valid Server-->>Server: Upgrade to full authenticated session Server-->>Client: 200 OK - set session cookie Client-->>User: Redirect to dashboard else Code invalid or expired Server-->>Client: 401 - invalid 2FA code end end
Copied to clipboard