Vulnerability research · Deep dive

Magical 2FA Bypass: two factors in the UI, one in the API

During an offensive application assessment, Uzair Kaaf came across a login endpoint that returned a fully privileged session token right after checking username and password, even though TOTP-based two-factor authentication was enabled for the account. The web interface duly asked for the code. By then, the API had already opened the door.

Class
CWE-287 · CWE-304
Severity
CVSS 3.1: 7.4 (High)
Prerequisite
The victim's valid password
Impact
Full account takeover
At a glance

The second factor existed only in the UI

Two-factor authentication is meant to stop a stolen password from opening an account on its own. Here it only stopped people using a browser. Anyone talking to the API directly did not need the second factor at all.

  • 01

    What happens

    The login endpoint checks username and password and immediately issues a fully privileged session token. The fact that a TOTP code is still missing appears only as a hint in the same response.

  • 02

    What the attacker needs

    The victim's username and password, for example from phishing, data leaks or credential stuffing. The attacker does not need the victim's phone or authenticator app.

  • 03

    What the attacker gains

    Everything the victim's account can do. In the tested case, it held the workspace_admin role: the attacker could have exported tenant data, changed billing, edited profile data and regenerated security keys.

  • 04

    How to classify it

    As Improper Authentication (CWE-287) and Missing Critical Step in Authentication (CWE-304). The underlying pattern is known as Client-Side Enforcement of Server-Side Security (CWE-602).

Severity per CVSS 3.1

7.4High

VectorCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N (opens external website)

Attack complexity is rated high because the attacker must first obtain the victim's password; the CVSS specification cites gathering such secrets as an example of high complexity. No privileges of their own are required, because without the second factor the password alone is not supposed to unlock anything. Everything else works over the network, without any action by the victim.

CWE-287 (opens external website)CWE-304 (opens external website)

99.22%

lower risk of compromise: that is what MFA achieved in an analysis of Microsoft Azure Active Directory accounts. A bypass like this one switches off exactly that protection.

How effective is multifactor authentication at deterring cyberattacks? (May 2023) (opens external website)

The affected platform is not named. Host names, identifiers and tokens on this page are sanitized; flows and responses match the finding.

The pattern

Where the check should have happened

Single-page applications and mobile apps talk to the back end through REST or GraphQL APIs. It becomes dangerous when part of the access control is left to client-side logic.

Observed pattern

  1. ClientSends username and password to the login endpoint.
  2. ServerResponds with the full session token plus a hint that the second factor is still missing.auth_token (scope: admin_access) + "two_factor_required": true
  3. Web interfaceReads the hint, shows the input field for the six-digit code and waits.
  4. AttackerIgnores the input field and uses the token directly against the API.Access without the second factor

The second-factor requirement lived in the presentation layer only, not in the protocol.

Secure pattern

  1. ClientSends username and password to the login endpoint.
  2. ServerResponds only with a short-lived challenge token that is good for nothing but the code check.challenge_token (scope: auth:mfa_challenge, 300 s)
  3. ClientSends the TOTP code together with the challenge token.
  4. ServerVerifies the code and only then issues the fully scoped session token.auth_token (scope: user:authenticated)

Without a valid code, no usable token exists.

The browser behaved correctly: it honored the flag and blocked navigation. But because the server issued the token at the same time, the entire protection depended on every client playing along voluntarily.

Proof of concept

The attack path in four steps

Requests and responses are sanitized; flow and content match the finding.

An account with TOTP enabled

TOTP-based two-factor authentication was enabled in the target account's security settings. Sign-in attempts in the browser confirmed it: without the six-digit code, the interface unlocked nothing.

Reproduce only on your own systems or with the operator's written permission.

Root cause

Two design flaws, one result

The flaw is not in a single line of code but in how the sign-in flow is designed.

1

The client-side enforcement trap

The back end assumed that every call goes through the intended interface and that the interface stops the flow as soon as two_factor_required is true. An attacker simply writes their own client.

2

No short-lived challenge token

The authentication service did not separate the “password verified” phase from the “session granted” phase. A full session token must not be issued until every factor has been verified.

Why the flaw goes unnoticed in daily use

For users, everything works as intended, and every response is an unremarkable 200 OK. The flaw only becomes visible when someone calls the endpoint without the interface and reads the response field by field.

Remediation

How to close the gap

The first measure fixes the root cause. The other two make sure the flaw does not come back somewhere else.

  1. 01

    Two-step login with a short-lived challenge token

    When the second factor is enabled, the login endpoint issues only a challenge token: short-lived (about five minutes), single-use, bound to the account and the sign-in attempt, and valid for the code check only. The session token is created in the second step.

    Two-phase flow
    POST /api/v5/login{ "username": "…", "password": "…" }→ 200 { "data": { "challenge_token": "ct_…", "scope": "auth:mfa_challenge", "expires_in": 300 } }POST /api/v5/login/verify-2fa{ "challenge_token": "ct_…", "totp_code": "123456" }→ 200 { "data": { "auth_token": "eyJ…", "scope": "user:authenticated" } }
  2. 02

    Enforcement in gateway and middleware

    The API gateway or middleware checks the token's claims at every entry point. Protected routes accept only tokens with the user:authenticated scope; a challenge token (auth:mfa_challenge) gets 401 or 403 there. If the session token also records which factors were verified, for example in the amr claim, every service can check this on its own.

    Middleware in front of protected routes
    if (!String(token.scope ?? '').split(' ').includes('user:authenticated')) {  return res.status(401).end()}const user = await users.findById(token.sub)if (user.mfaEnabled && !token.amr?.includes('otp')) {  return res.status(403).end()}
  3. 03

    Automated regression tests

    End-to-end tests log in an account with an active second factor through a raw HTTP client on every login path and make sure no protected endpoint is reachable without the code. That way, a regression surfaces in the next release rather than with the next attacker.

    Regression test (pytest)
    LOGIN_PATHS = ["/api/v5/login", "/api/v4/login"]  # every login route, incl. legacy versions@pytest.mark.parametrize("path", LOGIN_PATHS)def test_login_without_second_factor_yields_no_session(client, mfa_user, path):    r = client.post(path, json=mfa_user.credentials)    assert r.status_code == 200    data = r.json()["data"]    assert set(data) <= {"challenge_token", "scope", "expires_in"}    assert data["scope"] == "auth:mfa_challenge"    p = client.get("/api/v5/users/me/profile", headers={"Authorization": "Token " + data["challenge_token"]})    assert p.status_code in (401, 403)

