Naar hoofdinhoud

DCQL uitgelegd: hoe een verifier een wallet om precies de juiste gegevens vraagt

De Digital Credentials Query Language, DCQL, is het JSON-queryformaat dat OpenID4VP gebruikt binnen een presentatieverzoek. Een relying party beschrijft ermee welke credentials, en welke claims daarin, hij wil zien, op een manier die elke conforme wallet kan verwerken zonder maatwerkintegratie.

Het probleem dat DCQL oplost

Een business wallet kan credentials in verschillende formaten bevatten: een bedrijfsregistratie als SD-JWT VC, een beroepskwalificatie als mdoc, een W3C verifiable credential uit een eerdere pilot. Een verifier die alleen een registratienummer en een statutaire naam wil bevestigen, heeft geen overdraagbare manier om precies daarom te vragen over formaten heen. Hij moet dan de hele credential accepteren of per formaat en per walletleverancier een apart verzoek programmeren.

DCQL dicht die kloof aan de kant van het verzoek. Het is één JSON-object, opgenomen in het OpenID4VP-autorisatieverzoek, dat een of meer credential queries benoemt, elk gekoppeld aan een formaat en een set claimpaden. De wallet toetst de query aan de opgeslagen credentials, bepaalt welke overeenkomen en vraagt pas dan de houder om toestemming om precies die claims vrij te geven. De verifier krijgt een voorspelbare structuur, ongeacht welke wallet de houder gebruikt.

1. Verifier

Stuurt een OpenID4VP-verzoek met een dcql_query

2. Wallet

Vergelijkt de query met opgeslagen credentials

3. Houder

Geeft alleen de gevraagde claims vrij

4. Verifier

Ontvangt één presentatie per credential query id

De opbouw van een DCQL-query

Een DCQL-query is één JSON-object met een credentials-array en optioneel een credential_sets-array. Elk item in credentials is een credential query. Velden gemarkeerd met M zijn verplicht in die query.

dcql_query

credentials[ ]

Eén credential query per benodigde credential

id + format

Welke credential, in welk formaat

meta

Typefilter, zoals vct_values

claims[ ]

Claimpaden om te delen

claim_sets[ ]

Toegestane combinaties van claims

credential_sets[ ]

Optioneel: welke combinaties van credential queries aan het verzoek voldoen

VeldTypeVerplicht
idstringM
formatenum: dc+sd-jwt | mso_mdoc | jwt_vc_json | ldp_vcM
metaobject, vorm hangt af van format
claimsarray van claim queries
claim_setsarray van arrays met claim-id's
trusted_authoritiesarray van objecten met een type en values

Elk item in claims is zelf een object: een id waarmee je er vanuit claim_sets naar verwijst, een path, een array die de claim binnen de credential lokaliseert (bijvoorbeeld ["legal_name"] voor een SD-JWT-claim op het hoogste niveau, of ["org", "registration_number"] voor een geneste claim), en optioneel values, een lijst met waarden waaraan de claim moet voldoen.

Uitgewerkt voorbeeld: een controle van de bedrijfsregistratie

Bedrijfsregistratie

dc+sd-jwt

De verifier zegt: dit wil ik ontvangen

  • ✓ Registratienummerreg_nopath: ["registration_number"]
  • ✓ Statutaire naamlegal_namepath: ["legal_name"]
  • ✓ Land van registratiereg_countrypath: ["registration_country"]
{
  "credentials": [
    {
      "id": "company_registration",
      "format": "dc+sd-jwt",
      "meta": {
        "vct_values": ["urn:eudi:business:company-registration:1"]
      },
      "claims": [
        { "id": "reg_no", "path": ["registration_number"] },
        { "id": "legal_name", "path": ["legal_name"] },
        { "id": "reg_country", "path": ["registration_country"] }
      ]
    }
  ]
}

Voorbeeldantwoord van de wallet

De wallet antwoordt met een vp_token-object met de credential query id als sleutel. Elke waarde is een array van presentaties. Voor dc+sd-jwt bestaat een presentatie uit de door de issuer ondertekende JWT, één disclosure per vrijgegeven claim en een key binding JWT die de presentatie koppelt aan de nonce en de client van dit verzoek.

Wat de wallet verstuurt

