Skip to content

Consideration to forbid PKCE plain mode in OAuth 2.1 #236

Description

@corriganjeff

I recently noticed that "plain" mode is still a supported PKCE type in the OAuth 2.1 draft. I think 2.1 is a good opportunity to deprecate this. I had a sidebar with Aaron on the topic and he recommended creating an issue to open the idea up to further discussion. Here is a more formal description highlighting why PKCE plain mode doesn't make sense to continue supporting in 2.1


OAuth 2.1 Issue: Plain Code Challenge Method Creates Unverifiable Downgrade Attack Vector

Summary

The current OAuth 2.1 draft (version 14) permits authorization servers to support the plain code challenge method for clients that "cannot support S256 for some technical reason" (Section 4.1.1). This creates an unverifiable and exploitable downgrade attack vector that undermines PKCE security. Additionally, new specifications building on OAuth 2.1 are inheriting this language and perhaps without proper assessment, propagating the vulnerability to emerging protocols or implementations.

Recommendation: OAuth 2.1 should forbid authorization servers from supporting plain code challenge method entirely.


Current State in OAuth 2.1 Draft-14

Section 4.1.1 states:

"If the client is capable of using S256, it MUST use S256, as S256 is Mandatory To Implement (MTI) on the server."

"Clients are permitted to use plain only if they cannot support S256 for some technical reason, for example constrained environments that do not have a hashing function available"

"Clients SHOULD use code challenge methods that do not expose the code_verifier in the authorization request. Otherwise, attackers that can read the authorization request...can break the security provided by this mechanism. Currently, S256 is the only such method."

Analysis: While the draft discourages plain mode and acknowledges its security risks, it still permits authorization servers to support it for "technically incapable" clients.


The Problem: Unverifiable Capability Claims Enable Downgrade Attacks

Attack Vector

The "technical capability" exception is unverifiable by the authorization server, creating a downgrade attack surface:

1. Legitimate client initiates authorization with S256
   → code_challenge_method=S256
   → code_challenge=<SHA256 hash>

2. Network attacker (A4 per RFC 9700) intercepts request

3. Attacker modifies request:
   → code_challenge_method=plain
   → code_challenge=<original code_verifier>

4. Authorization server receives modified request with plain mode

5. Server cannot verify if client is "technically incapable" of S256

6. Server accepts plain mode (spec permits it for constrained clients)

7. PKCE protection is defeated

Why This Works

The authorization server has no mechanism to verify:

  • Whether the client genuinely lacks SHA-256 capability
  • Whether this request was downgraded by an attacker
  • Whether to reject the plain mode attempt

The attacker simply claims (via the modified request) that the client is "technically incapable" and the server must accept it or risk breaking legitimate constrained devices.

Attacker Model

This exploits Attacker A4 (RFC 9700): an adversary who can read and modify authorization requests. Real-world scenarios include:

  • TLS interception (corporate proxies, compromised CAs, malicious WiFi)
  • Browser extensions or malware
  • Compromised network infrastructure
  • Man-in-the-middle attacks on clients not using certificate pinning

Real-World Impact: Propagation to New Specifications

Case Study: Model Context Protocol (MCP)

The Model Context Protocol (MCP) is a new specification for AI agent integration. MCP references OAuth 2.1 for authorization and includes language derived from OAuth 2.1 draft:

"MCP clients MUST use the S256 code challenge method when technically capable, as required by OAuth 2.1 Section 4.1.1."

Problem: MCP inherited the "when technically capable" exception from OAuth 2.1.

MCP Client Profile:

  • Developer tools (IDEs like VS Code, CLIs like Claude Code)
  • AI-enabled consumer apps
  • Runs on modern computers/servers/devices
  • Requires contemporary runtime environments (Python 3.10+, Node 18+, Rust, Go)
  • All of these platforms have native SHA-256 support in standard libraries

Reality: There are likely zero legitimate MCP clients that cannot perform SHA-256. The plain mode exception was inherited by the references in the OAuth 2.1 draft

Result: MCP now has an unnecessary downgrade attack vector inherited from OAuth 2.1's permissive language.

Broader Pattern

OAuth 2.1 is positioned as the "current best practice" for OAuth. New specifications will reference OAuth 2.1 and inherit its language, often without deeply analyzing whether exceptions make sense in their specific context. By permitting plain mode in OAuth 2.1, we create a template that propagates this vulnerability to future protocols.


Technical Analysis: The "Constrained Device" Assumption Is Outdated

Historical Context

The plain code challenge method was included in RFC 7636 (2015) for devices that might lack cryptographic hashing functions. This was a reasonable assumption in 2015.

2026 Reality

