Saltar al contenido principal

DCQL explicado: cómo un verificador pide a una wallet exactamente lo que necesita

Digital Credentials Query Language, DCQL, es el formato de consulta JSON que OpenID4VP utiliza dentro de una solicitud de presentación. Permite a una parte usuaria describir qué credenciales, y qué claims dentro de ellas, quiere ver, de forma que cualquier wallet conforme pueda interpretarlo sin una integración a medida.

El problema que resuelve DCQL

Una business wallet puede contener credenciales en varios formatos: un registro mercantil como SD-JWT VC, una cualificación profesional codificada como mdoc, una W3C verifiable credential de un piloto anterior. Un verificador que solo necesita confirmar un número de registro y una razón social no dispone de una forma portable de pedir exactamente eso entre formatos, salvo aceptar la credencial completa o programar a mano una solicitud distinta por formato y por proveedor de wallet.

DCQL resuelve la parte de la solicitud. Es un único objeto JSON, incluido en la solicitud de autorización de OpenID4VP, que define una o varias consultas de credenciales, cada una vinculada a un formato y a un conjunto de rutas de claims. La wallet evalúa la consulta frente a sus credenciales guardadas, determina cuáles coinciden y solo entonces pide al titular que apruebe revelar esos claims concretos. El verificador obtiene una estructura predecible, sea cual sea la wallet que use el titular.

1. Verificador

Envía una solicitud OpenID4VP con una dcql_query

2. Wallet

Compara la consulta con las credenciales guardadas

3. Titular

Aprueba revelar solo los claims solicitados

4. Verificador

Recibe una presentación por cada id de consulta de credencial

La estructura de una consulta DCQL

Una consulta DCQL es un objeto JSON con un array credentials y, opcionalmente, un array credential_sets. Cada entrada de credentials es una consulta de credencial. Los campos marcados con M son obligatorios en esa consulta.

dcql_query

credentials[ ]

Una consulta por cada credencial necesaria

id + format

Qué credencial y en qué formato

meta

Filtro de tipo, como vct_values

claims[ ]

Rutas de los claims a revelar

claim_sets[ ]

Combinaciones de claims aceptables

credential_sets[ ]

Opcional: qué combinaciones de consultas de credenciales satisfacen la solicitud

CampoTipoObligatorio
idstringM
formatenum: dc+sd-jwt | mso_mdoc | jwt_vc_json | ldp_vcM
metaobjeto, su forma depende de format
claimsarray de consultas de claims
claim_setsarray de arrays de ids de claims
trusted_authoritiesarray de objetos con un type y values

Cada entrada de claims es a su vez un objeto: un id para hacer referencia a ella desde claim_sets, un path, un array que localiza el claim dentro de la credencial (por ejemplo ["legal_name"] para un claim SD-JWT de primer nivel, o ["org", "registration_number"] para uno anidado) y, opcionalmente, values, una lista de valores con los que debe coincidir el claim.

Ejemplo práctico: comprobación de un registro mercantil

Registro mercantil

dc+sd-jwt

El verificador dice: esto es lo que quiero recibir

  • ✓ Número de registroreg_nopath: ["registration_number"]
  • ✓ Razón sociallegal_namepath: ["legal_name"]
  • ✓ País de registroreg_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"] }
      ]
    }
  ]
}

Ejemplo de respuesta de la wallet

La wallet responde con un objeto vp_token cuyas claves son los ids de las consultas de credenciales. Cada valor es un array de presentaciones. Para dc+sd-jwt, una presentación es el JWT firmado por el emisor, una disclosure por cada claim revelado y un key binding JWT que la vincula al nonce y al cliente de esta solicitud.

Lo que envía la wallet

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

Claims que ve el verificador tras la validación

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

Pedir una alternativa: claim_sets

claim_sets enumera grupos de ids de claims por orden de preferencia. La wallet devuelve el primer grupo que puede satisfacer por completo con lo que el titular tiene realmente, en lugar de que el verificador tenga que enviar dos solicitudes distintas para el caso preciso y el alternativo.

Ejemplo práctico: número de registro o, como alternativa, solo la razón social

1. Preferido

reg_nolegal_name

Se devuelve si la credencial contiene ambos claims

2. Alternativa

legal_name

