fix(core): stop normalising issuer and resource identifiers - #22
Closed
RobertoIskandarani wants to merge 1 commit into
Closed
fix(core): stop normalising issuer and resource identifiers#22RobertoIskandarani wants to merge 1 commit into
RobertoIskandarani wants to merge 1 commit into
Conversation
RobertoIskandarani
force-pushed
the
fix/identifier-identity-trailing-slash
branch
from
July 29, 2026 14:09
bb4b327 to
40b353c
Compare
RFC 8414 §3.3 and RFC 9728 §3.3 require the advertised issuer/resource to be identical to the configured value — a simple string comparison — and both well-known URLs are formed by inserting the well-known path segment into the identifier verbatim (RFC 8414 §3 / RFC 9728 §3). The SDK stripped trailing slashes in four places: - verifier.NewTokenVerifier rewrote the configured issuer with TrimRight. The rewritten value became the expected iss at token verification, so an AS whose issuer identifier legitimately ends in "/" had every token rejected (RFC 9068 requires iss to carry the slash). - metadata.buildOAuthMetadataURL trimmed the issuer path's trailing slash against the RFC 8414 §3 insertion rule. - metadata.parse trimmed both sides of the issuer comparison, weakening the §3.3 identical-match MUST that defeats metadata substitution. - resource.wellKnownPRMPath collapsed a resource path of "/" to the bare well-known prefix; insertion preserves it. Both derivers now use the escaped path so percent-encoded octets are kept as sent, and PRMURL is composed by concatenation to avoid re-escaping. The OIDC discovery fallback keeps its terminating-slash trim — OIDC Discovery 1.0 §4 concatenates rather than inserts and mandates the trim. Both builders now carry comments explaining why the two same-looking constructions differ, plus tests pinning each, so a future normalisation sweep does not delete the OIDC trim. Identifiers are validated at construction instead via the new verifier.ValidateIdentifier (absolute http(s) URL with a host, no fragment — RFC 8707 §2), applied to issuer, audience, and resource URI, and never transformed. Conformance: extends the rfc9728 well-known-path case with the trailing-slash resource datum and adds two issuer variants — metadata issuer differing only by a trailing slash is rejected, and a token whose iss matches a configured trailing-slash issuer verifies. Migration: if a configured issuer or resource differs from the authorization server's actual identifier by a trailing slash, correct the config — the SDK no longer silently reconciles them.
RobertoIskandarani
force-pushed
the
fix/identifier-identity-trailing-slash
branch
from
July 29, 2026 14:11
40b353c to
4901bbd
Compare
Author
|
Closing — superseded. The identifier-handling change is being reworked against the corrected RFC 8414 §3.1 / RFC 9728 §3.1 (derivation strips the terminating slash) vs §3.3 (identity preserved verbatim) scope. A replacement PR will follow. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
RFC 8414 §3.3 and RFC 9728 §3.3 require the advertised issuer/resource to be identical to the configured value — a simple string comparison that exists to defeat metadata substitution — and both well-known URLs are formed by inserting the well-known path segment into the identifier verbatim (RFC 8414 §3 / RFC 9728 §3). The SDK stripped trailing slashes in four places; this PR removes all four, protects the one trim that is correct, and replaces silent rewriting with construction-time validation.
The user-visible bug
verifier.NewTokenVerifierrewrote the configured issuer (strings.TrimRight(issuer, "/")), and the token'sissis compared against that stored value. For an authorization server whose issuer identifier legitimately ends in/, RFC 9068 requiresissto carry the trailing slash — so every token was rejected for such a deployment. There was even a test pinning the wrong behaviour ("trailing slash should be trimmed"); it now asserts identity in both directions. Verified by reintroducing the trim: the new conformance accept-variant fails, and passes with the fix.The four sites
core/resource/verifier/verifier.go— issuer stored verbatim; validated, never rewritten.core/internal/metadata/metadata.go(buildOAuthMetadataURL) — pure insertion; issuerhttps://auth.example.com/tenant/now resolves to/.well-known/oauth-authorization-server/tenant/.core/internal/metadata/metadata.go(parse) — both sides of the issuer comparison were TrimRight'd, weakening the §3.3 identical-match MUST; the comparison is now exact.core/resource/resource.go(wellKnownPRMPath) — a resource path of/collapsed to the bare well-known prefix; insertion preserves it. Both derivers now use the escaped path so percent-encoded octets are kept as sent, andPRMURLis composed by concatenation to avoid re-escaping.The trim that must stay
buildOIDCDiscoveryURLkeeps itsTrimRight— OIDC Discovery 1.0 §4 concatenates rather than inserts and mandates removing a terminating/before appending. Both builders now carry comments explaining why the two same-looking constructions differ, plus a pinning test each, so a future normalisation sweep does not delete the OIDC trim and silently break OIDC discovery.Validation instead of repair
New
verifier.ValidateIdentifier: identifiers must be absolute http(s) URLs with a host and no fragment (RFC 8707 §2 forbids fragments). Trailing slashes, host case, and explicit ports are legal variations and preserved verbatim. Applied to issuer, audience, and resource URI.Conformance
rfc9728-well-known-path-must-derive-from-resource-uri— extended with the trailing-slash resource datum.rfc8414-metadata-issuer-must-match-configured-issuer— variant: metadata issuer differing only by a trailing slash is rejected (equivalent per RFC 3986 §6.2.3 is not identical).rfc9068-issuer-must-match— variant: a token whoseissis identical to a configured trailing-slash issuer verifies.Migration
If your configured issuer or resource differs from your authorization server's actual identifier by a trailing slash, correct the config — the SDK no longer silently reconciles them. (CHANGELOG entry included.)
Validation
All four workspace modules (core/http/mcp/mark3labs) build and test green,
go vetand gofmt clean, conformance suite green against the amended catalog.