-
Notifications
You must be signed in to change notification settings - Fork 23
Description
Parent PRD
#4067 (see PRD comment)
What to build
A CredentialSelector implementation that filters candidates using the credential_selection parameter from the access token request. Each key in credential_selection maps to a field id in the PD's input descriptor constraints; the value narrows the match to credentials where that field equals the given value.
This replaces #4089 (DCQL selector). The CredentialSelector injection point from #4088 (PR #4098) is reused as-is.
Example
Given a PD with:
{
"id": "patient_id",
"path": ["$.credentialSubject.hasEnrollment.patient.identifier.value"],
"filter": { "type": "string" }
}And a request with:
{ "credential_selection": { "patient_id": "123456789" } }The selector resolves the patient_id field for each candidate VC and keeps only those where the value equals "123456789".
Design
NewSelectionSelector factory (vcr/pe/selector.go):
Follows the same pattern as NewDCQLSelector on branch feature/4089-dcql-selector:
- At construction: validate that each
credential_selectionkey matches a fieldidin the PD's input descriptors. Return error if any key doesn't match. - Return a
CredentialSelectorclosure that, for each input descriptor:- Finds which selection keys apply (fields with matching
idin this descriptor's constraints) - If no selection keys apply to this descriptor → delegate to
FirstMatchSelector(fallback) - Otherwise: resolve the field paths for each candidate, keep only candidates where all selected field values match
- Zero remaining → return
ErrNoCredentials - Multiple remaining → return
ErrMultipleCredentials - Exactly one → return it
- Finds which selection keys apply (fields with matching
func NewSelectionSelector(
selection map[string]string,
pd PresentationDefinition,
fallback CredentialSelector,
) (CredentialSelector, error)Field value resolution: Reuse the existing matchField / getValueAtPath logic from presentation_definition.go to resolve field paths against candidates. The PD field's path array already points into the credential JSON.
Call stack
presenter.buildSubmission (vcr/holder/presenter.go)
→ pe.NewSelectionSelector(selection, pd, pe.FirstMatchSelector)
→ builder.SetCredentialSelector(selector)
→ builder.Build()
→ matchConstraints(vcs, selector)
→ selector(inputDescriptor, candidates)
→ resolve field paths, filter by values
Reusable code from existing branches
| Branch | File | What to reuse |
|---|---|---|
feature/4089-dcql-selector |
vcr/pe/selector.go |
Factory pattern, ID validation, fallback logic, error handling. Replace dcql.Match with field-value resolution. |
feature/4089-dcql-selector |
vcr/pe/selector_test.go |
Test structure: fallback, single match, zero matches (ErrNoCredentials), multiple matches (ErrMultipleCredentials), ID validation error, multi-descriptor independence. Adapt to use field IDs instead of DCQL queries. |
Acceptance criteria
-
NewSelectionSelectorfactory invcr/pe/selector.go - Validates selection keys against PD field
ids at construction — returns error for unknown keys - For input descriptors with matching selection keys: resolves field paths against candidates, filters by value equality
- For input descriptors without matching selection keys: falls back to
FirstMatchSelector - Zero matches after filtering →
ErrNoCredentials(soft failure inmatchConstraints, allowsmin: 0submission requirements) - Multiple matches after filtering →
ErrMultipleCredentials(hard failure) - Exactly one match → returns it
- Multiple selection keys applied to same descriptor use AND semantics (all must match)
- Test: single match success — selector picks the right credential
- Test: zero matches returns
ErrNoCredentials - Test: multiple matches returns
ErrMultipleCredentials - Test: unknown selection key returns construction error
- Test: no selection keys for a descriptor → fallback to first match
- Test: multiple selection keys (AND semantics)
- Test: multiple descriptors with independent selection keys
- All existing PD matching tests pass unchanged
Blocked by
- Refactor PD matching to return all candidates per input descriptor #4088 — CredentialSelector extension point (PR #4088: CredentialSelector extension point in PD matcher #4098)
Blocks
- Add credential_selection to access token request API #4090 — API layer integration
User stories addressed
- User story 1: select PatientEnrollmentCredential by patient ID
- User story 2: select HealthcareProviderTypeCredential by type
- User story 4: clear error on zero matches
- User story 5: clear error on multiple matches
- User story 6: simple key-value interface
- User story 8: backward compatible (fallback to first match)