Skip to content

fix(core,mcp,spring): preserve issuer and resource identity, strip the slash only at derivation - #19

Open
RobertoIskandarani wants to merge 2 commits into
mainfrom
port/issuer-identity-prm
Open

fix(core,mcp,spring): preserve issuer and resource identity, strip the slash only at derivation#19
RobertoIskandarani wants to merge 2 commits into
mainfrom
port/issuer-identity-prm

Conversation

@RobertoIskandarani

Copy link
Copy Markdown
Collaborator

What

Identifiers are identity, not something to normalise. The SDK reconciled them in
several places, and each was a silent rewrite of a value the operator configured.

Identity is preserved verbatim (RFC 8414/9728 §3.3); slash removal happens only
at derivation (§3.1).
Those are the two concerns the previous code conflated.

  • Issuer — the configured value is stored byte-for-byte, and the AS-metadata
    comparison no longer strips either side. RFC 8414 §4 specifies that comparison
    as code-point-for-code-point with no normalisation. Derivation is many-to-one:
    …/tenant and …/tenant/ share one well-known URL and the RFC provides no way
    to host both. The strict comparison is what turns that unavoidable collision
    into a clean discovery failure rather than a silent bind to a different issuer's
    metadata — the impersonation §3.3 exists to defeat.
  • Resource — the PRM path drops the terminating slash of the resource path, so
    a resource configured as /mcp/ is served at, and derived by a conformant client
    as, .../oauth-protected-resource/mcp. Before this the SDK registered a path no
    conformant client would request. The identifier itself is untouched; only the
    derived publication URL loses the slash.

The two halves have to move together. Preserving identity without fixing
derivation publishes a document nobody requests; fixing derivation without
preserving identity keeps rewriting the operator's configuration.

Conformance

The RFC 8414, 9068 and 9728 suites gain the identity and derivation rows the
catalog already carries — no new case ids, so this adopts nothing new from the
catalog.

Verification

Full suite against the pinned catalog revision:

module before after
core 727 737
mcp 62 62
spring 115 115

All green, BUILD SUCCESS.

Also verified to compose with the catalog-alignment assertion landing in the
pin PR: applying that assertion on top of this change, the both-directions check
passes. The two are deliberately separate — one is CI mechanics, the other is SDK
behaviour — but they will be on main together, so the combination was checked
rather than assumed.

Scope

Deliberately excludes the conformance catalog pin and its enforcement, which are
their own PR. The file sets are disjoint, so neither blocks the other.

…e slash only at derivation

Identifiers are identity, not something to normalise. The SDK reconciled them in
several places, and each of those was a silent rewrite of a value the operator
configured.

Identity is now preserved verbatim (RFC 8414/9728 §3.3) and slash removal happens
only at derivation (§3.1) — the two concerns the previous code conflated:

- The configured issuer is stored byte-for-byte and the AS-metadata issuer
  comparison no longer strips either side. RFC 8414 §4 specifies that comparison
  as code-point-for-code-point with no normalisation, and the strict form is what
  turns an unavoidable derivation collision into a clean discovery failure rather
  than a silent bind to a different issuer's metadata.
- The Protected Resource Metadata path drops the terminating slash of the
  resource path, so a resource configured as /mcp/ is served at, and derived by a
  conformant client as, .../oauth-protected-resource/mcp. The identifier itself is
  unchanged; only the derived publication URL loses the slash.

Both halves matter together: preserving identity without fixing derivation
publishes a document no conformant client will request, and fixing derivation
without preserving identity keeps rewriting the operator's configuration.

Conformance coverage follows the behaviour — the RFC 8414, 9068 and 9728 suites
gain the identity and derivation rows the catalog already carries.

Verified against the pinned catalog revision: 737 core, 62 mcp and 115 spring
tests green, up from 727 core on the base. Also verified to compose with the
catalog-alignment assertion that lands separately: the both-directions check
passes with this change applied.
@RobertoIskandarani
RobertoIskandarani requested a review from a team as a code owner August 10, 2026 16:00
Five tests in DocumentCacheTest asserted against wall time, three of them as
outright races. get_triggersBackgroundRefreshAt80PercentTtl slept 900 ms into an
unsynchronised 1 s TTL, then gave an asynchronous refresh 300 ms to finish before
asserting the fetch count — so on a loaded runner it fails with
"expected >= 2, was 1", the refresh having been triggered but not completed. Two
siblings slept 1500 ms into a 1 s TTL purely to force expiry.

DocumentCache now takes a java.time.Clock; the 5-arg public constructor supplies
Clock.systemUTC(), so no caller changes. Tests advance the clock instead of
sleeping, and the one asynchronous assertion is ordered by joining the refresh
rather than by a fixed delay.

No public API change and no token-validation path touched.

Observed here, not hypothesised: this test failed CI on the port branch that
carries none of this code, and passes on a re-run of the same commit. The class
now runs in 0.1 s instead of 5.8 s, with no Thread.sleep left in it.
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