npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bedrock/vc-delivery

v7.16.1

Published

Bedrock Verifiable Credential Delivery

Readme

@bedrock/vc-delivery

A Bedrock module that provides a Verifiable Credential (VC) Workflow Service for issuing and exchanging VCs using multiple protocols. It enables configurable, multi-step credential exchange workflows with support for the VC-API, OpenID for Verifiable Credential Issuance (OID4VCI), OpenID for Verifiable Presentations (OID4VP), and an Invite Request protocol.

Table of Contents

Background

This module implements the server-side infrastructure for Verifiable Credential delivery. A workflow is a reusable configuration that defines how credentials are issued and/or verified. An exchange is a single instance of that workflow — a live, stateful session between a workflow service and an exchange client (e.g. a digital wallet).

Workflows are registered with an associated set of authorization capabilities (zcaps) that grant the service permission to issue and verify credentials on behalf of the workflow owner.

Features

  • Multi-protocol support: VC-API, OID4VCI v1.0, OID4VP 1.0, and Invite Request (with backwards compatibility for OID4VCI Draft 13 and OID4VP Draft 18).
  • Multi-step exchanges: Define sequential steps that mix presentation verification and credential issuance within a single exchange.
  • JSONata credential templates: Dynamically generate credential content from exchange variables using JSONata expressions.
  • Multiple issuer instances: Configure up to 10 independent issuer instances per workflow, each with its own zcap and supported credential formats.
  • OID4VP client profiles: Configure up to 10 OID4VP client profiles per workflow, including support for signed authorization requests and mDL/ISO 18013-7 presentation flows.
  • OAuth2 / zcap authorization: Workflow management endpoints support both zcap-based and OAuth2-based authorization.
  • Configurable exchange TTL: Exchanges expire automatically (default 15 minutes, maximum 48 hours).

Requirements

  • Node.js >= 20
  • MongoDB (via @bedrock/mongodb)
  • A running Bedrock application with the required peer dependencies (see peerDependencies in package.json)

Installation

npm install @bedrock/vc-delivery

Then import the module in your Bedrock application entry point:

import '@bedrock/vc-delivery';

The module self-registers with Bedrock on import and will set up all routes and storage on startup.

Quick Start

1. Create a workflow configuration

// POST /workflows
{
  "sequence": 0,
  "controller": "did:key:z6Mk...",
  "credentialTemplates": [{
    "type": "jsonata",
    "template": "{ '@context': ['https://www.w3.org/2018/credentials/v1'], 'type': ['VerifiableCredential'], 'credentialSubject': {'id': subject} }"
  }],
  "initialStep": "issue",
  "steps": {
    "issue": {
      // you can use static step parameters like this, but also see
      // `stepTemplate` below for more powerful dynamic steps instead!
      "issueRequests": [{ "credentialTemplateIndex": 0 }]
    }
  },
  "zcaps": {
    "issue": { /* delegated issue zcap */ }
  }
}

2. Create an exchange

// POST /workflows/:workflowId/exchanges
{
  "ttl": 900,
  "variables": {
    "subject": "did:example:holder123"
  }
}
// Response: 204 No Content; Location header contains exchange URL

3. Execute the exchange (VC-API)

// POST /workflows/:workflowId/exchanges/:exchangeId
{}
// Response:
{
  "verifiablePresentation": { /* VP containing issued VCs */ }
}

Configuration

Workflow Config

Workflows are created and managed via the @bedrock/service-core HTTP API at /workflows. The configuration body accepts the following custom properties in addition to standard service-core fields:

| Property | Type | Required | Description | |----------|------|----------|-------------| | credentialTemplates | Array | No | List of JSONata credential templates to issue | | steps | Object | No | Named step definitions for multi-step exchanges | | initialStep | String | Required if steps present | Name of the first step to execute | | issuerInstances | Array | No | Up to 10 issuer instance configurations | | zcaps | Object | No | Authorization capability references |

If credentialTemplates are provided, either a top-level zcaps.issue or at least one issuerInstances entry with its own issue zcap is required.

Credential Templates

Credential templates use JSONata to dynamically produce credential JSON. Exchange variables and built-in globals are available as template bindings.

"credentialTemplates": [{
  "type": "jsonata",
  "id": "myTemplate",           // optional, for referencing in issueRequests
  "template": "{ ... }"         // JSONata expression returning a VC object
}]

Built-in template globals:

| Variable | Description | |----------|-------------| | globals.exchange.id | The local exchange ID | | globals.exchangeId | The full exchange URL | | globals.workflow.id | The workflow ID |

Steps

Steps define the interactive logic of an exchange. Each step can request a Verifiable Presentation, issue credentials, or both.

"steps": {
  "verify": {
    "verifiablePresentationRequest": { /* VPR object */ },
    "nextStep": "issue"
  },
  "issue": {
    "issueRequests": [
      { "credentialTemplateIndex": 0 }
    ]
  }
},
"initialStep": "verify"

Step fields:

| Field | Description | |-------|-------------| | issueRequests | Array of credential issue request parameters | | verifiablePresentationRequest | VPR to send to the client | | verifiablePresentation | VP to return immediately without client input | | createChallenge | true to generate and attach a challenge nonce to the VPR | | nextStep | Name of the step to transition to after this one completes | | openId | OID4VP options for this step | | jwtDidProofRequest | Request a DID-bound JWT proof | | divpDidProofRequest | Request a DI-VP DID proof | | callback.url | URL to call when exchange state updates | | presentationSchema | JSON Schema to validate received presentations | | verifyPresentationOptions | Additional options for presentation verification | | verifyPresentationResultSchema | JSON Schema to validate verification results | | allowUnprotectedPresentation | Allow presentations without a proof | | redirectUrl | URL to redirect the exchange client upon completion |

Steps may also be defined as templated steps, where a JSONata stepTemplate expression dynamically generates the step configuration from exchange variables at runtime:

"steps": {
  "issue": {
    "stepTemplate": {
      "type": "jsonata",
      "template": "{ 'issueRequests': [{ 'credentialTemplateIndex': 0 }] }"
    }
  }
}

Issuer Instances

issuerInstances allows a workflow to configure multiple issuers, each with different capabilities and supported credential media types. Up to 10 instances are supported.

"issuerInstances": [{
  "id": "myIssuer",                         // optional identifier
  "supportedMediaTypes": ["application/vc"],
  "zcapReferenceIds": {
    "issue": "myIssuerZcap"
  },
  "oid4vci": {
    "supportedCredentialConfigurations": {  // optional explicit OID4VCI config
      "MyCredential_ldp_vc": { /* credential configuration */ }
    }
  }
}]

Each issuer instance references a zcap in the workflow's zcaps map by its zcapReferenceIds.issue key.

OID4VCI Options

When creating an exchange with OID4VCI support, pass an openId object in the exchange creation body:

// POST /workflows/:workflowId/exchanges
{
  "openId": {
    "preAuthorizedCode": "abc123",
    "oauth2": {
      "generateKeyPair": { "algorithm": "ES256" }
      // or provide an existing key pair:
      // "keyPair": { "privateKeyJwk": {...}, "publicKeyJwk": {...} }
    }
  }
}

Deprecated: expectedCredentialRequests is deprecated and should not be used in new workflows. Credential configurations are inferred from issuerInstances instead.

OID4VP Client Profiles

OID4VP client profiles allow fine-grained control over how authorization requests are constructed and which wallet/verifier protocols are used. Up to 10 profiles are supported per workflow step.

// in a step:
"openId": {
  "clientProfiles": {
    "myProfile": {
      "createAuthorizationRequest": "/authzReqVar",
      "client_id": "https://verifier.example",
      "client_id_scheme": "redirect_uri",
      "response_mode": "direct_post",
      "response_uri": "https://verifier.example/callback",
      "protocolUrlParameters": {
        "name": "OID4VP",
        "scheme": "openid4vp",
        "version": "OID4VP-1.0"   // or "OID4VP-draft18"
      }
    }
  }
}

Deprecated: presentation_definition (Presentation Exchange) is a pre-OID4VP 1.0 mechanism and is deprecated. Prefer dcql_query for new implementations.

Supported response_mode values include direct_post, direct_post.jwt, dc_api, and dc_api.jwt.

HTTP API

All endpoints support CORS. Authorization uses zcaps or OAuth2 — not cookies — so CSRF is not a concern.

Exchanges

Create Exchange

POST /workflows/:workflowId/exchanges

