fix: stop normalising resource and issuer identifiers - #22
Open
RobertoIskandarani wants to merge 2 commits into
Open
fix: stop normalising resource and issuer identifiers#22RobertoIskandarani wants to merge 2 commits into
RobertoIskandarani wants to merge 2 commits into
Conversation
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 instead stripped slashes in five places:
- AuthplaneClient.create rewrote the configured issuer with rstrip("/").
The rewritten value became the verifier's expected iss claim, so an
AS whose issuer identifier legitimately ends in "/" had every token
rejected (RFC 9068 requires iss to carry the slash).
- build_prm_url and build_metadata_url used path.strip("/"), dropping
the trailing slash the insertion rule requires be preserved; both are
now pure insertion of the parsed path.
- MetadataCache rstripped both sides of the issuer comparison,
weakening the §3.3 identical-match MUST that defeats metadata
substitution.
Identifiers are now validated at construction instead (absolute
http(s) URL with an authority, no fragment — RFC 8707 §2) via the new
internal validate_identifier helper, 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 end to
end (discovery at the trailing-slash well-known URL included).
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:09
8f7b224 to
245d31a
Compare
ruff's newer formatter changed how it renders code blocks embedded in Markdown; CI installs the latest ruff, so the format check fails on files this branch does not otherwise touch. Formatting-only, no content changes. Expected to become a no-op once the in-flight change that already carries this reformatting lands on main.
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 slashes in five places; this PR removes all five and replaces silent rewriting with construction-time validation.
The user-visible bug
AuthplaneClient.createrewrote the configured issuer (issuer.rstrip("/")), and that value is what the verifier uses as the expectedissclaim. For an authorization server whose issuer identifier legitimately ends in/, RFC 9068 requires the token'sissto carry the trailing slash — so every token was rejected for such a deployment. Verified by reintroducing the strip: the new end-to-end conformance variant fails (discovery resolves the wrong well-known URL), and passes with the fix.The five sites
authplane/client.py— issuer stored verbatim; validated, never rewritten.authplane/internal/urls.py(two sites) —build_prm_urlandbuild_metadata_urlusedpath.strip("/"); both are now pure insertion ofparsed.path, sohttps://api.example.com/mcp/derives/.well-known/oauth-protected-resource/mcp/andhttps://auth.example.com/tenant/derives/.well-known/oauth-authorization-server/tenant/.authplane/internal/metadata.py(two sites) — both sides of the issuer comparison were rstripped, weakening the §3.3 identical-match MUST; the comparison is now exact. (These strips masked the client-level bug during discovery — fixed together so the symptom is removed, not moved.)Validation instead of repair
New
authplane/internal/identifiers.py: identifiers must be absolute http(s) URLs with an authority and no fragment (RFC 8707 §2 forbids fragments). Trailing slashes, host case, and explicit ports are legal variations and preserved verbatim. Applied atAuthplaneClient.create(issuer) andAuthplaneResource(resource).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 end to end, including discovery at the trailing-slash well-known URL.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
520 unit + 104 conformance tests green (against the amended catalog), ruff lint and format clean.