Skip to content

Add credential_selection to access token request API #4090

Closed

Description

@stevenvegt

Parent PRD

#4067 (see PRD comment)

What to build

Add credential_query as an optional field on the ServiceAccessTokenRequest and wire it through the full access token request flow.

Call stack

The credential_query flows through the following layers:

API handler: RequestServiceAccessToken (auth/api/iam/api.go)
  extracts credential_query from request body
  → RequestRFC021AccessToken (auth/client/iam/openid4vp.go)
    accepts credentialQueries as new parameter
    → wallet.BuildSubmission (vcr/holder/interface.go)
      accepts credentialQueries as new parameter alongside presentationDefinition
      → presenter.buildSubmission (vcr/holder/presenter.go)
        if credentialQueries provided:
          selector, _ = pe.NewDCQLSelector(credentialQueries, pd, pe.FirstMatchSelector)
          builder.SetCredentialSelector(selector)
        → builder.Build(format)

Changes per layer

OpenAPI spec (docs/_static/auth/v2.yaml): add credential_query (optional array of DCQL credential query objects) to ServiceAccessTokenRequest.

Generated types (auth/api/iam/generated.go): regenerate to include new field.

API handler (auth/api/iam/api.go): extract credential_query from request.Body, pass to RequestRFC021AccessToken.

Client interface (auth/client/iam/interface.go): add credentialQueries []dcql.CredentialQuery parameter to RequestRFC021AccessToken.

RFC021 client (auth/client/iam/openid4vp.go): accept credentialQueries, pass to wallet.BuildSubmission.

Wallet interface (vcr/holder/interface.go): add credentialQueries []dcql.CredentialQuery parameter to BuildSubmission, placed alongside presentationDefinition since they work together — the PD defines what's required, the queries narrow which specific credentials to use.

BuildSubmission(ctx context.Context, walletDIDs []did.DID,
    additionalCredentials map[did.DID][]vc.VerifiableCredential,
    presentationDefinition pe.PresentationDefinition,
    credentialQueries []dcql.CredentialQuery,
    params BuildParams,
) (*vc.VerifiablePresentation, *pe.PresentationSubmission, error)

SQL wallet (vcr/holder/sql_wallet.go): pass credentialQueries through to presenter.

Presenter (vcr/holder/presenter.go): if credentialQueries is non-empty, create a DCQL selector via pe.NewDCQLSelector(credentialQueries, pd, pe.FirstMatchSelector) and call builder.SetCredentialSelector(selector).

Design decisions

  • credential_query is a function parameter on BuildSubmission (not in BuildParams) because it's about which credentials to select, not about how to build the VP. BuildParams contains VP construction parameters (audience, format, nonce, expiry).
  • The DCQL selector is constructed in the presenter layer, keeping PE/DCQL concerns out of the API and client layers.
  • Coexistence: existing credentials array (self-attested VCs) continues to work alongside credential_query.
  • No match_policy parameter — the DCQL filter must narrow to exactly one credential per input descriptor. See decision comment.

Example request

{
  "scope": "medicatieoverdracht-gf",
  "authorization_server": "https://verifier.example.com",
  "credential_query": [
    {
      "id": "id_patient_enrollment",
      "claims": [
        {
          "path": ["credentialSubject", "hasEnrollment", "patient", "identifier", "value"],
          "values": ["123456789"]
        }
      ]
    }
  ]
}

Acceptance criteria

  • credential_query field added to ServiceAccessTokenRequest in OpenAPI spec (optional array)
  • Generated types include new field
  • RequestServiceAccessToken handler extracts and passes credential_query through the flow
  • RequestRFC021AccessToken accepts credentialQueries parameter
  • Wallet.BuildSubmission accepts credentialQueries parameter alongside presentationDefinition
  • Presenter creates DCQL selector when credentialQueries is non-empty
  • Existing credentials (self-attested) array works alongside credential_query
  • Request without credential_query behaves identically to current behavior
  • Mocks and test call sites updated for new interface signatures
  • End-to-end test: API call with credential_query selects correct credential from wallet
  • End-to-end test: API call with credential_query matching zero credentials returns descriptive error
  • End-to-end test: API call with credential_query matching multiple credentials returns descriptive error
  • Caching: different credential_query values produce different cache keys (existing hash-based cache handles this)
  • Descriptive error messages for credential selection failures (user story 13)

Blocked by

User stories addressed

  • User story 1: specify which patient's credential via API
  • User story 2: specify which organization type credential via API
  • User story 3: only provide credential_query for ambiguous types
  • User story 4: clear error on zero matches via API
  • User story 5: clear error on ambiguous matches via API
  • User story 6: standard DCQL syntax in API
  • User story 7: self-attested credentials alongside credential_query
  • User story 8: credential_query ID maps to PD input descriptor ID
  • User story 12: descriptive error messages

Activity

  1. changed the title [-]Add credential_query and match_policy to access token request API[/-] [+]Add credential_selection to access token request API[/+] on Mar 25, 2026
  2. stevenvegt commented on Mar 25, 2026

    @stevenvegt
    MemberAuthor

    Scope update: This issue now covers credential_selection (named parameters) instead of credential_query (DCQL). See updated PRD in #4067 (comment).

    The API parameter changes from a DCQL query array to a simple map[string]string mapping PD field IDs to values. The wiring through the call chain (API → client → wallet → presenter → builder) remains the same — code on branch feature/4090-api-credential-query is reusable with minimal changes.

    Blocked by #4120 (selection selector, replaces #4089).

  3. added 3 commits that reference this issue on Mar 25, 2026
  4. added 12 commits that reference this issue on Mar 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions