Verifiable Credential Issuance Guidelines
This document defines Verifiable Credential Issuance specification in compliance with OpenID for Verifiable Credential Issuance (version: openid-4-verifiable-credential-issuance-1_0-10).
|Verifiable Credential Issuance||VCI||Process defined in this documentation|
|Relying Party||RP||OAuth 2.0 compliant client|
|OpenID Provider||OP||A service that can Authenticate the End-User and provide Claims to a Relying Party about the Authentication event and the End-User.|
|Self-issued OpenID Provider||SIOP||An OpenID Provider (OP) used by the End-users to prove control over a cryptographically verifiable identifier. W3C refers to this as a Holder.|
|Issuer||ISS||A role an entity can perform by asserting claims about one or more subjects, creating a verifiable credential from these claims, and transmitting the verifiable credential to a holder.|
|OpenID for Verifiable Credentials||OID4VC||OpenID for Verifiable Credentials (OID4VC) defines an API and corresponding OAuth-based authorization mechanisms for issuance of Verifiable Credentials.|
|User Agent||UA||Holder Wallet or a Browser.|
|Verifiable Credential||VC||A set of one or more claims made by an issuer. A verifiable credential is a tamper-evident credential that has authorship that can be cryptographically verified. Verifiable credentials can be used to build verifiable presentations, which can also be cryptographically verified.|
|JSON Web Key Set||JWKS||A data structure that represents a set of JWKs as defined in |
VCI specification is split into two logical boundaries from the provider side, which can be co-located in the same service, or distributed as separate services. The logical boundaries are Authorisation Server and Credential Issuer. Authorisation Server does authentication and authorisation on behalf of the Credential Issuer. Credential Issuer acts as an OAuth 2.0 protected endpoint(s) for the actual issuance of the Verifiable Credentials.
The Provider's clients can be Holder Wallets or Service Wallets. The Wallets are not callable from the internet; instead, interactions are facilitated through redirects or QR codes. The VCI process can be initiated by the Issuer (using a QR code or redirect) or by the End-User through the Wallet.
End-User Initiated Flow
Alice (the user) opens her Holder Wallet with the intent of obtaining a digital diploma. She finds her university's issuer and applies for a diploma (user initiated). The issuer acknowledges this and asks her to log in with her university login (login with the existing method). The issuer also requests her consent to the creation of a digital diploma (user consent). She confirms and is sent back to her wallet. There, she is notified of the successful creation of the digital diploma (real-time VC issuance).
Issuer Initiated Flow
The user browses her university's home page, searching for a way to obtain a digital diploma. She finds the respective page, which shows a link "request your digital diploma" (issuer initiated). She clicks on this link and is redirected to her digital wallet. The wallet notifies her that an issuer offered to issue a diploma credential. She confirms this inquiry and is sent to the university's credential issuance service. She logs in with her university login (login with the existing method) and is asked to consent to the creation of a digital diploma (user consent). She confirms and is sent back to her wallet. There, she is notified of the successful creation of the digital diploma (real-time VC issuance).
The issuer initiated scenario can operate using one of two different interaction models:
Same-Device model: The user has a Holder wallet installed on the same device she uses to visit a credential issuer's website. The issuer initiates the interaction using an HTTP redirect.
Example: Alice has her SSI wallet installed on her smartphone. She uses her smartphone to visit the credential issuer's website (e.g., the University website where she can request the issuance of a diploma verifiable credential).
Cross-device model: The user has a Holder wallet installed on a different device than the one she uses to visit a credential issuer's website. The issuer initiates the interaction by providing a QR code, which the user must scan using the device that contains the Holder Wallet.
Example: Alice has her SSI wallet installed on her smartphone and visits the credential issuer's website (e.g., the University website where she can request issuance of a diploma verifiable credential) on her laptop. After she clicks on "Issue Credentials", a QR code is presented, and Alice can scan the QR code using her SSI wallet app on her smartphone.
HTTP Redirect and QR code content can be the same, but the QR code content should be minimalistic due to the data size limitation.
Users can authenticate through various methods:
- by using the issuer's existing authentication service
- by demonstrating control of a DID when the issuer already has a pre-established relationship with the DID
- by presenting a verifiable credential recognised by the issuer, which can serve as a means of authentication
- by employing a combination of the above methods
Note that Functional Flows are currently using forced ID Token Request and Response to authenticate the user. This is done to cover some authentication in conformance testing, thus this may be removed if the user does not need to be authenticated with DID, or some other authentication means are used.
End-User DID for the Verifiable Credential is presented during the Credential Request flow, in the JWT proof.
Example of Alice digital diploma authentication
Alice enters into the university website and applies for a digital diploma. Diploma issuer has established an elaborate authentication scheme, which requires a Verifiable Credential asserting the Alice's Holder Wallet qualities and end-user authentication with username and password in their own OIDC capable authentication system. After the wallet and the user has been authenticated, the Authorisation Server responds with an Authorisation Response, which content can be exchanged into Access Token.
Service Clients must use a signed
Request Object to ensure authenticity, integrity and non-repudiation for the request content. Service Wallets must expose the used keys as a JWKS, which can be referenced through Service Wallet Metadata.
Request Object follows the OpenID Connect Core, but it must be signed by the Relying Party (Client).
Holder Wallets must use PKCE - RFC 7636 when applying for code flow.
Issuers may want to initiate the Credential Offering process. To facilitate this, wallets must implement the
openid-credential-offer "endpoint". The wallet's endpoint will begin the flow (
pre-authorized) if the issuer is deemed trustworthy. The credential offering serves only as an instruction for the wallet. It is recommended to seek consent from the User before proceeding with the offering or deciding against it.
The Credential Offering can be given as a value, where the below data structure is added under
credential_offer field, or it may be given as a URI reference through
credential_offer_uri field, which will resolve to the same data structure with content type of
application/json. It is recommended to serve the Credential Offering under the same domain as the Credential Issuer is.
Note: As this is a Wallet endpoint/schema handler, the wallet does not generate any response to this and retains control of the user experience within the wallet itself. This can also be accessed through a QR code.
|REQUIRED||The URL of the Credential Issuer, from which the Wallet is requested to obtain one or more credentials.|
|REQUIRED||A JSON array, where every entry is a JSON object containing the data related to a certain credential type the Wallet MAY request.|
|REQUIRED||A JSON string identifying the format of this credential, e.g. |
|REQUIRED||A JSON array designating the types a certain credential type supports according to |
|OPTIONAL||A JSON object defining the Trust Framework details.|
|REQUIRED||A JSON string defining the Trust Framework name the credential will be issued under.|
|REQUIRED||A JSON string designating the behaviour and properties bound to the type. Types may include extra properties.|
|OPTIONAL||A JSON string associated with the |
|OPTIONAL||A JSON object indicating to the Wallet the Grant Types the Credential Issuer's AS is prepared to process for this credential offer. Each grant is represented by a key and an object. The key value is the Grant Type identifier, and the object MAY contain parameters that either dictate the way the Wallet MUST use the particular grant and/or parameters the Wallet MUST send with the respective request(s). If the |
|OPTIONAL||A JSON object for authorisation code flow.|
|OPTIONAL||A string value created by the Credential Issuer and opaque to the Wallet that is used to bind the subsequent Authorisation Request with the Credential Issuer to a context set up during previous steps. If the Wallet decides to use the Authorization Code Flow and receives a value for this parameter, it MUST include it in the subsequent Authorisation Request to the Credential Issuer as the |
|OPTIONAL||A JSON object for the pre-authorised flow.|
|REQUIRED||The code representing the Credential Issuer's authorisation for the Wallet to obtain Credentials of a certain type. This code MUST be short lived and single use. If the Wallet decides to use the Pre-Authorized Code Flow, this parameter value MUST be include in the subsequent Token Request with the Pre-Authorized Code Flow.|
|OPTIONAL||A boolean value specifying whether the Credential Issuer expects the presentation of a user PIN along with the Token Request in a Pre-Authorized Code Flow. The default value is false. This PIN is intended to bind the Pre-Authorized Code to a certain transaction to prevent the replay of this code by an attacker that, for example, scanned the QR code while standing behind the legitimate user. It is RECOMMENDED to send a PIN via a separate channel. If the Wallet decides to use the Pre-Authorized Code Flow, a PIN value MUST be sent in the user_pin parameter with the respective Token Request.|
Credential Offering - Non-normative example
HTTP 302 Location: openid-credential-offer://
Credential Offering as URI reference - Non-normative example
HTTP 302 Location: openid-credential-offer://
Pre-Authorised Code Flow
The issuer can also bypass the authentication and use the pre-authorised flow. This method is intended for use cases where the issuer's website enables one or more credentials to be shared, and the user is already authenticated with the website. Pre-authorisation employs Credential Offering capabilities but requires
pre-authorized_code and may optionally use the
user_pin_required parameter. The user PIN code must be delivered through other channels, as the QR code or request can be intercepted. It is recommended to use a PIN code with the pre-authorised flow.
The PIN code and pre-authorised code must be delivered to the Token Endpoint to be exchanged for an Access Token.
Pre-authorised Credential Offering - Non-normative example
HTTP 302 Location: openid-credential-offer://
Authorisation Server service
The Authorisation Server is responsible for authentication and authorisation on behalf of the Credential Issuer. The Authorisation Server has the capabilities to request Clients' ID Tokens, exchange information over Verifiable Presentation and issue access Tokens.
Please see the Credential Issuer Metadata specifications for further details.
This call is from the Client to the Authorisation Server.
The Authorisation Request builds on the OAuth 2.0 Rich Authorisation Request, where the user specifies which types of Verifiable Credentials she is requesting using the
authorization_details parameter. The full Authorisation Request is described in the table below.
The Authorisation Request must support a
Request Object, which is signed by the Relying Party. A Holder Wallet acting as a Relying Party, should only use PKCE.
Authorisation Request content:
|Value MUST be set to |
|REQUIRED. HTTPS URL for Service Wallets and DID for Holder Wallets.|
|REQUIRED. FQDN for redirection of the response|
|REQUIRED. Must contain "openid"|
|CONDITIONAL: REQUIRED if Credential Offering contained |
|RECOMMENDED. An opaque value used by the client to maintain state between the request and callback.The authorization server includes this value when redirecting the user-agent back to the client. The parameter SHOULD be used for preventing cross-site request forgery|
|REQUIRED. One to many Authorization Details objects below|
|Determines the authorisation details type. MUST be set to |
|CONDITIONAL. If the Credential Issuer metadata contains an |
|The format in which the credential is requested to be issued. Valid values defined by this specification are jwt_vc and ldp_vc. Profiles of this specification MAY define additional format values.|
|A JSON array designating the types a certain credential type supports according to |
|RECOMMENDED. A value used to associate a Client session with an ID Token, and to mitigate replay attacks|
CONDITIONAL: Only for Holder Wallets. In format of BASE64URL-ENCODE(SHA256(code_verifier as UTF-8 string))
|CONDITIONAL: Only for Holder Wallets. If the client is capable of using "S256", it MUST use "S256". Else "plain" can only be used if they cannot support "S256".|
|CONDITIONAL: Only for Holder and Service Wallets. Overwrites the defaults defined in the Holder Wallet Metadata. The object structure matches the metadata structure.|
Authorisation Request Non-normative examples
Service Wallet: Signed Authorisation Request
GET from https://api-conformance.ebsi.eu/conformance/v3/auth-mock/authorize?
Holder Wallet: Plain Authorisation Request
GET from https://api-conformance.ebsi.eu/conformance/v3/auth-mock/authorize
ID Token Request
Other User Authentication means can be used in place of ID Token, and it may be fully omitted if DID does not need to be known for the authorisation purposes. ID Token is used only to authenticate user through proof of control, and only for authorisation purposes.
This call is from the Authorisation Server to the Client and is always a redirect to the Client's defined
authorization_endpoint, which must be provided in the
client_metadata field of the initial Authorisation Request. The default value is
The ID Token Request must always use a signed
Request Object. Any other User Authentication method can be employed in place of the ID Token Request.
ID Token Request
HTTP 302 Location: openid://
ID Token Response
This call is from the Client to the Authorisation Server and is signed by the DID controlled keys.
The response mode
direct_post is derived from the ID Token Request's content.
redirect_uri serves as the POST call endpoint, and
nonce must be included in the subject signed ID Token.
state parameter is mandatory for the ID Token Response when it is present in the ID Token Request sent by the "Authorization Server." In such cases, the Client must ensure that the values of the
state parameter are identical in both.
The main purpose of the subject signed ID Token is to prove control of a DID, while the secondary purpose is to transfer user-declared parameters, such as a preferred email address. ID Token is purely meant for authentication, while the use of the authorization might contain other DIDs.
ID Token Response
POST into https://api-conformance.ebsi.eu/conformance/v3/auth-mock/direct_post
After successful authentication and authorisation, the Authorisation Response may be returned to the client.
The Authorisation Response builds upon the OID4VC Authentication Response defined in OID4VC Authentication Response.
The Authorisation Response schema is defined in the following table:
|redirect_uri||REQUIRED. Redirection URI to the wallet.|
|code||REQUIRED. A query parameter in the URI.|
|state||REQUIRED. A query parameter in the URI.|
Authorization Response - Non-normative example
HTTP/1.1 302 Found
Note: the client MUST check that the
state value matches the
state value from the Authorisation Request (to prevent CSRF).
An authorisation code (from the Authorisation Response) is exchanged for an Access Token and an ID Token, both of which are signed by the Authorisation Server. Service Wallet client authentications are performed using
client_assertion with a
urn:ietf:params:oauth:client-assertion-type:jwt-bearer, while Holder Wallets are authenticated with PKCE's
The Token Request schema is defined in the following table:
|grant_type||REQUIRED. MUST be |
|client_id||REQUIRED. HTTPS URL for Service Wallets and DID for Holder Wallets.|
|code||REQUIRED. MUST be the Authorisation code from the Authorisation Response.|
|redirect_uri||REQUIRED. Redirection URL to the wallet.|
CONDITIONAL & REQUIRED. Only used with
|user_pin||CONDITIONAL & OPTIONAL. Only used with |
|client_assertion||OPTIONAL. A minimal self signed JWT for |
|client_assertion_type||OPTIONAL. MUST be |
|code_verifier||OPTIONAL. Wallet generated secure random token, used to validate the original |
Token Request - Non-normative example
POST into https://api-conformance.ebsi.eu/conformance/v3/auth-mock/token
In addition to an
id_token, the response contains a challenge nonce
c_nonce to be used with the Credential Issuer. The specification does not define if the Authorisation Service should continue issuing new c_nonces or not, but it is recommended to deliver the first c_nonce within the
access_token and allow the Credential Issuer to manage the next
c_nonce attributes it needs.
The Token Response schema is defined in the following table:
|access_token||REQUIRED. The token is later used for the issuance of the actual Verifiable Credential.|
|id_token||REQUIRED. The token that contains the user's identifier as Subject Identifier.|
|token_type||REQUIRED. Value of the token type MUST be bearer.|
|expires_in||REQUIRED. Value denoting the lifetime in seconds of the token.|
|c_nonce||REQUIRED. A string containing a random challenge that needs to be signed to create a proof of possession of key material when requesting the actual Verifiable Credential. This value MUST be random and used only once.|
|c_nonce_expires_in||OPTIONAL. Value denoting the lifetime in seconds of the |
Token Response - Non-normative example
Credential Issuer service
The Credential Issuer service is an OAuth 2.0 protected service with two endpoints. The first endpoint is the Credentials Endpoint, which encompasses proofs, in-time issuance and the option to defer the issuance. The second endpoint is the
Deferred Credentials Endpoint, which manages asynchronous issuance through polling.
The Credential Request is sent to the Credential Issuer's Credentials Endpoint, as defined in the metadata. It initiates the issuance of the Verifiable Credential for the credentials specified in the initial Authorisation Request.
The endpoint may be called multiple times with different proofs of DID control, which will lead to multiple Verifiable Credentials for different DIDs, but for same identity/authority. This behaviour is the default, and must be restricted by the Issuer if needed.
The Credential Request schema is defined in the following table:
|types||string||REQUIRED. The type definition, identifying the Verifiable Credential.|
|format||string||OPTIONAL. The format in which the Verifiable Credential should be issued. It MUST be |
|proof||Proof object||CONDITIONAL. An object containing proof of possession of the key material. The proof is generated based on the |
Proof is mandatory for Verifiable Credentials that are bound into DID. The target DID for the Verifiable Credential is delivered in the Header
kid, which is a DID URI containing the DID and optionally the key id.
Note that ID Token is only used for authentication purposes, and it is separate from the DID the VC should be bound into. It is upto business/domain/trust framework to define if proof of DID control is enough to allow any DID for the VC or if the DID has be to known before the Credential Request.
|proof_type||string||REQUIRED. JSON String denoting the proof type. It MUST be "jwt".|
|jwt||Signed JWT||CONDITIONAL, when |
The signed JWT proof must contain the following parameters:
|Header||typ||string||REQUIRED. Must be |
|Header||alg||string||REQUIRED. Must be the algorithm used to sign the JWT.|
|Header||kid||string||REQUIRED. Must be DID URI which identifies a particular key in the DID document that the credential shall be bound to.|
|Body||iss||string||REQUIRED. MUST be the |
|Body||aud||string||REQUIRED. MUST be the |
|Body||exp||number||REQUIRED. MUST be the instant when the proof was created.|
|Body||nonce||string||REQUIRED. MUST be Token Response |
Credential Request - Non-normative example
POST into https://api-conformance.ebsi.eu/conformance/v3/issuer-mock/credentials
Authorization: BEARER eyJhbGciOiJSUzI1NiIsInR5cCI6Ikp..sHQ
Verifiable Credentials can be issued in two ways: in-time (synchronously) or deferred (asynchronously). Consequently, there are two types of Credential Responses: an in-time type containing format and credential properties, and a deferred type containing an
c_nonce is always returned for new credential requests.
The Credential Response schema is defined in the following table:
|format||string||CONDITIONAL. The format in which the Verifiable Credential was issued. It MUST be jwt_vc or ldp_vc.|
|credential||string||CONDITIONAL. Issued Verifiable Credentials in a format indicated by |
|acceptance_token||string||CONDITIONAL. A string containing a token that can be later used to obtain Verifiable Credentials (deferred flow).|
|c_nonce||string||OPTIONAL. A string containing a random challenge that needs to be signed to create proof of possession of key material. This value MUST be random and used only once.|
|c_nonce_expires_in||integer||OPTIONAL. Value denoting the lifetime in seconds of the |
Credential Response for in-time flow - Non-normative example
Credential Response for deferred flow - Non-normative example
Deferred Credential Request
The Deferred Credential Request is used ONLY for the deferred flow. This request uses an acceptance token (from the Credential Response) as the only parameter, which MUST be sent in the HTTP header
Authorization as a
Deferred Credential Request - Non-normative example
HTTP POST /credential_deferred HTTP/1.1
Authorization: BEARER 8xLOxBtZp8
Deferred Credential Response
The Deferred Credential Response uses the same schema as the Credential Response see Credential Response. It must contain the properties format and credentials.
All implementations MUST support TLS, and a TLS server certificate MUST be performed.