Templates
Types
EN

2FA Flow Diagram Guide

How to draw a 2FA flow diagram that covers what real ones need — the half-authenticated state, lockout branches, and three ready-to-edit examples: TOTP, SMS, and enrollment.

Published on ·9 min read
2faauthenticationsequence-diagramtemplate

What is a 2FA flow diagram?

A 2FA flow diagram shows what happens between "the password was correct" and "the user is signed in" — the second factor. It is a sequence diagram, because the step involves several parties that are easy to conflate: your app, whatever verifies the code, and the thing that produces the code, which is usually not under your control at all.

The single idea that makes this diagram worth drawing is that 2FA splits login into two transactions, and creates a state between them. After the password check the user is neither logged out nor logged in. They are something else — half authenticated — and that state needs its own short-lived credential, its own expiry, and its own attempt counter. Diagrams that skip it draw an arrow straight from "password ok" to "enter your code" and quietly imply that the server is holding the user's identity in a variable somewhere. That implication is where most real 2FA vulnerabilities live.

The second reason is that this flow is mostly failure branches. The happy path is four arrows and nobody argues about it. What teams argue about — and what support tickets are made of — is: how many wrong codes before we lock the account, what happens when the SMS never arrives, what a user does when their phone is in a river, whether an old code can be replayed. Every one of those is a branch, and a branch you have not drawn is a branch someone will improvise in a hotfix.

This page draws only the second factor. The password step appears in the skeleton just to show where the handoff is.

What a 2FA flow diagram has to show

Six things. The first is the one that separates a real diagram from a marketing illustration:

  • The half-authenticated state, as a named thing. Draw the token the server hands back after the password check — pre_auth_token, mfa_challenge, whatever you call it — with its expiry on the arrow. If your diagram has no such token, your diagram is describing a session that already exists before the second factor.
  • Who generates the code and who verifies it. For TOTP these are two different parties that never talk to each other — the authenticator app and your server independently compute the same number from a shared secret. Drawing the app as a lifeline makes that obvious; describing it in prose never does.
  • The attempt counter and what happens when it runs out. "Wrong code" is not one branch, it is two: wrong-but-you-may-retry, and wrong-and-you-are-done. Put the numbers on the diagram.
  • The time window. A TOTP code is valid for a 30-second step, usually with one step of tolerance either side; an SMS code has a TTL you chose. Both belong on the diagram, because "why did a code that looked right get rejected" is a support question with a clock-skew answer.
  • Single use. A verified code must be marked consumed, or a code observed over someone's shoulder stays valid for the rest of its window. This is one self-arrow and it is the cheapest thing on this list to draw.
  • A way back in. A 2FA diagram with no recovery path is a diagram of a system that will lock out its own users. Recovery codes, a backup factor, or a support process — draw whichever one you actually have.

The skeleton, with no failure branches at all. Notice how short it is — that is the entire happy path, and it is why a diagram that stops here tells you almost nothing.

View the Mermaid source
sequenceDiagram
    participant User
    participant App as Web App
    participant Auth as Auth Service
    participant DB as User Store

    User->>App: username and password
    App->>Auth: verify credentials
    Auth->>DB: look up the password hash
    DB-->>Auth: hash and two_factor_enabled
    Auth-->>App: password ok, second factor required
    App->>User: ask for the 6 digit code
    User->>App: 123456
    App->>Auth: verify the code
    Auth-->>App: code ok, session issued
    App->>User: signed in
A minimal 2FA flow diagram: password check, second factor required, code entry, session issued.

How to draw a 2FA flow diagram

Three steps. Step 2 is where the diagram stops being a picture and starts being a review.

Step 1 · Decide which factor, and who owns it

The three common second factors produce three different diagrams, and the difference is entirely about which participants you do not control:

TOTP (authenticator app) — the code generator is an app on the user's phone with no network connection to you. It never appears in an arrow to your server. Your diagram has a lifeline that only ever talks to the user.

SMS — there is a third-party gateway in the middle, which means an extra participant, an extra failure mode (delivery), and an extra cost per attempt that makes resend rate limits a business decision, not just a security one.

Passkeys / hardware keys — the browser mediates, and the interesting arrows are challenge and signature rather than a code a human retypes. Different enough that it deserves its own diagram, not a branch in this one.

Pick one per diagram. If you support two, draw two — the moment you fold SMS fallback into a TOTP diagram, both paths get harder to read, and the fallback is exactly the path an attacker will aim for.

Step 2 · Draw the failures first

Unusually for a diagram, start from the bottom. Write down every way this can fail before you draw a single successful arrow:

- wrong code, retries remaining - wrong code, retries exhausted - correct code, but too late (outside the window) - correct code, but already used - correct password, wrong username — does the response differ? It must not - the code never arrived (SMS only) - the user no longer has the factor at all

Now draw the happy path, and hang each failure off the arrow where it is detected. Doing it in this order matters: build the success path first and the failures get bolted on wherever there is room, which produces a diagram that is technically complete and useless for finding the gap. Build the failures first and the gap is the branch you could not attach anywhere.

The fifth item is the one that catches people. If a wrong username and a wrong password produce visibly different responses — different text, different status, noticeably different timing — the login page is a username oracle, and the 2FA step is where this most often leaks, because it is tempting to say "this account requires a code" before the password is even checked.

Step 3 · Draw it

Describe it in sentences and let text2diagram lay it out — "after the password is verified the server returns a pre-auth token valid five minutes, the user reads a code from their authenticator app and submits it, the server checks it against the shared secret, marks it consumed and issues a session; after five wrong codes the pre-auth token is burned" comes back as an editable sequenceDiagram with the alt blocks already in place.