Body (application/json):

| Field | Type | Description | |-------|------|-------------| | ttl | Number | Seconds until expiry (default: 900, max: 172800) | | expires | String | ISO 8601 expiry date (alternative to ttl) | | variables | Object | Key/value pairs available in credential templates and step templates | | openId | Object | OID4VCI configuration (see OID4VCI Options) |

Note: The schema uses additionalProperties: false; fields not listed above (including step) will be rejected by the validator.

Response: 204 No Content with a Location header pointing to the exchange URL.

Get Exchange

GET /workflows/:workflowId/exchanges/:exchangeId

Returns the current exchange state. Private keys and secrets are never included in the response.

Use Exchange (VC-API)

POST /workflows/:workflowId/exchanges/:exchangeId

Body (application/json):

| Field | Type | Description | |-------|------|-------------| | verifiablePresentation | Object | VP to submit (if requested by the current step) |

Response: A JSON object, which may contain:

  • verifiablePresentation — a VP with issued credentials
  • verifiablePresentationRequest — a VPR requiring the client to submit a VP

Protocols

Get Supported Protocols

GET /workflows/:workflowId/exchanges/:exchangeId/protocols

Returns a {"protocols": {...}} object listing all interaction URLs supported by the exchange. Key names are:

  • vcapi — always this literal string when VC-API is supported
  • inviteRequest — always this literal string when an invite request step is active
  • OID4VCI — always OID4VCI
  • OID4VP — defaults to OID4VP but is controlled by the protocolUrlParameters.name field on each OID4VP client profile, so named profiles may produce arbitrary keys (e.g. a profile with "name": "bar" produces a bar key)

OID4VCI Endpoints

The well-known metadata endpoints are served at two URL shapes for maximum wallet compatibility:

| Method | Path | Description | |--------|------|-------------| | GET | /.well-known/openid-credential-issuer/workflows/:wfId/exchanges/:id | Credential issuer metadata (prefix form) | | GET | /workflows/:wfId/exchanges/:id/.well-known/openid-credential-issuer | Credential issuer metadata (suffix form) | | GET | /.well-known/oauth-authorization-server/workflows/:wfId/exchanges/:id | AS metadata (prefix form) | | GET | /workflows/:wfId/exchanges/:id/.well-known/oauth-authorization-server | AS metadata (suffix form) | | GET | …/exchanges/:id/openid/credential-offer | Credential offer object | | POST | …/exchanges/:id/openid/token | Access token endpoint | | POST | …/exchanges/:id/openid/credential | Credential request endpoint | | POST | …/exchanges/:id/openid/batch_credential | Batch credential requests | | GET | …/exchanges/:id/openid/jwks | JSON Web Key Set | | POST | …/exchanges/:id/openid/nonce | Nonce endpoint |

OID4VP Endpoints

Legacy (no client profile ID) and modern (with client profile ID) routes are both supported:

| Method | Path | Description | |--------|------|-------------| | GET/POST | …/exchanges/:id/openid/client/authorization/request | Retrieve authorization request (default profile) | | POST | …/exchanges/:id/openid/client/authorization/response | Submit authorization response (default profile) | | GET/POST | …/exchanges/:id/openid/clients/:clientProfileId/authorization/request | Retrieve authorization request (named profile) | | POST | …/exchanges/:id/openid/clients/:clientProfileId/authorization/response | Submit authorization response (named profile) |

Invite Request Endpoints

| Method | Path | Description | |--------|------|-------------| | POST | …/exchanges/:id/invite-request/response | Submit invite response |

Supported Protocols

VC-API

The VC-API protocol is automatically supported for any workflow that has credentialTemplates, or for any step that has a verifiablePresentationRequest or verifiablePresentation. The exchange URL itself serves as the vcapi interaction endpoint.

OID4VCI

OpenID for Verifiable Credential Issuance is supported when an exchange is created with an openId configuration object. Supported grant types:

  • Pre-authorized code flow (urn:ietf:params:oauth:grant-type:pre-authorized_code)

Supported proof types for credential requests:

  • jwt (JWT-based DID proof)
  • di_vp (Data Integrity VP DID proof)

Supported credential formats (OID4VCI v1.0):

  • ldp_vc / application/vc (JSON-LD Verifiable Credential)
  • jwt_vc_json / application/jwt (JWT Verifiable Credential)

