Scope parsing, classification & policy config#4176
Open
stevenvegt wants to merge 5 commits intofeature/4144-mixed-scopesfrom
Open
Scope parsing, classification & policy config#4176stevenvegt wants to merge 5 commits intofeature/4144-mixed-scopesfrom
stevenvegt wants to merge 5 commits intofeature/4144-mixed-scopesfrom
Conversation
|
Coverage Impact ⬆️ Merging this pull request will increase total coverage on Modified Files with Diff Coverage (4)
🤖 Increase coverage with AI coding...🚦 See full report on Qlty Cloud » 🛟 Help
|
Rename the PDPBackend interface method and introduce new types (CredentialProfileMatch, ScopePolicy, credentialProfileConfig) to support mixed OAuth2 scopes. The policy config struct now uses explicit fields for organization/user PDs and scope_policy, defaulting to profile-only. All callers and mocks updated. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Tests cover: multi-scope with one profile scope + other scopes, multiple profile scopes (error), no profile scope (error), and empty scope string (error). Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Tests cover: scope_policy parsed from JSON config (dynamic, passthrough), invalid scope_policy rejected at load time, dynamic without AuthZen endpoint fails at startup, passthrough without endpoint succeeds. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- Introduce ErrAmbiguousScope for multiple credential profile scopes (instead of wrapping ErrNotFound which was semantically wrong) - Use strings.Fields instead of strings.Split for robust whitespace handling - Add nil-check: credential profile must define at least one of organization/user - Add doc comments on Config, ErrNotFound, FindCredentialProfile implementation - Use value receiver on toWalletOwnerMapping (small non-mutating struct) - Add test for consecutive spaces in scope string - Assert ScopePolicy in multi-scope test - Make Configure tests load single files instead of whole directory Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
24 tasks
This was referenced Apr 16, 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.

Parent PRD
#4144
Summary
Foundation layer for mixed OAuth2 scope support. Renames
PDPBackend.PresentationDefinitions()toFindCredentialProfile()and enriches its return type to include the credential profile scope, its policy, and any remaining ("other") scopes. Extends the policy JSON format with ascope_policyfield and adds apolicy.authzen.endpointCLI flag validated at startup.What changed
Policy interface (
policy/interface.go,policy/local.go):PresentationDefinitions→FindCredentialProfile, returning*CredentialProfileMatch(scope, WalletOwnerMapping, scope policy, other scopes) instead of justpe.WalletOwnerMapping.strings.Fields(tolerant of tabs, newlines, consecutive spaces).ErrAmbiguousScope(multiple profile scopes in one request), distinct fromErrNotFound.Policy JSON format (
policy/local.go,policy/test/scope_policy/):scope_policyfield per credential profile:"profile-only"(default),"passthrough", or"dynamic".scope_policycontinue to work (defaults toprofile-only).credentialProfileConfigreplaces the map-basedvalidatingWalletOwnerMappingwith an explicit struct (organization/user fields + scope_policy).validatingPresentationDefinition— moved from mapping-level to per-PD level.Config & validation (
policy/config.go,policy/cmd.go,policy/local.go):policy.authzen.endpointCLI flag.dynamicwithout an AuthZen endpoint configured.passthroughandprofile-onlycontinue to work without an endpoint.Callers updated (
auth/api/iam/):validation.goandapi.goupdated for the new return type, withErrAmbiguousScopemapped toinvalid_scope.*CredentialProfileMatch.How to review
Start with
policy/interface.go— understand the new types (CredentialProfileMatch,ScopePolicy) and the renamed interface method.Then
policy/local.go— the core logic:FindCredentialProfile(scope parsing + classification)credentialProfileConfig.UnmarshalJSON(JSON format handling)Configure(startup validation for AuthZen endpoint)Tests to focus on (
policy/local_test.go):TestLocalPDP_FindCredentialProfile— multi-scope parsing edge cases (consecutive whitespace, empty strings, multiple profile scopes, zero profile scopes)TestLocalPDP_ScopePolicyConfig— JSON parsing, invalid value rejectionTestLocalPDP_Configure— startup validation behaviorCallers (
auth/api/iam/) are mechanical updates — they extract.WalletOwnerMappingfrom the result to preserve existing behavior.Deviations from spec
credentialProfileConfiguses an explicit struct withOrganization/Userfields, not the map-based approach suggested in the spec. This avoided custom JSON parsing of the scope_policy field while preserving the existing flat JSON format. PD validation moved to per-PD level (still uses the v2 schema).ErrAmbiguousScopeas a distinct error (not in spec). WrappingErrNotFoundfor "too many profile scopes" was semantically wrong.organization/user— catches typos likeorganisationsthat standard struct unmarshaling would silently accept.strings.Fieldsinstead ofstrings.Split— handles tabs, newlines, and consecutive spaces robustly.Dependencies
None — this is the foundation PR. Subsequent PRs #4177–#4180 build on this interface.
Design context
passthroughmode was added in-scope after initial PR split (see PRD comment 4235711103)Acceptance Criteria
FindCredentialProfile()accepts space-delimited multi-scope strings (usesstrings.Fieldsfor robust whitespace handling)ErrNotFoundfor zero,ErrAmbiguousScopefor multiple)scope_policyfield parsed from policy config (defaults to"profile-only")scope_policyvalues rejected at load timeorganizationoruserpolicy.authzen.endpointconfig field and CLI flag addeddynamicscope policy is used without AuthZen endpointpassthroughandprofile-onlywork without AuthZen endpointvalidatingPresentationDefinition(validates against v2 JSON schema)Original implementation spec (used during AI-assisted development)
Parent PRD
#4144
Implementation Spec
Overview
Foundation layer for mixed OAuth2 scope support. This PR modifies the policy subsystem to:
scope_policyfield per credential profile.policy.authzen.endpointconfiguration field for the AuthZen PDP URL.scope_policy: "dynamic"requires an AuthZen endpoint.Key files modified
policy/interface.go— RenamedPDPBackend.PresentationDefinitions()toFindCredentialProfile(), addedCredentialProfileMatch,ScopePolicy,ErrAmbiguousScopetypespolicy/local.go— NewcredentialProfileConfigstruct with explicit Organization/User fields, multi-scope parsing viastrings.Fields, scope policy defaulting and validationpolicy/config.go— AddedAuthZenConfigwith endpoint fieldpolicy/cmd.go— Addedpolicy.authzen.endpointCLI flagauth/api/iam/validation.go— Updated caller to useFindCredentialProfile, handlesErrAmbiguousScopeauth/api/iam/api.go— Updated caller to useFindCredentialProfileDesign
Interface change
Renamed
PresentationDefinitionstoFindCredentialProfileto better reflect the method's responsibility: resolving a scope string against the policy configuration to find the matching credential profile.Policy configuration format
The per-scope JSON structure now supports
scope_policyalongside the existingorganization/userkeys:{ "urn:nuts:medication-overview": { "organization": { /* PD */ }, "scope_policy": "profile-only" } }Internally,
credentialProfileConfiguses a struct with explicit fields (instead of the old map-basedvalidatingWalletOwnerMapping), allowing standard JSON unmarshaling. Individual PDs are validated against the v2 JSON schema viavalidatingPresentationDefinition.Error handling
ErrNotFound— no credential profile scope matchedErrAmbiguousScope— multiple credential profile scopes found in the requestAcceptance Criteria
FindCredentialProfile()accepts space-delimited multi-scope strings (usesstrings.Fieldsfor robust whitespace handling)ErrNotFoundfor zero,ErrAmbiguousScopefor multiple)scope_policyfield parsed from policy config (defaults to"profile-only")scope_policyvalues rejected at load timeorganizationoruserpolicy.authzen.endpointconfig field and CLI flag addeddynamicscope policy is used without AuthZen endpointpassthroughandprofile-onlywork without AuthZen endpointvalidatingPresentationDefinition(validates against v2 JSON schema)