{
  "vp_token": {
    "company_registration": [
      "<issuer-signed JWT>~<disclosure: registration_number>~<disclosure: legal_name>~<disclosure: registration_country>~<key binding JWT>"
    ]
  }
}

Claims die de verifier na validatie ziet

{
  "vct": "urn:eudi:business:company-registration:1",
  "registration_number": "12345678",
  "legal_name": "Example Logistics B.V.",
  "registration_country": "NL"
}

Een terugvaloptie vragen: claim_sets

claim_sets somt groepen claim-id's op in volgorde van voorkeur. De wallet geeft de eerste groep terug waaraan hij volledig kan voldoen met wat de houder daadwerkelijk heeft. De verifier hoeft dus geen twee aparte verzoeken te sturen voor een exacte en een terugvalsituatie.

Uitgewerkt voorbeeld: registratienummer, of terugvallen op alleen de statutaire naam

1. Voorkeur

reg_nolegal_name

Teruggegeven als de credential beide claims bevat

2. Terugval

legal_name

Alleen teruggegeven als niet aan de eerste set kan worden voldaan

{
  "credentials": [
    {
      "id": "company_registration",
      "format": "dc+sd-jwt",
      "meta": { "vct_values": ["urn:eudi:business:company-registration:1"] },
      "claims": [
        { "id": "reg_no", "path": ["registration_number"] },
        { "id": "legal_name", "path": ["legal_name"] }
      ],
      "claim_sets": [
        ["reg_no", "legal_name"],
        ["legal_name"]
      ]
    }
  ]
}

Hier heeft de verifier liever een registratienummer plus de statutaire naam, maar accepteert hij ook alleen de statutaire naam als de credential van de houder geen claim met een registratienummer bevat.

Voorbeeldantwoord: de terugvaloptie is gebruikt

Wat de wallet verstuurt

{
  "vp_token": {
    "company_registration": [
      "<issuer-signed JWT>~<disclosure: legal_name>~<key binding JWT>"
    ]
  }
}

Claims die de verifier na validatie ziet

{
  "vct": "urn:eudi:business:company-registration:1",
  "legal_name": "Example Logistics B.V."
}

De credential van de houder heeft geen registratienummer, dus de wallet voldeed aan de tweede claim set en gaf één disclosure vrij. Het antwoord vermeldt niet welke set is gebruikt: de verifier leidt dat af uit de claims die hij ontvangt.

Credentials combineren: credential_sets

credential_sets werkt een niveau hoger dan claim_sets. Elk item somt options op, waarbij elke optie een groep credential query id's is. De wallet moet voldoen aan één optie van elk verplicht item. Zo krijgt een verifier EN- en OF-logica over credentials heen in één verzoek.

Verplicht

Bedrijfsregistratie

AND

Eén van

Btw-registratie

OR

Eén van

Bankrekeningattestatie

"credential_sets": [
  { "options": [["company_registration"]] },
  { "options": [["vat_registration"], ["bank_account"]] }
]

Voorbeeldantwoord: registratie plus bankrekening

Wat de wallet verstuurt

{
  "vp_token": {
    "company_registration": [
      "<issuer-signed JWT>~<disclosures>~<key binding JWT>"
    ],
    "bank_account": [
      "<issuer-signed JWT>~<disclosures>~<key binding JWT>"
    ]
  }
}

Claims die de verifier na validatie ziet

{
  "company_registration": {
    "vct": "urn:eudi:business:company-registration:1",
    "registration_number": "12345678",
    "legal_name": "Example Logistics B.V."
  },
  "bank_account": {
    "vct": "urn:eudi:business:bank-account:1",
    "iban": "NL91ABNA0417164300",
    "account_holder": "Example Logistics B.V."
  }
}

De houder heeft geen credential voor btw-registratie, dus de wallet koos de tweede optie van de tweede set. Query-id's die niet zijn gebruikt, hier vat_registration, ontbreken gewoon in vp_token.

Alleen vertrouwde issuers: trusted_authorities

trusted_authorities beperkt een credential query tot credentials waarvan de issuer wordt gedekt door een autoriteit die de verifier vertrouwt. Elk item heeft een type en een lijst values: aki voor een authority key identifier, etsi_tl voor een ETSI trusted list, of openid_federation voor een trust anchor in een federatie. De wallet biedt alleen credentials aan die daaraan voldoen.

Uitgewerkt voorbeeld: een registratie van een vermelde issuer