Or open one of the three below and rename the participants. The branch structure is the part worth keeping.

2FA flow diagram examples

Three diagrams: the common case done properly, the one with a third party in it, and the one that has to happen before either can.

1 · TOTP at sign-in, with lockout

Three details here are worth copying. The pre_auth_token with its five-minute life is the half-authenticated state from the list above, made concrete. mark this code consumed is the replay guard — one self-arrow, easy to leave out, and its absence means a shoulder-surfed code works for the rest of its window.

The third is the first branch: when the password is wrong, the response says nothing about whether 2FA is enabled. That has to be true even though the server already knows, and it is the reason the branch sits above the pre-auth token rather than after it. Diagrams that check 2FA status first are documenting an account-enumeration endpoint.

Note also that the authenticator app never has an arrow to the server. Both sides compute the same six digits from a secret they agreed on at enrollment, and never speak again.

View the Mermaid source
sequenceDiagram
    autonumber
    participant User
    participant App as Web App
    participant Auth as Auth Service
    participant TOTP as Authenticator App

    User->>App: username and password
    App->>Auth: verify credentials
    alt password wrong
        Auth-->>App: 401 - never reveal whether 2fa is on
        App->>User: generic sign in failed
    else password correct, 2fa enabled
        Auth-->>App: pre_auth_token, valid 5 minutes
        App->>User: enter the 6 digit code
        User->>TOTP: read the current code
        TOTP-->>User: 123456
        User->>App: 123456
        App->>Auth: verify code with pre_auth_token
        alt code matches inside the time window
            Auth->>Auth: mark this code consumed
            Auth-->>App: full session
            App->>User: signed in
        else wrong code, attempts left
            Auth-->>App: 401 with attempts remaining
            App->>User: try again
        else 5 failed attempts
            Auth->>Auth: burn the pre_auth_token
            Auth-->>App: 429 start over from the password
        end
    end
A TOTP two-factor sign-in diagram with a pre-auth token, code consumption, retry counting and lockout after five failures.

2 · SMS code, including the delivery that never happens

The extra participant changes the diagram in a way people underestimate. The gateway accepting your request (queued) is not the same event as the phone receiving the message, so there are two arrows, and the second one you cannot observe. That gap is why "resend" exists as a feature and why it needs a rate limit — drawn here as a note rather than a branch, because it governs the whole interaction rather than one step.

The failure branch is the one worth arguing about in review: when the gateway is down, does the user get a backup method or a dead end? Every SMS 2FA system needs an answer, and a surprising number of them answer "dead end" simply because nobody drew this arrow.

View the Mermaid source
sequenceDiagram
    autonumber
    participant User
    participant App as Web App
    participant Auth as Auth Service
    participant SMS as SMS Gateway
    participant Phone as User Phone

    App->>Auth: password verified, send a code
    Auth->>Auth: generate 6 digits, store the hash, ttl 5 min
    Auth->>SMS: send to the stored number
    alt gateway accepted it
        SMS-->>Auth: queued
        SMS->>Phone: your code is 123456
        User->>App: 123456
        App->>Auth: verify the code
        Auth-->>App: full session
    else gateway rejected or timed out
        SMS--xAuth: delivery failed
        Auth-->>App: 502 could not send
        App->>User: offer a backup method
    end
    Note over App,Auth: resend is rate limited - 1 per minute, 3 per hour
An SMS-based 2FA flow diagram with an SMS gateway participant, delivery failure branch and resend rate limiting.

3 · Enrollment — turning 2FA on

The flow everyone implements second and draws never. Two things in it are load-bearing.

The secret stays pending until a code proves it works. Activating 2FA the moment the QR code is displayed locks out every user who closes the tab before scanning — a self-inflicted support queue. The status pending on that self-arrow and the rejection branch at the bottom are the same decision seen from two sides.

Recovery codes are minted here or nowhere. This is the only moment in the entire system where the user is both authenticated and not yet dependent on the new factor, which makes it the only safe place to hand out the way back in. Note shown once — they are stored hashed, exactly like passwords, so this screen genuinely cannot be re-rendered later.

View the Mermaid source
sequenceDiagram
    autonumber
    participant User
    participant App as Web App
    participant Auth as Auth Service
    participant TOTP as Authenticator App

    User->>App: turn on two factor auth
    App->>Auth: start enrollment
    Auth->>Auth: generate a shared secret, status pending
    Auth-->>App: secret as a QR code
    App->>User: scan this code
    User->>TOTP: scan
    TOTP-->>User: 654321
    User->>App: 654321
    App->>Auth: confirm enrollment with that code
    alt code matches the pending secret
        Auth->>Auth: activate 2fa, mint 10 recovery codes
        Auth-->>App: recovery codes, shown once
        App->>User: save these somewhere offline
    else code does not match
        Auth-->>App: 400 - secret stays pending, 2fa still off
        App->>User: scan again
    end
A 2FA enrollment flow diagram: pending shared secret, QR scan, verification before activation, and one-time recovery codes.

Read only the half-authenticated span, and ask what it can do

Take the finished diagram and cover everything except the arrows between "password ok" and "session issued". In that span the user holds a credential that is not a session. Now ask three questions: can that credential reach any endpoint other than the verify one? Does it survive longer than the code it is waiting for? Can a second one be minted while the first is alive?

The answers should be no, no, and no. When they are not, the bug is a real one — an account where the password alone is sufficient if you call the right endpoint — and it is invisible in code review because the pre-auth token and the endpoints it can reach are usually in different files. On the diagram it is one covered rectangle.

FAQ

Continue reading

Try text2diagram now

Open the tool
← Back to all tutorials