Przejdź do głównej treści

DCQL w pigułce: jak weryfikator prosi portfel dokładnie o to, czego potrzebuje

Digital Credentials Query Language, czyli DCQL, to format zapytań JSON, którego OpenID4VP używa w żądaniu prezentacji. Pozwala stronie ufającej opisać, które poświadczenia i które zawarte w nich atrybuty chce zobaczyć, w sposób zrozumiały dla każdego zgodnego portfela bez dedykowanej integracji.

Problem, który rozwiązuje DCQL

Portfel biznesowy może przechowywać poświadczenia w kilku formatach: rejestrację spółki jako SD-JWT VC, kwalifikacje zawodowe zakodowane w mdoc, poświadczenie weryfikowalne W3C z wcześniejszego pilotażu. Weryfikator, który musi jedynie potwierdzić numer rejestrowy i nazwę prawną, nie ma przenośnego sposobu, by poprosić dokładnie o to niezależnie od formatu, bez akceptowania całego poświadczenia albo ręcznego kodowania osobnego żądania dla każdego formatu i każdego dostawcy portfela.

DCQL zamyka tę lukę po stronie żądania. To pojedynczy obiekt JSON, osadzony w żądaniu autoryzacji OpenID4VP, który określa jedno lub więcej zapytań o poświadczenia, każde przypisane do formatu i zestawu ścieżek atrybutów. Portfel ocenia zapytanie względem przechowywanych poświadczeń, ustala, które pasują, i dopiero wtedy prosi posiadacza o zgodę na udostępnienie tych konkretnych atrybutów. Weryfikator otrzymuje przewidywalną strukturę do przetworzenia, niezależnie od portfela, z którego skorzystał posiadacz.

1. Weryfikator

Wysyła żądanie OpenID4VP z dcql_query

2. Portfel

Dopasowuje zapytanie do przechowywanych poświadczeń

3. Posiadacz

Zatwierdza udostępnienie tylko żądanych atrybutów

4. Weryfikator

Otrzymuje jedną prezentację na każdy id zapytania o poświadczenie

Budowa zapytania DCQL

Zapytanie DCQL to jeden obiekt JSON z tablicą credentials i opcjonalnie tablicą credential_sets. Każdy element credentials jest zapytaniem o poświadczenie. Pola oznaczone literą M są w tym zapytaniu obowiązkowe.

dcql_query

credentials[ ]

Jedno zapytanie na każde potrzebne poświadczenie

id + format

Które poświadczenie, w jakim formacie

meta

Filtr typu, np. vct_values

claims[ ]

Ścieżki atrybutów do ujawnienia

claim_sets[ ]

Akceptowane kombinacje atrybutów

credential_sets[ ]

Opcjonalnie: które kombinacje zapytań o poświadczenia spełniają żądanie

PoleTypObowiązkowe
idstringM
formatenum: dc+sd-jwt | mso_mdoc | jwt_vc_json | ldp_vcM
metaobiekt, struktura zależy od formatu
claimstablica zapytań o atrybuty
claim_setstablica tablic identyfikatorów atrybutów
trusted_authoritiestablica obiektów z type i values

Każdy element claims jest również obiektem: zawiera id, przez które odwołuje się do niego claim_sets, path, czyli tablicę wskazującą położenie atrybutu w poświadczeniu (na przykład ["legal_name"] dla atrybutu SD-JWT najwyższego poziomu lub ["org", "registration_number"] dla zagnieżdżonego), oraz opcjonalnie values, listę wartości, z którymi atrybut musi się zgadzać.

Przykład: weryfikacja rejestracji spółki

Rejestracja spółki

dc+sd-jwt

Weryfikator mówi: to chcę otrzymać

  • ✓ Numer rejestrowyreg_nopath: ["registration_number"]
  • ✓ Nazwa prawnalegal_namepath: ["legal_name"]
  • ✓ Kraj rejestracjireg_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"] }
      ]
    }
  ]
}

Przykładowa odpowiedź portfela

Portfel odpowiada obiektem vp_token, którego kluczami są id zapytań o poświadczenia. Każda wartość to tablica prezentacji. Dla dc+sd-jwt prezentacja składa się z JWT podpisanego przez wystawcę, jednego disclosure na każdy udostępniony atrybut oraz JWT key binding, który wiąże ją z nonce i klientem tego żądania.

Co wysyła portfel

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

Atrybuty widoczne dla weryfikatora po walidacji

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

Wariant rezerwowy: claim_sets

claim_sets wymienia grupy identyfikatorów atrybutów w kolejności preferencji. Portfel zwraca pierwszą grupę, którą może w pełni spełnić na podstawie tego, co faktycznie posiada posiadacz, więc weryfikator nie musi wysyłać dwóch osobnych żądań: dla wariantu dokładnego i rezerwowego.

Przykład: numer rejestrowy albo, rezerwowo, sama nazwa prawna

