DCQL expliqué : comment un vérificateur demande à un wallet exactement ce dont il a besoin
Le Digital Credentials Query Language, DCQL, est le format de requête JSON qu’OpenID4VP utilise dans une demande de présentation. Il permet à une partie utilisatrice de décrire quelles attestations, et quels claims qu’elles contiennent, elle souhaite consulter, sous une forme que tout wallet conforme peut interpréter sans intégration spécifique.
Le problème que résout DCQL
Un business wallet peut contenir des attestations dans plusieurs formats : une immatriculation d’entreprise en SD-JWT VC, une qualification professionnelle encodée en mdoc, une W3C verifiable credential issue d’un pilote antérieur. Un vérificateur qui doit seulement confirmer un numéro d’immatriculation et une dénomination sociale n’a aucun moyen portable de demander exactement cela, quel que soit le format, sans accepter l’attestation entière ou coder à la main une requête distincte par format et par fournisseur de wallet.
DCQL comble ce manque côté requête. Il s’agit d’un objet JSON unique, intégré à la demande d’autorisation OpenID4VP, qui définit une ou plusieurs requêtes d’attestation, chacune liée à un format et à un ensemble de chemins de claims. Le wallet évalue la requête par rapport aux attestations stockées, détermine celles qui correspondent et ne demande qu’ensuite au titulaire d’autoriser la divulgation de ces claims précis. Le vérificateur reçoit une structure prévisible, quel que soit le wallet utilisé par le titulaire.
1. Vérificateur
Envoie une requête OpenID4VP avec une dcql_query
2. Wallet
Compare la requête aux attestations stockées
3. Titulaire
Autorise la seule divulgation des claims demandés
4. Vérificateur
Reçoit une présentation par id de requête d’attestation
La structure d’une requête DCQL
Une requête DCQL est un objet JSON contenant un tableau credentials et, en option, un tableau credential_sets. Chaque entrée de credentials est une requête d’attestation. Les champs marqués M sont obligatoires dans cette requête.
dcql_query
credentials[ ]
Une requête par attestation nécessaire
id + format
Quelle attestation, dans quel format
meta
Filtre de type, par exemple vct_values
claims[ ]
Chemins des claims à divulguer
claim_sets[ ]
Combinaisons de claims acceptées
credential_sets[ ]
Facultatif : quelles combinaisons de requêtes d’attestation satisfont la demande
| Champ | Type | Obligatoire |
|---|---|---|
| id | string | M |
| format | enum: dc+sd-jwt | mso_mdoc | jwt_vc_json | ldp_vc | M |
| meta | objet, forme selon format | |
| claims | tableau de requêtes de claims | |
| claim_sets | tableau de tableaux d’ids de claims | |
| trusted_authorities | tableau d’objets avec un type et des values |
Chaque entrée de claims est elle-même un objet : un id permettant d’y faire référence depuis claim_sets, un path, tableau qui situe le claim dans l’attestation (par exemple ["legal_name"] pour un claim SD-JWT de premier niveau, ou ["org", "registration_number"] pour un claim imbriqué), et en option values, une liste de valeurs auxquelles le claim doit correspondre.
Exemple détaillé : vérification d’une immatriculation d’entreprise
Immatriculation
dc+sd-jwtLe vérificateur dit : voici ce que je veux recevoir
- ✓ Numéro d’immatriculationreg_nopath: ["registration_number"]
- ✓ Dénomination socialelegal_namepath: ["legal_name"]
- ✓ Pays d’immatriculationreg_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"] }
]
}
]
}Exemple de réponse du wallet
Le wallet répond avec un objet vp_token dont les clés sont les ids des requêtes d’attestation. Chaque valeur est un tableau de présentations. Pour dc+sd-jwt, une présentation se compose du JWT signé par l’émetteur, d’une disclosure par claim divulgué et d’un key binding JWT qui la lie au nonce et au client de cette requête.
Ce que le wallet envoie
{
"vp_token": {
"company_registration": [
"<issuer-signed JWT>~<disclosure: registration_number>~<disclosure: legal_name>~<disclosure: registration_country>~<key binding JWT>"
]
}
}Claims visibles par le vérificateur après validation
{
"vct": "urn:eudi:business:company-registration:1",
"registration_number": "12345678",
"legal_name": "Example Logistics B.V.",
"registration_country": "NL"
}Prévoir une solution de repli : claim_sets
claim_sets liste des groupes d’ids de claims par ordre de préférence. Le wallet renvoie le premier groupe qu’il peut satisfaire entièrement avec ce que le titulaire détient réellement, ce qui évite au vérificateur d’envoyer deux requêtes distinctes pour le cas précis et le cas de repli.
Exemple détaillé : numéro d’immatriculation, ou à défaut la seule dénomination sociale
1. Préféré
Renvoyé si l’attestation contient les deux claims
2. Solution de repli
Renvoyé seulement si le premier ensemble ne peut pas être satisfait
{
"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"]
]
}
]
}Ici, le vérificateur préfère un numéro d’immatriculation et la dénomination sociale, mais accepte la dénomination sociale seule si l’attestation du titulaire ne contient pas de claim de numéro d’immatriculation.
Exemple de réponse : la solution de repli a été utilisée
Ce que le wallet envoie
{
"vp_token": {
"company_registration": [
"<issuer-signed JWT>~<disclosure: legal_name>~<key binding JWT>"
]
}
}Claims visibles par le vérificateur après validation
{
"vct": "urn:eudi:business:company-registration:1",
"legal_name": "Example Logistics B.V."
}L’attestation du titulaire ne comporte pas de numéro d’immatriculation : le wallet a donc satisfait le second ensemble de claims et divulgué une seule disclosure. La réponse n’indique pas quel ensemble a été utilisé : le vérificateur le déduit des claims reçus.
Combiner des attestations : credential_sets
credential_sets agit un niveau au-dessus de claim_sets. Chaque entrée liste des options, chaque option étant un groupe d’ids de requêtes d’attestation. Le wallet doit satisfaire une option de chaque entrée obligatoire, ce qui offre au vérificateur une logique ET et OU entre attestations dans une seule requête.
Obligatoire
Immatriculation
L’un de
Numéro de TVA
L’un de
Attestation bancaire
"credential_sets": [
{ "options": [["company_registration"]] },
{ "options": [["vat_registration"], ["bank_account"]] }
]Exemple de réponse : immatriculation et compte bancaire
Ce que le wallet envoie
{
"vp_token": {
"company_registration": [
"<issuer-signed JWT>~<disclosures>~<key binding JWT>"
],
"bank_account": [
"<issuer-signed JWT>~<disclosures>~<key binding JWT>"
]
}
}Claims visibles par le vérificateur après validation
{
"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."
}
}Le titulaire ne possède pas d’attestation de numéro de TVA : le wallet a donc choisi la seconde option du second ensemble. Les ids de requête non utilisés, ici vat_registration, sont simplement absents de vp_token.
Uniquement des émetteurs de confiance : trusted_authorities
trusted_authorities restreint une requête d’attestation aux attestations dont l’émetteur est garanti par une autorité à laquelle le vérificateur fait confiance. Chaque entrée comporte un type et une liste de values : aki pour un authority key identifier, etsi_tl pour une ETSI trusted list, ou openid_federation pour un trust anchor de fédération. Le wallet ne propose que les attestations correspondantes.
Exemple détaillé : une immatriculation issue d’un émetteur listé
Le vérificateur dit : uniquement des émetteurs de cette liste de confiance
https://ec.europa.eu/tools/lotl/eu-lotl.xml
Attestation d’immatriculation d’un émetteur listé
l’émetteur figure sur la liste de confiance
✓ Correspond à la requête
Attestation d’immatriculation d’un émetteur non listé
l’émetteur ne figure pas sur la liste de confiance
✗ Ne correspond pas, non proposé au titulaire
{
"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"] }
]
}
]
}Exemple de réponse : seule l’attestation de l’émetteur listé
Ce que le wallet envoie
{
"vp_token": {
"company_registration": [
"<issuer-signed JWT>~<disclosure: registration_number>~<disclosure: legal_name>~<key binding JWT>"
]
}
}Claims visibles par le vérificateur après validation
{
"vct": "urn:eudi:business:company-registration:1",
"registration_number": "12345678",
"legal_name": "Example Logistics B.V."
}Le titulaire possédait aussi une attestation d’immatriculation d’un émetteur non listé, mais le wallet ne l’a pas proposée. trusted_authorities est un filtre pour le wallet, pas une garantie : le vérificateur contrôle lui-même l’émetteur par rapport à la liste de confiance lors de la validation de la présentation.
Correspondance de valeur : claims.values
Une requête de claim peut contenir values, une liste de chaînes, d’entiers ou de booléens. Le wallet ne renvoie le claim que si son type et sa valeur correspondent exactement à l’un d’eux. Un vérificateur peut ainsi contrôler une condition sans rien demander d’autre au préalable.
Exemple détaillé : uniquement des entreprises immatriculées aux Pays-Bas ou en Belgique
Le vérificateur dit : uniquement une entreprise immatriculée dans l’un de ces pays
Entreprise néerlandaise
registration_country: "NL"
✓ Correspond à la requête
Entreprise allemande
registration_country: "DE"
✗ Ne correspond pas, non proposé au titulaire
{
"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"]
}
]
}
]
}Exemple de réponse : une entreprise néerlandaise
Ce que le wallet envoie
{
"vp_token": {
"company_registration": [
"<issuer-signed JWT>~<disclosure: legal_name>~<disclosure: registration_country>~<key binding JWT>"
]
}
}Claims visibles par le vérificateur après validation
{
"vct": "urn:eudi:business:company-registration:1",
"legal_name": "Example Logistics B.V.",
"registration_country": "NL"
}L’attestation d’une entreprise allemande a registration_country "DE" : elle ne satisfait donc pas la requête et le wallet n’a rien à renvoyer pour elle. Le vérificateur doit néanmoins contrôler la valeur dans les claims validés plutôt que de se fier au filtrage du wallet.
Contraintes propres au format dans meta
SD-JWT VC : vct_values
Pour dc+sd-jwt, meta.vct_values liste les identifiants de type d’attestation acceptés par le vérificateur. Une requête ne correspond qu’à une attestation stockée dont le vct figure parmi ces valeurs. Un vérificateur qui ne fait confiance qu’au type d’attestation d’immatriculation d’un seul émetteur indique donc exactement cet identifiant.
mso_mdoc : doctype_value et namespace
Pour mso_mdoc, meta.doctype_value fixe le DocType ISO 18013-5, et chaque chemin de claim commence par le namespace mdoc dont relève le claim plutôt que par un simple nom de champ, car mdoc regroupe les claims par namespace et non dans un objet plat.
Où en est DCQL aujourd’hui
- DCQL est défini au sein même de la spécification OpenID4VP, et non dans un document distinct. Il fait partie du brouillon depuis l’introduction du mécanisme destiné à remplacer une dépendance antérieure à DIF Presentation Exchange pour les requêtes OpenID4VP.
- L’EUDI Wallet Architecture and Reference Framework désigne OpenID4VP comme protocole de présentation et, par conséquent, DCQL comme mécanisme de requête que les parties utilisatrices et les wallets de l’écosystème sont censés prendre en charge.
- Les implémentations de référence de wallets et de vérificateurs du programme EUDI Wallet Reference Implementation ont convergé vers DCQL. Les nouvelles intégrations de business wallets construites aujourd’hui sur OpenID4VP doivent donc partir du principe que DCQL, et non Presentation Exchange, est le format de requête des demandes de présentation.
Termes associés
Questions fréquentes
En quoi DCQL diffère-t-il de DIF Presentation Exchange ?
Les deux décrivent ce qu’un vérificateur attend d’un wallet, mais DCQL est limité à OpenID4VP et défini directement dans cette spécification, tandis que Presentation Exchange est une spécification DIF distincte qui couvre aussi d’autres protocoles. DCQL est volontairement plus restreint : il ne comporte ni input descriptor groups ni submission requirements, et il exprime les contraintes propres à chaque format, comme un doctype mdoc ou un type SD-JWT VC, directement dans un objet de requête plutôt que par un filtre JSON Schema générique. L’écosystème EUDI Wallet a retenu DCQL comme standard pour les présentations OpenID4VP.
Une requête DCQL peut-elle demander plusieurs attestations ?
Oui. Le tableau credentials peut contenir plusieurs requêtes d’attestation, chacune avec son propre id. Un wallet qui dispose d’une correspondance pour chaque entrée renvoie une présentation par entrée. L’objet facultatif credential_sets peut en plus exiger certaines combinaisons, par exemple accepter soit une attestation d’immatriculation seule, soit une attestation d’immatriculation accompagnée d’une déclaration UBO, sans solliciter deux fois le titulaire.
Quel problème claim_sets résout-il au sein d’une seule requête d’attestation ?
Une attestation ne contient pas toujours tous les claims qu’un vérificateur souhaiterait. claim_sets liste des groupes alternatifs d’ids de claims qui suffiraient chacun à satisfaire la demande, classés du plus au moins souhaité. Le wallet choisit le premier groupe qu’il peut satisfaire entièrement avec les claims dont le titulaire dispose réellement. Un vérificateur peut ainsi demander un numéro d’identité précis lorsqu’il existe et se rabattre sur un contrôle plus sommaire, comme un indicateur de majorité, sans envoyer deux requêtes distinctes.
DCQL est-il propre à l’EUDI Wallet ?
Non. DCQL fait partie de la spécification de base d’OpenID4VP et toute implémentation d’OpenID4VP peut l’utiliser. L’écosystème EUDI Wallet en est un utilisateur majeur : l’Architecture and Reference Framework impose OpenID4VP avec DCQL comme mécanisme de présentation que les parties utilisatrices doivent prendre en charge. C’est pourquoi il compte tout particulièrement pour les wallets et vérificateurs conçus pour le marché européen.
DCQL effectue-t-il lui-même la divulgation sélective ?
Non. DCQL décrit seulement ce qui est demandé. La capacité du wallet à révéler exactement ces claims et rien d’autre dépend du format de l’attestation : un SD-JWT VC comme un ISO mdoc permettent de divulguer une partie de leurs claims, et un tableau claims DCQL s’appuie sur cette capacité. DCQL fonctionnerait aussi avec un format sans divulgation sélective, mais le titulaire devrait alors transmettre l’attestation complète, même pour une requête portant sur un seul claim.
Sources
Cette page est fournie à titre informatif et ne constitue pas un avis juridique. Pour des informations faisant autorité, consultez directement l’OpenID Foundation et la Commission européenne.