Solo se devuelve si no se puede satisfacer el primer conjunto

{
  "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"]
      ]
    }
  ]
}

Aquí el verificador prefiere un número de registro más la razón social, pero acepta solo la razón social si la credencial del titular no incluye un claim de número de registro.

Ejemplo de respuesta: se usó la alternativa

Lo que envía la wallet

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

Claims que ve el verificador tras la validación

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

La credencial del titular no tiene número de registro, así que la wallet satisfizo el segundo conjunto de claims y reveló una única disclosure. La respuesta no indica qué conjunto se utilizó: el verificador lo deduce de los claims que recibe.

Combinar credenciales: credential_sets

credential_sets funciona un nivel por encima de claim_sets. Cada entrada enumera options, donde cada opción es un grupo de ids de consultas de credenciales. La wallet debe satisfacer una opción de cada entrada obligatoria, lo que da al verificador lógica Y y O entre credenciales en una sola solicitud.

Obligatorio

Registro mercantil

AND

Uno de

Registro de IVA

OR

Uno de

Certificado de cuenta bancaria

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

Ejemplo de respuesta: registro más cuenta bancaria

Lo que envía la wallet

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

Claims que ve el verificador tras la validación

{
  "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."
  }
}

El titular no tiene una credencial de registro de IVA, así que la wallet eligió la segunda opción del segundo conjunto. Los ids de consulta que no se usaron, en este caso vat_registration, simplemente no aparecen en vp_token.

Solo emisores de confianza: trusted_authorities

trusted_authorities limita una consulta de credencial a credenciales cuyo emisor está respaldado por una autoridad en la que confía el verificador. Cada entrada tiene un type y una lista de values: aki para un authority key identifier, etsi_tl para una ETSI trusted list u openid_federation para un trust anchor de federación. La wallet solo ofrece las credenciales que coinciden.

Ejemplo práctico: un registro de un emisor incluido en la lista

El verificador dice: solo de emisores de esta lista de confianza

type: etsi_tl

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

Credencial de registro de un emisor incluido

el emisor está en la lista de confianza

✓ Cumple la consulta

Credencial de registro de un emisor no incluido

el emisor no está en la lista de confianza

✗ No cumple, no se ofrece al titular

{
  "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"] }
      ]
    }
  ]
}

Ejemplo de respuesta: solo la credencial del emisor incluido

Lo que envía la wallet

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

Claims que ve el verificador tras la validación

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

El titular también tenía una credencial de registro de un emisor no incluido, pero la wallet no la ofreció. trusted_authorities es un filtro para la wallet, no una garantía: el verificador sigue comprobando por su cuenta al emisor frente a la lista de confianza al validar la presentación.

Coincidencia de valores: claims.values

Una consulta de claim puede incluir values, una lista de cadenas, enteros o booleanos. La wallet solo devuelve el claim cuando su tipo y su valor coinciden exactamente con uno de ellos, de modo que un verificador puede comprobar una condición sin pedir antes ningún otro dato.

Ejemplo práctico: solo empresas registradas en los Países Bajos o Bélgica

El verificador dice: solo una empresa registrada en uno de estos países

reg_countryvalues:"NL""BE"

Empresa neerlandesa

registration_country: "NL"

✓ Cumple la consulta

Empresa alemana

registration_country: "DE"

✗ No cumple, no se ofrece al titular

{
  "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"]
        }
      ]
    }
  ]
}

Ejemplo de respuesta: una empresa neerlandesa

Lo que envía la wallet

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

Claims que ve el verificador tras la validación

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

La credencial de una empresa alemana tiene registration_country "DE", así que no satisface la consulta y la wallet no tiene nada que devolver para ella. Aun así, el verificador debe comprobar el valor en los claims validados en lugar de confiar en el filtrado de la wallet.

Restricciones específicas de formato en meta

SD-JWT VC: vct_values

Para dc+sd-jwt, meta.vct_values enumera los identificadores de tipo de credencial que acepta el verificador. Una consulta solo coincide con una credencial guardada cuyo vct sea uno de esos valores, así que un verificador que solo confía en el tipo de credencial de registro de un emisor indica exactamente ese identificador.

mso_mdoc: doctype_value y namespace

