Skip to content

oauth: reject redirects for token endpoint requests#738

Closed
shriza1991 wants to merge 1 commit into
Gitlawb:mainfrom
shriza1991:fix/oauth-token-redirect
Closed

oauth: reject redirects for token endpoint requests#738
shriza1991 wants to merge 1 commit into
Gitlawb:mainfrom
shriza1991:fix/oauth-token-redirect

Conversation

@shriza1991

@shriza1991 shriza1991 commented Jul 18, 2026

Copy link
Copy Markdown

Summary

This PR hardens OAuth token requests by rejecting HTTP redirects.

Changes

  • Clone the selected HTTP client before issuing token requests.
  • Install a token-specific CheckRedirect policy to prevent following redirects.
  • Use the cloned client when sending token requests.

Tests

  • Added a regression test for 307 redirects during authorization code exchange.
  • Added a regression test for 308 redirects during token refresh.
  • Verified that redirected endpoints do not receive credential-bearing requests.

Summary by CodeRabbit

  • Bug Fixes
    • Improved OAuth token security by preventing automatic redirects from token endpoints.
    • Token exchanges and refreshes now reject redirect responses without sending credentials to the redirected destination.

Copilot AI review requested due to automatic review settings July 18, 2026 17:45

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

OAuth token requests now use a cloned HTTP client that returns redirect responses instead of following them. Tests cover 307 and 308 redirects for code exchange and token refresh, confirming redirect targets receive no requests.

Changes

OAuth redirect protection

Layer / File(s) Summary
Token redirect policy
internal/oauth/flow.go
PostToken clones the provided client, applies checkTokenRedirect, and executes token requests without following redirects.
Redirect rejection tests
internal/oauth/flow_test.go
Tests verify that 307 and 308 token endpoint redirects return errors and do not contact redirect targets.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PostToken
  participant tokenClient
  participant tokenEndpoint
  participant redirectTarget
  PostToken->>tokenClient: Execute token request
  tokenClient->>tokenEndpoint: POST token request
  tokenEndpoint-->>tokenClient: 307 or 308 redirect
  tokenClient-->>PostToken: Return redirect response
  Note over tokenClient,redirectTarget: redirectTarget is not contacted
Loading

Possibly related issues

  • Gitlawb/zero issue 729: Covers the same OAuth token redirect protection and 307/308 credential-leakage tests.

Possibly related PRs

  • Gitlawb/zero#708: Also modifies PostToken in internal/oauth/flow.go, creating an overlapping code-review area.

Suggested reviewers: copilot, vasanthdev2004

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: token endpoint requests now reject HTTP redirects.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
internal/oauth/flow_test.go (1)

191-227: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove unnecessary test state variables.

The attackerHit boolean is unnecessary and could theoretically cause a data race if the test ever encounters a timeout, as the main test thread and the HTTP handler could execute concurrently under failure conditions.

testing.T.Errorf is fully thread-safe and reliably marks the test as failed. Furthermore, defer attacker.Close() blocks the main test goroutine until all active requests (and their handlers) have completely finished. You can safely remove the boolean state and rely entirely on the implicit synchronization provided by the test server closure.

  • internal/oauth/flow_test.go#L191-L227: Remove attackerHit initialization, its assignment in the handler, and the final check block in TestExchangeCodeRejects307Redirect.
  • internal/oauth/flow_test.go#L229-L263: Remove attackerHit initialization, its assignment in the handler, and the final check block in TestRefreshRejects308Redirect.
♻️ Proposed refactor for both tests

For TestExchangeCodeRejects307Redirect:

-	var attackerHit bool
-
 	attacker := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		attackerHit = true
 		t.Errorf("redirect target received unexpected request")
 	}))
 	defer attacker.Close()
 	if err == nil {
 		t.Fatal("expected redirect error")
 	}
-
-	if attackerHit {
-		t.Fatal("redirect target received credential-bearing request")
-	}
 }

For TestRefreshRejects308Redirect:

-	var attackerHit bool
-
 	attacker := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		attackerHit = true
 		t.Errorf("redirect target received unexpected request")
 	}))
 	defer attacker.Close()
 	if err == nil {
 		t.Fatal("expected redirect error")
 	}
-
-	if attackerHit {
-		t.Fatal("redirect target received credential-bearing request")
-	}
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/oauth/flow_test.go` around lines 191 - 227, Remove the attackerHit
state and its assignment and final assertion from
TestExchangeCodeRejects307Redirect in internal/oauth/flow_test.go lines 191-227,
relying on the handler’s testing.T.Errorf. Apply the same removal in
TestRefreshRejects308Redirect at lines 229-263, leaving the redirect rejection
assertions unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/oauth/flow_test.go`:
- Around line 191-227: Remove the attackerHit state and its assignment and final
assertion from TestExchangeCodeRejects307Redirect in internal/oauth/flow_test.go
lines 191-227, relying on the handler’s testing.T.Errorf. Apply the same removal
in TestRefreshRejects308Redirect at lines 229-263, leaving the redirect
rejection assertions unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6f73ed8c-0c69-4e50-8918-94702282dd06

📥 Commits

Reviewing files that changed from the base of the PR and between 015452c and 5a98ed3.

📒 Files selected for processing (2)
  • internal/oauth/flow.go
  • internal/oauth/flow_test.go

@Vasanthdev2004

Copy link
Copy Markdown
Collaborator

Thanks for tackling this, @shriza1991 - the redirect-replay risk on OAuth token requests was a real and worthwhile thing to fix. Since you opened this, #741 landed (merged as 974fc03) and closed the same issue #729, covering the token exchange/refresh path plus the device-authorization and poll flows in one pass, so it's now fully handled on main (and this branch has gone into conflict). Closing as already-resolved, but the instinct and the regression-test approach were spot on. Please keep the contributions coming - the surrounding endpoint-validation work is a good next area. Really appreciate you taking the time on a security fix.

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.

3 participants