# OAuth 2.1 vs OAuth 2.0: What Changed and How to Migrate

> OAuth 2.1 folds a decade of security fixes into one spec: PKCE for everyone, no implicit or password grants, and stricter rules for tokens and redirects.

URL: https://www.matthewswong.com/en/blog/oauth-2-1-vs-2-0-migration/

- Author: Matthews Wong (https://www.matthewswong.com/en/about/)
- Published: 2026-08-01
- Updated: 2026-08-01
- Category: Security

> **Key takeaway:** OAuth 2.1 consolidates OAuth 2.0 and its security best practices into one specification. It makes PKCE mandatory for every client using the authorization code flow, removes the implicit and password grants, bans bearer tokens in URL query strings, requires exact redirect URI matching, and mandates refresh token rotation for public clients.

OAuth 2.0 was never a single document. It was RFC 6749 from 2012, the bearer token rules in RFC 6750, and then a decade of separate best-practice notes patching holes that the original framework left open. If you learned OAuth from a tutorial written in 2015, you almost certainly learned patterns that the community has since quietly retired. OAuth 2.1 is the cleanup: one specification that keeps what works, deletes what proved dangerous, and folds the security guidance directly into the normative text.

I have shipped enough integrations to know the gap between what the old spec allowed and what you should actually do in production. OAuth 2.1 closes that gap by making the safe path the only path. This post walks through exactly what changed, shows an accurate Authorization Code plus PKCE exchange, and gives you a migration order you can follow without breaking live clients.

## OAuth 2.1 is a consolidation, not a reinvention

The draft is explicit that it replaces and obsoletes RFC 6749 and RFC 6750, consolidating the information from those documents and the security best current practice into a single Standards Track specification while removing features found to be insecure. There is no new token format, no new core grant, and no new endpoint. If you already run the authorization code flow correctly today, most of your system is already OAuth 2.1 compliant. The work is in what you must stop doing.

## PKCE is now mandatory for every client

Under OAuth 2.0, Proof Key for Code Exchange was an add-on, recommended mainly for mobile and single-page apps that could not keep a secret. OAuth 2.1 promotes it to a baseline requirement: PKCE is required for all clients using the authorization code flow, confidential web servers included. The client generates a random verifier, sends only its hash as a challenge on the authorization request, and reveals the verifier when it redeems the code. An attacker who intercepts the code cannot exchange it without the original verifier.

> The example verifier and challenge values above are the canonical test vectors from RFC 7636 Appendix B. Use them to sanity-check your own S256 implementation: hashing that exact verifier with SHA-256 and base64url-encoding it must reproduce that exact challenge, or your encoding is wrong.

## Two grant types are gone

OAuth 2.1 omits two flows that RFC 6749 defined. Both leaked tokens or credentials in ways no configuration could fully fix, so instead of documenting workarounds the spec simply removes them.

- The implicit grant, where the authorization server returned an access token directly in the redirect fragment, is omitted. Tokens in the URL end up in browser history, logs, and referrer headers. Browser apps now use the authorization code flow with PKCE instead.
- The resource owner password credentials grant, where the app collected the user's actual username and password and sent them to the token endpoint, is omitted. It defeats the entire point of delegated authorization and blocks federation and multi-factor prompts.

If you have either flow in production, that is the migration that carries real risk, because it changes how clients obtain tokens. Everything else in OAuth 2.1 is mostly tightening rules you were probably already close to following.

## Tokens and redirects get stricter rules

Three smaller but load-bearing changes harden the parts of OAuth that attackers target most.

- Bearer tokens must not travel in a URL query string. RFC 6750 permitted a query parameter; OAuth 2.1 removes that option, so access tokens live only in the Authorization header or a form-encoded request body where they stay out of logs and history.
- Redirect URIs must be compared using exact string matching. The old habit of allowing a prefix or wildcard match is out, because it opened the door to open-redirect and token-theft attacks. Register every callback URL in full.
- Refresh tokens for public clients must be sender-constrained or one-time use. In practice that means rotation: each refresh returns a new refresh token and invalidates the old one, so a stolen refresh token stops working the moment the legitimate client uses the next one.

> Exact redirect URI matching breaks integrations that relied on dynamic query parameters appended to the callback. Move any per-request data into the state parameter or PKCE-protected session, not the redirect URI, before you flip the switch, or your callbacks will start getting rejected.

## OAuth 2.0 vs OAuth 2.1 at a glance

| Aspect | OAuth 2.0 | OAuth 2.1 |
| --- | --- | --- |
| PKCE | Optional, recommended for public clients | Required for all clients using the authorization code flow |
| Implicit grant (token in redirect) | Defined and allowed | Removed from the specification |
| Password grant (owner credentials) | Defined and allowed | Removed from the specification |
| Bearer token in a URL query string | Permitted by RFC 6750 | Not allowed; header or request body only |
| Redirect URI matching | Loose matching common in practice | Exact string matching required |
| Refresh tokens for public clients | Long-lived and reusable | Must be sender-constrained or one-time use |
| Base documents | RFC 6749 plus RFC 6750 plus separate notes | One spec that obsoletes 6749 and 6750 |

## Why MCP chose OAuth 2.1

The reason OAuth 2.1 suddenly matters to a lot of developers is the Model Context Protocol. MCP is how AI clients connect to external tools and data servers, and its authorization spec is built directly on OAuth 2.1. The MCP specification states that authorization servers must implement OAuth 2.1 with appropriate security measures for both confidential and public clients, and that MCP clients must implement PKCE according to OAuth 2.1. It pins the exact reference: draft-ietf-oauth-v2-1-13.

In MCP terms, a protected MCP server acts as an OAuth 2.1 resource server, the MCP client is an OAuth 2.1 client, and access tokens travel only in the Authorization header, never in a query string. MCP also layers on resource indicators from RFC 8707 so a token is bound to the specific server it was issued for, and it requires refresh token rotation for public clients. If you build an MCP server, you are implementing OAuth 2.1 whether you set out to or not.

## How I would migrate

You do not need a big-bang rewrite. Migrate in an order that removes the riskiest patterns first and leaves the compatible tightening for last.

- Add PKCE to every authorization code client, including confidential server-side apps. It is backward compatible with servers that already support it, so you can roll it out before anything else changes.
- Retire the implicit and password grants. Move browser and mobile apps to authorization code with PKCE, and move machine-to-machine clients to the client credentials grant.
- Stop reading bearer tokens from query strings and reject them there. Accept access tokens only from the Authorization header or a form-encoded body.
- Register exact redirect URIs and turn on refresh token rotation for public clients, watching your logs for callbacks that relied on loose matching.

Do these four steps in order and you land on OAuth 2.1 without a flag day. The payoff is a smaller attack surface, one authoritative spec instead of a scavenger hunt across RFCs, and a token setup that is already compatible with MCP and the wave of AI tooling built on it.

## Sources

- The OAuth 2.1 Authorization Framework (IETF draft, datatracker)
- OAuth 2.1 overview and differences (oauth.net)
- Model Context Protocol Authorization specification
- OAuth 2.1: How Many RFCs Does it Take (Okta Developer Blog)

## Source links

- https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1
- https://oauth.net/2.1/
- https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization
- https://developer.okta.com/blog/2019/12/13/oauth-2-1-how-many-rfcs

## FAQ

### What is the main difference between OAuth 2.1 and OAuth 2.0?

OAuth 2.1 consolidates OAuth 2.0 (RFC 6749) and its security best practices into one specification that obsoletes RFC 6749 and RFC 6750. It removes insecure features rather than adding new ones: PKCE becomes mandatory, the implicit and password grants are gone, and rules for tokens and redirects are tightened.

### Is PKCE mandatory in OAuth 2.1?

Yes. In OAuth 2.0 PKCE was optional and mainly recommended for mobile and single-page apps. OAuth 2.1 requires PKCE for all clients using the authorization code flow, including confidential server-side web apps, to prevent authorization code interception and injection attacks.

### Which OAuth 2.0 grant types were removed in OAuth 2.1?

Two grants are omitted: the implicit grant, which returned an access token directly in the redirect URL, and the resource owner password credentials grant, which had the app collect the user's real password. Browser apps should use authorization code with PKCE, and machine clients should use client credentials.

### Why does the Model Context Protocol (MCP) require OAuth 2.1?

MCP's authorization specification is built directly on OAuth 2.1. It states authorization servers MUST implement OAuth 2.1 and MCP clients MUST implement PKCE, referencing draft-ietf-oauth-v2-1-13. A protected MCP server acts as an OAuth 2.1 resource server and access tokens must be sent in the Authorization header, never in a query string.

### How do I migrate from OAuth 2.0 to OAuth 2.1?

Migrate incrementally: first add PKCE to every authorization code client since it is backward compatible, then retire the implicit and password grants, then stop accepting bearer tokens in query strings, and finally enforce exact redirect-URI matching and refresh-token rotation for public clients. No big-bang rewrite is needed.

---

- HTML version: https://www.matthewswong.com/en/blog/oauth-2-1-vs-2-0-migration/
- Bahasa Indonesia: https://www.matthewswong.com/id/blog/oauth-2-1-vs-2-0-migration/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
