OAuth 2.0 vs OAuth 2.1: What changed, why it matters, and how to upgrade
Learn what’s new in OAuth 2.1, why it’s replacing OAuth 2.0, and how to upgrade your app securely with modern best practices.
OAuth 2.0 has been the backbone of modern authentication for over a decade, powering everything from logging into apps to connecting enterprise systems. But OAuth 2.0’s biggest strength—its flexibility—has also been its greatest weakness. By giving developers too many choices, the spec left room for risky flows and insecure defaults that often led to bad implementations.
OAuth 2.1 is here to fix that. While still technically in draft form as of April 2025, it’s already being adopted in production by leading organizations. Anthropic, for example, has made OAuth 2.1 a foundational part of the Model Context Protocol (MCP), which governs how AI agents securely access external tools and data. That’s a glimpse of the future, but the real story is that OAuth 2.1 makes secure implementations easier and safer for everyone, across web apps, APIs, and beyond.
What is OAuth 2.1 and why it matters
OAuth 2.1 isn’t a radical rewrite of the standard; it’s a curated update aimed at making secure OAuth implementations easier for all developers. The idea is simple: remove bad choices and make best practices mandatory.
One of the biggest issues with OAuth 2.0 was that it gave developers too many options. That flexibility meant teams often made different decisions, sometimes picking approaches that were easier to build but carried long-term security risks. OAuth 2.1 addresses this by reducing ambiguity. For example, it eliminates dangerous flows like the implicit and password grants, both of which created opportunities for tokens or user credentials to leak. By removing these options entirely, OAuth 2.1 closes off entire classes of mistakes before they can happen.
It also raises the baseline for security by making best practices mandatory instead of optional. In OAuth 2.0, protections like PKCE or refresh token rotation were optional. In OAuth 2.1, they’re required.
- Every authorization code flow must use PKCE.
- Bearer tokens can no longer be passed in URLs.
- Refresh tokens must be rotated or sender-constrained.
Even a minimal implementation is now significantly harder to exploit than under OAuth 2.0.
Most importantly, OAuth 2.1 reflects years of hard-earned lessons from the field. It’s a security upgrade that bakes in the experience of a decade’s worth of attacks, research, and best practices.
These changes aren’t just relevant to web and mobile apps. As AI agents become more capable and widely deployed, the Model Context Protocol (MCP) relies on OAuth 2.1 to ensure agents can request and use external tools safely. That makes OAuth 2.1 a cornerstone not only for human-driven apps but also for secure, autonomous systems.
What’s changing from OAuth 2.0
OAuth 2.0 left developers chasing down multiple RFCs, extensions, and best-practice documents just to figure out how to implement a flow correctly. OAuth 2.1 pulls all of that into a single, unified specification. It tells you how to use OAuth securely, both as a client and as an authorization server, without having to cross-reference half a dozen standards.
Here are the most notable changes included in OAuth 2.1:
- PKCE is mandatory for all authorization code flows, not just public clients.
- The implicit grant is removed due to token leakage risks.
- The Resource Owner Password Credentials (ROPC) grant is removed, eliminating scenarios where apps directly handle user credentials.
- Redirect URIs must use exact string matching, blocking open-redirector vulnerabilities.
- Bearer tokens in query strings are prohibited, ensuring tokens aren’t exposed in URLs.
- Refresh tokens must either be sender-constrained or rotated on each use, reducing replay risks if stolen.
As you can see, OAuth 2.1 doesn’t introduce new functionality beyond what’s already in OAuth 2.0. Instead, it deprecates risky flows and formalizes best practices into the core specification.
Let’s see what each one means in more detail.
OAuth 2.0 vs. OAuth 2.1: Key differences
1. PKCE is now mandatory
Originally introduced for mobile and native apps, the Proof Key for Code Exchange (PKCE) flow is now required for all authorization code flows, including those used by server-side clients.
The goal of this flow is to prevent authorization code interception attacks (ie, when an attacker steals the temporary authorization code and uses it to get a token).
!!For a more thorough explanation of how PKCE works and why it was created, see Secure authentication for frontend apps with PKCE.!!
PKCE adds 3 new parameters to stop this type of attack:
- The
code_verifier
: A cryptographically random string that the application creates when a user requests to log in. - The
code_challenge
: A hash of the code verifier. - The
code_challenge_method
: The hashing algorithm used to generate the code challenge. This should beSHA256
.
How PKCE works:
- When a user wants to log in, the client (ie, your app) generates a
code_verifier
(random string) and transforms it into acode_challenge
using thecode_challenge_method
. - The
code_challenge
is sent with the initial auth request to the authorization server (eg, WorkOS). The server stores the value and proceeds to authenticate the user. - After the user authenticates, the client gets an authorization code and sends it to the authorization server in exchange for a token. Alongside the authorization code, the
code_verifier
is also sent with the token request. - The authorization server hashes the
code_verifier
and compares it to the storedcode_challenge
.
This effectively solves the code interception attack problem since the malicious application would need not only the authorization code but also the code_verifier
to get an access token. Intercepting the code_challenge
won’t help either since SHA256 is a one-way hashing algorithm that cannot be decrypted.

