Skip to main content
European CommissionEBSI European Blockchain
Access Help Desk(opens in a new tab)

Credential Issuance Guidelines

Context

This document defines Credential Issuance flows in compliance with OIDC 4 Verifiable Credential Issuance (version: openid-connect-4-verifiable-credential-issuance-1_0-05).

Verifiable Credential Lifecycle - OpenID Connect

Assumptions and boundaries:

  • The user has a single User-Agent installed on her device.
  • The User-Agent supports the openid:// custom scheme.
  • The User-Agent can generate secure random values.
  • The User-Agent can store values in session memory.
  • openid:// custom scheme is used. The trust-framework-specific scheme is not yet considered.
  • Flows describe only the "happy scenarios".

Verifiable Presentation exchange is described in the Verifiable Presentation exchange guidelines

Glossary

TermAbbreviationDescription
Relying PartyRPA service requiring end-user authentication and claims from a (Self-Issued) OpenID Provider. W3C refers to this as Verifier.
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) is used by the End-users to prove control over a cryptographically verifiable identifier. W3C refers to this as a Holder.
IssuerA 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.
AuthenticationAuthN
AuthorizationAuthZ
OpenID ConnectOIDCOpenID Connect (OIDC) is a simple identity layer on top of the OAuth 2.0 protocol.
User AgentSSI wallet, Holder Wallet
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.

Overview

Below we present a high-level action diagram:

VC issuance diagram

PlantUML diagram code
@startuml credential-issuance-v2

title High-level overview

:Issuance initiation;
:User authentication with issuer;
:User authorised for credentials;
:User consents with credential issuance;
:Create VC and give it to Holder wallet;

@enduml

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, issuer is known in advance). She clicks on this link and is being sent to her digital wallet. The wallet notifies her that an issuer offered to issue a diploma credential. She confirms this inquiry and is being sent to the university's credential issuance service. She logs in with her university login (login with the existing method) and is being asked to consent to the creation of a digital diploma. She confirms and is sent back to her wallet (user consent). There she is notified of the successful creation of the digital diploma (real-time VC issuance).

Steps of the flow can also be seen in a sequence diagram below:

Issuer initiated credential issuance flow

PlantUML diagram code
@startuml credential-issuance-v2
title Issuer initiated credential issuance flow

participant "User" as u
participant "Holder Wallet" as w
participant "Credential Issuer" as ci

autonumber

u -> ci: User clicks a link to issue a VC on issuer's web page
ci -> w: Initiate Issuance Request (QR code or redirect)
w -> u: Notify that issuer offered to issue a VC
u -> w: Consent
w -> ci: Open login page with\nwith Initiate Issuance details (Authorize)
u -> ci: Login to issuer's page
w -> ci: Token Request
ci -> w: Token Response
w -> u: Confirm to continue with Credentials creation
u -> w: Consent Credentials creation
w -> ci: Credential Request
ci -> w: Credential Response (receive VC)
w -> u: VC was successfully created and stored

Issuance initiation

Any issuance initiation scenario mentioned above must support two models of interaction:

  • Same-Device model: The user has a Holder wallet installed on the same device she uses to visit a credential issuer's website.

    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 on a different device than the one she uses to visit a credential issuer's website.

    Example: Alice has her SSI wallet installed on her smartphone, and she 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 Agent.

In contrast to same-device scenarios, neither the Issuer nor the SSI wallet can communicate with each other via HTTP redirects through an SSI wallet. The protocol flow is therefore modified as follows:

  1. The Issuer prepares Initiate Issuance Request and renders it as a QR code.
  2. The user scans the QR code with her SSI wallet's QR code scanner or smartphone's camera app (in the latter case, the standard mechanisms for invoking the SSI wallet are used on the smartphone - based on the openid:// custom scheme).
  3. The SSI wallet processes the request.
  4. Upon completion of the request, the SSI wallet directly sends an HTTP GET request with the response to an endpoint exposed by the Issuer.

QR code method is mainly discussed as a mechanism to initiate a cross-device protocol flow, however, other means to initiate a cross-device flow are possible.

Flow model

Find more technical details on same-device and cross-device flows here: Verifiable Presentation exchange guidelines in the Verifiable Presentation exchange scenarios section.

User authentication

Users can authenticate via different means

  • via existing issuer's authentication service
  • by presenting a verifiable credential that is recognised by the issuer and can be used for authentication means

Details of the issuer-initiated flow

Issuers might want to kick-start the Credentials Issuance process, for this, the wallet must implement the initiate_issuance endpoint. The wallet's endpoint will start the authorisation code flow process if the issuer is trustworthy. The issuance initiation works only as an instruction for the wallet to start the process automatically.

Initiate Issuance Request

Note: As this is a wallet schema/endpoint handler, the wallet doesn't form any response to this and keeps the UX control in the wallet itself. This can also be consumed as a QR code. The wallet can continue the authorisation code flow with the given information to obtain access and later credentials.

ParameterValueDescription
issuerstringREQUIRED. The issuer URL of the credential issuer, the wallet is requested to obtain one or more credentials from
credential_typeuri encoded multi-value stringCONDITIONAL. A JSON string denoting the type of credential the wallet shall request. MUST be present if manifest_id is not present
manifest_iduri encoded multi-value stringCONDITIONAL. A JSON string referring to a credential manifests published by the credential issuer. MUST be present if credential_type is not present.
op_statestringOPTIONAL. String value created by the Credential Issuer and opaque to the wallet that is used to bind the subsequent authentication request with the Credential Issuer to a context set up during previous steps. If the client receives a value for this parameter, it MUST include it in the subsequent Authentication Request to the Credential Issuer as the op_state parameter value.
pre-authorized_codestringCONDITIONAL. Only used when Pre-Authorized Code Flow is used. Contains the code, which can be used to get access token from the token endpoint.
user_pin_requiredbooleanCONDITIONAL. Only used when Pre-Authorized Code Flow is used. Instruction to send user_pin into the token endpoint.
Initiate Issuance Request - Non-normative example
HTTP/1.1 302 Found
Location: openid://initiate_issuance?
issuer=https%3A%2F%2Fserver%2Eexample%2Ecom
&credential_type=https%3A//api-pilot.ebsi.eu/trusted-schemas-registry/v2/schemas/0x1ee207961aba4a8ba018bacf3aaa338df9884d52e993468297e775a585abe4d8
&op_state=eyJhbGciOiJSU0Et...FYUaBy

Pre-Authorised Code Flow

The issuer can also skip the authentication and use pre-authorised flow. This is intended for use cases where the issuer's website ultimately results in one or more credentials to be shared. Pre-authorisation will also use the same Initiate Issuance Request and endpoint but will add the mandatory pre-authorized_code and optional user_pin_required parameters into the request. User PIN code must be delivered through other channels, as the QR code or request can be intercepted.

Initiate Issuance Request - Non-normative example
------
INITIATE ISSUANCE REQUEST
------
HTTP/1.1 302 Found
Location: openid://initiate_issuance?
issuer=https%3A%2F%2Fserver%2Eexample%2Ecom
&credential_type=https%3A//api-pilot.ebsi.eu/trusted-schemas-registry/v2/schemas/0x1ee207961aba4a8ba018bacf3aaa338df9884d52e993468297e775a585abe4d8
&pre-authorized_code=SplxlOBeZQQYbYS6WxSbIA
&user_pin_required=true

------
ACCESS TOKEN REQUEST
------

HTTP POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0Mzo=

grant_type=pre-authorized_code
&pre-authorized_code=SplxlOBeZQQYbYS6WxSbIA
&user_pin=94091238

Access with Authorisation Code Flow

Authorisation Code Flow is either started by the user from the wallet or continued by the wallet for the Initiate Issuance Request. The documentation is about the Native client variant of the Authorization Code Flow, which uses PKCE for further security. Native wallet app must have issuer assigned client id for the communications.

Authorisation Request

Authorisation Request builds upon the OAuth 2.0 Rich Authorization Request. The user specifies which types of Verifiable Credentials he/she is requesting using the Authorization Details, which is described in the following table:

Authorization Request

ParameterValueDescription
response_typestringValue MUST be set to code.
client_idstringREQUIRED. The client identifier
redirect_uristringREQUIRED. FQDN for redirection of the response
scopestringREQUIRED. Must contain "openid"
statestringRECOMMENDED. 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_detailsuri encodedJSON arrayof objectsREQUIRED. One to many Authorization Details objects below
request_uristringCONDITIONAL: Only used with Push Authentication Request. Other parameters are not used with PAR.
code_challengestring

OPTIONAL: In format of BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

code_verifier is client generated secure random, which will be used with token endpoint.
code_challenge_methodstringOPTIONAL: If the client is capable of using "S256", it MUST use "S256". Else "plain" can only be used if they cannot support "S256".

Authorisation Details

ParameterValueDescription
typeJSON stringREQUIRED. Determines the authorisation details type. MUST be set to openid_credential for the purpose of this specification
credential_typeJSON stringCONDITIONAL. Denoting the type of the requested credential. MUST be present if manifest_id is not present
manifest_idJSON stringCONDITIONAL. Referring to a credential manifest published by the credential issuer. MUST be present if the type is not present.Manifest can only be used if published by the Server Metadata.
formatJSON stringOPTIONAL. 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.
locationsarray of JSON stringsOPTIONAL. Allows a client to specify the location of the resource server(s), allowing the AS to mint audience restricted access tokens
Authorisation Details example
Claim to request a Natural Person Verifiable ID - Non-normative example
[
{
"type": "openid_credential",
"credential_type": "https://api-pilot.ebsi.eu/trusted-schemas-registry/v2/schemas/0x1ee207961aba4a8ba018bacf3aaa338df9884d52e993468297e775a585abe4d8",
"format": "jwt_vc"
}
]
Push Authorisation Request (PAR)

It is recommended to use PAR as an authorisation request. It will overcome the URI limitations and ensures confidentiality, integrity, and authenticity of the request data. Authorisation Request is inside the authorization_details form field. PAR starts from its own endpoint and will continue into the authorised endpoint.

Push Authorization Request - Non-normative example
PAR REQUEST
----
POST /par HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded

response_type=code
&client_id=client_id_for_issuer
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
&authorization_details=%5B%7B%22type%22%3A%22openid_credential...abe4d8%22%2C%22format%22%3A%22jwt_vc%22%7D%5D
&redirect_uri=https%3A%2F%2Fwallet.example.org%2Fcb

--------
PAR RESPONSE
--------

HTTP/1.1 201 Created
Content-Type: application/json

{
"request_uri": "urn:example:bwc4JK-ESC0w8acc191e-Y1LTC2",
"expires_in": 3600
}


-------
AUTHORIZATION
-------

GET /authorize?request_uri=urn:example:bwc4JK-ESC0w8acc191e-Y1LTC2 HTTP/1.1
Host: server.example.com
Plain Authorisation Request

Another option is to use older Authorization Code Flow with authorisation details. Authorisation Request is inside the authorization_details form field.

Plain Authorization Request - Non-normative example
Redirect or GET
https://server.example.com/authorize?
response_type=code
&client_id=<wallet-specific-client-id>
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
&authorization_details=%5B%7B%22type%22%3A%22openid_credential...abe4d8%22%2C%22format%22%3A%22jwt_vc%22%7D%5D
&redirect_uri=https%3A%2F%2Fwallet.example.org%2Fcb

Authorisation Response

Authorisation Response builds upon the OIDC Authentication Response defined in OIDC Authentication Response.

The authorisation Response schema is defined in the following table:

ParameterValueDescription
redirect_uristringREQUIRED. Redirection URI to the wallet.
codestringREQUIRED. Query parameter.
statestringREQUIRED. Query parameter.
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

Token Request is defined in the OIDC Token Request. Authorisation code (from Authorisation Response) is exchanged for an Access Token and an ID Token.

Token Request schema is defined in the following table:

ParameterValueDescription
grant_typestringREQUIRED. MUST be authorisation_code or pre-authorised_code.
codestringREQUIRED. MUST be the Authorisation code from the Authorisation Response.
redirect_uristringREQUIRED. Redirection URL to the wallet.
code_verifierstringCONDITIONAL: required if client is "public/native mobile" and if authorise endpoint was start with code_challenge.REQUIRED for conformance testing.Wallet generated secure random token, used to validate authorisation original code_challenge.
pre-authorised_codestring

CONDITIONAL & REQUIRED. Only used when Pre-Authorised Code Flow is used. Code received in the Pre-Authorisation flow.

code parameter cannot co-exist in the same request.
user_pinstringCONDITIONAL & OPTIONAL. Only used when Pre-Authorised Code Flow is used. Maximum of 8 numbers (0-9).
Token Request - Non-normative example
HTTP POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
&redirect_uri=https%3A%2F%2Fwallet.example.org%2Fcb

Token Response

Token Response is defined in the OIDC Token Response. In addition to the parameters defined in the OIDC, an additional top-level property c_none MUST be added.

Token Response schema is defined in the following table:

ParameterValueDescription
access_tokenstringREQUIRED. The token is later used for the issuance of the actual Verifiable Credential.
id_tokenstringREQUIRED. The token that contains the user's identifier as Subject Identifier.
token_typestringREQUIRED. Value of the token type MUST be bearer.
expires_inintegerREQUIRED. Value denoting the lifetime in seconds of the token.
c_noncestringREQUIRED. 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_inintegerOPTIONAL. Value denoting the lifetime in seconds of the c_nonce.
authorization_pendingbooleanCONDITIONAL & OPTIONAL. Only used when Pre-Authorized Code Flow is used.The token request is still pending as the issuer is waiting for end user end user interaction to complete. The client SHOULD repeat the token request. Before each new request, the client MUST wait at least the number of seconds specified by the interval response parameter.
intervalintegerCONDITIONAL & OPTIONAL. Only used when Pre-Authorized Code Flow is used.The minimum amount of time in seconds that the client SHOULD wait between polling requests to the token endpoint. If no value is provided, clients MUST use 5 as the default.
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
}

Requesting Credentials with Access

Credential Request

A credential request is sent to request the issuance of an actual Verifiable Credential. The user can request Verifiable Credentials requested in the Authorisation Request (if defined).

The credential Request schema is defined in the following table:

ParameterValueDescription
typestringREQUIRED. URI to the schema of requested Verifiable Credential. The URI MUST point to a schema in the Trusted Schema Registry.
formatstringOPTIONAL. The format in which the Verifiable Credential should be issued. It MUST be jwt_vc or ldp_vc.
proofProof objectOPTIONAL. An object containing proof of possession of the key material. The proof is generated based on the c_nonce from Token Response.

Proof

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

Signed JWT proof must contain the following parameters:

LocationParameterValueDescription
HeaderkidstringCONDITIONAL. JWT header containing the key ID. If the credential shall be bound to a DID, the kid refers to a DID URL which identifies a particular key in the DID document that the credential shall be bound to.
HeaderjwkJWK objectCONDITIONAL. JWT header containing the key material the new credential shall be bound to. MUST NOT be present if kid is present.REQUIRED for EBSI DID Method for Natural Persons.
BodyissstringREQUIRED. MUST contain the client_id of the sender.
BodyaudstringREQUIRED. MUST contain the issuer URL of the credential issuer.
BodyiatnumberREQUIRED. MUST contain the instant when the proof was created.
BodynoncestringREQUIRED. MUST be Token Response c_nonce as provided by the issuer.
Credential Request data - Non-normative example

