Repository navigation
auth: reuse the dynamically registered client across authorizations - #1419
Merged
guglielmo-san merged 2 commits intoOct 20, 2026
Merged
guglielmo-san merged 2 commits into
guglielmo-san merged 2 commits into
Conversation
AuthorizationCodeHandler.Authorize called handleRegistration every time it ran, and did not keep the registration response. With dynamic client registration, every authorization registered a new client: the initial 401, every step-up 403 with insufficient_scope, and every re-authorization after a refresh is rejected with invalid_grant. Each one left an orphaned client on the authorization server, which the SDK never deletes (it does not implement RFC 7592). It also showed the user a consent screen for a new client on each step-up, and the scopes accumulated in grantedScopes had been granted to a client that was no longer used. Keep the registered client on the handler, keyed by the authorization server issuer, and reuse it in handleRegistration. The TypeScript SDK also registers only when it has no stored client information. The client is registered again when: - its client_secret_expires_at has passed (RFC 7591, section 3.2.1), or - an authorization that used it fails, since the authorization server may have deleted it. This keeps the previous behavior, a new registration on the next attempt, for that case. Tests: - TestAuthorize_ReusesDynamicRegistration: a step-up authorization uses the client_id registered by the first one. Fails without the change. - TestAuthorize_ForgetsDynamicRegistrationAfterFailure: after an authorization with a client the server no longer knows fails, the next one registers a new client. - TestHandleRegistration_StoredDynamicRegistration: a stored client is reused unless its client secret has expired. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
guglielmo-san
approved these changes
Oct 20, 2026
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.
Fixes #1416.
AuthorizationCodeHandler.AuthorizecalledhandleRegistrationevery time it ran, and did not keep the registration response. With dynamic client registration, every authorization registered a new client: the initial 401, every step-up 403 withinsufficient_scope, and every re-authorization after a refresh is rejected withinvalid_grant. Each one left an orphaned client on the authorization server, which the SDK never deletes (it does not implement RFC 7592). It also showed the user a consent screen for a new client on each step-up, and the scopes accumulated ingrantedScopeshad been granted to a client that was no longer used.This keeps the registered client on the handler, keyed by the authorization server issuer, and reuses it in
handleRegistration. The TypeScript SDK also registers only when it has no stored client information. The client is registered again when:client_secret_expires_athas passed (RFC 7591, section 3.2.1), orDynamicClientRegistrationConfigdocuments the reuse. There is no API change.Tests:
TestAuthorize_ReusesDynamicRegistration: a step-up authorization uses theclient_idregistered by the first one. Fails without the change.TestAuthorize_ForgetsDynamicRegistrationAfterFailure: after an authorization with a client the server no longer knows fails, the next one registers a new client.TestHandleRegistration_StoredDynamicRegistration: a stored client is reused unless its client secret has expired.The last two exercise the new code, so they cannot fail on
main. I checked each one by removing the part of the change it covers: each then fails, and only that one.This touches the same part of
Authorizeas #1104. The two are independent, but whichever lands second will need a small rebase.The client conformance suite shows the change too. On
main,auth/scope-step-upandauth/scope-retry-limiteach record a secondclient-registrationwhen the step-up happens. With this change they record one, so the total goes from 239 to 237 passed checks, with no failures.go test ./...,go test -raceonauthandmcp,go vet ./..., staticcheck v0.6.1 andscripts/client-conformance.share clean.AI assistance: prepared with Claude Code; I reviewed and verified the change.
🤖 Generated with Claude Code