How to test 2FA/TOTP login: what an agent can actually verify
Most guides on how to test 2FA/TOTP login start by generating the code. That's the least interesting part. Here's how to test the enforcement, lockout, backup codes, and disable flow around TOTP in plain English — and an honest note on what a browser agent can and can't do with a rotating code.
How to test 2FA/TOTP login: what an agent can actually verify
Every guide on how to test 2FA/TOTP login opens the same way: here's how to generate the six-digit code in your test. Store the shared secret as a fixture, run it through an otpauth library, type the result into the form. That's genuinely useful for a scripted suite, and it's also the least interesting thing about a two-factor login. The code generation is the part that already works — it's a well-specified algorithm with a dozen libraries. The bugs don't live there. They live in everything the app does around the code: whether it actually requires the second factor, whether it locks out after a hundred wrong guesses, whether a used backup code still works, whether "remember this device" quietly turns the whole thing off. That's the surface this playbook tests, and most of it doesn't need a valid TOTP at all.
The honest limit, stated first
A browser agent driving a real Chromium session cannot compute a rotating TOTP code on its own. TOTP is, per OWASP's testing guide, "a code based on HMAC of a secret and the current time" — a fresh six digits every 30 seconds derived from a secret you'd have to hold and a clock you'd have to track. Without a code-generating tool wired in, the agent has no way to produce the number your authenticator app would show. So if you're picturing an agent that scans the QR code, becomes your authenticator, and logs in through 2FA end to end on its own — that's not the honest pitch, and any guide implying it is glossing over the mechanism.
Here's what changes once you accept that: almost none of the interesting 2FA tests require the agent to generate a live code. Enforcement, lockout, backup-code behavior, the disable/reset flow, the UX of a wrong code — every one of those is testable from the outside with either no code, a deliberately wrong code, or a static fixture value you supply. TOTP has one companion secret that a browser agent can use perfectly well: the single-use backup codes the app hands you at setup. They're just strings. Provide one as a scenario credential and the agent can complete a real authenticated login without ever computing a time-based number. So the real testable surface is wide — it's just not the part the tutorials fixate on.
Prompt 1: is the second factor actually enforced?
The first and most important 2FA test has nothing to do with codes. It's whether the app can be walked past the second step entirely. OWASP names the classic bypass directly: "it may be possible to bypass it by completing the first step of the authentication process (entering the username and password), and then force-browsing to the application or making direct API requests without completing the second stage (entering the MFA code)." If entering the password gets you a session and the 2FA screen is just a page you can navigate away from, the second factor is decoration.
Step 3 is the whole test. A screenshot of the code-entry screen proves nothing; what matters is what the protected request returns while you're stuck on that screen. The agent watches the network, so a /dashboard that quietly returns 200 with real data after only the password is the bypass, reported as one — not hidden behind a redirect that looked fine to a human. This is the same reason an agent finds the bugs a script walks past: the script was written to enter the code, so it never tries the door that skips it.
While you're here, OWASP's other enforcement note is worth a second scenario: "all of the different login methods should be reviewed, to ensure that MFA is enforced consistently." If your app has a separate mobile login, an API sign-in, or a federated path, 2FA has to hold on every one of them. The agent can't test your native app, but it can hit an alternate web login route or an API login endpoint and confirm the second factor isn't optional there.
Prompt 2: lockout and the wrong-code path
A six-digit code has a million possibilities, which sounds like a lot until you realize it isn't if the endpoint accepts unlimited guesses. OWASP's MFA testing checklist leads with exactly this: "Is the account locked out after multiple failed MFA attempts? Is the user's IP address blocked after multiple failed MFA attempts across different accounts? Are failed MFA attempts logged?" You test all three with codes you know are wrong, so no generation needed.
"No lockout at all" is the finding, and it's common, because rate-limiting the second factor is an easy thing to forget when the happy path works on the first try. The agent gives you the concrete number — where the throttle kicks in, or that it never does — which is the difference between a checkbox and evidence.
If your app sends codes by email or SMS instead of (or alongside) TOTP, there's a second wrong-path bug worth a scenario, and the agent can drive it because it involves requesting codes, not computing them. OWASP flags two things to check: whether "multiple OTPs are valid at once, or generating a new code invalidates the previous one," and whether there's "sufficient rate-limiting to prevent an attacker requesting large numbers of codes." The MFA cheat sheet's rule is that on "resend, generate a new OTP and overwrite the old record" — so an agent can hammer the "resend code" button a dozen times and report whether the app throttles it (correct) or cheerfully fires a dozen emails that each cost you money and, worse, whether every one of those codes still works. A pile of simultaneously-valid codes is a bigger brute-force surface than the six digits suggest.
Prompt 3: backup codes, and whether "used" means used
This is where a browser agent completes a real authenticated 2FA login, because backup codes are static strings you can hand it as a credential. OWASP's requirement for them is precise: recovery codes "can only be used once," must have "brute-force protection… (such as account lockout)," and the user should be "notified… when a code is used." The single-use rule is the one that breaks in practice.
Substitute a real backup code from your test account. If step 3 logs you in, the app is treating single-use codes as reusable, which turns the recovery path into a permanent bypass. Because backup codes are static, this is a flow the agent can drive fully — the same durable, intent-described approach the magic-link auth playbook and the password-reset playbook use for the auth flows that get rebuilt most often.
Prompt 4: the disable/reset flow, and "remember this device"
The riskiest 2FA code path is the one that turns 2FA off. OWASP's cheat sheet is blunt that changing or removing a factor must "require reauthentication with an existing enrolled factor before allowing changes" and must "not rely solely on the active session, as it may be hijacked." In other words: an attacker who steals a live session should not be able to silently disable your second factor from the settings page.
"Remember this device" is where teams accidentally ship an off switch labeled as a convenience. It should scope one browser as trusted; it should not mean "never ask again, anywhere." The agent reports which one you built. And the disable-without-reauth case is the quiet one — it looks fine in a demo because the demo user is legitimately logged in, and it's a full account-takeover primitive the moment a session leaks.
Reading the results
Every run returns a Monito Session: a screenshot timeline of each state — password step, code-entry, wrong-code lockout, backup-code login, settings — plus the network log with every request and its status code. For 2FA the network log is the evidence that matters, because the whole question is server-side enforcement: each network event is captured with its method, URL, and status, so a protected page that returned 200 after only the password is provably a bypass, and a clean 401/302 is provably a pass. None of the prompts above names a selector — only the behavior — which is why they survive the frequent redesigns that auth screens go through, the same describe-the-intent durability that makes agent runs hold up where scripts rot. For where 2FA sits in the broader auth surface, the signup-flow and session-timeout playbooks cover the neighbors.
The Monito CLI reads your account and Environment config; the docs on authenticating the CLI cover wiring a staging Environment and test credentials so these scenarios point at the right build.
The one to run right now
If you run a single 2FA check today, run the enforcement probe — it needs no code, and it proves the one property that makes the whole feature real: that the second factor can't be skipped. Save it as a Test Scenario, point it at staging, and wire it into CI on every deploy that touches auth or middleware.
A full run is typically 8–13 credits — roughly $0.08–$0.13 — and your first run is free. Monito plans start at $99/mo, with Enterprise at $129/mo. One honest caveat, to match the one at the top: this tests 2FA from the outside, the way a user or attacker meets it, which is exactly the coverage scripted suites skip — but it's not a substitute for a code-level review of your TOTP verification and secret storage. Treat it as the every-deploy proof that the second factor you shipped still actually stands between the password and the account.