1. Preferowany

reg_nolegal_name

Zwracany, gdy poświadczenie zawiera oba atrybuty

2. Rezerwowy

legal_name

Zwracany tylko wtedy, gdy nie można spełnić pierwszego zestawu

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

Tutaj weryfikator woli numer rejestrowy wraz z nazwą prawną, ale zaakceptuje samą nazwę prawną, jeśli poświadczenie posiadacza nie zawiera atrybutu z numerem rejestrowym.

Przykładowa odpowiedź: użyto wariantu rezerwowego

Co wysyła portfel

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

Atrybuty widoczne dla weryfikatora po walidacji

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

Poświadczenie posiadacza nie zawiera numeru rejestrowego, więc portfel spełnił drugi zestaw atrybutów i udostępnił jedno disclosure. Odpowiedź nie wskazuje, który zestaw został użyty: weryfikator odczytuje to z otrzymanych atrybutów.

Łączenie poświadczeń: credential_sets

credential_sets działa poziom wyżej niż claim_sets. Każdy element wymienia options, gdzie każda opcja jest grupą identyfikatorów zapytań o poświadczenia. Portfel musi spełnić jedną opcję z każdego wymaganego elementu, co daje weryfikatorowi logikę AND i OR dla wielu poświadczeń w jednym żądaniu.

Wymagane

Rejestracja spółki

AND

Jedno z

Rejestracja VAT

OR

Jedno z

Poświadczenie rachunku bankowego

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

Przykładowa odpowiedź: rejestracja i rachunek bankowy

Co wysyła portfel

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

Atrybuty widoczne dla weryfikatora po walidacji

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

Posiadacz nie ma poświadczenia rejestracji VAT, więc portfel wybrał drugą opcję drugiego zestawu. Identyfikatory zapytań, które nie zostały użyte, tutaj vat_registration, po prostu nie występują w vp_token.

Tylko zaufani wystawcy: trusted_authorities

trusted_authorities zawęża zapytanie do poświadczeń, których wystawca jest wspierany przez podmiot uznawany przez weryfikatora za zaufany. Każdy element ma type i listę values: aki dla identyfikatora klucza urzędu (authority key identifier), etsi_tl dla zaufanej listy ETSI lub openid_federation dla kotwicy zaufania federacji. Portfel proponuje wyłącznie pasujące poświadczenia.

Przykład: rejestracja od wystawcy z listy

Weryfikator mówi: tylko od wystawców z tej zaufanej listy

type: etsi_tl

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

Poświadczenie rejestracji od wystawcy z listy

wystawca jest na zaufanej liście

✓ Pasuje do zapytania

Poświadczenie rejestracji od wystawcy spoza listy

wystawcy nie ma na zaufanej liście

✗ Nie pasuje, nie jest proponowane posiadaczowi

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

Przykładowa odpowiedź: tylko poświadczenie wystawcy z listy

Co wysyła portfel

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

Atrybuty widoczne dla weryfikatora po walidacji

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

Posiadacz miał też poświadczenie rejestracji od wystawcy spoza listy, ale portfel go nie zaproponował. trusted_authorities to filtr dla portfela, a nie gwarancja: weryfikator i tak sam sprawdza wystawcę na zaufanej liście podczas walidacji prezentacji.

Dopasowanie wartości: claims.values

Zapytanie o atrybut może zawierać values, listę ciągów znaków, liczb całkowitych lub wartości logicznych. Portfel zwraca atrybut tylko wtedy, gdy jego typ i wartość dokładnie odpowiadają jednej z nich, dzięki czemu weryfikator może sprawdzić warunek bez wcześniejszego proszenia o cokolwiek innego.

Przykład: tylko spółki zarejestrowane w Holandii lub Belgii

Weryfikator mówi: tylko spółka zarejestrowana w jednym z tych krajów

reg_countryvalues:"NL""BE"

Spółka holenderska

registration_country: "NL"

✓ Pasuje do zapytania

Spółka niemiecka

registration_country: "DE"

✗ Nie pasuje, nie jest proponowane posiadaczowi

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

Przykładowa odpowiedź: spółka holenderska

Co wysyła portfel

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

Atrybuty widoczne dla weryfikatora po walidacji

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

Poświadczenie spółki niemieckiej ma registration_country "DE", więc nie spełnia zapytania i portfel nie ma dla niego nic do zwrócenia. Weryfikator powinien mimo to sprawdzić wartość w zwalidowanych atrybutach, zamiast polegać na filtrowaniu przez portfel.

Ograniczenia specyficzne dla formatu w meta

SD-JWT VC: vct_values

Dla dc+sd-jwt meta.vct_values wymienia identyfikatory typów poświadczeń akceptowane przez weryfikatora. Zapytanie pasuje tylko do przechowywanego poświadczenia, którego vct jest jedną z wymienionych wartości, więc weryfikator ufający wyłącznie typowi poświadczenia rejestracji jednego wystawcy podaje dokładnie ten identyfikator.

