fix(sdk): normalize issuer URL before OIDC discovery#3261
fix(sdk): normalize issuer URL before OIDC discovery#3261damorris25 merged 1 commit intoopentdf:mainfrom
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses an issue where OIDC discovery fails for identity providers that include a trailing slash in their issuer URL. By ensuring the URL is properly formatted before appending the discovery path, the SDK avoids generating invalid request URLs that lead to 301 redirects and subsequent parsing errors. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. The slash at the end of the line, Caused errors that made us repine. With a trim of the string, We fix the whole thing, And make the discovery shine. Footnotes
|
📝 WalkthroughWalkthroughThe Changes
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~3 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Code Review
This pull request updates the OIDC configuration URL construction in sdk/sdk.go to correctly handle trailing slashes in the issuer URL using strings.TrimRight. Feedback suggests using url.JoinPath for a more idiomatic and robust implementation, and recommends unifying this discovery logic into a shared package to address duplication and potential inconsistencies across the codebase.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
sdk/sdk.go (2)
449-489:⚠️ Potential issue | 🟠 MajorAdd unit tests for issuer normalization behavior.
This SDK behavior changed, but no corresponding test is shown for trailing-slash and non-trailing-slash issuers. Please add coverage for both forms to prevent regressions.
As per coding guidelines, "Run
go test ./...ormake testand ensure all existing tests pass; add tests for new functionality".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/sdk.go` around lines 449 - 489, Add unit tests for getTokenEndpoint to verify issuer normalization with both trailing and non-trailing slashes: create two table-driven test cases supplying config objects whose PlatformConfiguration["platform_issuer"] is "http://example.org" and "http://example.org/" respectively, start an httptest.Server that serves a /.well-known/openid-configuration JSON including "token_endpoint":"http://example.org/token", and set c.httpClient (or inject the test server URL via the client Transport) so getTokenEndpoint uses the test server; assert the returned token endpoint matches the expected value and that no error is returned to prevent regressions in issuer trimming.
467-481:⚠️ Potential issue | 🟠 MajorCheck HTTP response status before unmarshaling JSON in OIDC discovery.
The code calls
client.Do(req)and checks only for the error return value, but does not validateresp.StatusCode. Non-2xx responses (3xx/4xx/5xx) will fall through tojson.Unmarshal, which fails with a misleading JSON parsing error instead of revealing the actual HTTP failure. Add a status code check immediately after reading the response body:Suggested fix
resp, err := client.Do(req) if err != nil { return "", err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return "", err } +if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return "", fmt.Errorf("oidc discovery failed: status=%d url=%s body=%q", resp.StatusCode, oidcConfigURL, string(body)) +} var config map[string]interface{} if err = json.Unmarshal(body, &config); err != nil { return "", err }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/sdk.go` around lines 467 - 481, The code calls client.Do(req) and proceeds to read and unmarshal resp.Body without validating resp.StatusCode, causing misleading JSON errors for non-2xx responses; after reading body (or immediately after client.Do), check resp.StatusCode (e.g., if resp.StatusCode < 200 || resp.StatusCode >= 300) and return a clear error that includes resp.StatusCode and a snippet of the response body. Update the block around client.Do(req), resp, io.ReadAll(resp.Body) and json.Unmarshal so non-2xx responses are detected and reported (referencing client.Do, resp, resp.Body, io.ReadAll, and json.Unmarshal).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@sdk/sdk.go`:
- Around line 449-489: Add unit tests for getTokenEndpoint to verify issuer
normalization with both trailing and non-trailing slashes: create two
table-driven test cases supplying config objects whose
PlatformConfiguration["platform_issuer"] is "http://example.org" and
"http://example.org/" respectively, start an httptest.Server that serves a
/.well-known/openid-configuration JSON including
"token_endpoint":"http://example.org/token", and set c.httpClient (or inject the
test server URL via the client Transport) so getTokenEndpoint uses the test
server; assert the returned token endpoint matches the expected value and that
no error is returned to prevent regressions in issuer trimming.
- Around line 467-481: The code calls client.Do(req) and proceeds to read and
unmarshal resp.Body without validating resp.StatusCode, causing misleading JSON
errors for non-2xx responses; after reading body (or immediately after
client.Do), check resp.StatusCode (e.g., if resp.StatusCode < 200 ||
resp.StatusCode >= 300) and return a clear error that includes resp.StatusCode
and a snippet of the response body. Update the block around client.Do(req),
resp, io.ReadAll(resp.Body) and json.Unmarshal so non-2xx responses are detected
and reported (referencing client.Do, resp, resp.Body, io.ReadAll, and
json.Unmarshal).
Replace manual string concatenation with url.JoinPath when building the OIDC discovery URL from the issuer. This prevents double-slash URLs when the issuer has a trailing slash. Fixed in two locations: - sdk/sdk.go getTokenEndpoint(): SDK's OIDC discovery for otdfctl/SDK consumers (uses SafeHTTPClient which does not follow redirects, so the double-slash 301 redirect caused "unexpected end of JSON input") - service/internal/auth/discovery.go DiscoverOIDCConfiguration(): platform's own OIDC discovery (uses http.DefaultClient which follows redirects, so it worked but made an unnecessary redirect round-trip) IDPs like Authentik always include a trailing slash in their issuer URL (e.g., https://idp.example/app/). This is RFC-compliant (RFC 8414). Fixes opentdf#3260 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Dana Morris <dmorris@virtru.com>
a39e1cb to
98a53e8
Compare
## Summary Documents user-facing changes from the [SDK v0.16.0 release](https://github.com/opentdf/platform/releases/tag/sdk/v0.16.0): - **KAS error classification (breaking change)**: New `ErrKASRequestError` sentinel distinguishes misconfiguration from tamper. Adds migration guide with error classification table and code snippet for Go SDK consumers who check `ErrTampered`. ([opentdf/platform#3166](opentdf/platform#3166)) - **OIDC trailing-slash issuer fix**: Troubleshooting entry for `unexpected end of JSON input` during OIDC discovery with IDPs like Authentik that use trailing-slash issuer URLs. ([opentdf/platform#3261](opentdf/platform#3261)) - **GetObligationTrigger RPC**: New collapsible section with Go/JS signatures, parameters, and examples — follows the existing Add/List/Remove trigger pattern. ([opentdf/platform#3318](opentdf/platform#3318)) - **SDK version annotations**: New methods now include an *Available since [SDK vX.Y.Z](link)* note after the signature block, linking to the release ## Test plan - [x] `npx docusaurus build` succeeds - [x] `vale` passes with 0 errors/warnings/suggestions - [ ] Verify Surge preview renders all three sections correctly 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
Use
url.JoinPathinstead of string concatenation when constructing OIDC discovery URLs from the issuer. This prevents double-slash URLs when the issuer has a trailing slash (e.g., Authentik).Problem
IDPs like Authentik include a trailing slash in their OIDC issuer URL (e.g.,
https://idp.example/app/). Two places in the codebase concatenate the issuer with/.well-known/openid-configuration, producing a double-slash URL:sdk/sdk.go:456— SDK's OIDC discovery forotdfctl/SDK consumers. UsesSafeHTTPClientwhich does not follow redirects, so the 301 redirect (empty body) causesjson.Unmarshalto fail withunexpected end of JSON input. This is the user-facing bug.service/internal/auth/discovery.go:35— Platform's own OIDC discovery. Useshttp.DefaultClientwhich follows redirects, so it works but makes an unnecessary redirect round-trip.Fix
Replace manual string concatenation with
url.JoinPath(Go 1.19+), which handles path joining with proper slash normalization:url.JoinPathis a no-op when the issuer has no trailing slash (Keycloak), so this is backwards-compatible.Fixes #3260
Test plan
go test ./sdk/...andgo test ./service/...)otdfctl --with-access-tokensucceeds against a trailing-slash issuer🤖 Generated with Claude Code