De verifier zegt: alleen van issuers op deze vertrouwde lijst

type: etsi_tl

https://ec.europa.eu/tools/lotl/eu-lotl.xml

Registratiecredential van een vermelde issuer

issuer staat op de vertrouwde lijst

✓ Voldoet aan de query

Registratiecredential van een niet-vermelde issuer

issuer staat niet op de vertrouwde lijst

✗ Voldoet niet, niet aangeboden aan de houder

{
  "credentials": [
    {
      "id": "company_registration",
      "format": "dc+sd-jwt",
      "meta": { "vct_values": ["urn:eudi:business:company-registration:1"] },
      "trusted_authorities": [
        {
          "type": "etsi_tl",
          "values": ["https://ec.europa.eu/tools/lotl/eu-lotl.xml"]
        }
      ],
      "claims": [
        { "id": "reg_no", "path": ["registration_number"] },
        { "id": "legal_name", "path": ["legal_name"] }
      ]
    }
  ]
}

Voorbeeldantwoord: alleen de credential van de vermelde issuer

Wat de wallet verstuurt

{
  "vp_token": {
    "company_registration": [
      "<issuer-signed JWT>~<disclosure: registration_number>~<disclosure: legal_name>~<key binding JWT>"
    ]
  }
}

Claims die de verifier na validatie ziet

{
  "vct": "urn:eudi:business:company-registration:1",
  "registration_number": "12345678",
  "legal_name": "Example Logistics B.V."
}

De houder had ook een registratiecredential van een niet-vermelde issuer, maar de wallet bood die niet aan. trusted_authorities is een filter voor de wallet, geen garantie: de verifier controleert de issuer bij het valideren van de presentatie zelf nog tegen de vertrouwde lijst.

Een waarde matchen: claims.values

Een claim query kan values bevatten, een lijst met strings, integers of booleans. De wallet geeft de claim alleen terug als het type en de waarde exact overeenkomen met een ervan. Zo kan een verifier een voorwaarde controleren zonder eerst om iets anders te vragen.

Uitgewerkt voorbeeld: alleen bedrijven geregistreerd in Nederland of België

De verifier zegt: alleen een bedrijf geregistreerd in een van deze landen

reg_countryvalues:"NL""BE"

Nederlands bedrijf

registration_country: "NL"

✓ Voldoet aan de query

Duits bedrijf

registration_country: "DE"

✗ Voldoet niet, niet aangeboden aan de houder

{
  "credentials": [
    {
      "id": "company_registration",
      "format": "dc+sd-jwt",
      "meta": { "vct_values": ["urn:eudi:business:company-registration:1"] },
      "claims": [
        { "id": "legal_name", "path": ["legal_name"] },
        {
          "id": "reg_country",
          "path": ["registration_country"],
          "values": ["NL", "BE"]
        }
      ]
    }
  ]
}

Voorbeeldantwoord: een Nederlands bedrijf

Wat de wallet verstuurt

{
  "vp_token": {
    "company_registration": [
      "<issuer-signed JWT>~<disclosure: legal_name>~<disclosure: registration_country>~<key binding JWT>"
    ]
  }
}

Claims die de verifier na validatie ziet

{
  "vct": "urn:eudi:business:company-registration:1",
  "legal_name": "Example Logistics B.V.",
  "registration_country": "NL"
}

De credential van een Duits bedrijf heeft registration_country "DE", dus die voldoet niet aan de query en de wallet heeft daarvoor niets terug te geven. De verifier moet de waarde toch zelf controleren in de gevalideerde claims in plaats van te vertrouwen op de filtering van de wallet.

Formaatspecifieke beperkingen in meta

SD-JWT VC: vct_values

Voor dc+sd-jwt somt meta.vct_values de credential type identifiers op die de verifier accepteert. Een query matcht alleen een opgeslagen credential waarvan de vct een van die waarden is. Een verifier die alleen het registratiecredentialtype van één issuer vertrouwt, vermeldt dus precies die identifier.

mso_mdoc: doctype_value en namespace

Voor mso_mdoc legt meta.doctype_value het ISO 18013-5 DocType vast, en elk claimpad begint met de mdoc-namespace waaronder de claim valt in plaats van met een gewone veldnaam, omdat mdoc claims groepeert per namespace in plaats van in een plat object.