Additional hardening

  • Treat every login path the sameOlder API versions, mobile apps, SSO, token refresh and password reset need the same check. The OWASP guidance on MFA explicitly mentions separate APIs that users can log in through.
  • Limit code attemptsStrict limits on failed code verifications prevent a six-digit code from being brute-forced.
  • Keep challenge tokens narrowDiscard them after a successful check or a few failed attempts, and never accept them in place of a session token.
  • Log unusual flowsSuccessful password checks without a subsequent code check, followed by access to sensitive endpoints, are a strong signal for monitoring and the SOC.
  • Test endpoints in isolationPentests and API security tests should exercise authentication endpoints without the interface and inspect every response for prematurely issued tokens.

References: Multifactor Authentication Cheat Sheet (opens external website) · SP 800-63B-4, Digital Identity Guidelines: Authentication and Authenticator Management (July 2025) (opens external website) · RFC 6238: TOTP: Time-Based One-Time Password Algorithm (May 2011) (opens external website) · RFC 8176: Authentication Method Reference Values (June 2017) (opens external website)

Self-check

Is your API affected?

Eight questions for development and security teams. The self-check is no substitute for testing, but it shows where to look more closely. Your answers stay in your browser.

  1. 01With the second factor enabled, does your login endpoint return nothing but a short-lived challenge token?
  2. 02Does every protected route reject a token without a completed second factor with 401 or 403?
  3. 03Does the session token verifiably record which factors were checked, for example in the amr claim?
  4. 04Do the same rules apply to every login path: older API versions, mobile apps, SSO, token refresh and password reset?
  5. 05Is the challenge token single-use, bound to the account and the sign-in attempt, and expired after a few minutes?
  6. 06Do you limit failed attempts at code entry?
  7. 07Are there automated tests that run the login through a raw HTTP client without the second factor?
  8. 08Have your authentication endpoints ever been tested in isolation, that is, without the interface?

Answered: 0/8

The result is guidance, not evidence of compliance.

GRC and audit view

When MFA only works on paper

Many frameworks require multi-factor authentication or strong access controls. A bypass like this one means the control is documented but not effective.

ISO/IEC 27001

The “Secure authentication” control requires secure authentication procedures, and an ISMS must evaluate the effectiveness of its controls. Audits should therefore look at the API's login flow too, not just the configuration of the interface.

ISMS per ISO 27001

Cyber Resilience Act

From 11 December 2027, manufacturers of products with digital elements must protect them against unauthorized access through appropriate controls such as authentication. Reporting obligations for actively exploited vulnerabilities have applied since 11 September 2026. Pure SaaS offerings are usually out of scope; distributed apps and software are in scope, including the manufacturer's back-end services without which they could not perform one of their functions.

Regulation (EU) 2024/2847 (Cyber Resilience Act) (opens external website)

CRA compliance

This overview is not legal advice.

Whitepaper · free

Magical 2FA Bypass – Effective MFA for CISOs

What the Magical 2FA Bypass teaches about API authentication, prepared for decision-makers: pattern and login paths, two-phase login and token validation, testing and evidence, questions for suppliers, the regulatory view and a 30-60-90-day plan.

Cover of the whitepaper “Magical 2FA Bypass – Effective MFA for CISOs”
PDF · 22 pagesEnglish & GermanAuthor: Uzair Kaaf
  • Ten key messages for the board and internal audit
  • Requirements for challenge tokens, gateway and amr checks
  • Test catalogue for every login path and questions for SaaS vendors
  • Regulatory view on NIS2, ISO 27001, GDPR and the CRA
Free Download

Request Whitepaper

Magical 2FA Bypass – Effective MFA for CISOs

Language version *
Keep exploring

Related research

Frequently asked questions

Short answers

What is a 2FA bypass?

A way to use an account protected by two-factor authentication without presenting the second factor. In the Magical 2FA Bypass, the login API issued the session token right after the password check.

Does the attacker need the victim's phone?

No. Username and password are enough, which is exactly the scenario the second factor is meant to protect against.

How do I check whether my application is affected?

Log in a test account with an active second factor through a raw HTTP client at the login endpoint and read the full response. If it contains a token that opens protected endpoints without the code, the application is affected. Only test your own systems, or systems you have written permission to test.

Is it enough to remove the token from the login response?

No. Every protected endpoint must also reject tokens without a completed second factor. Otherwise, the flaw comes back through another path, such as an older API version or token refresh.

Which platform was affected?

We do not disclose that. All host names, identifiers and tokens on this page are sanitized.

How does the finding relate to the OWASP API Security Top 10?

It falls under API2:2023 Broken Authentication. The OWASP guidance on MFA explicitly names separate login APIs as a place where MFA should apply as well, or other protections should be in place.

How secure is your login flow?

We test authentication endpoints the way attackers do and show you where a token is created too early.