iGrant.io verifier backend (OpenID4VP + DCQL)
When to use
Build or extend the server that a verifier / relying-party frontend talks to.
It composes igrantio-backend-proxy + igrantio-backend-webhooks +
igrantio-backend-sse for the verification flow only (least privilege). For the
issuer equivalent use igrantio-issuer-backend (separate skill). Read
igrantio-ows-overview first.
Before you build: run the integrator intake in igrantio-ows-overview - environment, API key, tenancy, backend host, webhooks, frontend - one question at a time, a recommended default with each.
What it does
- Proxy
GET|POST|PUT|DELETE ${PROXY_PREFIX}/{tenant}/... → OWS, injecting the tenant's
ApiKey. Allow-lists only OWS verification endpoints (send, history).
- Register the verifier webhook idempotently (
VERIFIER_TOPICS) via
scripts/register-webhook.ts.
- Receive
POST /webhook, verify the X-iGrant-Signature HMAC, extract the
presentationExchangeId, store the event.
- SSE
GET /webhook/sse/{presentationExchangeId} streams the event to the
browser; DELETE /webhook/{id} consume-and-delete.
Reference implementation
Runnable Express + TypeScript app in ./references. Same modules as
the issuer backend; the only role differences are in src/server.ts
(verification allow-list) and scripts/register-webhook.ts (VERIFIER_TOPICS).
Default PORT=6002 so it can run alongside an issuer backend.
Steps
cd references && cp .env.example .env; set OWS_ENV (demo|staging, default
demo), WEBHOOK_SECRET_KEY, PUBLIC_BASE_URL, CORS_ORIGINS, and one
OWS_TENANT_<SLUG>_API_KEY per organisation.
npm install && npm run dev - backend on :6002.
- Register the webhook once per tenant:
npm run register-webhook -- <tenant>
(idempotent).
- Point the verifier frontend base URL at
${PUBLIC_BASE_URL}${PROXY_PREFIX}/<tenant>
and the webhook base at ${PUBLIC_BASE_URL}/webhook.
Verification contract (what the frontend drives through this backend)
POST …/verification/send → response verificationHistory.presentationExchangeId
(SSE key) + verificationHistory.vpTokenQrCode (QR URI).
- The send body may carry optional
transactionData - wallet-displayed and
wallet-signed transaction data (SCA): simple payment_data, an EUDI SCA
rulebook (TS12) payload (payment / e-mandate / login-risk / account
access), or qes_data (QES document signing). The proxy passes it through
unchanged; see igrantio-ows-overview/references/api-reference.md §2.1 for
the exact shapes and §2.2 for verifying the returned
transaction_data_hashes.
- Done on webhook
openid.presentation.presentation_acked.v3 /
digitalwallet.presentation.verified; the SSE event carries
data.presentation.verified and data.presentation.presentation[0] (claims).
See igrantio-ows-overview/references/api-reference.md §2 and §4.
Adapting
- Query patterns: ready-made DCQL workflows (trusted authority, claim
sets, credential sets, KYC, optional groups, multiple instances, value
matching) live in the
igrantio-dcql-* workflow skills.
- Multi-instance: replace
InMemoryEventStore with a shared store.
- Key storage: replace
EnvTenantStore with a DB/secret-manager TenantStore.
- Path scope:
VERIFIER_PERMITTED_PREFIXES in server.ts is the allow-list.
Validation / done criteria
npm run typecheck passes.
- A wrong/absent
X-iGrant-Signature yields 401; a valid one stores the event.
- Hitting a non-verification OWS path through the proxy yields 404 (least privilege).
- The browser SSE fires within ~1s of the webhook, keyed by
presentationExchangeId.
Documentation & workflows
When anything is unclear, consult the iGrant.io documentation before guessing:
1---2name: igrantio-verifier-backend3description: Build the backend for an OpenID4VP + DCQL credential VERIFIER / relying party against the iGrant.io Organisation Wallet Suite (OWS). A tenant-aware Node/TypeScript (Express) service that hides per-organisation API keys behind a proxy, registers and receives OWS verification webhooks (HMAC-verified), and pushes the verified result to the browser over SSE. Use when an application must request and verify a credential presentation from an eIDAS 2.0 EUDI Wallet (EUDIW) or European Business Wallet (EUBW) and the API key must never reach the frontend. Supports wallet-signed transaction data (SCA payments, e-mandates, login/risk authentication, account access, QES document signing).4license: Apache-2.05---67# iGrant.io verifier backend (OpenID4VP + DCQL)89## When to use10Build or extend the server that a **verifier / relying-party** frontend talks to.11It composes `igrantio-backend-proxy` + `igrantio-backend-webhooks` +12`igrantio-backend-sse` for the verification flow only (least privilege). For the13issuer equivalent use `igrantio-issuer-backend` (separate skill). Read14`igrantio-ows-overview` first.1516**Before you build**: run the integrator intake in `igrantio-ows-overview` - environment, API key, tenancy, backend host, webhooks, frontend - one question at a time, a recommended default with each.1718## What it does19- **Proxy** `GET|POST|PUT|DELETE ${PROXY_PREFIX}/{tenant}/...` → OWS, injecting the tenant's20 `ApiKey`. Allow-lists only OWS **verification** endpoints (send, history).21- **Register** the verifier webhook idempotently (`VERIFIER_TOPICS`) via22 `scripts/register-webhook.ts`.23- **Receive** `POST /webhook`, verify the `X-iGrant-Signature` HMAC, extract the24 `presentationExchangeId`, store the event.25- **SSE** `GET /webhook/sse/{presentationExchangeId}` streams the event to the26 browser; `DELETE /webhook/{id}` consume-and-delete.2728## Reference implementation29Runnable Express + TypeScript app in [`./references`](./references). Same modules as30the issuer backend; the only role differences are in `src/server.ts`31(verification allow-list) and `scripts/register-webhook.ts` (`VERIFIER_TOPICS`).32Default `PORT=6002` so it can run alongside an issuer backend.3334## Steps351. `cd references && cp .env.example .env`; set `OWS_ENV` (demo|staging, default36 demo), `WEBHOOK_SECRET_KEY`, `PUBLIC_BASE_URL`, `CORS_ORIGINS`, and one37 `OWS_TENANT_<SLUG>_API_KEY` per organisation.382. `npm install && npm run dev` - backend on `:6002`.393. Register the webhook once per tenant: `npm run register-webhook -- <tenant>`40 (idempotent).414. Point the verifier frontend base URL at `${PUBLIC_BASE_URL}${PROXY_PREFIX}/<tenant>`42 and the webhook base at `${PUBLIC_BASE_URL}/webhook`.4344## Verification contract (what the frontend drives through this backend)45- `POST …/verification/send` → response `verificationHistory.presentationExchangeId`46 (SSE key) + `verificationHistory.vpTokenQrCode` (QR URI).47- The send body may carry optional `transactionData` - wallet-displayed and48 wallet-signed transaction data (SCA): simple `payment_data`, an EUDI SCA49 rulebook (TS12) `payload` (payment / e-mandate / login-risk / account50 access), or `qes_data` (QES document signing). The proxy passes it through51 unchanged; see `igrantio-ows-overview/references/api-reference.md` §2.1 for52 the exact shapes and §2.2 for verifying the returned53 `transaction_data_hashes`.54- Done on webhook `openid.presentation.presentation_acked.v3` /55 `digitalwallet.presentation.verified`; the SSE event carries56 `data.presentation.verified` and `data.presentation.presentation[0]` (claims).57See `igrantio-ows-overview/references/api-reference.md` §2 and §4.5859## Adapting60- **Query patterns**: ready-made DCQL workflows (trusted authority, claim61 sets, credential sets, KYC, optional groups, multiple instances, value62 matching) live in the `igrantio-dcql-*` workflow skills.63- **Multi-instance**: replace `InMemoryEventStore` with a shared store.64- **Key storage**: replace `EnvTenantStore` with a DB/secret-manager `TenantStore`.65- **Path scope**: `VERIFIER_PERMITTED_PREFIXES` in `server.ts` is the allow-list.6667## Validation / done criteria68- `npm run typecheck` passes.69- A wrong/absent `X-iGrant-Signature` yields 401; a valid one stores the event.70- Hitting a non-verification OWS path through the proxy yields 404 (least privilege).71- The browser SSE fires within ~1s of the webhook, keyed by `presentationExchangeId`.7273## Documentation & workflows7475When anything is unclear, consult the iGrant.io documentation before guessing:7677- iGrant.io developer APIs (index): https://docs.igrant.io/docs/developer-apis78- Getting started: https://docs.igrant.io/docs/get-started/79- OpenID4VC API (issuer / verifier / webhook): https://docs.igrant.io/docs/category/openid4vc-api/issuer80- Workflow: send and verify credentials (OID4VP): https://docs.igrant.io/docs/openID4vc-send-verify-credentials/81- Configure a webhook: https://docs.igrant.io/docs/openid4vc-api/config-create-webhook