Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
- [SD-JWT VC Support](./technical_docs/SD_JWT_Support.md)
- [Data Integrity Proof Support](./technical_docs/Data_Integrity_Proof_Support.md)
- [VC Revocation Support](./technical_docs/VC_Revocation_Support.md)
- [Presentation During Issuance](./technical_docs/Presentation_During_Issuance.md)
- [DCQL Support](./technical_docs/DCQL_Support.md)
- [Inji Verify as a Library](./technical_docs/Inji_Verify_As_A_Library.md)
- [DPoP Support (RFC 9449)](./technical_docs/DPoP_Support.md)

# Integrator READMEs

Expand Down
13 changes: 10 additions & 3 deletions docs/stoplight_docs/inji-certify-openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2176,7 +2176,7 @@ components:
description: 'Document type identifier, used for mDL/mDoc credentials.'
cryptographic_binding_methods_supported:
type: array
description: 'List of supported cryptographic binding methods (e.g., did, holder\_binding).'
description: 'List of supported cryptographic binding methods (e.g., did:jwk, did:web, cose_key).'
items:
type: string
credential_signing_alg_values_supported:
Expand Down Expand Up @@ -2575,7 +2575,10 @@ components:
description: Applicable for subsequent request - auth session value shared as response of initial IAR
openid4vp_response:
type: string
description: Applicable for subsequent request - OpenID4VP presentation submission response contains vp_token and presentation_submission
description: |
Applicable for subsequent request - OpenID4VP presentation submission response.
In DCQL mode (OpenID4VP 1.0) it contains a vp_token keyed by the DCQL credential
query id; there is no presentation_submission.
AuthorizationSuccess:
type: object
required:
Expand All @@ -2598,7 +2601,11 @@ components:
description: random string to identify session for auth flow, sent in response to initial request
openid4vp_request:
type: object
description: OpenId4VP compliant VP request. sent in response to initial request
description: |
OpenID4VP 1.0 compliant VP request, sent in response to the initial request.
Carries a dcql_query (Digital Credentials Query Language) describing the
credential(s) the wallet must present, together with nonce, response_mode
(only iae_post is supported for now) and response_uri.
code:
type: string
description: Authorization code to exchange for access token, sent in subsequent request once VP is submitted and verified successfully
Expand Down
172 changes: 172 additions & 0 deletions docs/technical_docs/DCQL_Support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# DCQL Support (Digital Credentials Query Language)

This document explains how Inji Certify uses **DCQL — the Digital Credentials Query Language** defined by [OpenID for Verifiable Presentations (OpenID4VP)](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-digital-credentials-query-l). DCQL is the mechanism Certify uses to tell a wallet *which* credential(s) it must present during the **Presentation During Issuance** (Interactive Authorization Request / IAR) flow.

> **Note:** DCQL is used **inside** the Presentation During Issuance flow. Read [Presentation During Issuance](./Presentation_During_Issuance.md) first for the end-to-end sequence; this document zooms into how the presentation request is expressed.

---

## Overview

When Certify requires the user to present an existing credential before issuing a new one, it must describe *what* it wants — a credential type, format, and the constraints it must satisfy. Earlier OpenID4VP drafts expressed this with `presentation_definition` (DIF Presentation Exchange). OpenID4VP 1.0 replaces that with **DCQL**, a compact JSON query language that:

- names the credential **format** (`ldp_vc`, `dc+sd-jwt`, `mso_mdoc`, …),
- constrains it by **type / claims / metadata**, and
- expresses **sets of alternatives** ("present A, or B and C").

Because the response is keyed by the query, DCQL mode does **not** use a `presentation_submission` object — the `vp_token` alone maps each presented credential back to the query that asked for it.

**How Certify uses it:**

1. Certify reads a **`dcqlQuery`** block from its VP request configuration file (`mosip.certify.vp-request.config-file-url`).
2. The embedded [inji-verify library](./Inji_Verify_As_A_Library.md) turns it into an OpenID4VP authorization request (by value), parsing it into a `DCQLQueryDto`.
3. Certify embeds the query as **`dcql_query`** inside the `openid4vp_request` returned to the wallet in the Interactive Authorization Response.
4. The wallet selects credentials that satisfy the query, builds a DCQL-keyed `vp_token`, and posts it back to Certify's `/oauth/iae` endpoint.
5. Certify forwards the `vp_token` to the inji-verify library for verification. In DCQL mode **only `vp_token` (and state) are required — there is no `presentation_submission`.**

---

## The `dcqlQuery` Configuration Block

The query lives in the VP request config file. Below is the shipped default (`vp_request_config.json`), which asks for a single MOSIP identity credential in `ldp_vc` format:

```json
{
"dcqlQuery": {
"credentials": [
{
"id": "mosip_verifiable_credential_id",
"format": "ldp_vc",
"meta": {
"type_values": [
[
"https://www.w3.org/2018/credentials#VerifiableCredential",
"https://inji.github.io/inji-config/contexts/mosip-identity-context.json#MOSIPVerifiableCredential"
]
]
}
}
],
"credential_sets": [
{
"options": [
[
"mosip_verifiable_credential_id"
]
]
}
]
}
}
```

| Field | Meaning |
|---|---|
| `credentials[]` | The list of credential queries. Each has a unique `id` used to key the wallet's `vp_token`. |
| `credentials[].id` | Identifier the wallet echoes back so each presented credential maps to the query it answers. |
| `credentials[].format` | Requested credential format (`ldp_vc`, `dc+sd-jwt`, `mso_mdoc`). |
| `credentials[].meta.type_values` | Format-specific type constraint. For `ldp_vc` this is the set of JSON-LD `@context`/type IRIs the credential must carry. |
| `credential_sets[]` | Groups of `options`, where each option is a list of credential `id`s that together satisfy the request. Expresses "present this set, **or** that set". |

To require a *different* or *additional* credential, add another entry to `credentials[]` and reference its `id` in `credential_sets.options`.

---

## The `dcql_query` in the Wallet-Facing Request

After the inji-verify library produces the authorization request, Certify assembles the `openid4vp_request` and embeds the query under the `dcql_query` key (see `IarVpRequestService.convertToOpenId4VpRequest`), for example:

```json
{
"response_type": "vp_token",
"client_id": "certify-verifier-client",
"nonce": "…generated by the verify library…",
"dcql_query": { "credentials": [ ... ], "credential_sets": [ ... ] },
"response_mode": "iae_post",
"response_uri": "http://localhost:8090/v1/certify/oauth/iae"
}
```

`response_mode` is mapped from the verify library's `direct_post` / `direct_post.jwt` to Certify's `iae_post` / `iae_post.jwt`. Only `iae_post` (unencrypted) is supported for now — `iae_post.jwt` (encrypted) is not yet processed. `response_uri` points at Certify's own `/oauth/iae` endpoint so the wallet submits the VP back to Certify.

---

## The DCQL `vp_token` Response

In DCQL mode the wallet returns a `vp_token` that is **keyed by the credential query id**, and **no `presentation_submission`**:

```json
{
"vp_token": {
"mosip_verifiable_credential_id": "…the presentation answering that query…"
}
}
```

Certify serialises the `vp_token` intact (it does not flatten it) and hands it to the verify library's submission service. The library resolves each key against the DCQL query it issued and verifies the corresponding presentation.

---

## Sequence Diagram for DCQL in the IAR Flow

```mermaid
sequenceDiagram
participant W as 👛 Wallet
box Inji Certify #E6F3FF
participant OAuth as 🔗 OAuthController (/oauth/iae)
participant ReqSvc as ⚙️ IarVpRequestService
participant Verify as 🛡️ Inji Verify Library
participant PresSvc as 📜 IarPresentationService
end

Note over W,Verify: 1. Build the DCQL presentation request
W->>OAuth: POST /oauth/iae (Interactive Authorization Request)
OAuth->>ReqSvc: createVpRequest()
ReqSvc->>ReqSvc: Load dcqlQuery from vp_request_config
ReqSvc->>Verify: createAuthorizationRequest(dcqlQuery)
Verify-->>ReqSvc: OpenID4VP request by value (nonce, dcql_query)
ReqSvc->>ReqSvc: convertToOpenId4VpRequest() → embed dcql_query, map response_mode
OAuth-->>W: 200 require_interaction + openid4vp_request { dcql_query }

Note over W,Verify: 2. Present the credential(s)
W->>W: Select credential(s) matching the DCQL query
W->>OAuth: POST /oauth/iae { auth_session, openid4vp_response: vp_token (DCQL-keyed) }
OAuth->>PresSvc: processVpPresentation()
PresSvc->>Verify: submitVerifiablePresentation(vp_token) — no presentation_submission
Verify->>Verify: Resolve each key against the DCQL query & verify
Verify-->>PresSvc: Verification result
alt All checks successful
PresSvc-->>OAuth: status=ok + authorization_code
OAuth-->>W: 200 { authorization_code }
else Verification failed
PresSvc-->>OAuth: status=error
OAuth-->>W: 400 { status: "error" }
end
```

---

## Local Testing Note

On the `local` profile Certify uses `vp_request_config-local.json`, which additionally carries hardcoded `clientId` and `nonce` values matching a sample VP token, so the verify library's signature / domain / challenge checks pass deterministically without regenerating a VP per run. **These overrides are honored only on the `local` profile** — on every other profile they are ignored (with a warning) so the verify library generates a fresh nonce and VP replay protection is preserved. Deployed configurations must use `vp_request_config.json` with no hardcoded `clientId` / `nonce`. On non-`local` profiles the file is fetched remotely from `mosip.certify.vp-request.config-file-url`, so serve it over **HTTPS** and restrict write access to it — a modified `dcqlQuery` changes which credentials Certify accepts.

---

## Configuration Properties

| Property Name | Description | Example Value |
|---|---|---|
| `mosip.certify.vp-request.config-file-url` | Location of the VP request configuration file carrying the `dcqlQuery` block. Classpath resource on the `local` profile; fetched over HTTP(S) otherwise. | `vp_request_config-local.json` (local); `http://certify-nginx/vp_request_config.json` (deployed) |
| `mosip.certify.verify.service.verifier-client-id` | Verifier `client_id` used by the embedded inji-verify library when creating the request. | `certify-verifier-client` |
| `mosip.certify.oauth.interactive-authorization-endpoint` | Certify's own IAR endpoint, used as the `response_uri` for VP submission. | `${mosip.certify.authorization.url}${server.servlet.path}/oauth/iae` |

---

## References

- [OpenID for Verifiable Presentations 1.0 — Digital Credentials Query Language (DCQL)](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-digital-credentials-query-l)
- [OpenID for Verifiable Credential Issuance — Interactive Authorization](https://openid.github.io/OpenID4VCI/openid-4-verifiable-credential-issuance-1_1-wg-draft.html#name-interactive-authorization)
- [Presentation During Issuance](./Presentation_During_Issuance.md) — the flow DCQL is used within.
- [Inji Verify as a Library](./Inji_Verify_As_A_Library.md) — the embedded library that parses the DCQL query and verifies the VP.

---
Loading
Loading