mso_mdoc: doctype_value i namespace

Dla mso_mdoc meta.doctype_value określa DocType z ISO 18013-5, a każda ścieżka atrybutu zaczyna się od namespace mdoc, do którego należy atrybut, a nie od zwykłej nazwy pola, ponieważ mdoc grupuje atrybuty według namespace zamiast w płaskim obiekcie.

Obecny status DCQL

  • DCQL jest zdefiniowany w samej specyfikacji OpenID4VP, a nie w osobnym dokumencie, i jest częścią wersji roboczej od czasu wprowadzenia tego mechanizmu w miejsce wcześniejszej zależności od DIF Presentation Exchange w żądaniach OpenID4VP.
  • EUDI Wallet Architecture and Reference Framework określa OpenID4VP jako protokół prezentacji, a wraz z nim DCQL jako mechanizm zapytań, który mają obsługiwać strony ufające i portfele w ekosystemie.
  • Referencyjne implementacje portfeli i weryfikatorów w programie EUDI Wallet Reference Implementation zbiegły się na DCQL, dlatego nowe integracje portfeli biznesowych budowane dziś w oparciu o OpenID4VP powinny zakładać DCQL, a nie Presentation Exchange, jako format zapytań w żądaniach prezentacji.

Powiązane pojęcia

Najczęściej zadawane pytania

Czym DCQL różni się od DIF Presentation Exchange?

Oba opisują, czego weryfikator oczekuje od portfela, ale DCQL jest ograniczony do OpenID4VP i zdefiniowany bezpośrednio w tej specyfikacji, natomiast Presentation Exchange to odrębna specyfikacja DIF, która obejmuje także inne protokoły. DCQL jest celowo mniejszy: nie ma grup input descriptors ani submission requirements, a ograniczenia specyficzne dla formatu, takie jak doctype mdoc czy typ SD-JWT VC, wyraża bezpośrednio w obiekcie zapytania, a nie za pomocą ogólnego filtra JSON Schema. Ekosystem EUDI Wallet przyjął DCQL jako standard dla prezentacji OpenID4VP.

Czy jedno zapytanie DCQL może dotyczyć więcej niż jednego poświadczenia?

Tak. Tablica credentials może zawierać kilka zapytań o poświadczenia, każde z własnym id. Portfel, który ma dopasowania dla wszystkich pozycji, zwraca jedną prezentację na pozycję. Opcjonalny obiekt credential_sets może dodatkowo wymagać określonych kombinacji, na przykład akceptować samo poświadczenie rejestracji spółki albo poświadczenie rejestracji spółki razem z deklaracją UBO, bez dwukrotnego pytania posiadacza.

Jaki problem rozwiązują claim_sets w ramach jednego zapytania o poświadczenie?

Poświadczenie nie zawsze zawiera wszystkie atrybuty, których życzyłby sobie weryfikator. claim_sets wymienia alternatywne grupy identyfikatorów atrybutów, z których każda sama w sobie spełnia żądanie, uporządkowane od najbardziej do najmniej preferowanej. Portfel wybiera pierwszą grupę, którą może w pełni spełnić atrybutami faktycznie posiadanymi przez posiadacza, dzięki czemu weryfikator może poprosić o dokładny numer identyfikacyjny, jeśli jest dostępny, a w przeciwnym razie poprzestać na mniej szczegółowej kontroli, np. fladze pełnoletności, bez wysyłania dwóch osobnych żądań.

Czy DCQL jest specyficzny dla EUDI Wallet?

Nie. DCQL jest częścią podstawowej specyfikacji OpenID4VP i może z niego korzystać każda implementacja OpenID4VP. Ekosystem EUDI Wallet jest jego ważnym użytkownikiem: Architecture and Reference Framework określa OpenID4VP z DCQL jako mechanizm prezentacji, który muszą obsługiwać strony ufające, dlatego ma on szczególne znaczenie dla portfeli i weryfikatorów tworzonych na rynek europejski.

Czy DCQL sam realizuje selektywne ujawnianie?

Nie. DCQL opisuje jedynie, o co się prosi. To, czy portfel może ujawnić dokładnie te atrybuty i nic więcej, zależy od formatu poświadczenia: zarówno SD-JWT VC, jak i ISO mdoc pozwalają ujawnić podzbiór atrybutów, więc tablica claims w DCQL korzysta z tej możliwości. DCQL zastosowany do formatu bez selektywnego ujawniania nadal by działał, ale posiadacz musiałby udostępnić całe poświadczenie, aby spełnić nawet zapytanie o jeden atrybut.

Źródła

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

Ta strona ma charakter wyłącznie informacyjny i nie stanowi porady prawnej. Wiążących wskazówek należy szukać bezpośrednio w OpenID Foundation i Komisji Europejskiej.

Porozmawiaj z nami o integracji z EUDI Wallet