Skip to content

Conversation

@technicalpickles
Copy link
Contributor

@technicalpickles technicalpickles commented Jan 25, 2026

Summary

When autoDetectResource() encounters a 429 rate limit response, retry with appropriate backoff instead of immediately falling back to the server URL.

Related #271

Problem

Some OAuth providers (e.g., Runlayer/Anysphere) aggressively rate limit preflight requests, causing resource auto-detection to fail during OAuth login flows. This results in the resource parameter being set to the server URL instead of the correct value from Protected Resource Metadata, which can cause authentication failures.

Solution

Rate Limit Handling (internal/oauth/config.go)

  • Add parseRateLimitWait() helper to extract wait duration from:
    • Retry-After header (seconds or HTTP-date per RFC 7231)
    • JSON body with reset_at field (Unix timestamp)
  • Rewrite autoDetectResource() with retry loop:
    • On 429: wait per hints (capped at 30s), retry up to 3 times
    • On other 4xx/5xx: fall back to server URL immediately
    • On success (401): extract resource from WWW-Authenticate

Test Infrastructure (tests/oauthserver/)

  • Add rate limit injection to OAuth test server via ErrorMode:
    • MCPRateLimitCount - return 429 N times before real response
    • MCPRateLimitRetryAfter - Retry-After header value
    • MCPRateLimitUseResetAt - use JSON body with reset_at instead
  • Add /.well-known/oauth-protected-resource endpoint (RFC 9728)
  • Add MCPURL and ProtectedResourceMetadataURL to ServerResult
  • Add ResetRateLimitCounter() helper for test isolation

Test plan

  • Unit tests for parseRateLimitWait() (7 test cases)
  • Unit tests for autoDetectResource() rate limit handling (4 test cases)
  • OAuth test server rate limit injection tests (3 test cases)
  • Protected resource metadata endpoint test
  • E2E integration test with OAuth test server

🤖 Generated with Claude Code

technicalpickles and others added 4 commits January 25, 2026 14:37
When autoDetectResource() encounters a 429 rate limit response, retry with
appropriate backoff instead of immediately falling back to the server URL.

## Changes
- Add parseRateLimitWait() helper to extract wait duration from:
  - Retry-After header (seconds or HTTP-date per RFC 7231)
  - JSON body with reset_at field (Unix timestamp)
- Rewrite autoDetectResource() with retry loop:
  - On 429: wait per hints (capped at 30s), retry up to 3 times
  - On other 4xx/5xx: fall back to server URL immediately
  - On success (401): extract resource from WWW-Authenticate
- Add comprehensive unit tests for rate limit handling

## Context
Some OAuth providers (e.g., Runlayer/Anysource) aggressively rate limit
preflight requests, causing resource auto-detection to fail during
OAuth login flows.
Add rate limit injection capabilities to the OAuth test server for testing
autoDetectResource() retry behavior with 429 responses.

## Changes
- Add MCPRateLimitCount, MCPRateLimitRetryAfter, MCPRateLimitUseResetAt to ErrorMode
- Add rate limit check to oauthMiddleware (before auth check)
- Add /.well-known/oauth-protected-resource endpoint (RFC 9728)
- Add ProtectedResourceMetadata type
- Add MCPURL and ProtectedResourceMetadataURL to ServerResult
- Add ResetRateLimitCounter() helper for tests
- Add tests for rate limiting and protected resource metadata
Fix staticcheck QF1012 warnings by using fmt.Fprintf(w, ...) directly
instead of w.Write([]byte(fmt.Sprintf(...))). This is more idiomatic and
avoids allocating an intermediate byte slice.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add context cancellation support to autoDetectResource so rate limit
retries can be interrupted during shutdown or timeout scenarios.

Changes:
- Add ctx parameter to autoDetectResource and CreateOAuthConfigWithExtraParams
- Use http.NewRequestWithContext for context-aware HTTP requests
- Replace blocking time.Sleep with context-aware select for rate limit waits
- Add parseRateLimitWaitWithSource to track wait time source for logging
- Add TestAutoDetectResource_ContextCancellation to verify cancellation works
- Update all 5 call sites in connection.go to pass context
- Update test files to pass context.Background()

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@juice928
Copy link

👋 Hi, I'm an automated AI code review bot. I ran some checks on this PR and found 1 point that might be worth attention (could be false positives, please use your judgment):

  1. Enhancing memory safety when reading response bodies
    • Location: internal/oauth/config.go:L498
    • Impact: Reading a full response body into memory without a size limit could potentially lead to high memory usage or application instability if an upstream server returns an unexpectedly large payload.
    • Suggestion: Consider using io.LimitReader to wrap the response body and cap the read size to a safe maximum (e.g., 1MB) before processing.

If you find these suggestions disruptive, you can reply "stop" , and I'll automatically skip this repository in the future.

Add io.LimitReader to cap response body reads at 1MB to prevent
potential memory exhaustion from unexpectedly large responses.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants