Skip to main content
European CommissionEBSI European Blockchain

How to issue Verifiable Credentials

Last updated on

Context

This document defines Verifiable Credential Issuance specification in compliance with OpenID for Verifiable Credential Issuance (version: openid-4-verifiable-credential-issuance-1_0-10).

Glossary

TermAbbreviationDescription
Verifiable Credential IssuanceVCIProcess defined in this documentation
Relying PartyRPOAuth 2.0 compliant client
OpenID ProviderOPA 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 ProviderSIOPAn OpenID Provider (OP) used by the End-users to prove control over a cryptographically verifiable identifier. W3C refers to this as a Holder.
IssuerISSA 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 CredentialsOID4VCOpenID for Verifiable Credentials (OID4VC) defines an API and corresponding OAuth-based authorization mechanisms for issuance of Verifiable Credentials.
User AgentUAHolder Wallet or a Browser.
Verifiable CredentialVCA 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 SetJWKSA data structure that represents a set of JWKs as defined in RFC 7517.

Overview

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.

User authentication

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
ID Token

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.

The example shows only authentication part, which happens after Credential Offering has been received by Holder and Holder has completed discovery.

Client authentication

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.

The 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.

Credential Offering

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 (code or 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.

ParameterRequirementDescription
credential_issuerREQUIREDThe URL of the Credential Issuer, from which the Wallet is requested to obtain one or more credentials.
credentialsREQUIREDA JSON array, where every entry is a JSON object containing the data related to a certain credential type the Wallet MAY request.
credentials
.format
REQUIREDA JSON string identifying the format of this credential, e.g. jwt_vc.
credentials
.types
REQUIREDA JSON array designating the types a certain credential type supports according to VC_DATA, Section 4.3.
credentials
.trust_framework
OPTIONALA JSON object defining the Trust Framework details.
credentials
.trust_framework
.name
REQUIREDA JSON string defining the Trust Framework name the credential will be issued under.
credentials
.trust_framework
.type
REQUIREDA JSON string designating the behaviour and properties bound to the type. Types may include extra properties.
credentials
.trust_framework
.uri
OPTIONALA JSON string associated with the type field, having the value of Accreditation, which points to the Verifiable Accreditation that serves as the basis for the issuance of the Verifiable Credential.
grantsOPTIONALA 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 grants field is not present or empty, the Wallet MUST determine the Grant Types supported by the Credential Issuer's AS using the respective metadata. When multiple grants are present, it is at the Wallets discretion to decide which one to use.
grants
.authorization_code
OPTIONALA JSON object for authorisation code flow.
grants
.authorization_code
.issuer_state
OPTIONALA 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 issuer_state parameter value.
grants
.urn:ietf:params:oauth:grant-type:pre-authorized_code
OPTIONALA JSON object for the pre-authorised flow.
grants
.urn:ietf:params:oauth:grant-type:pre-authorized_code
.pre-authorized_code
REQUIREDThe 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.
grants
.urn:ietf:params:oauth:grant-type:pre-authorized_code
.user_pin_required
OPTIONALA 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_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fapi-conformance.ebsi.eu%2Fconformance%2Fv4%2Fissuer-mock%22%2C%22credentials%22%3A%5B%7B%22format%22%3A%22jwt_vc%22%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22VerifiableAttestation%22%2C%22CTWalletInTime%22%5D%2C%22trust_framework%22%3A%7B%22name%22%3A%22EBSI%22%2C%22type%22%3A%22Accreditation%22%2C%22id%22%3A%22https%3A%2F%2Fapi-pilot.ebsi.eu%2Ftrusted-issuers-registry%2Fv4%2Fissuers%2Fdid%3Aebsi%3AzZeKyEJfUTGwajhNyNX928z%2Fattributes%2F60ae46e4fe9adffe0bc83c5e5be825eafe6b5246676398cd1ac36b8999e058aa%22%7D%7D%5D%2C%22grants%22%3A%7B%22authorization_code%22%3A%7B%22issuer_state%22%3A%22tracker%3Dvcfghhj%22%7D%7D%7D
Credential Offering as URI reference - Non-normative example
HTTP 302 Location: openid-credential-offer://
?credential_offer_uri=https%3A%2F%2Fapi-conformance.ebsi.eu%2Fconformance%2Fv4%2Fissuer-mock%2Foffers%2Fzyx

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://
?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fapi-conformance.ebsi.eu%2Fconformance%2Fv4%2Fissuer-mock%22%2C%22credentials%22%3A%5B%7B%22format%22%3A%22jwt_vc%22%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22VerifiableAttestation%22%2C%22CTWalletInTime%22%5D%2C%22trust_framework%22%3A%7B%22name%22%3A%22EBSI%22%2C%22type%22%3A%22Accreditation%22%2C%22id%22%3A%22https%3A%2F%2Fapi-pilot.ebsi.eu%2Ftrusted-issuers-registry%2Fv4%2Fissuers%2Fdid%3Aebsi%3AzZeKyEJfUTGwajhNyNX928z%2Fattributes%2F60ae46e4fe9adffe0bc83c5e5be825eafe6b5246676398cd1ac36b8999e058aa%22%7D%7D%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22SplxlOBeZQQYbYS6WxSbIA%22%2C%22user_pin_required%22%3Atrue%7D%7D%7D

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.

Metadata configuration

Please see the Credential Issuer Metadata specifications for further details.

Authorisation Request

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:

ParameterDescription
response_typeValue MUST be set to code.
client_idREQUIRED. HTTPS URL for Service Wallets and DID for Holder Wallets.
redirect_uriREQUIRED. FQDN for redirection of the response
scopeREQUIRED. Must contain "openid"
issuer_stateCONDITIONAL: REQUIRED if Credential Offering contained issuer_state.
stateRECOMMENDED. 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
authorization_detailsREQUIRED. One to many Authorization Details objects below
authorization_details
.type
Determines the authorisation details type. MUST be set to openid_credential for the purpose of this specification
authorization_details
.locations
CONDITIONAL. If the Credential Issuer metadata contains an authorization_server parameter, the field MUST be set to the Credential Issuer's client_id value, which allows the AS to mint audience restricted access tokens.
authorization_details
.format
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.
authorization_details
.types
A JSON array designating the types a certain credential type supports according to VC_DATA, Section 4.3.
nonceRECOMMENDED. A value used to associate a Client session with an ID Token, and to mitigate replay attacks
code_challenge

CONDITIONAL: Only for Holder Wallets. In format of BASE64URL-ENCODE(SHA256(code_verifier as UTF-8 string))

code_verifier is client generated secure random, which will be used with token endpoint. It is between 43 and 128 characters long, and contains characters A-Z, a-z, 0-9, hyphen, period, underscore, and tilde. Please see RFC 7636
code_challenge_methodCONDITIONAL: 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".
client_metadataCONDITIONAL: 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?
client_id=https%3A%2F%2Fmy-issuer.eu%2Fsuffix%2Fxyz
&response_type=code
&scope=openid
&redirect_uri=https%3A%2F%2Fmy-issuer.eu%2Fsuffix%2Fxyz%2Fcode-cb
&request=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IkpwLTliRUk4dTY4LWU5NW1Ud25DRktLSWdUbkR1Ukhjb1RIYXpwWENKdDQifQ.eyJpc3MiOiJodHRwczovL215LWlzc3Vlci5ldS9zdWZmaXgveHl6IiwiYXVkIjoiaHR0cHM6Ly9hcGktY29uZm9ybWFuY2UuZWJzaS5ldS9jb25mb3JtYW5jZS92My9hdXRoLW1vY2siLCJleHAiOjE1ODk2OTg4MDAsInJlc3BvbnNlX3R5cGUiOiJjb2RlIiwic2NvcGUiOiJvcGVuaWQiLCJub25jZSI6Imdsa0ZGb2lzZGZFdWk0MyIsImNsaWVudF9pZCI6Imh0dHBzOi8vbXktaXNzdWVyLmV1L3N1ZmZpeC94eXoiLCJhdXRob3JpemF0aW9uX2RldGFpbHMiOlt7InR5cGUiOiJvcGVuaWRfY3JlZGVudGlhbCIsImZvcm1hdCI6Imp3dF92YyIsInR5cGVzIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVmVyaWZpYWJsZUF0dGVzdGF0aW9uIiwiVmVyaWZpYWJsZUF1dGhvcmlzYXRpb25Ub09uYm9hcmQiXX1dLCJyZWRpcmVjdF91cmkiOiJodHRwczovL215LWlzc3Vlci5ldS9zdWZmaXgveHl6L2NvZGUtY2IiLCJjbGllbnRfbWV0YWRhdGEiOnsiandrc191cmkiOiJodHRwczovL215LWlzc3Vlci5ldS9zdWZmaXgveHl6L2p3a3MiLCJhdXRob3JpemF0aW9uX2VuZHBvaW50Ijoib3BlbmlkOiJ9fQ.oYltoDJlhVFlww7D0QsbSGmfWt2_mRbLg-5DYccfPphc8mYW4oGMz4YjGLXokGcfwdB-jMIAzEkkQATMSq4kmQ

JWT Header:
{
typ: 'JWT',
alg: 'ES256',
kid: 'Jp-9bEI8u68-e95mTwnCFKKIgTnDuRHcoTHazpXCJt4'
}
JWT Payload:
{
iss: 'https://my-issuer.eu/suffix/xyz',
aud: 'https://api-conformance.ebsi.eu/conformance/v3/auth-mock',
exp: 1589698800,
response_type: 'code',
scope: 'openid',
nonce: 'glkFFoisdfEui43',
client_id: 'https://my-issuer.eu/suffix/xyz',
authorization_details: [
{
type: 'openid_credential',
format: 'jwt_vc',
locations: ['https://api-conformance.ebsi.eu/conformance/v3/issuer-mock'],
types: [
'VerifiableCredential',
'VerifiableAttestation',
'VerifiableAuthorisationToOnboard'
]
}
],
redirect_uri: 'https://my-issuer.eu/suffix/xyz/code-cb',
client_metadata: {
jwks_uri: 'https://my-issuer.eu/suffix/xyz/jwks',
authorization_endpoint: 'openid:'
}
}
Holder Wallet: Plain Authorisation Request
GET from https://api-conformance.ebsi.eu/conformance/v3/auth-mock/authorize
?response_type=code
&scope=openid
&issuer_state=tracker%3Dvcfghhj
&state=af0ifjsldkj
&client_id=did%3Akey%3Az2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbsEYvdrjxMjQ4tpnje9BDBTzuNDP3knn6qLZErzd4bJ5go2CChoPjd5GAH3zpFJP5fuwSk66U5Pq6EhF4nKnHzDnznEP8fX99nZGgwbAh1o7Gj1X52Tdhf7U4KTk66xsA5r
&authorization_details=%5B%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22jwt_vc%22%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22VerifiableAttestation%22%2C%22CTWalletInTime%22%5D%7D%5D
&redirect_uri=openid%3A
&nonce=glkFFoisdfEui43
&code_challenge=YjI0ZTQ4NTBhMzJmMmZhNjZkZDFkYzVhNzlhNGMyZDdjZDlkMTM4YTY4NjcyMTA5M2Q2OWQ3YjNjOGJlZDBlMSAgLQo%3D
&code_challenge_method=S256
&client_metadata=%7B%22vp_formats_supported%22%3A%7B%22jwt_vp%22%3A%7B%22alg%22%3A%5B%22ES256%22%5D%7D%2C%22jwt_vc%22%3A%7B%22alg%22%3A%5B%22ES256%22%5D%7D%7D%2C%22response_types_supported%22%3A%5B%22vp_token%22%2C%22id_token%22%5D%2C%22authorization_endpoint%22%3A%22openid%3A%22%7D

ID Token Request

ID Token

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 openid:.

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://
?client_id=https%3A%2F%2Fapi-conformance.ebsi.eu%2Fconformance%2Fv4%2Fauth-mock
&response_type=id_token
&scope=openid
&redirect_uri=https%3A%2F%2Fapi-conformance.ebsi.eu%2Fconformance%2Fv4%2Fauth-mock%2Fdirect_post
&request=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImM0S3JlcEpYem1CTVctcW8ybnREQ3drVGdMbTJDYl81ZWFiemtsalRoXzAifQ.eyJpc3MiOiJodHRwczovL2FwaS1jb25mb3JtYW5jZS5lYnNpLmV1L2NvbmZvcm1hbmNlL3YzL2F1dGgtbW9jayIsImF1ZCI6Imh0dHBzOi8vbXktaXNzdWVyLmV1L3N1ZmZpeC94eXoiLCJleHAiOjE1ODk2OTkxNjIsInJlc3BvbnNlX3R5cGUiOiJpZF90b2tlbiIsInJlc3BvbnNlX21vZGUiOiJkaXJlY3RfcG9zdCIsImNsaWVudF9pZCI6Imh0dHBzOi8vYXBpLWNvbmZvcm1hbmNlLmVic2kuZXUvY29uZm9ybWFuY2UvdjMvYXV0aC1tb2NrIiwicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9hcGktY29uZm9ybWFuY2UuZWJzaS5ldS9jb25mb3JtYW5jZS92My9hdXRoLW1vY2svZGlyZWN0X3Bvc3QiLCJzY29wZSI6Im9wZW5pZCIsInN0YXRlIjoiNDhhMmJhYzYtMTMwYS00Mzc4LWJjYzItMDRlYjU3YzU0M2I5Iiwibm9uY2UiOiJuLTBTNl9XekEyTWoifQ.d-3D3w99BvRq_N1tmUaDwlaG8oGnOiA4mVs1Cgp1USc1Yhf8TN9G8Vt_SO_LmJGspuqST8ESwUUkmYvXOYj5Pw

JWT Header:
{
typ: 'JWT',
alg: 'ES256',
kid: 'c4KrepJXzmBMW-qo2ntDCwkTgLm2Cb_5eabzkljTh_0'
}
JWT Payload:
{
iss: 'https://api-conformance.ebsi.eu/conformance/v3/auth-mock',
aud: 'https://my-issuer.eu/suffix/xyz',
exp: 1589699162,
response_type: 'id_token',
response_mode: 'direct_post',
client_id: 'https://api-conformance.ebsi.eu/conformance/v3/auth-mock',
redirect_uri: 'https://api-conformance.ebsi.eu/conformance/v3/auth-mock/direct_post',
scope: 'openid',
state: '48a2bac6-130a-4378-bcc2-04eb57c543b9',
nonce: 'n-0S6_WzA2Mj'
}

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.

The 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.

caution

Please note that the ID Token Response's state is different from the Authorisation Request's state.

Auth Mock server responds to the /auth-mock/authorize request with an ID Token request that has a new random state, it doesn’t reuse the state from the initial Authorisation request.

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
Content-Type: application/x-www-form-urlencoded

id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDplYnNpOnpkUGoxR1BYamZFUlh4WFBFMVlUWWRKIzdqM1RwYU5kUE5UT3pPdG91T09rbmxPTFFrM0pQLXlrVGZyYVd0WTNHTUUifQ.eyJpc3MiOiJkaWQ6ZWJzaTp6ZFBqMUdQWGpmRVJYeFhQRTFZVFlkSiIsInN1YiI6ImRpZDplYnNpOnpkUGoxR1BYamZFUlh4WFBFMVlUWWRKIiwiYXVkIjoiaHR0cHM6Ly9hcGktY29uZm9ybWFuY2UuZWJzaS5ldS9jb25mb3JtYW5jZS92My9hdXRoLW1vY2siLCJleHAiOjE1ODk2OTkzNjAsImlhdCI6MTU4OTY5OTI2MCwic3RhdGUiOiI0OGEyYmFjNi0xMzBhLTQzNzgtYmNjMi0wNGViNTdjNTQzYjkiLCJub25jZSI6Im4tMFM2X1d6QTJNaiJ9.NjkSEc6RXbcALTSTV5kHL-KdBXPzWTdvEPxUgjSuSqASWQpsK0i2PxDcpSAMwcXNoYY-Gq15R7ui97YFBZYN7Q

JWT Header:
{
typ: 'JWT',
alg: 'ES256',
kid: 'did:ebsi:zdPj1GPXjfERXxXPE1YTYdJ#7j3TpaNdPNTOzOtouOOknlOLQk3JP-ykTfraWtY3GME'
}
JWT Payload:
{
iss: 'did:ebsi:zdPj1GPXjfERXxXPE1YTYdJ',
sub: 'did:ebsi:zdPj1GPXjfERXxXPE1YTYdJ',
aud: 'https://api-conformance.ebsi.eu/conformance/v3/auth-mock',
exp: 1589699360,
iat: 1589699260,
state: '48a2bac6-130a-4378-bcc2-04eb57c543b9',
nonce: 'n-0S6_WzA2Mj'
}

The state parameter management

The following sequence diagram illustrates the management of the state parameter between the Wallet and the Authorisation Server.

Authorisation Response

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:

ParameterDescription
redirect_uriREQUIRED. Redirection URI to the wallet.
codeREQUIRED. A query parameter in the URI.
stateREQUIRED. A query parameter in the URI.
Authorization Response - Non-normative example
HTTP/1.1 302 Found
Location: <redirect_uri>?
code=SplxlOBeZQQYbYS6WxSbIA
&state=af0ifjsldkj

Note: the client MUST check that the state value matches the state value from the Authorisation Request (to prevent CSRF).

Token Request

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 client_assertion_type of urn:ietf:params:oauth:client-assertion-type:jwt-bearer, while Holder Wallets are authenticated with PKCE's code_verifier.

The Token Request schema is defined in the following table:

ParameterDescription
grant_typeREQUIRED. MUST be authorisation_code or pre-authorised_code.
client_idREQUIRED. HTTPS URL for Service Wallets and DID for Holder Wallets.
codeREQUIRED. MUST be the Authorisation code from the Authorisation Response.
redirect_uriREQUIRED. Redirection URL to the wallet.
pre-authorised_code

CONDITIONAL & REQUIRED. Only used with pre-authorised_code. Code received in the Pre-Authorisation flow.

code parameter cannot co-exist in the same request.
user_pinCONDITIONAL & OPTIONAL. Only used with pre-authorised_code. Maximum of 8 numbers (0-9).
client_assertionOPTIONAL. A minimal self signed JWT for jwt-bearer.
client_assertion_typeOPTIONAL. MUST be urn:ietf:params:oauth:client-assertion-type:jwt-bearer.
code_verifierOPTIONAL. Wallet generated secure random token, used to validate the original code_challenge provided in initial Authorisation Request.
Token Request - Non-normative example
POST into https://api-conformance.ebsi.eu/conformance/v3/auth-mock/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&client_id=https%3A%2F%2Fmy-issuer.eu%2Fsuffix%2Fxyz
&code=SplxlOBeZQQYbYS6WxSbIA
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IkpwLTliRUk4dTY4LWU5NW1Ud25DRktLSWdUbkR1Ukhjb1RIYXpwWENKdDQifQ.eyJpc3MiOiJodHRwczovL215LWlzc3Vlci5ldS9zdWZmaXgveHl6Iiwic3ViIjoiaHR0cHM6Ly9teS1pc3N1ZXIuZXUvc3VmZml4L3h5eiIsImF1ZCI6Imh0dHBzOi8vYXBpLWNvbmZvcm1hbmNlLmVic2kuZXUvY29uZm9ybWFuY2UvdjMvYXV0aC1tb2NrIiwianRpIjoiZjJlODFjOTYtZTA4ZS00MzhlLTk1YmMtOGI3ZDA5ZGI2Y2IxIiwiZXhwIjoxNTg5Njk5NjYwLCJpYXQiOjE1ODk2OTk1NjB9.ep0iemeZGl4vPUKXGx7GP1pkE6GiJ8tZyChTHrztjj_641omRE_pC1n43dwYSPNnMknQfsndOWHjaKLmI7qTGg

JWT Header:
{
typ: 'JWT',
alg: 'ES256',
kid: 'Jp-9bEI8u68-e95mTwnCFKKIgTnDuRHcoTHazpXCJt4'
}
JWT Payload:
{
iss: 'https://my-issuer.eu/suffix/xyz',
sub: 'https://my-issuer.eu/suffix/xyz',
aud: 'https://api-conformance.ebsi.eu/conformance/v3/auth-mock',
jti: 'f2e81c96-e08e-438e-95bc-8b7d09db6cb1',
exp: 1589699660,
iat: 1589699560
}

Token Response

In addition to an access_token and 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:

ParameterDescription
access_tokenREQUIRED. The token is later used for the issuance of the actual Verifiable Credential.
id_tokenREQUIRED. The token that contains the user's identifier as Subject Identifier.
token_typeREQUIRED. Value of the token type MUST be bearer.
expires_inREQUIRED. Value denoting the lifetime in seconds of the token.
c_nonceREQUIRED. 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_inOPTIONAL. Value denoting the lifetime in seconds of the c_nonce.
Token Response - Non-normative example
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6Ikp..sHQ",
"token_type": "bearer",
"expires_in": 86400,
"id_token": "eyJodHRwOi8vbWF0dHIvdGVuYW50L..3Mz",
"c_nonce": "PAPPf3h9lexTv3WYHZx8ajTe",
"c_nonce_expires_in": 86400
}

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.

Credential Request

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.

Multiple entires

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:

ParameterValueDescription
typesstringREQUIRED. The type definition, identifying the Verifiable Credential.
formatstringOPTIONAL. The format in which the Verifiable Credential should be issued. It MUST be jwt_vc or ldp_vc.
proofProof objectCONDITIONAL. An object containing proof of possession of the key material. The proof is generated based on the c_nonce from Token Response.

Proof

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.

ID Token

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.

ParameterValueDescription
proof_typestringREQUIRED. JSON String denoting the proof type. It MUST be "jwt".
jwtSigned JWTCONDITIONAL, when proof_type is jwt. Objects of this type contain a single jwt element with a signed JWT as proof of possession.

The signed JWT proof must contain the following parameters:

LocationParameterValueDescription
HeadertypstringREQUIRED. Must be openid4vci-proof+jwt.
HeaderalgstringREQUIRED. Must be the algorithm used to sign the JWT.
HeaderkidstringREQUIRED. Must be DID URI which identifies a particular key in the DID document that the credential shall be bound to.
BodyissstringREQUIRED. MUST be the client_id of the sender.
BodyaudstringREQUIRED. MUST be the client_id of the Credential Issuer.
BodyiatnumberREQUIRED. MUST be the instant when the proof was created
BodyexpnumberREQUIRED. MUST be the instant when the proof will expire.
BodynoncestringREQUIRED. MUST be Token Response c_nonce as provided by the issuer.
Credential Request - Non-normative example
POST into https://api-conformance.ebsi.eu/conformance/v3/issuer-mock/credentials
Content-Type: application/json
Authorization: BEARER eyJhbGciOiJSUzI1NiIsInR5cCI6Ikp..sHQ

{
types: [
'VerifiableCredential',
'VerifiableAttestation',
'VerifiableAuthorisationToOnboard'
],
format: 'jwt_vc',
proof: {
proof_type: 'jwt',
jwt: 'eyJ0eXAiOiJvcGVuaWQ0dmNpLXByb29mK2p3dCIsImFsZyI6IkVTMjU2Iiwia2lkIjoiZGlkOmVic2k6emRQajFHUFhqZkVSWHhYUEUxWVRZZEojN2ozVHBhTmRQTlRPek90b3VPT2tubE9MUWszSlAteWtUZnJhV3RZM0dNRSJ9.eyJpc3MiOiJkaWQ6ZWJzaTp6ZFBqMUdQWGpmRVJYeFhQRTFZVFlkSiIsImF1ZCI6Imh0dHBzOi8vYXBpLWNvbmZvcm1hbmNlLmVic2kuZXUvY29uZm9ybWFuY2UvdjMvaXNzdWVyLW1vY2siLCJpYXQiOjE1ODk2OTk1NjIsImV4cCI6MTU4OTY5OTk2Miwibm9uY2UiOiJQQVBQZjNoOWxleFR2M1dZSFp4OGFqVGUifQ.MbVwusjJsHbhYa-X2opfrahelrJ_ksIK2KKWq8rEiaZEdxIYVN1s4toIKDWoO7j5cJ5AU9HW2cii_-ZWaj3MeA'
}
}