Waar DCQL nu staat

  • DCQL is gedefinieerd binnen de OpenID4VP-specificatie zelf, niet als apart document. Het maakt deel uit van de draft sinds het mechanisme werd geïntroduceerd om een eerdere afhankelijkheid van DIF Presentation Exchange voor OpenID4VP-verzoeken te vervangen.
  • Het EUDI Wallet Architecture and Reference Framework schrijft OpenID4VP voor als presentatieprotocol en daarmee DCQL als het querymechanisme dat relying parties en wallets in het ecosysteem geacht worden te ondersteunen.
  • Referentie-implementaties van wallets en verifiers in het EUDI Wallet Reference Implementation-programma zijn op DCQL uitgekomen. Nieuwe business wallet-integraties die vandaag op OpenID4VP worden gebouwd, moeten dus uitgaan van DCQL, niet van Presentation Exchange, als queryformaat voor presentatieverzoeken.

Gerelateerde begrippen

Veelgestelde vragen

Wat is het verschil tussen DCQL en DIF Presentation Exchange?

Beide beschrijven wat een verifier van een wallet wil, maar DCQL is afgebakend tot OpenID4VP en rechtstreeks in die specificatie gedefinieerd, terwijl Presentation Exchange een aparte DIF-specificatie is die ook andere protocollen dekt. DCQL is bewust kleiner: het kent geen input descriptor groups of submission requirements, en het drukt formaatspecifieke beperkingen, zoals een mdoc-doctype of een SD-JWT VC-type, direct uit in een query-object in plaats van via een generiek JSON Schema-filter. Het EUDI Wallet-ecosysteem heeft DCQL als standaard gekozen voor OpenID4VP-presentaties.

Kan één DCQL-query om meer dan één credential vragen?

Ja. De credentials-array kan meerdere credential queries bevatten, elk met een eigen id. Een wallet die voor elk item een passende credential heeft, geeft per item één presentatie terug. Het optionele credential_sets-object kan daarbovenop bepaalde combinaties vereisen, bijvoorbeeld óf alleen een bedrijfsregistratiecredential, óf een bedrijfsregistratiecredential samen met een UBO-verklaring, zonder de houder twee keer te bevragen.

Welk probleem lost claim_sets op binnen één credential query?

Een credential bevat niet altijd elke claim die een verifier zou willen. claim_sets somt alternatieve groepen claim-id's op die elk op zichzelf aan het verzoek voldoen, gerangschikt van meest naar minst gewenst. De wallet kiest de eerste groep waaraan hij volledig kan voldoen met de claims die de houder daadwerkelijk heeft. Zo kan een verifier om een exact ID-nummer vragen waar dat beschikbaar is en terugvallen op een grovere controle, zoals een 18-plusindicatie, zonder twee aparte verzoeken te sturen.

Is DCQL specifiek voor de EUDI Wallet?

Nee. DCQL maakt deel uit van de kernspecificatie van OpenID4VP en elke OpenID4VP-implementatie kan het gebruiken. Het EUDI Wallet-ecosysteem is wel een belangrijke gebruiker: het Architecture and Reference Framework schrijft OpenID4VP met DCQL voor als het presentatiemechanisme dat relying parties moeten ondersteunen. Daarom is het vooral relevant voor wallets en verifiers die voor de Europese markt worden gebouwd.

Voert DCQL zelf selective disclosure uit?

Nee. DCQL beschrijft alleen wat er wordt gevraagd. Of de wallet precies die claims kan onthullen en verder niets, hangt af van het credentialformaat: zowel een SD-JWT VC als een ISO mdoc ondersteunt het delen van een deel van de claims, dus een DCQL claims-array sluit daarop aan. DCQL werkt ook met een formaat zonder selective disclosure, maar dan moet de houder de volledige credential vrijgeven, zelfs voor een query naar één claim.

Bronnen

  1. OpenID for Verifiable Presentations 1.0, sectie Digital Credentials Query Language (DCQL)
  2. EUDI Wallet Architecture and Reference Framework
  3. DIF Presentation Exchange 2.0.0-specificatie

Deze pagina is informatief en vormt geen juridisch advies. Raadpleeg voor gezaghebbende informatie rechtstreeks de OpenID Foundation en de Europese Commissie.

Praat met ons over integratie met de EUDI Wallet