Repository navigation
fix(server)!: confine resource-less requests to AllowedResources; resources at consent - #699
Merged
Merged
Conversation
…owedResources A request naming no RFC 8707 resource got an access token addressed to the issuer, and AllowedResources was never checked against it. Every resource server accepting the issuer as an audience (one migrating with AcceptIssuerAudience or the issuer in AdditionalAudiences, and the UserInfo endpoint) took that token, so a client refused a resource could reach it by naming none. For a client with AllowedResources, a request naming no resource now gets a default: with one entry, it is for that resource (recorded on the grant and checked against Config.Resources and the ResourcePolicy as a named one is); with several, it is refused with invalid_target. The default applies wherever a resource can be named (PAR, CIBA, client_credentials, IssueAccessToken and IssueRefreshToken) and to a grant made without resources before the client was confined, at its next code exchange, refresh or CIBA token request, so neither the embedder APIs nor an older grant can produce an issuer-audience token for a confined client. Clients without AllowedResources, and servers with resource indicators disabled, are unchanged. RegisteredClient.AllowedResources returns the registered list. BREAKING CHANGE: a client registered with AllowedResources no longer gets issuer-audience access tokens when resource indicators are enabled. With a single entry, a request naming no resource is for that resource; with several, it fails with invalid_target. Such a client that calls the UserInfo endpoint must list the issuer identifier in its AllowedResources (and the server must accept it as a resource), then name a resource in every request. See UPGRADING.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n clients' resources InteractionRequest.Resources and BackchannelInteractionRequest.Resources carry the RFC 8707 resources the tokens would be restricted to (the client's AllowedResources default included), so where several resource servers or OpenID4VCI Credential Issuers share the authorization server, the consent step can show which one a token is for. The encoded InteractionRequest carries them too. GrantedAuthorization.Resources narrows them to the subset the resource owner approved; nil grants all of them, as before, and one that wasn't requested fails the completion with invalid_request, as an unrequested scope does. AutomaticRegistrationConfig.AllowedResources (federation and server) confines every client registered through OpenID Federation to the operator's resources, the way AllowedScopes sets their scopes: never read from an RP's own metadata. Each entry must be an absolute URI without a fragment, none repeated. Empty leaves them unconfined, as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…sage in helpers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… helper Also covers a non-string CIBA binding_message. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
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.



Summary
RFC 8707 resource-indicator fixes around
AllowedResources, consent and automatic registration.Breaking (
fix(server)!): a request naming no resource is confined to the client'sAllowedResources.A request that named no resource used to get an access token addressed to the issuer, and that token was never checked against
AllowedResources. Every resource server that accepts the issuer as an audience took it: one migrating withserverresource.Options.AcceptIssuerAudienceor with the issuer inAdditionalAudiences, and the UserInfo endpoint. So a client refusedresource=Bgot a token B accepts by leavingresourceout. Now, when resource indicators are enabled and the client hasAllowedResources:Config.Resources/ResourcePolicylike a named resource.invalid_target("a resource is required").This applies to PAR (plain and request object), CIBA,
client_credentialsand the embedder APIsIssueAccessToken/IssueRefreshToken. It also covers a grant made without resources before the client was confined, at its next code exchange, refresh or CIBA token request. The default lives incheckRequestedResources, which all those paths, including the token-time re-check innarrowedAudience, already call. An omittedresourceat the token endpoint still means the whole set the grant holds. Clients withoutAllowedResources, and servers with resource indicators disabled, behave as before.This also closes the embedder gap: a refresh token issued through
IssueRefreshTokenwith fewerResourcescan no longer widen later tokens to the issuer audience for a confined client. New accessor:storage.RegisteredClient.AllowedResources().Additive (
feat(server)):InteractionRequest.ResourcesandBackchannelInteractionRequest.Resources: the resources the tokens would be for, including the default above. They are cloned, carried byMarshalText/ParseInteractionRequest, and returned byLookupBackchannelInteraction.GrantedAuthorization.Resources: narrows the request's resources to the subset the user approved, atCompleteAuthorizationandCompleteBackchannelAuthentication. Nil grants all of them, which is the old behaviour. A resource that wasn't requested getsinvalid_request, the same check used for scopes.AutomaticRegistrationConfig.AllowedResourcesin bothfederationandserver: an operator-level confinement applied to every client registered through OpenID Federation. It is never read from RP metadata, and it is validated likeConfig.Resources(absolute URI, no fragment, no repeats).Docs: updated the doc comments for
AllowedResources,Config.Resources,IssueAccessTokenRequest/IssueRefreshTokenRequest.ResourcesandAcceptIssuerAudience, and GETTING_STARTED's RFC 8707 section. Its old statement that consent can't see resources was removed. UPGRADING.md has a new v0.61.0 section.Refactors (no behaviour change):
completeAuthorizenow validates the approved grant incheckApprovedGrantand the login_required conditions inloginRequiredProblem. CIBA hint andbinding_messagevalidation moved intovalidateBackchannelHintsandvalidateBindingMessage. These keep the touched functions within Sonar's cognitive-complexity limit.Breaking change
A client with
AllowedResourcesno longer gets issuer-audience tokens while resource indicators are enabled:invalid_target.AllowedResources, and the server must accept the issuer as a resource. Then it must name a resource on every request.Test plan
fapitest/resource_indicators_allowed_test.go, covering the code flow and refresh with JWT and opaque tokens, client credentials, and multi-entry refusal. Each one checks the token is refused by a resource server that accepts the issuer audience and by the issuer's own verifier. The existing client-credentials "no resource" case now expectsinvalid_target.resource_indicators_default_test.go(PAR, the default being re-checked againstConfig.Resources/the policy, disabled indicators, earlier grants, CIBA, client credentials, embedder access and refresh) andresource_indicators_consent_test.go(interaction resources, narrowing, refusal onCompleteAuthorization, and on CIBA viaLookupBackchannelInteraction). Also new federation config tests, a server wiring test and an extended round-trip test.AllowedResourcestest fail.GOTOOLCHAIN=go1.26.9 go test ./...,go test -raceon server, fapitest and federation,go vet ./...,GOOS=linux GOARCH=arm go vet ./...,golangci-lint run ./...(0 issues), and vet and tests for everyexamples/*module and demokit.🤖 Generated with Claude Code