JWT Header:
{
typ: 'openid4vci-proof+jwt',
alg: 'ES256',
kid: 'did:ebsi:zdPj1GPXjfERXxXPE1YTYdJ#7j3TpaNdPNTOzOtouOOknlOLQk3JP-ykTfraWtY3GME'
}
JWT Payload:
{
iss: 'did:ebsi:zdPj1GPXjfERXxXPE1YTYdJ',
aud: 'https://api-conformance.ebsi.eu/conformance/v3/issuer-mock',
iat: 1589699562,
exp: 1589699962,
nonce: 'PAPPf3h9lexTv3WYHZx8ajTe'
}

Credential Response

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 acceptance_token. A c_nonce is always returned for new credential requests.

The Credential Response schema is defined in the following table:

ParameterValueDescription
formatstringCONDITIONAL. The format in which the Verifiable Credential was issued. It MUST be jwt_vc or ldp_vc.
credentialstringCONDITIONAL. Issued Verifiable Credentials in a format indicated by format parameter
acceptance_tokenstringCONDITIONAL. A string containing a token that can be later used to obtain Verifiable Credentials (deferred flow).
c_noncestringOPTIONAL. 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_inintegerOPTIONAL. Value denoting the lifetime in seconds of the c_nonce.
Credential Response for in-time flow - Non-normative example
{
"format": "jwt_vc",
"credential": "LUpixVCWJk0eOt4CXQe1NXK....WZwmhmn9OQp6YxX0a2L",
"c_nonce": "fGFF7UkhLa",
"c_nonce_expires_in": 86400
}
Credential Response for deferred flow - Non-normative example
{
"acceptance_token": "eyJ0eXAiOiJKV1QiLCJhbGci..zaEhOOXcifQ",
"c_nonce": "wlbQc6pCJp",
"c_nonce_expires_in": 86400
}

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 bearer type.

Deferred Credential Request - Non-normative example
HTTP POST /credential_deferred HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
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.

Security Considerations

All implementations MUST support TLS, and a TLS server certificate MUST be performed.

OIDC Security Considerations

Nonce implementation notes