Skip to content

Harden OAuth token handling#154

Merged
mbakgun merged 3 commits into
heymrun:mainfrom
VykosMolt:codex/oauth-token-hardening
Jun 7, 2026
Merged

Harden OAuth token handling#154
mbakgun merged 3 commits into
heymrun:mainfrom
VykosMolt:codex/oauth-token-hardening

Conversation

@VykosMolt

Copy link
Copy Markdown
Contributor

Summary

This tightens the OAuth flow in a few places that came up during the audit:

  • public OAuth clients now have to use PKCE with S256 during authorization and token exchange
  • newly issued access and refresh tokens are stored as SHA-256 hashes instead of raw bearer values
  • MCP bearer-token checks look up hashed tokens, while still accepting older plaintext rows until they expire or rotate
  • dynamic client registration now rejects unsupported token_endpoint_auth_method values instead of silently treating them like public clients
  • auth-code and refresh-token grant lookups use row locks where the database supports them, which helps avoid concurrent replay/rotation races

Verification

  • SECRET_KEY=test-secret-key-that-is-long-enough-for-oauth-tests ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000 UV_CACHE_DIR=/tmp/heym-uv-cache uv run pytest tests/test_oauth_api.py tests/test_mcp_servers.py -q
  • SECRET_KEY=test-secret-key-that-is-long-enough-for-oauth-tests ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000 UV_CACHE_DIR=/tmp/heym-uv-cache ./run_tests.sh

@mbakgun

mbakgun commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Thanks a lot for putting this together @VykosMolt 🚀 This is a solid hardening pass, and the direction looks right to me. Hashing stored OAuth tokens, enforcing PKCE S256 for public clients, rejecting unsupported client auth methods, using a timing-safe PKCE comparison, and adding row locks around replay-sensitive grants all make sense.

I think this is very close. Before merging, we have two requests:

  1. Could we add endpoint-level coverage for the hardened OAuth flow? In particular, a public client without PKCE should be rejected, and the authorization code/token exchange path should exercise the real /authorize and /token behavior rather than only helper-level tests.

  2. Could we add coverage for the hashed bearer-token lookup paths in the MCP endpoints, including the legacy plaintext fallback? Those are the exact resource-server paths that need to keep working after the storage change.

One transition note: the legacy plaintext fallback is useful for compatibility, but existing plaintext refresh tokens can remain usable until expiry or rotation. I do not think we need to force a revoke migration in this PR unless we want the security benefit to land immediately, but it would be helpful to document the intended transition behavior in the PR description so the tradeoff is explicit.

A small optional cleanup: _validate_pkce_request validates stripped PKCE values, but the original unstripped code_challenge and code_challenge_method are stored on the auth code. Storing the normalized values would keep authorize and token exchange behavior consistent. Not a security blocker, just a consistency improvement.

One separate follow-up idea: the MCP endpoints still accept raw bearer tokens through the ?token= query param, which can leak through logs, browser history, or referrers. Since this is broader than the token-storage change and may exist for SSE compatibility, I am fine tracking that separately.

Thanks again 🙏. This is good work and I think it is close to ready. 🤗

@mbakgun mbakgun added the next label Jun 7, 2026

@mbakgun mbakgun left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the quick follow-up. I validated the latest PR head in a clean temporary checkout.

Looks good to merge. Thanks again.

@mbakgun mbakgun merged commit 7b7efb3 into heymrun:main Jun 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants