Blog · Guides

The implicit flow is deprecated: migrating to code flow with PKCE

Why OAuth removed the implicit flow, what breaks when tokens arrive in the URL fragment, and how to move single-page, native and TV apps to code flow with PKCE.

Casdoor Team · November 3, 2026 · 5 min read

This guide is written by the team that builds Casdoor and describes the standards as published (listed under Sources), with notes on how Casdoor implements them as of November 3, 2026. Spotted a mistake? Tell us.

The implicit flow was the original answer to a real problem: how a browser-only app gets tokens when it cannot keep a client secret. The authorization server returns tokens directly in the redirect, with response_type=token (OAuth) or response_type=id_token (OpenID Connect), and the client never calls the token endpoint.

It solved the problem for its era, and then the security guidance changed. The OAuth 2.0 Security Best Current Practice (RFC 9700) tells clients not to use it, and OAuth 2.1 removes it from the specification entirely. New applications should not be built on it.

Why implicit was deprecated

The flow puts tokens in the URL fragment:

https://app.example.com/callback#access_token=...&token_type=Bearer&expires_in=3600

That design has consequences that cannot be fixed with configuration:

  • Tokens end up in places URLs are recorded. Browser history, proxy logs, crash reports, analytics and screenshots can all capture the fragment.
  • There is no code to bind. The authorization code flow with PKCE binds the exchange to the client instance that started the flow. Implicit returns the token immediately, so there is nothing to bind.
  • Any script in the page can read the token. A single XSS bug exposes the access token without needing to steal a code and exchange it.
  • There is no refresh token in OpenID Connect implicit. Sessions end abruptly, which pushes apps toward longer-lived access tokens — the opposite of what you want.
  • The token response is harder to validate. There is no back-channel exchange where the client can authenticate itself and prove the response belongs to its request.

None of this means implicit is instantly exploitable in every app. It means the flow has no safe configuration for new work, and every mitigation is a workaround.

What replaced it

The replacement is the authorization code flow with PKCE:

  1. The client creates a code verifier and sends its SHA-256 hash as code_challenge.
  2. The browser is redirected with response_type=code.
  3. The authorization server redirects back with a short-lived code.
  4. The client exchanges the code plus the verifier at the token endpoint.
  5. The server returns tokens, and the verifier proves the exchange came from the same client instance.

Public clients still have no client secret, and that is fine: PKCE is the proof. RFC 9700 recommends PKCE for confidential clients too, and OAuth 2.1 requires it for every authorization code flow.

For applications that should never see tokens in the browser at all, use a backend for frontend (BFF): the browser holds a session cookie, the backend holds the tokens, and the token endpoint is called server-side. This is the strongest option for a web app that handles sensitive data.

Migration by client type

Client Old New
Single-page app response_type=token response_type=code + PKCE, or BFF
Native desktop / mobile implicit or bespoke code + PKCE with loopback redirect (RFC 8252)
TV, CLI, device without a browser password or implicit device authorization grant (RFC 8628)
Server-to-server user flow client credentials
Legacy app that cannot change implicit put it behind a gateway, shorten token lifetimes, add a strict CSP

The migration is usually smaller than it looks. Most maintained OAuth and OIDC libraries already support code flow with PKCE; the work is configuration plus a redirect URI change. The harder cases are old SDKs pinned to implicit and apps that relied on the token in the fragment for client-side routing.

How to tell whether you still use it

  • Search the codebase for response_type=token, response_type=id_token, response_type=token id_token.
  • Look at authorization requests in the browser network panel. If the redirect comes back with #access_token=, the app is using implicit.
  • Check the OAuth library: options named "implicit", "token flow" or "web flow without backend" usually mean implicit.
  • Check the provider's discovery document for response_types_supported. A provider listing implicit types is not forcing you to use them; it is keeping old clients working.

Common migration mistakes

  • Keeping a client secret in a SPA. A public client must not ship a secret. Use PKCE and token_endpoint_auth_method: none.
  • Using PKCE with the plain method. Use S256. The plain method offers little protection.
  • Losing the verifier between redirects. Store it in memory or session storage for the duration of the flow, and generate a new one per request.
  • Mixing up state and PKCE. state protects against CSRF; PKCE protects the code exchange. Keep both.
  • Forgetting the exact redirect URI. Code flow is less forgiving than implicit; register exact URIs, including loopback ports for native apps.
  • Assuming a token endpoint means a confidential client. Public clients call the token endpoint too; PKCE is what makes that safe.

Implicit in Casdoor

Casdoor's discovery document still advertises the implicit response types (token, id_token and their combinations) so that older clients keep working, and it supports the authorization code flow with PKCE using S256.

For new applications:

  • Set the application to use the authorization code grant.
  • Use response_type=code with code_challenge and code_challenge_method=S256.
  • For a SPA, use token_endpoint_auth_method: none; for a server-side app, use the client secret or a private key JWT.

The endpoints and application settings are in the Casdoor OIDC documentation. Casdoor is open source, and Casdoor Cloud starts at $6.67/month billed yearly for a dedicated instance.

FAQ

Is implicit flow insecure by definition? It removes several protections that code flow with PKCE provides, and it exposes tokens to the browser's URL handling. That is enough for the OAuth security guidance to retire it for new clients.

Can I keep implicit for an internal app? You can, but you inherit the same risks and you will have to migrate eventually. If the app cannot change, shorten token lifetimes and add a strict Content Security Policy while you plan the move.

Does code flow with PKCE need a backend? No. A single-page app can do the code exchange in the browser with PKCE and no client secret. A backend-for-frontend is still better when you want tokens out of the browser entirely.

What about refresh tokens in a SPA? Use short-lived access tokens and refresh token rotation, or move tokens to a BFF. Never store a long-lived refresh token in local storage.

Is OAuth 2.1 final? The draft is the consolidation of the security best practices, and RFC 9700 already recommends the same direction. Treat implicit as legacy regardless of when the final specification lands.