Para mso_mdoc, meta.doctype_value fija el DocType de ISO 18013-5, y cada ruta de claim empieza por el namespace de mdoc al que pertenece el claim en lugar de por un simple nombre de campo, ya que mdoc agrupa los claims por namespace en vez de en un objeto plano.

Situación actual de DCQL

  • DCQL está definido dentro de la propia especificación OpenID4VP, no como documento aparte, y forma parte del borrador desde que se introdujo el mecanismo para sustituir una dependencia anterior de DIF Presentation Exchange en las solicitudes OpenID4VP.
  • El EUDI Wallet Architecture and Reference Framework establece OpenID4VP como protocolo de presentación y, con él, DCQL como el mecanismo de consulta que deben admitir las partes usuarias y las wallets del ecosistema.
  • Las implementaciones de referencia de wallets y verificadores del programa EUDI Wallet Reference Implementation han convergido en DCQL, por lo que las nuevas integraciones de business wallets basadas hoy en OpenID4VP deberían dar por hecho DCQL, y no Presentation Exchange, como formato de consulta para las solicitudes de presentación.

Términos relacionados

Preguntas frecuentes

¿En qué se diferencia DCQL de DIF Presentation Exchange?

Ambos describen lo que un verificador quiere obtener de una wallet, pero DCQL se limita a OpenID4VP y está definido directamente en esa especificación, mientras que Presentation Exchange es una especificación independiente de DIF que también abarca otros protocolos. DCQL es deliberadamente más pequeño: no tiene input descriptor groups ni submission requirements, y expresa las restricciones propias de cada formato, como un doctype de mdoc o un tipo de SD-JWT VC, directamente en un objeto de consulta en lugar de mediante un filtro genérico de JSON Schema. El ecosistema de la EUDI Wallet ha adoptado DCQL como estándar para las presentaciones OpenID4VP.

¿Puede una consulta DCQL pedir más de una credencial?

Sí. El array credentials puede incluir varias consultas de credenciales, cada una con su propio id. Una wallet que tenga coincidencias para todas las entradas devuelve una presentación por entrada. Además, el objeto opcional credential_sets puede exigir combinaciones concretas, por ejemplo aceptar solo una credencial de registro mercantil o bien una credencial de registro mercantil junto con una declaración de UBO, sin preguntar dos veces al titular.

¿Qué problema resuelve claim_sets dentro de una única consulta de credencial?

Una credencial no siempre contiene todos los claims que un verificador desearía. claim_sets enumera grupos alternativos de ids de claims que satisfarían la solicitud por sí solos, ordenados de mayor a menor preferencia. La wallet elige el primer grupo que puede satisfacer por completo con los claims que el titular tiene realmente, de modo que un verificador puede pedir un número de identificación exacto cuando esté disponible y recurrir a una comprobación más general, como un indicador de mayoría de edad, sin enviar dos solicitudes distintas.

¿DCQL es exclusivo de la EUDI Wallet?

No. DCQL forma parte de la especificación principal de OpenID4VP y cualquier implementación de OpenID4VP puede usarlo. El ecosistema de la EUDI Wallet es uno de sus principales usuarios: el Architecture and Reference Framework establece OpenID4VP con DCQL como el mecanismo de presentación que deben admitir las partes usuarias, y por eso es especialmente relevante para wallets y verificadores desarrollados para el mercado europeo.

¿Realiza DCQL por sí mismo la divulgación selectiva?

No. DCQL solo describe lo que se solicita. Que la wallet pueda revelar exactamente esos claims y nada más depende del formato de la credencial: tanto un SD-JWT VC como un ISO mdoc permiten revelar un subconjunto de sus claims, por lo que un array claims de DCQL se apoya en esa capacidad. DCQL también funcionaría con un formato sin divulgación selectiva, pero el titular tendría que entregar la credencial completa incluso para una consulta de un solo claim.

Fuentes

  1. OpenID for Verifiable Presentations 1.0, sección Digital Credentials Query Language (DCQL)
  2. EUDI Wallet Architecture and Reference Framework
  3. Especificación DIF Presentation Exchange 2.0.0

Esta página es informativa y no constituye asesoramiento jurídico. Para obtener información autorizada, consulte directamente a la OpenID Foundation y a la Comisión Europea.

Hable con nosotros sobre la integración con la EUDI Wallet