Some PKCE best practices to keep in mind:
- Don’t hard-code the
code_verifier
instead, generate a new one each time a user requests to log in. If not, you are exposing yourself to replay attacks. - Use
SHA-256
for thecode_challenge_method
, even for confidential clients (ie, apps running on the server).plain
is primarily included for backward compatibility with older implementations or for highly constrained environments where SHA-256 hashing is not feasible.
2. Implicit flow is removed
The implicit flow (where tokens are returned directly via the URL fragment) is no longer part of the spec. This change is due to its known security risks, most notably token leakage in browser history or logs.
If you're using SPAs, switch to authorization code + PKCE, modern browsers now support CORS, same-origin policies, and fetch()
securely enough to handle this.
3. Refresh tokens for SPAs (securely)
OAuth 2.1 formalizes how single-page apps (SPAs) can securely use refresh tokens, leveraging secure HTTP-only cookies or trusted storage mechanisms with modern browser APIs. This is a shift from the earlier pattern of forcing users to reauthenticate frequently.
OAuth 2.1 allows public clients like SPAs to use refresh tokens, provided they:
- Use secure HTTPS-only cookies for token storage, or
- Use IndexedDB or Web Storage with strict controls
- Implement Refresh Token Rotation (RTR). Each time a refresh token is used to acquire a new access token, a brand new refresh token is also generated and the previous one is invalidated.
4. Bearer tokens remain, but use them carefully
In OAuth, a bearer token is an access token that grants access to a resource without any additional proof of possession. Whoever holds the token (in memory, in a browser, in transit) can use it. That’s why it’s called a "bearer" token: possession equals permission.
The problem? If someone steals that token, they can impersonate the client or user, whether they're the legitimate party or not. Common attack vectors include:
- Man-in-the-middle attacks on insecure connections
- JavaScript access via XSS
- Logging tokens in browser history or server logs
- Token replay from intercepted network traffic
OAuth 2.1 does not deprecate bearer tokens, but it heavily emphasizes secure transport and scope minimization to mitigate these risks:
- Always use HTTPS: This is non-negotiable. The transport layer (TLS) must be trusted and validated to protect token confidentiality during transit.
- Minimize token scope and lifetime: Use narrowly scoped tokens that limit:
- What they can access (e.g.,
read:user
instead of full access) - How long they are valid (short-lived tokens reduce replay risk)
- What they can access (e.g.,
- Secure storage on the client: Tokens should never be stored in LocalStorage (vulnerable to XSS) or URL fragments (visible in browser history or logs). Instead:
- Use secure, HTTP-only cookies
- Or use memory-only storage with refresh token rotation
OAuth 2.1 does not mandate sender-constrained tokens, but it acknowledges them as stronger alternatives. These bind the access token to a specific client or device. Examples:
- Mutual TLS (mTLS)
- The client must present a TLS certificate when using the token
- Resource server verifies the client matches the certificate originally used
- DPoP (Demonstration of Proof-of-Possession)
- A lightweight alternative to mTLS
- The client signs each request with a key pair and includes a DPoP proof header
DPoP ensures:
- The token can only be used by the holder of the private key
- Prevents token replay from a different client
These are not required in OAuth 2.1, but you should keep them in mind for high-risk scenarios, like financial services, healthcare, or regulated industries.
5. Redirect URIs: No more wildcards
OAuth 2.1 simplifies validation by requiring exact match of redirect URIs for all clients. Wildcard or partial matches are discouraged to prevent open redirect vulnerabilities.
Bad:
Good:
This prevents open redirect vulnerabilities and misrouted token deliveries.
6. Password grant is deprecated
The Resource Owner Password Credentials (ROPC) grant, where apps collect a user’s username and password directly, is officially deprecated. This insecure pattern had long been discouraged and is now formally excluded.
Instead you should use:
- The Device Authorization Flow for non-browser interfaces.
- The Authorization Code + PKCE for the rest.
How to migrate from OAuth 2.0 to OAuth 2.1
!!Should I upgrade now, even though it’s still a draft? You don’t need to rush. OAuth 2.1 is still in draft form, and OAuth 2.0 servers aren’t going away any time soon. But since OAuth 2.1 only formalizes best practices and removes risky flows, there’s no downside to adopting it today. If you’re starting a new project, you should follow the OAuth 2.1 guidance from the start. By following the checklist in this section, you will only be making your applications more secure.!!
Here’s a practical checklist to upgrade to OAuth 2.1:
- Audit and update your OAuth flows
- Implement PKCE for all clients (public and confidential)
- Confirm that your OAuth libraries and SDKs support OAuth 2.1 defaults like PKCE.
- Update all clients (web, mobile, native, SPAs) to use the secure standard flow.
- Generate and send
code_challenge
- Require and validate
code_verifier
on token request - Don’t use
code_challenge_method=plain
, unless truly unavoidable (e.g. legacy)
- Eliminate use of the Implicit flow (
response_type=token
) - Eliminate use of the Resource Owner Password Credentials (ROPC) flow
- Implement PKCE for all clients (public and confidential)
- Audit and update token usage and storage
- Always use HTTPS.
- Never pass bearer tokens in URLs; use headers or POST bodies instead.
- Use short-lived authorization codes and tokens
- Narrow token scopes to least privilege needed
- Enable Refresh Token Rotation (RTR) or use sender-constrained tokens (DPoP, mTLS) for highly sensitive apps.
- Store tokens securely. In SPAs:
- Use secure HTTPS-only cookies for token storage, or
- Use IndexedDB or Web Storage with strict controls
- Harden redirect URI handling
- Remove wildcards.
- Validate exact
redirect_uri
match on every authorization request.
How WorkOS can help
Migrating to OAuth 2.1 doesn’t have to mean reinventing your authentication stack.
WorkOS provides a fully managed, OAuth 2.1–compliant authorization layer out of the box, so you can adopt best practices without rolling your own server code.
With WorkOS:
- PKCE enforcement, redirect URI matching, and token rotation are handled for you.
- Modern OAuth defaults are built into the platform, making your implementation secure from the start.
- Enterprise-ready features come built in. WorkOS integrates with identity providers like Okta, Azure AD, and Google Workspace. That means upgrading to OAuth 2.1 with WorkOS doesn’t just harden your security, it also prepares your app for SSO and SCIM, without extra work.
- If you’re building apps that integrate with AI agents or MCP servers, WorkOS gives you an OAuth 2.1–compliant server out of the box, so you can connect securely without building all the plumbing yourself.
In short, WorkOS takes the complexity of OAuth 2.1 off your team's plate, allowing them to focus on building features instead of managing protocol details.
Final thoughts
OAuth 2.1 is more than just an incremental revision. It’s the culmination of a decade of hard-earned security lessons. By eliminating risky flows, enforcing secure defaults, and consolidating best practices, it gives developers a safer foundation to build on.
Upgrading from OAuth 2.0 means auditing your current flows, modernizing your token handling, and adopting PKCE everywhere. For teams that want a smoother path, WorkOS offers OAuth 2.1 compliance out of the box, ensuring your apps are secure today and ready for the future.
Now is the time to modernize your authentication, and OAuth 2.1 is the path forward.