Supported for backwards compatibility with OID4VCI Draft 13 (deprecated):

  • jwt_vc_json-ld (JSON-LD-based VC-JWT envelope)

Deprecated: OID4VCI Draft 13 credential request formats are supported for backwards compatibility but are deprecated. New implementations should use OID4VCI v1.0.

OID4VP

OpenID for Verifiable Presentations is supported when a step includes an openId configuration. The module supports:

  • OID4VP 1.0+ (with prefixed client_id schemes and request_uri_method=post)
  • Signed authorization requests (via authorizationRequestSigningParameters)
  • Multiple response modes: direct_post, direct_post.jwt, dc_api, dc_api.jwt
  • mDL / ISO 18013-7 presentations (Annex B, C, and D handover types)
  • DCQL queries (dcql_query)

Supported for backwards compatibility (deprecated):

  • OID4VP Draft 18
  • Presentation Definitions (presentation_definition)

Invite Request

A lightweight invite-based protocol. When a step exposes an inviteRequest property, the exchange will include an inviteRequest URL in its protocols response. The client POSTs an invite response (containing a url, purpose, and optional referenceId) to complete this step.

Because the static computedStep schema does not declare an inviteRequest property (it uses additionalProperties: false), the property must be injected at runtime via a stepTemplate rather than declared directly in the workflow configuration:

"steps": {
  "myStep": {
    "stepTemplate": {
      "type": "jsonata",
      "template": "{ \"inviteRequest\": inviteRequest }"
    }
  }
}

The exchange variable inviteRequest must be set to true when creating the exchange to activate the invite request protocol for that step.

Exchange Lifecycle

graph TD
    A[Create Exchange]:::accent0 --> B[Exchange Pending]:::accent1
    B --> C{Protocol}:::accent2
    C -->|VC-API| D[POST to exchange URL]
    C -->|OID4VCI| E[Pre-auth code flow]
    C -->|OID4VP| F[Fetch authz request]
    C -->|Invite| G[POST invite response]
    D & E & F & G --> H{Step complete?}:::accent3
    H -->|Next step exists| B
    H -->|No more steps| I[Exchange Complete]:::accent4
    B -->|TTL expires| J[Exchange Expired]:::accent5

Exchanges are stored in MongoDB. By default they expire after 15 minutes (ttl: 900). The maximum TTL is 48 hours (ttl: 172800). Expired exchanges are retained for an additional grace period of 3 days before being eligible for removal.

Exchange variables hold the mutable state that flows between steps, including per-step results accessible at variables.results[stepName].

Multi-Step Workflows

A workflow can chain multiple steps using the nextStep field. This enables patterns such as:

  1. Verify then Issue — Require the holder to present an existing credential before receiving a new one.
  2. OID4VP then OID4VCI — Verify via OID4VP and then issue via OID4VCI in the same exchange session.
  3. Invite then Issue — Collect wallet/app metadata via invite request before issuing credentials.

Step results are stored in exchange.variables.results[stepName] and are accessible in subsequent JSONata step templates or credential templates.

Security

  • Workflow management routes require zcap or OAuth2 authorization.
  • Exchange execution routes (VC-API POST) are unauthenticated by design, relying on capability URLs for authorization; exchanges are single-use, short-lived, and scoped to a specific workflow. Workflows themselves can add authentication by asking for verifiable presentations and verifiable credentials.
  • CORS is enabled on all endpoints. This is safe because authorization is performed using HTTP signatures and capabilities (not cookies), making CSRF impossible.
  • Private key material (privateKeyJwk) and exchange secrets are stripped from all API responses.
  • VC-API exchange creation and exchange-use payloads are limited to 10 MB (configured via config.express.bodyParser.routes). OID4VP authorization-response form posts are also limited to 10 MB (via an inline urlencoded body parser). Other OID4VCI endpoints (token, credential, etc.) rely on the framework's default body-size limits. A feature exists to enable larger exchange state, but it has not been enabled at this time.

Backwards Compatibility

This module previously exposed a vc-exchanger service at /exchangers. That service type and route prefix are still registered as aliases and will continue to function, but new deployments should use vc-workflow and /workflows.

License

New BSD License © 2022-2026 Digital Bazaar, Inc.