Summary
The Go SDK constructs a double-slash OIDC discovery URL when the platform issuer has a trailing slash. Combined with SafeHTTPClient's no-redirect policy, this causes all SDK/otdfctl operations to fail with unexpected end of JSON input against any IDP whose issuer ends with /.
This affects Authentik (and potentially other IDPs). Trailing slashes in issuer URLs are permitted by RFC 8414.
Root Cause
sdk/sdk.go:456 concatenates without normalizing:
oidcConfigURL := issuerURL + "/.well-known/openid-configuration"
When issuerURL = https://idp.example/app/ (trailing slash from IDP), this produces:
https://idp.example/app//.well-known/openid-configuration
sdk/httputil/http.go SafeHTTPClient is configured to never follow redirects (http.ErrUseLastResponse). The double-slash URL returns a 301 redirect with an empty body → json.Unmarshal("") → unexpected end of JSON input.
Why the issuer has a trailing slash
The platform's auth init (service/internal/auth/authn.go:128-129) replaces the configured issuer with the issuer field from the OIDC discovery response. Authentik's discovery response always includes a trailing slash (hardcoded in Django's URL routing). This issuer is then exposed in /.well-known/opentdf-configuration, which the SDK reads.
Reproduction
# Deploy platform with Authentik as IDP, then:
curl -sk https://platform:9000/.well-known/opentdf-configuration | jq .configuration.platform_issuer
# → "https://idp:9443/application/o/app/" ← trailing slash
otdfctl policy attributes list --host https://platform:9000 --tls-no-verify --with-access-token "$TOKEN"
# → "Unexpected error: unexpected end of JSON input"
# Same API works via curl:
curl -sk -X POST https://platform:9000/policy.attributes.AttributesService/ListAttributes \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{}'
# → valid JSON
Suggested Fix
Option A — Normalize issuer URL (minimal, recommended)
// sdk/sdk.go:456
oidcConfigURL := strings.TrimRight(issuerURL, "/") + "/.well-known/openid-configuration"
Option B — Follow redirects for discovery endpoints (defense in depth)
// sdk/httputil/http.go - SafeHTTPClient
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if strings.Contains(req.URL.Path, ".well-known") || strings.Contains(req.URL.Path, "/jwks") {
return nil
}
return http.ErrUseLastResponse
},
Both can be applied together — A prevents the bad URL, B adds resilience against other IDP URL normalization differences.
Workaround
Placing an nginx reverse proxy with merge_slashes on in front of the IDP normalizes the double-slash before it reaches the IDP. With this in place, otdfctl --with-access-token works. This is infrastructure-level only — the SDK still constructs an invalid URL.
Environment
| Component |
Version |
| otdfctl |
v0.26.2 |
| OpenTDF Go SDK |
v0.11.0 |
| OpenTDF Platform |
v2.7.8 |
| Authentik |
2026.2.1 |
Summary
The Go SDK constructs a double-slash OIDC discovery URL when the platform issuer has a trailing slash. Combined with
SafeHTTPClient's no-redirect policy, this causes all SDK/otdfctloperations to fail withunexpected end of JSON inputagainst any IDP whose issuer ends with/.This affects Authentik (and potentially other IDPs). Trailing slashes in issuer URLs are permitted by RFC 8414.
Root Cause
sdk/sdk.go:456concatenates without normalizing:When
issuerURL=https://idp.example/app/(trailing slash from IDP), this produces:sdk/httputil/http.goSafeHTTPClientis configured to never follow redirects (http.ErrUseLastResponse). The double-slash URL returns a 301 redirect with an empty body →json.Unmarshal("")→unexpected end of JSON input.Why the issuer has a trailing slash
The platform's auth init (
service/internal/auth/authn.go:128-129) replaces the configured issuer with theissuerfield from the OIDC discovery response. Authentik's discovery response always includes a trailing slash (hardcoded in Django's URL routing). This issuer is then exposed in/.well-known/opentdf-configuration, which the SDK reads.Reproduction
Suggested Fix
Option A — Normalize issuer URL (minimal, recommended)
Option B — Follow redirects for discovery endpoints (defense in depth)
Both can be applied together — A prevents the bad URL, B adds resilience against other IDP URL normalization differences.
Workaround
Placing an nginx reverse proxy with
merge_slashes onin front of the IDP normalizes the double-slash before it reaches the IDP. With this in place,otdfctl --with-access-tokenworks. This is infrastructure-level only — the SDK still constructs an invalid URL.Environment