{
"type": "https://api-pilot.ebsi.eu/trusted-schemas-registry/v2/schemas/0x1ee207961aba4a8ba018bacf3aaa338df9884d52e993468297e775a585abe4d8",
"format": "jwt_vc",
"proof": {
"proof_type": "jwt",
"jwt": "eyJraWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEva2V5cy8
xIiwiYWxnIjoiRVMyNTYiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJzNkJoZFJrcXQzIiwiYXVkIjoiaHR
0cHM6Ly9zZXJ2ZXIuZXhhbXBsZS5jb20iLCJpYXQiOiIyMDE4LTA5LTE0VDIxOjE5OjEwWiIsIm5vbm
NlIjoidFppZ25zbkZicCJ9.ewdkIkPV50iOeBUqMXCC_aZKPxgihac0aW9EkL1nOzM"
}
}
Decoded proof JWT - Non-normative example
{
"alg": "ES256",
"typ": "JWT",
"kid":"did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1"
}.
{
"iss": "s6BhdRkqt3",
"aud": "https://server.example.com",
"iat": "2018-09-14T21:19:10Z",
"nonce": "tZignsnFbp"
}
Credential Request - Non-normative example
HTTP POST /credential HTTP/1.1
Host: server.example.com
Content-Type: application/json
Authorization: BEARER <ACCESS TOKEN>

{
"type": "https://api-pilot.ebsi.eu/trusted-schemas-registry/v2/schemas/0x1ee207961aba4a8ba018bacf3aaa338df9884d52e993468297e775a585abe4d8",
"format": "jwt_vc",
"proof": {
"proof_type": "jwt",
"jwt": "eyJraWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEva2V5cy8
xIiwiYWxnIjoiRVMyNTYiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJzNkJoZFJrcXQzIiwiYXVkIjoiaHR
0cHM6Ly9zZXJ2ZXIuZXhhbXBsZS5jb20iLCJpYXQiOiIyMDE4LTA5LTE0VDIxOjE5OjEwWiIsIm5vbm
NlIjoidFppZ25zbkZicCJ9.ewdkIkPV50iOeBUqMXCC_aZKPxgihac0aW9EkL1nOzM"
}
}

Credential Response

As described in the first section of this page, Verifiable Credentials can be issued in two ways: immediately (or synchronous) or deferred. Therefore, there are two types of Credential Response: synchronous containing format and credential properties; deferred containing acceptance_token.

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 in synchronous flow - Non-normative example
{
"format": "jwt_vc",
"credential": "LUpixVCWJk0eOt4CXQe1NXK....WZwmhmn9OQp6YxX0a2L",
"c_nonce": "fGFF7UkhLa",
"c_nonce_expires_in": 86400
}
Credential Response in deferred flow - Non-normative example
{
"acceptance_token": "8xLOxBtZp8",
"c_nonce": "wlbQc6pCJp",
"c_nonce_expires_in": 86400
}

Deferred Credential Request

Deferred Credential Request is used ONLY in the deferred flow. This request uses an acceptance token (from Credential Response) as the only parameter, which MUST be sent as an access token in the HTTP header.

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

Deferred Credential Response uses the same schema as Credential Response (see Credential Response). Properties format and credentials MUST be contained.

Security Considerations

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

OIDC Security Considerations

Nonce implementation notes

References