Skip to content

fix: commonhttp client oath2 secrets injections#268

Open
nnicora wants to merge 1 commit intomainfrom
fix/oauth2-commonhttp
Open

fix: commonhttp client oath2 secrets injections#268
nnicora wants to merge 1 commit intomainfrom
fix/oauth2-commonhttp

Conversation

@nnicora
Copy link
Copy Markdown
Contributor

@nnicora nnicora commented Mar 25, 2026

Summary by CodeRabbit

Bug Fixes

  • Improved authentication parameter handling for various token request authentication schemes, including those using form-based parameters.
  • Enhanced error reporting for authentication requests with better error messages.
  • Fixed request body management in authentication flows to ensure proper parameter injection.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 25, 2026

Walkthrough

The OAuth2 round tripper implementation has been refactored to handle form-encoded request bodies instead of always appending parameters to URL query strings. The RoundTrip method now clones incoming requests, detects POST requests with application/x-www-form-urlencoded content, parses the existing form body, and injects OAuth2 parameters (client credentials and assertion fields) into the form data before rewriting the request body. Corresponding test updates validate parameters by inspecting the request body as form data rather than URL query parameters. Error handling for body reading and form parsing has been added.

Poem

🐰 Oh, what a hop through form-encoded ways!\
Cloning requests, parsing bodies with grace,\
OAuth2 secrets now hide in the frame,\
No more naked queries—credentials stay safe!\
A rabbit's delight, this refactored embrace! 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning No pull request description was provided by the author, violating the template requirement for detailed explanation of changes and rationale. Add a comprehensive description following the template, including 'What this PR does / why we need it' section explaining the OAuth2 parameter injection changes and any special reviewer notes.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title describes the main change (OAuth2 secrets injection fixes) and is directly related to the changeset modifications in client_oauth2.go.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/oauth2-commonhttp

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

@push-tags-from-workflow push-tags-from-workflow bot added bug Something isn't working tests labels Mar 25, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/commonhttp/client_oauth2.go`:
- Around line 349-350: When Body is nil the code copies newReq.URL.Query() into
q and then serializes q into the form body, but leaves newReq.URL.RawQuery
intact so parameters are sent twice and sensitive params remain in the URL;
after seeding q from newReq.URL.Query() (the branch where Body == nil and
variable q is assigned from newReq.URL.Query()), clear the URL query (set
newReq.URL.RawQuery to an empty string) so the parameters exist only in the
request body; apply the same change to the other code path that seeds q and
serializes it (the block around the code that handles form encoding—references
to newReq, q, Body, and RawQuery) so no params are duplicated or left in the
URL.
- Around line 334-335: The code allows client_auth_method values
"client_secret_post", "client_secret_jwt", and "private_key_jwt" to fall through
and send a partially-authenticated request when isFormBody is false; instead,
detect when client authentication requires an application/x-www-form-urlencoded
body (using isFormBody which checks newReq.Method and
newReq.Header.Get("Content-Type")) and fail fast by returning an error before
forwarding the request if !isFormBody and client_auth_method is one of those
three values. Update the branch that currently appends only client_id (and the
similar code paths) to validate client_auth_method against isFormBody and return
an explicit error rather than forwarding a malformed request.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 19b19617-398e-4c4a-bc8f-fd32a3d00a07

📥 Commits

Reviewing files that changed from the base of the PR and between e322fbe and e4dbb05.

📒 Files selected for processing (2)
  • pkg/commonhttp/client_oauth2.go
  • pkg/commonhttp/client_oauth2_test.go

Comment on lines +334 to +335
isFormBody := newReq.Method == http.MethodPost &&
strings.HasPrefix(newReq.Header.Get("Content-Type"), "application/x-www-form-urlencoded")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fail fast instead of sending a partially-authenticated request.

For client_secret_post, client_secret_jwt, and private_key_jwt, the secret/assertion is only added when isFormBody is true. Lines 397-401 still forward the non-form path, so a malformed request can go out with only client_id appended and no actual client authentication.

🐛 Proposed fix
  isFormBody := newReq.Method == http.MethodPost &&
      strings.HasPrefix(newReq.Header.Get("Content-Type"), "application/x-www-form-urlencoded")
+
+ requiresFormAuth :=
+     (t.ClientSecretPost != nil && *t.ClientSecretPost != "") ||
+     (t.ClientSecretJWT != nil && *t.ClientSecretJWT != "") ||
+     (t.ClientAssertion != nil && t.ClientAssertionType != nil)
+ if requiresFormAuth && !isFormBody {
+     return nil, errors.New("oauth2 client authentication requires POST application/x-www-form-urlencoded")
+ }

Also applies to: 359-387, 397-401

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/commonhttp/client_oauth2.go` around lines 334 - 335, The code allows
client_auth_method values "client_secret_post", "client_secret_jwt", and
"private_key_jwt" to fall through and send a partially-authenticated request
when isFormBody is false; instead, detect when client authentication requires an
application/x-www-form-urlencoded body (using isFormBody which checks
newReq.Method and newReq.Header.Get("Content-Type")) and fail fast by returning
an error before forwarding the request if !isFormBody and client_auth_method is
one of those three values. Update the branch that currently appends only
client_id (and the similar code paths) to validate client_auth_method against
isFormBody and return an explicit error rather than forwarding a malformed
request.

Comment on lines +349 to +350
} else {
q = newReq.URL.Query()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Clear the URL query after reusing it as the form source.

If Body is nil, Line 350 seeds q from newReq.URL.Query(), and Lines 390-396 then serialize the same values into the new form body. Because RawQuery is left intact, those params are sent twice, and any caller-supplied credentials/assertions remain exposed in the URL.

🐛 Proposed fix
  if isFormBody && newReq.Body != nil {
      bodyBytes, err := io.ReadAll(newReq.Body)
      if err != nil {
          return nil, fmt.Errorf("reading request body: %w", err)
      }

      newReq.Body.Close()

      q, err = url.ParseQuery(string(bodyBytes))
      if err != nil {
          return nil, fmt.Errorf("parsing form body: %w", err)
      }
  } else {
      q = newReq.URL.Query()
+     if isFormBody {
+         newReq.URL.RawQuery = ""
+     }
  }

Also applies to: 390-399

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/commonhttp/client_oauth2.go` around lines 349 - 350, When Body is nil the
code copies newReq.URL.Query() into q and then serializes q into the form body,
but leaves newReq.URL.RawQuery intact so parameters are sent twice and sensitive
params remain in the URL; after seeding q from newReq.URL.Query() (the branch
where Body == nil and variable q is assigned from newReq.URL.Query()), clear the
URL query (set newReq.URL.RawQuery to an empty string) so the parameters exist
only in the request body; apply the same change to the other code path that
seeds q and serializes it (the block around the code that handles form
encoding—references to newReq, q, Body, and RawQuery) so no params are
duplicated or left in the URL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant