Skip to content

#4067: Credential selection when multiple credential from wallet match#4137

Merged
stevenvegt merged 2 commits intomasterfrom
feature/4067-credential-selection-dcql
Apr 1, 2026
Merged

#4067: Credential selection when multiple credential from wallet match#4137
stevenvegt merged 2 commits intomasterfrom
feature/4067-credential-selection-dcql

Conversation

@stevenvegt
Copy link
Copy Markdown
Member

@stevenvegt stevenvegt commented Mar 30, 2026

This PR closes #4067 and adds an credentian_selector parameter to the access token request so the API user is able to deterministically select a specific credential in case of more than one candidate. E.g. the PatientEnrollmentCredential with one VC per patient.

This PR contains all squashed commits from the approved PRs in the chain.

* #4088: Extract CredentialSelector and refactor matchConstraints

Introduces CredentialSelector function type and FirstMatchSelector.
Refactors matchConstraints to collect all matching VCs per input
descriptor, then call the selector to pick one. Match delegates to
MatchWithSelector with FirstMatchSelector. No behavior change —
all existing tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* #4088: Add SetCredentialSelector to PresentationSubmissionBuilder

Adds SetCredentialSelector method to configure a custom CredentialSelector
on the builder. Build passes it through to MatchWithSelector. Falls back
to FirstMatchSelector when not set.

Tests verify: custom selector picks non-first credential, default behavior
preserved, selector receives all matching candidates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* #4088: Document CredentialSelector contract, add ErrMultipleCredentials

CredentialSelector doc now describes return value semantics: when to
return nil (unfulfilled), ErrNoCredentials (no match), or
ErrMultipleCredentials (ambiguous). Adds error propagation test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* #4088: Soft-fail ErrNoCredentials for submission requirements compatibility

When a CredentialSelector returns ErrNoCredentials, treat it as a nil
selection (unfulfilled descriptor) instead of a hard failure. This allows
submission requirements with pick rules (e.g., min: 0) to evaluate
whether zero fulfilled descriptors is acceptable.

ErrMultipleCredentials and other errors remain hard failures.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* #4120: Selection CredentialSelector — filter candidates by PD field ID values (#4121)

* #4120: Selection CredentialSelector — filter candidates by PD field ID values

NewSelectionSelector creates a CredentialSelector that filters candidates
using named field ID values from the credential_selection API parameter.
Selection keys are validated against PD field IDs at construction time.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* #4120: Simplify key validation with matchedKeys set

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* - Rename NewSelectionSelector → NewFieldSelector for clarity
- Return matchConstraint errors instead of swallowing them
- Simplify matchesSelections to assert string type directly
- Add comment explaining constant-only matching design

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* #4121: Remove fallback parameter from NewFieldSelector

Selectors are not composable — default behavior belongs in the builder.
NewFieldSelector now returns (nil, nil) for unmatched descriptors,
and the builder falls back to FirstMatchSelector when a selector
returns (nil, nil) with candidates available.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* #4090: Wire credential_selection through API to presenter (#4122)

* #4090: RED — e2e test for credential_query selection

Adds a credential_query test to the RFC021 e2e flow:
- Issues two NutsOrganizationCredentials with different org names
- Uses credential_query to select "Second Org B.V." by name
- Verifies extended introspection contains the selected organization

Test correctly fails: credential_query is currently ignored, so the
default PD matcher picks "Caresoft B.V." instead of "Second Org B.V."

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* #4090: Wire credential_query through API to presenter

- OpenAPI spec: add credential_query to ServiceAccessTokenRequest
- Regenerate types and mocks
- API handler: extract credential_query, convert to dcql.CredentialQuery
- Client.RequestRFC021AccessToken: accept credentialQueries parameter
- Wallet.BuildSubmission: accept credentialQueries parameter
- Presenter: create DCQL selector when credentialQueries is non-empty
- All existing tests pass with nil for new parameter

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* #4090: Wire credential_selection through API to presenter

Replace credential_query (DCQL) with credential_selection (named
parameters) throughout the callstack. The API accepts a simple
map[string]string mapping PD field IDs to expected values, which is
propagated through the IAM client, wallet, and presenter to
NewSelectionSelector.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* #4090: Fix shellcheck issues, add presenter credential_selection test

- Double-quote shell variables in e2e test to prevent globbing/splitting
- Use single-quoted heredoc for JSON literal
- Fix empty-response check to use proper test syntax
- Add unit test for buildSubmission with credential_selection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fixup: Use renamed NewFieldSelector in presenter

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* #4122: Address review feedback

- Capitalize "field id" → "field ID" in OpenAPI spec
- Add godoc for credentials and credentialSelection parameters
- Document default behavior when credential_selection is omitted
- Add comment explaining FirstMatchSelector fallback
- Add comment explaining nil credential_selection is safe (read-only)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* #4122: Remove fallback parameter from NewFieldSelector call

The builder now handles the fallback to FirstMatchSelector when a
selector returns (nil, nil), so the explicit fallback argument is
no longer needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@qltysh
Copy link
Copy Markdown

qltysh bot commented Mar 30, 2026

0 new issues

Tool Category Rule Count

@qltysh
Copy link
Copy Markdown

qltysh bot commented Mar 30, 2026

Qlty

Coverage Impact

⬆️ Merging this pull request will increase total coverage on master by 0.06%.

Modified Files with Diff Coverage (9)

RatingFile% DiffUncovered Line #s
Coverage rating: B Coverage rating: B
vcr/holder/sql_wallet.go100.0%
Coverage rating: B Coverage rating: B
vcr/holder/presenter.go71.4%65-66
Coverage rating: B Coverage rating: B
auth/api/iam/api.go60.0%780-781
Coverage rating: A Coverage rating: A
vcr/pe/presentation_definition.go87.1%173-176
Coverage rating: B Coverage rating: B
auth/api/iam/openid4vp.go100.0%
Coverage rating: B Coverage rating: B
auth/client/iam/openid4vp.go100.0%
Coverage rating: F Coverage rating: F
vcr/holder/memory_wallet.go0.0%63-75
Coverage rating: B Coverage rating: B
vcr/pe/presentation_submission.go100.0%
New file Coverage rating: B
vcr/pe/selector.go83.1%71, 75, 103, 107-108...
Total83.1%
🤖 Increase coverage with AI coding...

In the `feature/4067-credential-selection-dcql` branch, add test coverage for this new code:

- `auth/api/iam/api.go` -- Line 780-781
- `vcr/holder/memory_wallet.go` -- Line 63-75
- `vcr/holder/presenter.go` -- Line 65-66
- `vcr/pe/presentation_definition.go` -- Line 173-176
- `vcr/pe/selector.go` -- Lines 71, 75, 103, 107-108, 110, 131-132, and 139-142

🚦 See full report on Qlty Cloud »

🛟 Help
  • Diff Coverage: Coverage for added or modified lines of code (excludes deleted files). Learn more.

  • Total Coverage: Coverage for the whole repository, calculated as the sum of all File Coverage. Learn more.

  • File Coverage: Covered Lines divided by Covered Lines plus Missed Lines. (Excludes non-executable lines including blank lines and comments.)

    • Indirect Changes: Changes to File Coverage for files that were not modified in this PR. Learn more.

- Support all JSON scalar types (string, float64, bool) in field
  value matching via type switch, not just strings
- Move CredentialSelector type and FirstMatchSelector to selector.go
- Add test for numeric field value matching

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@stevenvegt stevenvegt requested a review from reinkrul March 31, 2026 10:09
@stevenvegt stevenvegt merged commit b157e99 into master Apr 1, 2026
9 checks passed
@stevenvegt stevenvegt deleted the feature/4067-credential-selection-dcql branch April 1, 2026 07:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Credential selection when multiple credential from wallet match

2 participants