OAuth 2.1 Security Requirements
Security requirements and removed features in OAuth 2.1.
TLS Requirements
All Endpoints MUST Use HTTPS
| Endpoint | Requirement |
|---|---|
| Authorization Endpoint | HTTPS required |
| Token Endpoint | HTTPS required |
| Resource Endpoints | HTTPS required |
| Redirect URIs | HTTPS required* |
*Exception: Loopback URIs may use HTTP
Loopback Exception
Native apps may use HTTP with:
localhost127.0.0.1[::1]
TLS Version
- TLS 1.3 recommended (current at time of OAuth 2.1)
- Follow BCP 195 for TLS recommendations
Certificate Validation
- MUST validate TLS certificate chains per RFC 9110 Section 4.3.4
- Prevents DNS hijacking attacks
HTTP Redirect Requirements
Prohibited: HTTP 307
- MUST NOT use 307 status for redirects containing user credentials
- 307 preserves request method and body → credential exposure risk
Recommended: HTTP 303
- SHOULD use 303 ("See Other") for such redirects
- 303 always uses GET for redirect
Allowed
- 302 Found (common)
- 303 See Other (recommended)
- JavaScript redirect
- Meta refresh
Removed Grant Types
Implicit Grant (REMOVED)
# NO LONGER IN OAuth 2.1
response_type=token ✗ REMOVED
Why Removed
- Access tokens in URL fragment vulnerable to leakage
- Cannot be sender-constrained
- Browser history, referrer headers expose tokens
- No way to verify token was issued to client
Alternative
Use Authorization Code Flow with PKCE, even for browser-based apps.
Resource Owner Password Credentials (REMOVED)
# NO LONGER IN OAuth 2.1
grant_type=password ✗ REMOVED
Why Removed
- Exposes user credentials to client
- Cannot support MFA, passwordless
- Undermines OAuth security model
Alternative
Use standard authorization flows with user-agent.
PKCE (REQUIRED)
PKCE is no longer optional in OAuth 2.1.
Client Requirements
- All clients MUST use code_challenge and code_verifier
- S256 method required when client supports it
Server Requirements
- MUST support code_challenge parameter
- MUST enforce PKCE for public clients
- SHOULD enforce for confidential clients
Exemption
PKCE may be skipped only when BOTH:
- Client is confidential
- Client properly implements OpenID Connect nonce
Even when exempt, PKCE is RECOMMENDED.
Sender-Constrained Tokens (RECOMMENDED)
Bind tokens to specific sender.
DPoP (RFC 9449)
GET /resource HTTP/1.1
Authorization: DPoP eyJhbGciOiJSUzI1NiJ9...
DPoP: eyJhbGciOiJFUzI1NiIsInR5cCI6ImRwb3Arand0In0...
- Client proves possession of private key
- Token bound to client's key
Mutual TLS (RFC 8705)
- Client certificate presented during TLS
- Token bound to certificate thumbprint
Benefits
- Stolen tokens unusable without private key
- Prevents token replay attacks
- Stronger security for sensitive resources
Authorization Code Security
Code Lifetime
- MUST expire shortly after issuance
- RECOMMENDED: Maximum 10 minutes
Single Use
- MUST return access token only once per code
- Second use MUST be rejected
- SHOULD revoke all tokens from reused code
Replay Detection
def handle_token_request(code, code_verifier):
if code.already_used:
revoke_tokens_issued_for(code)
return error("invalid_grant")
if not validate_pkce(code, code_verifier):
# Invalid code_verifier - possible injection
return error("invalid_grant")
mark_as_used(code)
return issue_tokens(code)
Code Binding
Authorization code bound to:
- client_id
- redirect_uri
- code_challenge
Mix-Up Attack Prevention
Problem
Client interacts with multiple authorization servers and can be tricked into sending code to wrong server.
Solution: Issuer Identifier
Authorization response MAY include iss parameter:
HTTP/1.1 302 Found
Location: https://client.example.com/cb?
code=SplxlOBeZQQYbYS6WxSbIA
&state=xyz
&iss=https%3A%2F%2Fauthorization-server.example.com
Client validates iss matches expected authorization server.
See RFC 9207 for details.
Bearer Token Security
Prohibited
- Query parameter transmission
- Storage in cookies (unless properly secured)
- Logging
Required
- TLS for transmission
- Certificate validation
- Short lifetimes for browser environments
Recommended
- Audience restriction
- Minimum scope
CSRF Protection
Authorization Request
- code_challenge provides CSRF protection
- State parameter as additional layer
Redirect Endpoint
- Validate state matches sent value
- code_challenge/code_verifier prevents code injection
Open Redirector Prevention
- Validate redirect_uri against registered URIs
- Reject unregistered URIs
- Do not allow arbitrary redirects
Comparison with OAuth 2.0
| Feature | OAuth 2.0 | OAuth 2.1 |
|---|---|---|
| PKCE | Extension (RFC 7636) | REQUIRED |
| Implicit grant | Defined | REMOVED |
| Password grant | Defined | REMOVED |
| Query token | Discouraged | PROHIBITED |
| redirect_uri matching | Flexible | Exact match |
| Refresh token (public) | No constraint | Sender-constrained or rotation |
Implementation Checklist
Authorization Server
- Use TLS for all endpoints
- Support PKCE (S256 required)
- Enforce PKCE for public clients
- Do not implement implicit grant
- Do not implement password grant
- Enforce exact redirect_uri matching
- Short authorization code lifetime
- Enforce single-use authorization codes
- Use 303 for credential-containing redirects
- Consider issuer identifier in response
- Support sender-constrained tokens
- Validate TLS certificates
Client
- Use TLS for all requests
- Implement PKCE (prefer S256)
- Do not use implicit flow
- Do not send tokens in query parameters
- Validate state parameter
- Validate issuer identifier if present
- Use sender-constrained tokens if available
Source: maronnjapan/maronn-openid-provider — distributed by TomeVault.