Skip to content

fix(core): stop normalising issuer and resource identifiers - #22

Closed
RobertoIskandarani wants to merge 1 commit into
mainfrom
fix/identifier-identity-trailing-slash
Closed

fix(core): stop normalising issuer and resource identifiers#22
RobertoIskandarani wants to merge 1 commit into
mainfrom
fix/identifier-identity-trailing-slash

Conversation

@RobertoIskandarani

Copy link
Copy Markdown

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.NewTokenVerifier rewrote the configured issuer (strings.TrimRight(issuer, "/")), and the token's iss is compared against that stored value. For an authorization server whose issuer identifier legitimately ends in /, RFC 9068 requires iss to 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; issuer https://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, and PRMURL is composed by concatenation to avoid re-escaping.

The trim that must stay

buildOIDCDiscoveryURL keeps its TrimRight — 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 whose iss is 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 vet and gofmt clean, conformance suite green against the amended catalog.

@RobertoIskandarani
RobertoIskandarani requested a review from a team as a code owner July 29, 2026 14:03
@RobertoIskandarani RobertoIskandarani self-assigned this Jul 29, 2026
@RobertoIskandarani
RobertoIskandarani force-pushed the fix/identifier-identity-trailing-slash branch from bb4b327 to 40b353c Compare July 29, 2026 14:09
@RobertoIskandarani
RobertoIskandarani removed request for a team and muralx July 29, 2026 14:10
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
RobertoIskandarani force-pushed the fix/identifier-identity-trailing-slash branch from 40b353c to 4901bbd Compare July 29, 2026 14:11
@RobertoIskandarani

Copy link
Copy Markdown
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.

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.

1 participant