Client-side multi-scope token flow#4178
Open
stevenvegt wants to merge 5 commits into4144-1-scope-parsing-and-configfrom
Open
Client-side multi-scope token flow#4178stevenvegt wants to merge 5 commits into4144-1-scope-parsing-and-configfrom
stevenvegt wants to merge 5 commits into4144-1-scope-parsing-and-configfrom
Conversation
|
Coverage Impact This PR will not change total coverage. Modified Files with Diff Coverage (4)
🤖 Increase coverage with AI coding...🚦 See full report on Qlty Cloud » 🛟 Help
|
Introduces PresentationDefinitionResolver that abstracts PD resolution. When the remote AS metadata advertises a PD endpoint, the PD is fetched remotely and the full scope string is returned for the token request. Local fallback path is stubbed for the next cycle. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
When no remote PD endpoint exists, the resolver calls FindCredentialProfile locally. Profile-only rejects extra scopes, passthrough/dynamic forward all. Tests cover both remote and local paths with all scope policies. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Replace direct PD fetch in RequestRFC021AccessToken with the PresentationDefinitionResolver. The resolver is a dependency on OpenID4VPClient, wired through Auth → NewClient. The policy backend is passed through Auth to enable local PD fallback. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- Add nil guard on policyBackend in resolveLocal - Return canonical credential profile scope for profile-only (not raw input) - Add comment explaining dynamic treated same as passthrough on client side - Add tests: nil policy backend, missing org PD, remote endpoint error - Fix import grouping in test file Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
f1ee8fb to
0b190e6
Compare
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.

Parent PRD
#4144
Summary
Modifies the client-side S2S token request flow to support mixed OAuth2 scopes. Introduces a
PresentationDefinitionResolverthat decides whether to fetch the PD from the remote AS (Nuts-to-Nuts, trust the server) or fall back to local policy resolution (integration with non-Nuts AS), and enforces scope policy when resolving locally.What changed
New
PresentationDefinitionResolver(auth/client/iam/pd_resolver.go):FindCredentialProfilelocally; enforce scope policy (profile-only rejects extras; passthrough/dynamic forward all)OpenID4VPClientwiring (auth/client/iam/openid4vp.go):RequestRFC021AccessTokenreplaces its direct PD fetch withc.pdResolver.Resolve(...)and usesresolved.Scopefor the token requestOpenID4VPClientgets apdResolverfield;NewClienttakes apolicy.PDPBackendparameter and wires the resolver internallyPropagation through
Auth(auth/auth.go,cmd/root.go):Authstruct gets apolicyBackendfieldNewAuthInstancetakespolicy.PDPBackendas a new parametercmd/root.goreorders:policy.New()moves beforeauth.NewAuthInstanceso it can be passed inDynamic = passthrough on client: the client doesn't call the AuthZen PDP. The server enforces dynamic at token-grant time (#4179).
How to review
Start with
pd_resolver.go— the core logic:Resolvedispatches to remote or localresolveLocalenforces scope policy and selects the organization PD (TODO comment for Server-side RFC 7523 JWT Bearer grant with two VPs (PSA 10.10) #4080 two-VP flow)pd_resolver_test.gocover both paths with all scope policies, nil guards, and missing-PD / unknown-scope edge cases (10 cases total)Then
openid4vp.go— the call site change is minimal (replacing the direct PD fetch withpdResolver.Resolve). The important detail is thatdata.Set(oauth.ScopeParam, resolved.Scope)uses the resolver's output, not the raw input.Wiring review —
auth.goandcmd/root.goadd the policy backend dependency. Note the line reordering incmd/root.go(policyInstance moved up).Deviations from spec
presentationDefinitionForScope, but that's server-side only. The actual client-side flow goes throughRequestRFC021AccessToken. The resolver replaces the direct PD fetch there.PresentationDefinitionResolverwas added as an abstraction instead of inlining the logic inRequestRFC021AccessToken. Keeps the OAuth client focused on the OAuth flow and makes PD resolution independently testable.CredentialProfileScope(not raw input) — caught during self-review to avoid echoing whitespace/formatting artifacts.Dependencies
Depends on: #4176 (base branch, provides
FindCredentialProfileand scope policy types).Independent of #4177 (AuthZen client) — client-side doesn't call the PDP. Can be reviewed in parallel with #4177.
Design context
Acceptance Criteria
PresentationDefinitionResolverresolves PD from remote when PD endpoint existsFindCredentialProfilewhen no remote PD endpointRequestRFC021AccessTokenuses resolver instead of direct PD fetchOriginal implementation spec (used during AI-assisted development)
Parent PRD
#4144
Implementation Spec
Overview
Modify the client-side token request flow to support mixed OAuth2 scopes. Introduces a
PresentationDefinitionResolverthat abstracts the decision of whether to fetch a PD from the remote AS or fall back to local policy resolution. When using local resolution, scope policy is enforced.Design decisions
PresentationDefinitionResolverabstraction: The client (RequestRFC021AccessToken) should not decide where the PD comes from. A resolver encapsulates the remote-vs-local decision and scope policy enforcement, keeping the client focused on the OAuth flow.FindCredentialProfilelocally to get the PD and scope classification. Scope policy is enforced locally: profile-only rejects extra scopes, passthrough/dynamic forward all scopes.OpenID4VPClient: The resolver is a struct dependency, wired throughAuth→NewClient. Policy backend passed throughAuthto enable local PD fallback.Deviations from original spec
presentationDefinitionForScopewas the modification point: That helper is server-side only. The actual client-side flow goes throughRequestRFC021AccessToken→ remote PD endpoint. The resolver replaces the direct PD fetch.Authstruct: AddedpolicyBackendparameter toNewAuthInstanceandiam.NewClientto propagate the policy backend to the resolver.Modified flow
Acceptance Criteria
PresentationDefinitionResolverresolves PD from remote when PD endpoint existsFindCredentialProfilewhen no remote PD endpointRequestRFC021AccessTokenuses resolver instead of direct PD fetch