Any device capable of implementing OAuth 2.1 authorization code flow should necessarily also support SHA-256:

  1. TLS Requirement:

    • OAuth 2.1 requires TLS 1.2+ for all communications
    • TLS 1.2 requires SHA-256 for certificate validation (RFC 5246)
    • TLS 1.3 requires SHA-256 for handshake (RFC 8446)
    • Conclusion: If a device can make HTTPS requests, it has SHA-256
  2. Platform Support:

    • Python: hashlib.sha256() - standard library since 2.5 (2006)
    • JavaScript: crypto.subtle.digest('SHA-256') - Web Crypto API (2015+)
    • Go: crypto/sha256 - standard library since 1.0 (2012)
    • Rust: sha2 crate - universally available
    • Java: MessageDigest.getInstance("SHA-256") - since Java 1.4 (2002)
    • C/C++: OpenSSL, mbedTLS - available on all platforms
  3. IoT and Embedded Devices:

    • Modern IoT devices have hardware SHA-256 accelerators for TLS
    • Devices without SHA-256 support cannot implement TLS 1.2+
    • Such devices are incompatible with OAuth 2.1's TLS requirements
  4. Legacy Devices:

    • Devices genuinely lacking SHA-256 (e.g., 2010-era devices with only TLS 1.0/1.1) are already incompatible with OAuth 2.1
    • Industry precedent: TLS 1.0/1.1 deprecated in 2020, SHA-1 certificates deprecated, 3G networks shutdown
    • Security requirements evolve; old devices become unsupported

Conclusion: The "constrained environment" exception has no contemporary use case for OAuth 2.1 compliant clients.


Compatibility Considerations

Impact of Forbidding Plain Mode

Existing Authorized Sessions:

  • Refresh tokens do not use PKCE
  • Existing logged-in devices continue working (longer term, based on implementation details)
  • Only new authorization flows are affected

Migration Path for Legacy Devices:
Authorization providers concerned about legacy devices have several options outside the OAuth 2.1 specification:

  1. Extended Refresh Tokens:

    • Issue longer-lived refresh tokens before deprecation
    • Most users stay logged in throughout device lifetime
    • Natural attrition as devices are replaced
  2. Device Authorization Grant (RFC 8628):

    • Does not require PKCE at all
    • Better UX for constrained devices (authenticate on phone/computer)
    • Industry standard (Netflix, YouTube, Spotify, etc.)
    • Could require a client firmware update (may not be possible for frozen legacy devices)
  3. Accept Breakage:

    • Industry precedent for deprecating insecure protocols
    • Old devices eventually stop working (acceptable outcome)
    • User communication: "Please upgrade your device"
  4. Out-of-Spec Legacy Support:

    • Deployments with specific legacy requirements can run isolated legacy endpoints
    • Not documented in OAuth 2.1 specification
    • Each implementor evaluates their own risk/compatibility trade-offs
    • Avoids standardizing vulnerable patterns
    • e.g. continue supporting OAuth 2.0 authorization & token flows for minor subset of known scenarios during a sunset timeframe

Precedent for Breaking Changes

  • TLS 1.0/1.1: Deprecated 2020 - old devices stopped working
  • SHA-1 Certificates: Deprecated 2017 - old devices can't connect
  • 3G Networks: Shutdown 2022 - old phones stopped working, connected cars, etc.
  • Adobe Flash: End-of-life 2020 - old content inaccessible
  • SSLv3: Immediate deprecation after POODLE (security emergency)

Security requirements evolve. OAuth 2.1 has an opportunity to establish a strong security baseline rather than carrying forward historical compromises.


Recommendation

Proposed Specification Change

(feel free to modify while keeping the intent intact)

Current Language (OAuth 2.1 Draft-14, Section 4.1.1):

"If the client is capable of using S256, it MUST use S256, as S256 is Mandatory To Implement (MTI) on the server."

"Clients are permitted to use plain only if they cannot support S256 for some technical reason"

Proposed Language:

"Clients MUST use the S256 code challenge method. Authorization servers MUST support S256 and MUST NOT support plain."

Additional Security Consideration (Section 7):

7.X Plain Code Challenge Method

The plain code challenge method, defined in RFC 7636, is explicitly forbidden in OAuth 2.1. The plain method offers no protection against authorization code interception by attackers who can read the authorization request (Attacker A4 in RFC 9700), as the code verifier is transmitted in plaintext in the authorization request.

Historical arguments for supporting plain mode (devices lacking cryptographic hashing capabilities) are obsoleted by OAuth 2.1's requirement for TLS 1.2+, which mandates SHA-256 support for certificate validation. Any device capable of implementing OAuth 2.1 necessarily supports SHA-256.

Authorization servers that support plain code challenge method create an unverifiable downgrade attack vector, as servers cannot distinguish legitimate constrained clients from attacker-modified requests claiming to be constrained.

Deployments with existing clients that cannot support S256 SHOULD issue extended-lifetime refresh tokens before discontinuing support for legacy authorization methods, as the refresh token grant (Section 4.X) does not require PKCE.


Benefits of This Change

  1. Eliminates Downgrade Attack Vector:

    • No ambiguity about "technical capability"
    • Authorization servers can safely reject any plain requests
    • Strengthens PKCE protection against A4 attackers
  2. Provides Clear Guidance for New Specifications:

    • New protocols (like MCP) referencing OAuth 2.1 inherit secure defaults
    • No need to evaluate "constrained device" exceptions for modern contexts
    • Reduces spec implementor inheritance of insecure patterns
  3. Simplifies Implementation:

    • Authorization servers don't need to implement or maintain plain mode support
    • Reduces code complexity and attack surface
  4. Future-Proofs the Specification:

    • Aligns OAuth 2.1 with contemporary security requirements
    • Sets strong baseline for next decade of OAuth usage
    • Avoids lock-in to legacy compromises
  5. Interoperability on Security Posture:

    • All OAuth 2.1 compliant servers have same PKCE requirements
    • No fragmentation between "strict" and "permissive" implementations
    • Clear conformance criteria

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions