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
Term | Abbreviation | Description |
---|---|---|
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 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
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.
Parameter | Requirement | Description |
---|---|---|
credential_issuer | REQUIRED | The URL of the Credential Issuer, from which the Wallet is requested to obtain one or more credentials. |
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. |
credentials .format | REQUIRED | A JSON string identifying the format of this credential, e.g. jwt_vc . |
credentials .types | REQUIRED | A JSON array designating the types a certain credential type supports according to VC_DATA , Section 4.3. |
credentials .trust_framework | OPTIONAL | A JSON object defining the Trust Framework details. |
credentials .trust_framework .name | REQUIRED | A JSON string defining the Trust Framework name the credential will be issued under. |
credentials .trust_framework .type | REQUIRED | A JSON string designating the behaviour and properties bound to the type. Types may include extra properties. |
credentials .trust_framework .uri | OPTIONAL | A 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. |
grants | 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 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 | OPTIONAL | A JSON object for authorisation code flow. |
grants .authorization_code .issuer_state | 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 issuer_state parameter value. |
grants .urn:ietf:params:oauth:grant-type:pre-authorized_code | OPTIONAL | A JSON object for the pre-authorised flow. |
grants .urn:ietf:params:oauth:grant-type:pre-authorized_code .pre-authorized_code | 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. |
grants .urn:ietf:params:oauth:grant-type:pre-authorized_code .user_pin_required | 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_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:
Parameter | Description |
---|---|
response_type | Value MUST be set to code . |
client_id | REQUIRED. HTTPS URL for Service Wallets and DID for Holder Wallets. |
redirect_uri | REQUIRED. FQDN for redirection of the response |
scope | REQUIRED. Must contain "openid" |
issuer_state | CONDITIONAL: REQUIRED if Credential Offering contained issuer_state . |
state | 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 |
authorization_details | REQUIRED. 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. |
nonce | RECOMMENDED. 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_method | 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". |
client_metadata | 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?
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
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.
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'
}
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:
Parameter | Description |
---|---|
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
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:
Parameter | Description |
---|---|
grant_type | REQUIRED. MUST be authorisation_code or pre-authorised_code . |
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. |
pre-authorised_code | CONDITIONAL & REQUIRED. Only used with code parameter cannot co-exist in the same request. |
user_pin | CONDITIONAL & OPTIONAL. Only used with pre-authorised_code . Maximum of 8 numbers (0-9). |
client_assertion | OPTIONAL. A minimal self signed JWT for jwt-bearer . |
client_assertion_type | OPTIONAL. MUST be urn:ietf:params:oauth:client-assertion-type:jwt-bearer . |
code_verifier | OPTIONAL. 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:
Parameter | Description |
---|---|
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 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.
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:
Parameter | Value | Description |
---|---|---|
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 jwt_vc or ldp_vc . |
proof | Proof object | CONDITIONAL. 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.
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.
Parameter | Value | Description |
---|---|---|
proof_type | string | REQUIRED. JSON String denoting the proof type. It MUST be "jwt". |
jwt | Signed JWT | CONDITIONAL, 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:
Location | Parameter | Value | Description |
---|---|---|---|
Header | typ | string | REQUIRED. Must be openid4vci-proof+jwt . |
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 client_id of the sender. |
Body | aud | string | REQUIRED. MUST be the client_id of the Credential Issuer. |
Body | iat | number | REQUIRED. MUST be the instant when the proof was created |
Body | exp | number | REQUIRED. MUST be the instant when the proof will expire. |
Body | nonce | string | REQUIRED. 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:
Parameter | Value | Description |
---|---|---|
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 format parameter |
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 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.