Context
This document defines Verifiable Presentation (VP) exchanges in compliance with OpenID for Verifiable Presentations (version: openid-4-verifiable-presentations-1_0-13). Unlike the flow described in the OpenID for Verifiable Presentations, where the flow is typically initiated by the Verifier, this document details a flow initiated by the Wallet. This approach eliminates the need for the Wallet to expose external endpoints.
In this flow, the VP Token Request is sent to the Wallet as a redirect location URI in response to the Wallet's Authorise Request to the Verifier. The VP is a JSON Web Token (JWT) that is signed using the Holder's private key, which matches the authentication key specified in the Holder's DID document. This signature proves that the Holder controls the private key, as only the Holder of the private key can generate a valid signature for the JWT.
The processes for Authorisation Request and Response, Token Request and Response, and Credential Request and Response are semantically the same with in-time flow of Verifiable Credential Issuance. The direct_post
method must be executed according to the provided redirect_uri
.
VP Token Request
The VP Token Request is initiated by the Authorisation Server (AS) to the Wallet and triggers the VP token exchange. The redirect URI schema is registered with the client_metadata.authorization_endpoint
specified in the Authorisation Request. This request is transmitted as a signed request object containing the fields in the table below.
Parameter | Requirement | Description |
---|---|---|
response_type | REQUIRED | vp_token |
client_id | REQUIRED | Client ID of the Verifier. |
redirect_uri | REQUIRED | An HTTPS endpoint where the Wallet must post the VP Token Response. |
scope | REQUIRED | openid |
response_mode | REQUIRED | direct_post |
presentation_definition | OPTIONAL | A string containing presentation definition. Either presentation_definition or presentation_definition_uri must be present. |
presentation_definition_uri | OPTIONAL | A URI where the Wallet will fetch the presentation definition. Either presentation_definition or presentation_definition_uri must be present. |
state | RECOMMENDED | An opaque value used by the client to maintain state between the request and callback. The Authorisation Server may include this value. The parameter SHOULD be used for preventing cross-site request forgery. |
nonce | REQUIRED | Unique to this request. Used to mitigate replay attacks. |
VP Token Request - Non-normative example
HTTP 302 Location: openid://
?client_id=https%3A%2F%2Fapi-conformance.ebsi.eu%2Fconformance%2Fv4%2Fauth-mock
&response_type=vp_token
&scope=openid
&redirect_uri=https%3A%2F%2Fapi-conformance.ebsi.eu%2Fconformance%2Fv4%2Fauth-mock%2Fdirect_post
&request=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImM0S3JlcEpYem1CTVctcW8ybnREQ3drVGdMbTJDYl81ZWFiemtsalRoXzAifQ.eyJpc3MiOiJodHRwczovL2FwaS1jb25mb3JtYW5jZS5lYnNpLmV1L2NvbmZvcm1hbmNlL3YzL2F1dGgtbW9jayIsImF1ZCI6Imh0dHBzOi8vbXktaXNzdWVyLmV1L3N1ZmZpeC94eXoiLCJleHAiOjE1ODk2OTkxNjIsInJlc3BvbnNlX3R5cGUiOiJ2cF90b2tlbiIsInJlc3BvbnNlX21vZGUiOiJkaXJlY3RfcG9zdCIsImNsaWVudF9pZCI6Imh0dHBzOi8vYXBpLWNvbmZvcm1hbmNlLmVic2kuZXUvY29uZm9ybWFuY2UvdjMvYXV0aC1tb2NrIiwicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9hcGktY29uZm9ybWFuY2UuZWJzaS5ldS9jb25mb3JtYW5jZS92My9hdXRoLW1vY2svZGlyZWN0X3Bvc3QiLCJzY29wZSI6Im9wZW5pZCIsIm5vbmNlIjoiRmdrZUVyZjkxa2ZsIiwicHJlc2VudGF0aW9uX2RlZmluaXRpb25fdXJpIjoiaHR0cHM6Ly9hcGktY29uZm9ybWFuY2UuZWJzaS5ldS9jb25mb3JtYW5jZS92My9hdXRoLW1vY2svZGVmaW5pdGlvbnMveHl6In0.i4VIO7jx9M9XnYDSzsNBf6Nd2QQSw5KiczSeh4SVUuzvbSw7f8D72Bld1VDde-CAGgGGRpW_vMoVCZm-Vt9wSg
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: 'vp_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',
nonce: 'FgkeErf91kfl',
presentation_definition_uri: 'https://api-conformance.ebsi.eu/conformance/v3/auth-mock/definitions/xyz'
}
VP Token Response
The VP Token Response is sent to the AS by posting to URI defined in direct_post
. The payload should be formatted as application/x-www-form-urlencoded
data. The presentation submission parameters define how the requested VP should be interpreted. For detailed definitions, refer to the Presentation Exchange specification, though in this context, it is simplified to key-value pairs.
The state
parameter is required in the VP Token Response if it was present in the VP Token Request from the AS. In such cases, the Client must ensure that the the state
parameter values match in both requests.
VP Token Response - Non-normative example
HTTP POST into: https://api-conformance.ebsi.eu/conformance/v3/auth-mock/direct_post
vp_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDplYnNpOnpkUGoxR1BYamZFUlh4WFBFMVlUWWRKIzdqM1RwYU5kUE5UT3pPdG91T09rbmxPTFFrM0pQLXlrVGZyYVd0WTNHTUUifQ.eyJpc3MiOiJkaWQ6ZWJzaTp6ZFBqMUdQWGpmRVJYeFhQRTFZVFlkSiIsImF1ZCI6Imh0dHBzOi8vYXBpLWNvbmZvcm1hbmNlLmVic2kuZXUvY29uZm9ybWFuY2UvdjMvYXV0aC1tb2NrIiwic3ViIjoiZGlkOmVic2k6emRQajFHUFhqZkVSWHhYUEUxWVRZZEoiLCJpYXQiOjE1ODk2OTkyNjAsIm5iZiI6MTU4OTY5OTI2MCwiZXhwIjoxNTg5Njk5MjYwLCJub25jZSI6IkZna2VFcmY5MWtmbCIsImp0aSI6InVybjp1dWlkOjA3MDYwNjFhLWUyY2EtNDYxNC05ZGU3LTljMTQ1MTkzNWYwMiIsInZwIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sImlkIjoidXJuOnV1aWQ6MDcwNjA2MWEtZTJjYS00NjE0LTlkZTctOWMxNDUxOTM1ZjAyIiwidHlwZSI6WyJWZXJpZmlhYmxlUHJlc2VudGF0aW9uIl0sImhvbGRlciI6ImRpZDplYnNpOnpkUGoxR1BYamZFUlh4WFBFMVlUWWRKIiwidmVyaWZpYWJsZUNyZWRlbnRpYWwiOlsiZXlKMGVYQWlPaUpLVjFRaUxDSmhiR2NpT2lKRlV6STFOaUlzSW10cFpDSTZJbVJwWkRwbFluTnBPbnAyU0ZkWU16VTVRVE5EZG1aS2JrTlpZVUZwUVdSbEkwWXdjalZQZVhSZmJHRm9kblo2TmsxWGJGbHpNMjFqV1U1TFdtbHBVV1JWWm5GMk9IUnphRWhPT1hjaWZRLmV5SnBjM01pT2lKa2FXUTZaV0p6YVRwNmRraFhXRE0xT1VFelEzWm1TbTVEV1dGQmFVRmtaU0lzSW5OMVlpSTZJbVJwWkRwbFluTnBPbnBrVUdveFIxQllhbVpGVWxoNFdGQkZNVmxVV1dSS0lpd2lhV0YwSWpveE5UZzVOams1TWpZd0xDSnVZbVlpT2pFMU9EazJPVGt5TmpBc0ltVjRjQ0k2TVRVNE9UWTVPVEkyTUN3aWFuUnBJam9pZFhKdU9uVjFhV1JpTmpreE5tTXhNQzA0WWpZMExUUTBNamd0T0dKbU5TMWpaR1kwT0RNNE16TXhNR01pTENKMll5STZleUpBWTI5dWRHVjRkQ0k2V3lKb2RIUndjem92TDNkM2R5NTNNeTV2Y21jdk1qQXhPQzlqY21Wa1pXNTBhV0ZzY3k5Mk1TSmRMQ0pwWkNJNkluVnlianAxZFdsa09tSTJPVEUyWXpFd0xUaGlOalF0TkRReU9DMDRZbVkxTFdOa1pqUTRNemd6TXpFd1l5SXNJblI1Y0dVaU9sc2lWbVZ5YVdacFlXSnNaVU55WldSbGJuUnBZV3dpTENKV1pYSnBabWxoWW14bFFYUjBaWE4wWVhScGIyNGlMQ0pEVkZkaGJHeGxkRk5oYldWSmJsUnBiV1VpWFN3aWFYTnpkV0Z1WTJWRVlYUmxJam9pTWpBeU1DMHdOUzB4TjFRd056b3dOem8wTUZvaUxDSjJZV3hwWkVaeWIyMGlPaUl5TURJd0xUQTFMVEUzVkRBM09qQTNPalF3V2lJc0luWmhiR2xrVlc1MGFXd2lPaUl5TURJd0xUQTFMVEUzVkRBM09qQTNPalF3V2lJc0ltVjRjR2x5WVhScGIyNUVZWFJsSWpvaU1qQXlNQzB3TlMweE4xUXdOem93TnpvME1Gb2lMQ0pwYzNOMVpXUWlPaUl5TURJd0xUQTFMVEUzVkRBM09qQTNPalF3V2lJc0ltTnlaV1JsYm5ScFlXeFRkV0pxWldOMElqcDdJbWxrSWpvaVpHbGtPbVZpYzJrNmVtUlFhakZIVUZocVprVlNXSGhZVUVVeFdWUlpaRW9pZlN3aVkzSmxaR1Z1ZEdsaGJGTmphR1Z0WVNJNmV5SnBaQ0k2SW1oMGRIQnpPaTh2WVhCcExYQnBiRzkwTG1WaWMya3VaWFV2ZEhKMWMzUmxaQzF6WTJobGJXRnpMWEpsWjJsemRISjVMM1l5TDNOamFHVnRZWE12TUhneU16QXpPV1UyTXpVMlpXRTJZamN3TTJObE5qY3laVGRqWm1Gak1HSTBNamMyTldJeE5UQm1Oak5rWmpjNFpUSmlaREU0WVdVM09EVTNPRGRtTm1FeUlpd2lkSGx3WlNJNklrWjFiR3hLYzI5dVUyTm9aVzFoVm1Gc2FXUmhkRzl5TWpBeU1TSjlmWDAuOURya3kzcGpCTXFRT3JZSzRzSVRfLXFfM0g5WVlrSG9IRHA1V0hQc055VTVsNExPSFJPajlCOHRLbVZuVnc0aGEzMlVEd0xTd2Q4YzZFbHpUSzNfLVEiXX19.fGUeTLm5mplufL1VpCNbxKGmUzBeMS-NnSkaHNf6_08pFg6iEKfJmD2Wt2eDvj4qijXwnnbLFYV4zBt3PHfpWA
&presentation_submission=%7B%22id%22%3A%22a30e3b91-fb77-4d22-95fa-871689c322e2%22%2C%22definition_id%22%3A%2232f54163-7166-48f1-93d8-ff217bdb0653%22%2C%22descriptor_map%22%3A%5B%7B%22id%22%3A%22same-device-in-time-credential%22%2C%22path%22%3A%22%24%22%2C%22format%22%3A%22jwt_vp%22%2C%22path_nested%22%3A%7B%22format%22%3A%22jwt_vc%22%2C%22path%22%3A%22%24.vp.verifiableCredential%5B0%5D%22%7D%7D%5D%7D
JWT Header:
{
typ: 'JWT',
alg: 'ES256',
kid: 'did:ebsi:zdPj1GPXjfERXxXPE1YTYdJ#7j3TpaNdPNTOzOtouOOknlOLQk3JP-ykTfraWtY3GME'
}
JWT Payload:
{
iss: 'did:ebsi:zdPj1GPXjfERXxXPE1YTYdJ',
aud: 'https://api-conformance.ebsi.eu/conformance/v3/auth-mock',
sub: 'did:ebsi:zdPj1GPXjfERXxXPE1YTYdJ',
iat: 1589699260,
nbf: 1589699260,
exp: 1589699260,
nonce: 'FgkeErf91kfl',
jti: 'urn:uuid:0706061a-e2ca-4614-9de7-9c1451935f02',
vp: {
'@context': [ 'https://www.w3.org/2018/credentials/v1' ],
id: 'urn:uuid:0706061a-e2ca-4614-9de7-9c1451935f02',
type: [ 'VerifiablePresentation' ],
holder: 'did:ebsi:zdPj1GPXjfERXxXPE1YTYdJ',
verifiableCredential: [
'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDplYnNpOnp2SFdYMzU5QTNDdmZKbkNZYUFpQWRlI0YwcjVPeXRfbGFodnZ6Nk1XbFlzM21jWU5LWmlpUWRVZnF2OHRzaEhOOXcifQ.eyJpc3MiOiJkaWQ6ZWJzaTp6dkhXWDM1OUEzQ3ZmSm5DWWFBaUFkZSIsInN1YiI6ImRpZDplYnNpOnpkUGoxR1BYamZFUlh4WFBFMVlUWWRKIiwiaWF0IjoxNTg5Njk5MjYwLCJuYmYiOjE1ODk2OTkyNjAsImV4cCI6MTU4OTY5OTI2MCwianRpIjoidXJuOnV1aWRiNjkxNmMxMC04YjY0LTQ0MjgtOGJmNS1jZGY0ODM4MzMxMGMiLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJpZCI6InVybjp1dWlkOmI2OTE2YzEwLThiNjQtNDQyOC04YmY1LWNkZjQ4MzgzMzEwYyIsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJWZXJpZmlhYmxlQXR0ZXN0YXRpb24iLCJDVFdhbGxldFNhbWVJblRpbWUiXSwiaXNzdWFuY2VEYXRlIjoiMjAyMC0wNS0xN1QwNzowNzo0MFoiLCJ2YWxpZEZyb20iOiIyMDIwLTA1LTE3VDA3OjA3OjQwWiIsInZhbGlkVW50aWwiOiIyMDIwLTA1LTE3VDA3OjA3OjQwWiIsImV4cGlyYXRpb25EYXRlIjoiMjAyMC0wNS0xN1QwNzowNzo0MFoiLCJpc3N1ZWQiOiIyMDIwLTA1LTE3VDA3OjA3OjQwWiIsImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmVic2k6emRQajFHUFhqZkVSWHhYUEUxWVRZZEoifSwiY3JlZGVudGlhbFNjaGVtYSI6eyJpZCI6Imh0dHBzOi8vYXBpLXBpbG90LmVic2kuZXUvdHJ1c3RlZC1zY2hlbWFzLXJlZ2lzdHJ5L3YyL3NjaGVtYXMvMHgyMzAzOWU2MzU2ZWE2YjcwM2NlNjcyZTdjZmFjMGI0Mjc2NWIxNTBmNjNkZjc4ZTJiZDE4YWU3ODU3ODdmNmEyIiwidHlwZSI6IkZ1bGxKc29uU2NoZW1hVmFsaWRhdG9yMjAyMSJ9fX0.9Drky3pjBMqQOrYK4sIT_-q_3H9YYkHoHDp5WHPsNyU5l4LOHROj9B8tKmVnVw4ha32UDwLSwd8c6ElzTK3_-Q'
]
}
}
Presentation Definition and Presentation Submission
{
id: '32f54163-7166-48f1-93d8-ff217bdb0653',
format: { jwt_vc: { alg: [ 'ES256' ] }, jwt_vp: { alg: [ 'ES256' ] } },
input_descriptors: [
{
id: 'same-device-in-time-credential',
format: { jwt_vc: { alg: [ 'ES256' ] } },
constraints: {
fields: [
{
path: [ '$.vc.type' ],
filter: {
type: 'array',
contains: { const: 'CTWalletSameAuthorisedInTime' }
}
}
]
}
}
]
}
{
id: "a30e3b91-fb77-4d22-95fa-871689c322e2",
definition_id: "32f54163-7166-48f1-93d8-ff217bdb0653",
descriptor_map: [
{
id: "same-device-in-time-credential",
path: "$",
format: "jwt_vp",
path_nested: {
id: "same-device-in-time-credential",
format: "jwt_vc",
path: "$.vp.verifiableCredential[0]",
},
},
],
};
Service-to-Service Token Flow
A VP Token can used by a service to request an access token from the AS. Before invoking the token endpoint, the service must first request a presentation definition from the AS that includes the desired scopes. The AS maps these scopes to corresponding VP definitions. The service then invokes the token endpoint with a presentation_submission
encoded in the same manner as described for the VP Token Response above.