OpenID4VCI and OpenID4VP
A wallet uses two protocols from the OpenID for Verifiable Credentials family:
- OpenID for Verifiable Credential Issuance (OpenID4VCI) to receive credentials from issuers
- OpenID for Verifiable Presentations (OpenID4VP) to present credentials to verifiers
To do both, configure and enable at least one instance of each: an
OpenID4VCI instance in issuanceProtocol and an OpenID4VP instance in
verificationProtocol. This page explains how to configure them for a
wallet.
- To configure issuance, see OpenID4VCI
- To configure verification, see OpenID4VP
- For every supported parameter, see the configuration reference.
Example configuration
issuanceProtocol:
OPENID4VCI_FINAL1:
display: "exchange.openIdFinal1"
order: 1
type: "OPENID4VCI_FINAL1"
params:
private:
encryption: "<32-byte hex>"
nonce:
signingKey: "<32-byte hex>"
verificationProtocol:
OPENID4VP_FINAL1:
display: "exchange.openid4vpFinal1"
order: 1
type: "OPENID4VP_FINAL1"
params:
public:
holder:
supportedClientIdSchemes:
[
redirect_uri,
verifier_attestation,
decentralized_identifier,
x509_san_dns,
x509_hash,
]
The sections below explain each part of this configuration, along with optional features such as DPoP, the Authorization Code Flow, and transaction data.
Receiving credentials with OpenID4VCI
Enable OpenID4VCI
Configure at least one enabled instance of type OPENID4VCI_FINAL1 in the
issuanceProtocol object. See the
issuance protocols reference
for all options.
Keys
Each OpenID4VCI instance needs its own keys:
encryption: Encrypts the access and refresh tokens the wallet receives from issuers. If you change this key, stored tokens can no longer be used. Credentials already held continue to work, but they can no longer be refreshed and new batches cannot be requested.nonce.signingKey: Signs nonces when Core issues credentials. Required for every instance, even if you only use Core as a wallet.
Keys are 32-byte hex strings. Supply them as environment variables rather than in configuration files. See Secrets and Keys for details.
DPoP
Some issuers require Demonstrating Proof of Possession (DPoP), which binds the tokens they issue to a key held by the wallet, so a stolen token cannot be used by anyone else. The wallet binds both the access token and, in the Authorization Code Flow, the authorization code itself, so a captured code cannot be redeemed with a different key.
To support DPoP, set holderDpopKeyStorage to the name of a configured
key storage provider. The wallet uses that provider to generate a DPoP
key, then signs its DPoP proofs with that key.
issuanceProtocol:
OPENID4VCI_FINAL1:
params:
public:
holderDpopKeyStorage: "<key storage instance>"
If holderDpopKeyStorage is not configured, the wallet does not support
DPoP.
Even when it is configured, the wallet can only use DPoP with an issuer if both of the following are true:
- At least one key algorithm the issuer accepts for DPoP is enabled in the wallet's key algorithm configuration.
- The selected key storage provider can generate a key with that algorithm.
If either is not the case, the wallet logs a warning and continues without DPoP. Whether issuance still succeeds depends on the issuer: issuers that require DPoP reject the request, while others issue without it.
Authorization Code Flow
When an issuer offers credentials using the Authorization Code Flow, the
wallet identifies itself to the issuer with a client ID. Add an entry to
the root-level credentialIssuer object for each issuer you want to
accept these offers from, with the client ID the wallet should use:
credentialIssuer:
EUDI_PID_FLOW:
display:
en: "EUDI Issuer"
de: "EUDI Issuer"
order: 1
enabled: true
params:
public:
logo: "<logo>"
issuer: "https://issuer.eudiw.dev"
private:
clientId: "my-wallet-id"
URL scheme
By default, the wallet accepts credential offers using the
openid-credential-offer URL scheme, as specified in the OpenID4VCI
standard.
To accept offers using another scheme, configure an additional
OpenID4VCI instance and set its urlScheme parameter. Any scheme can be
used. When the wallet receives a credential offer, it uses the instance
whose scheme matches the offer URL.
HAIP
The OpenID4VC High Assurance Interoperability Profile
(HAIP)
defines the haip-vci scheme for credential offers. To accept HAIP
offers, configure an instance with that scheme:
issuanceProtocol:
OPENID4VCI_FINAL1_HAIP:
display: "exchange.openIdFinal1Haip"
order: 2
type: "OPENID4VCI_FINAL1"
params:
public:
urlScheme: haip-vci
Setting urlScheme to haip-vci does not make an instance
HAIP-compliant, and the system does not check the rest of the instance's
configuration against HAIP. Configure the instance to meet HAIP's
requirements yourself. For example, HAIP requires DPoP support; see
DPoP.
Rejecting credential offers
When a holder rejects a credential offer, the wallet can generate a temporary identifier for the rejection. To enable this, set both:
rejectionIdentifier.didMethod: The DID method to use, by configuration instance name.rejectionIdentifier.keyAlgorithm: The key algorithm to use, by configuration instance name, for exampleECDSA.
Presenting credentials with OpenID4VP
Enable OpenID4VP
Configure at least one enabled instance of type OPENID4VP_FINAL1 in the
verificationProtocol object. See the
verification protocols reference
for all options.
URL scheme
Verifiers share proof requests as URLs, and the URL scheme signals which
flavor of OpenID4VP the request uses. When the wallet handles an
invitation URL, it matches the URL's scheme against the urlScheme of
its OpenID4VP instances and uses the matching instance.
OPENID4VP_FINAL1 instances use openid4vp by default.
To accept requests that use another scheme, configure an additional
instance with that urlScheme. Any scheme can be used. See
Presenting ISO mdocs for an example.
HAIP
HAIP defines the haip-vp scheme for proof requests. To accept HAIP
requests, configure an instance with that scheme:
verificationProtocol:
OPENID4VP_FINAL1_HAIP:
display: "exchange.openid4vpFinal1Haip"
order: 3
type: "OPENID4VP_FINAL1"
params:
public:
urlScheme: haip-vp
holder:
supportedClientIdSchemes: [x509_hash]
Setting urlScheme to haip-vp does not make an instance
HAIP-compliant, and the system does not check the rest of the instance's
configuration against HAIP. Configure the instance to meet HAIP's
requirements yourself. For example, HAIP requires signed requests to use
the x509_hash Client Identifier Scheme.
Client Identifier Schemes
Client Identifier Schemes (called "Client Identifier Prefixes" in OpenID4VP 1.0) define how the wallet validates the identity of a verifier. When a verifier sends a proof request, its scheme determines how the wallet checks that the request is legitimate.
Schemes provide different levels of trust assurance. For example,
redirect_uri provides no cryptographic verification of the verifier's
identity, while x509_san_dns requires the verifier to prove control of
a domain through certificate validation.
Use holder.supportedClientIdSchemes to set which schemes the wallet
accepts. The wallet rejects proof requests that use any other scheme.
For OPENID4VP_FINAL1, use decentralized_identifier for DID-based
verifiers. It replaces the did scheme used in earlier drafts.
For all supported schemes, see the verification protocols reference.
Transaction data
OpenID4VP lets a verifier attach transaction_data to a proof request:
details of a transaction, such as a document to sign or a payment to
confirm, that the holder must review and approve before presenting a
credential. The holder's approval is then bound to the presentation.
To handle these requests, configure at least one provider in the
root-level transactionDataProvider object. When a request arrives, the
wallet uses the provider that supports the request's transaction data
type, and fails the invitation if none does. For the supported provider
types, see the
configuration reference.
For the full holder flow, see
Transaction Authorization for Wallets.
Display parameters
Every provider requires transactionDataDisplayParams, which controls how
transaction data is turned into human-readable blocks for the holder to
review. The wallet returns these blocks as transactionDataDisplay when
you fetch a transaction data entry.
groupPath: JSONPath selecting what to render as separate blocks. An array produces one block per element; an object or the root$produces a single block. If nothing matches, the result is an empty list.titlePath: Optional. JSONPath, relative to each block, selecting the value used as the block's title.attributes: The values to display in each block. Each entry pairs apath(JSONPath, relative to each block) with adisplaylabel, which becomes thekeyin the key-value pairs returned to the wallet. If a path doesn't resolve for a block, that attribute is left out of the block.
These paths are evaluated against the transaction data as it is sent over
the wire. For QES_APPROVAL, this means one titled block per document:
transactionDataProvider:
QES_APPROVAL:
type: QES_APPROVAL
display:
en: "QES transaction data"
order: 1
enabled: true
params:
public:
transactionDataDisplayParams:
groupPath: "$.documentInfos[*]"
titlePath: "$.label"
attributes:
- path: "$.access.oneTimePassword"
display: "transactionData.qesApproval.documentInfo.access.oneTimePassword"
- path: "$.href"
display: "transactionData.qesApproval.documentInfo.href"
- path: "$.checksum"
display: "transactionData.qesApproval.documentInfo.checksum"
- path: "$.signed_props"
display: "transactionData.qesApproval.documentInfo.signedProps"
The SCA types use snake_case field names nested under a payload
wrapper, so they use groupPath: "$.payload" to produce a single
untitled block per entry:
transactionDataProvider:
SCA_PAYMENT_CONFIRMATION:
type: SCA_PAYMENT_CONFIRMATION
display:
en: "Payment confirmation"
order: 2
enabled: true
params:
public:
leewaySeconds: 60
transactionDataDisplayParams:
groupPath: "$.payload"
attributes:
- path: "$.payee.name"
display: "transactionData.scaPaymentConfirmation.payee.name"
- path: "$.execution_date"
display: "transactionData.scaPaymentConfirmation.executionDate"
Leeway for SCA types
All SCA provider types also require leewaySeconds, which sets how far
in the past an executionDate can be and still be accepted as not yet
past. QES_APPROVAL does not use it.
Presenting ISO mdocs
Wallets can present ISO mdocs over OpenID4VP. ISO/IEC 18013-7 defines how mdocs are presented online, and its next edition is expected to build on HAIP, which profiles OpenID4VP 1.0 for both SD-JWT VC and mdoc credentials. Procivis One supports HAIP, so you can prepare a wallet to present mdocs this way now.
To do so, configure a dedicated OPENID4VP_FINAL1 instance for mdoc
requests:
verificationProtocol:
MDOC_OPENID4VP:
display: "exchange.mdocOpenid"
order: 2
type: "OPENID4VP_FINAL1"
params:
public:
urlScheme: "<url scheme>"
holder:
supportedClientIdSchemes: [x509_hash]
urlScheme: Set this to the scheme the verifiers you work with use for mdoc requests. HAIP defineshaip-vp; check the applicable edition of ISO/IEC 18013-7 or your ecosystem's requirements for the scheme to use. If verifiers use more than one scheme, configure one instance per scheme.holder.supportedClientIdSchemes: HAIP requires wallets to acceptx509_hashfor signed requests.
As with any HAIP instance, the system does not check this configuration against HAIP or ISO/IEC 18013-7, so make sure the instance meets the requirements of the standard your verifiers follow.
Query language
Verifiers using OpenID4VP 1.0 describe the credentials they want with the Digital Credentials Query Language (DCQL). DCQL support requires no configuration. For the DCQL features the wallet supports, see Submitting a Presentation.