Skip to content

OAuth 2.1 — Plan and Tracking #99002

@dcramer

Description

@dcramer

Note on cache semantics (clarification):

  • Ensure endpoints use Django's never_cache for non-cacheable responses. Most of our OAuth views already do (e.g., /oauth/authorize via AuthLoginView and /oauth/token directly). Confirm /oauth/userinfo as well.
  • Do not add manual Cache-Control or Pragma headers in these flows; Pragma is deprecated.

OAuth 2.1 Migration — Plan and Tracking

Summary

Modernize Sentry's OAuth implementation to comply with OAuth 2.1 specifications and security best practices. This includes PKCE enforcement, strict redirect URI validation, refresh token rotation, proper error handling, and discovery endpoints.

Goals

  • Align with OAuth 2.1 draft specification
  • Improve security through PKCE, strict redirect validation, and token rotation
  • Add OAuth 2.0 Authorization Server Metadata discovery (RFC 8414)
  • Support Dynamic Client Registration (RFC 7591/7592)
  • Support CIMD (RFC)
  • Maintain backward compatibility during transition

Technical Requirements

OAuth 2.1 Core Requirements

  • Remove implicit grant flow (or feature flag to disable)
  • Require PKCE for authorization code flow
  • Enforce exact redirect URI matching (no prefix matching)
  • Implement refresh token rotation with reuse detection
  • Support client_secret_basic authentication method
  • Add proper error responses per RFC 6749

Discovery & Metadata (RFC 8414)

  • /.well-known/oauth-authorization-server endpoint
  • Expose supported grant types, response types, and PKCE methods
  • Optional OIDC discovery if ID tokens are supported

Security Requirements

  • HTTPS enforcement (except loopback for native apps per RFC 8252)
  • One-time authorization code usage
  • Token family revocation on refresh token reuse
  • No credentials in query parameters
  • Proper WWW-Authenticate challenges for Bearer tokens

Implementation Checklist

Phase 1: Core Security Updates

Authorization Endpoint (/oauth/authorize)

Token Endpoint (/oauth/token)

Public Client Support & Refresh Token Rotation

Per RFC 9700 (OAuth 2.0 Security Best Current Practice) §4.14.2:

  • Add is_public support to ApiApplication (nullable client_secret)
  • Add rotation fields to ApiToken: token_family_id, previous_refresh_token_hash, is_refresh_token_active
  • Allow public clients to use grant_type=refresh_token without client_secret
  • Implement refresh token rotation: issue new refresh token on each use, invalidate old
  • Implement replay detection: if old (rotated) refresh token is reused, revoke entire token family
  • Assign token family on first refresh for legacy tokens (lazy migration)
  • Add metrics for rotation events and family revocations
  • Add UI option in application creation to mark app as "Public Client" (CLI/native app)
  • Hide client_secret field for public clients in application detail view

Discovery Endpoints

  • Implement /.well-known/oauth-authorization-server
  • Return authorization server metadata per RFC 8414
  • Include supported PKCE methods and client auth methods

Bearer Token Usage (RFC 6750)

  • Audit resource endpoints for proper Bearer token handling
  • Reject tokens in query parameters
  • Return WWW-Authenticate: Bearer on auth errors

Phase 2: Migration & Deprecation

Implicit Flow Removal

  • Add oauth2.1:disable-implicit feature flag
  • Return unsupported_response_type when disabled
  • Document migration path for affected clients

Redirect URI Migration

  • Add metrics for prefix-matched redirects
  • Implement oauth2.1:strict-redirects feature flag
  • Migration tooling for converting prefix patterns to exact URIs
  • Gradual rollout plan with per-app overrides

Phase 3: Advanced Features

Dynamic Client Registration

  • Implement registration endpoint (RFC 7591)
  • Client management API (RFC 7592)
  • Access control via Initial Access Tokens
  • Audit logging for client operations

Client ID Metadata Document (CIMD)

  • TBD

Token Management

  • Token introspection endpoint (RFC 7662)
  • Token revocation endpoint (RFC 7009)
  • Refresh token scope reduction

Sender-Constrained Tokens via DPoP (RFC 9449)

DPoP (Demonstrating Proof of Possession) provides cryptographic sender-constraining for tokens, preventing token theft/replay even if tokens are exfiltrated.

Server-side:

  • Parse and validate DPoP proof JWT from DPoP header
  • Verify proof signature using embedded public key (JWK)
  • Validate proof claims: jti (unique), htm (HTTP method), htu (URL), iat (timestamp)
  • Implement jti replay cache (Redis, ~5 min window) to prevent proof reuse
  • Add dpop_jkt field to ApiToken to store bound key thumbprint
  • On token requests with DPoP: bind issued tokens to the proof's public key
  • On refresh with DPoP: verify new proof is signed by same key as bound token
  • Return DPoP-Nonce header for nonce-based replay protection (optional)
  • Make DPoP optional: clients without DPoP header use rotation-only protection

Resource server (API endpoints):

  • Accept DPoP auth scheme in Authorization header for DPoP-bound tokens
  • Validate DPoP proof on each request for DPoP-bound access tokens
  • Verify proof key thumbprint matches token's dpop_jkt binding

Client requirements (for CLI/SDK implementers):

  • Generate asymmetric key pair (ES256 recommended) on first run
  • Store private key securely (OS keychain, encrypted file)
  • Create signed DPoP proof JWT for each token request
  • Include DPoP: <proof> header in token and API requests

Data Model Changes

ApiGrant

  • code_challenge: Store PKCE challenge
  • code_challenge_method: S256 or plain

ApiApplication

  • client_secret: Make nullable to support public clients (RFC 6749 §2.1)
  • allow_redirect_prefix_match: Migration flag for redirect behavior
  • token_endpoint_auth_method: Client authentication method
  • grant_types: Supported grant types
  • response_types: Supported response types
  • require_pkce: PKCE enforcement control

ApiToken

  • token_family_id: UUID linking tokens in a rotation chain (for replay detection)
  • previous_refresh_token_hash: Hash of rotated-out refresh token (for replay detection)
  • is_refresh_token_active: Whether this token's refresh_token can still be used
  • dpop_jkt: JWK thumbprint of bound DPoP key (for sender-constrained tokens, future)

Rollout Strategy

Feature Flags

  • oauth2.1:strict-redirects: Enable exact redirect matching
  • oauth2.1:disable-implicit: Disable implicit flow
  • oauth2.1:require-pkce: Enforce PKCE
  • oauth2.1:dynamic-registration: Enable client registration

Migration Path

  1. Deploy with all flags OFF (backward compatible)
  2. Enable logging/metrics for deprecated behaviors
  3. Gradual flag enablement with per-app overrides
  4. Remove legacy code after full migration

Related Work

References

Success Criteria

  • Full OAuth 2.1 compliance
  • Zero breaking changes for existing clients during migration
  • 100% test coverage for OAuth flows
  • Security audit passed

Metadata

Metadata

Assignees

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