Fix guide · medium · oauth_implicit_flow
OAuth uses implicit flow (response_type=token) — deprecated
Your authorize URL requests response_type=token. The OAuth 2.0 Security Best Current Practice (BCP) explicitly deprecates this — access tokens land in the URL fragment, where they leak via Referer headers, browser history, and analytics.
Why it matters
The implicit flow returns the access token directly to the browser as part of the redirect URL fragment (#access_token=...). It was originally designed for SPAs that couldn't securely store a client_secret — but it has well-documented leak channels:
- Browser history — the URL with the token in the fragment is stored.
- Referer headers — even though fragments aren't normally sent in Referer, many SPA frameworks parse the fragment, then
history.replaceStateto a different URL that carries the token in *its* state, which then leaks. - Analytics and RUM — front-end analytics tools often capture the full URL including fragments.
- Browser sync — Chrome/Firefox sync history (and tokens in URLs) across the user's devices.
- Embeddable share buttons — Twitter/Facebook share widgets routinely capture full URL.
Modern OAuth (RFC 8252, OAuth 2.1 draft) standardizes on authorization-code flow with PKCE for SPAs and mobile apps:
- The code-exchange step happens after the redirect, via JavaScript.
- PKCE (Proof Key for Code Exchange) binds the code-to-token exchange to the original flow without a client_secret.
- Tokens never appear in URLs; they live in memory or HttpOnly cookies.
Every major OAuth provider (Google, GitHub, Microsoft, Auth0, Okta) supports PKCE. There is no scenario where implicit flow is the right choice for a new build.
How to fix it
Switch to authorization-code flow with PKCE:
- Change
response_type=tokentoresponse_type=codein your authorize URL. - Generate a PKCE code_verifier (random 43–128 char string) and its SHA-256 challenge:
``js const verifier = base64url(crypto.getRandomValues(new Uint8Array(32))); const challenge = base64url(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier))); ``
- Add
code_challenge=<challenge>&code_challenge_method=S256to the authorize URL. - Store the
verifierin sessionStorage (NOT localStorage — the latter persists across tabs). - On callback, POST to the token endpoint with
grant_type=authorization_code, the receivedcode, and the originalcode_verifier. - Use a battle-tested OAuth library:
oauth4webapi(vanilla),@auth/core(Auth.js),react-oidc-context(React) — they handle PKCE correctly out of the box.
Don't roll this yourself. OAuth is one of those protocols where every state machine subtlety is the difference between secure and pwned.
Did vibecheck flag this on your app?
If you reached this page from a vibecheck inspection report, the redacted match in your scan output is the exact string we found in your bundle. After applying the fix above, run the inspection again — the finding should clear.
Run another inspection