Presenting A Requested Credential (W3C, SD-JWT VC, mDL) Via OID4VP

There are several methods by which a user can present a credential to a verifier, and all are supported by the wallet API:

  1. User may receive a direct link, which opens the wallet and prompts credential sharing.
  2. A website may display a QR code for the user to scan and share the credential.
  3. The user may manually input a credential presentation request URL string into a text field.

Irrespective of the chosen method, these are just different ways of receiving, parsing and fulfilling a credential presenting request URL.

Credential Presentation Request URL

A credential presentation URL is a standardised method, as per the OID4VP specification, to communicate the needed credentials and claims from verifier to the wallet. The URL can take various forms, such as QR code or a link, and generally begins with openid4vp:// or haip://.

Example Presentation Request

openid4vp://authorize
?response_type=vp_token
&client_id=<client_id>
&response_mode=direct_post
&state=<state>
&presentation_definition=<presentation_definition>
&client_id_scheme=redirect_uri
&response_uri=<response_uri>

The most important parameter for us will be the presentation_definition. The others are more important for internal behaviour of the wallet, response types, formats and secure communication between wallet and verifier, which we don't need to worry about for the moment as the wallet-api will manage all of this for us. In a more advanced guide we will also dive into the other parameters.

The Presentation Definition

The presentation definition specifies the criteria for the wallet to know what credentials and claims should be requested from the user and shard with the verifier.

Example

{
  "input_descriptors": [
    {
      "id": "VerifiableId",
      "format": {
        "jwt_vc_json": {
          "alg": [
            "EdDSA"
          ]
        }
      },
      "constraints": {
        "fields": [
          {
            "path": [
              "$.type"
            ],
            "filter": {
              "type": "string",
              "pattern": "VerifiableId"
            }
          }
        ]
      }
    }
  ]
}

The definition says that one credential is requested in the JWT format, specifically with the algorithm "EdDSA" and it must be of type "VerifiableId", as indicated by the constraint that filters for this specific string pattern in the credential's type field. You can find more info about definition documents here.

Fulfilling A Presentation Request

In order to fulfill the presentation request received by the verifier, we need to go through the following steps:

  1. Extract Presentation Definition from OID4VC presentation request
  2. Find credentials matching the presentation definition in the user wallet
  3. Request consent from the end-user of the wallet to share the credentials
  4. Wrapping the credentials in a Verifiable Presentation and signing it using the holder (wallet user) DID
  5. (optional) Redirect the user to the verification result page returned from the verifier

1. Extract Presentation Definition

A simple example for the JavaScript browser environment would look like the following:

async function decodePresentationURL(offerURL) {
    // Create URL object
    const url = new URL(offerURL);

    // Get `presentation_definition_uri` query parameter
    const offerParam = url.searchParams.get("presentation_definition_uri");

    // Resolve the URL and get the result
    const response = await fetch(offerParam);
    if (!response.ok) {
        throw new Error('Network response was not ok');
    }

    // Convert result to text
    const resultText = await response.text();

    // Decode and parse resultText to an Object
    const offerObject = JSON.parse(resultText);

    return offerObject;
}

The output of the function would look as follows and will be used in the next step. It's identical to the one example we already saw of a presentation definition.

{
  "input_descriptors": [
    {
      "id": "VerifiableId",
      "format": {
        "jwt_vc_json": {
          "alg": [
            "EdDSA"
          ]
        }
      },
      "constraints": {
        "fields": [
          {
            "path": [
              "$.type"
            ],
            "filter": {
              "type": "string",
              "pattern": "VerifiableId"
            }
          }
        ]
      }
    }
  ]
}

Optionally decode the presentation request URL with a site like this, then extract and resolve presentation_definition by pasting it into your browser search bar.

2. Find Credential(s) Matching Presentation Definition

Use the matchCredentialsForPresentationDefinition endpoint in the wallet API to find all user credentials matching the presentation request constraints.

CURL

Endpoint:/wallet-api/wallet/{wallet}/exchange/matchCredentialsForPresentationDefinition| API Reference

Example Request

curl -X 'POST' \
  'https://wallet.walt.id/wallet-api/wallet/{wallet_id}/exchange/matchCredentialsForPresentationDefinition' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "input_descriptors": [
    {
      "id": "VerifiableId",
      "format": {
        "jwt_vc_json": {
          "alg": [
            "EdDSA"
          ]
        }
      },
      "constraints": {
        "fields": [
          {
            "path": [
              "$.type"
            ],
            "filter": {
              "type": "string",
              "pattern": "VerifiableId"
            }
          }
        ]
      }
    }
  ]
}'

Path Parameters

  • wallet_id - Extract needed wallet id parameter via the /wallet-api/wallet/accounts/wallets endpoint.

Body

The extracted presentation definition as JSON, as explained here.

{
  "input_descriptors": [
    {
      "id": "VerifiableId",
      "format": {
        "jwt_vc_json": {
          "alg": [
            "EdDSA"
          ]
        }
      },
      "constraints": {
        "fields": [
          {
            "path": [
              "$.type"
            ],
            "filter": {
              "type": "string",
              "pattern": "VerifiableId"
            }
          }
        ]
      }
    }
  ]
}

Body Parameters

The body takes the extracted presentation definition as JSON, as explained here.


Example Response

If credentials matching the presentation definition are found, a list of these credentials will be returned. If no match is found, an empty list will be returned. Take a note of the credential id(s) returned as we will be needing them in a later step.

[
  {
    "wallet": "5f2eb7d7-7d04-461c-b93d-28d95efbf15b",
    "id": "urn:uuid:fb09ba3d-b73d-49ad-9d88-3d018878ea83",
    "document": "eyJraWQiOiJkaWQ6andrOmV5SnJkSGtpT2lKRlF5SXNJbU55ZGlJNklsQXRNalUySWl3aWEybGtJam9pTTFsT1pEbEdibmc1U214NVVGWlpkMmRYUmtVek4wVXpSM2RKTUdWSGJFTkxPSGRHYkZkNFIyWndUU0lzSW5naU9pSkdiM1paTWpGTVFVRlBWR3huTFcwdFRtVkxWMmhhUlV3MVlVWnlibEl3ZFdOS2FrUTFWRXR3UjNWbklpd2llU0k2SWtOeVJrcG1SMVJrVURJNVNrcGpZM0JSV0hWNVRVOHpiMmgwZW5KVWNWQjZRbEJDU1ZSWmFqQnZaMEVpZlEiLCJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJkaWQ6andrOmV5SnJkSGtpT2lKRlF5SXNJbU55ZGlJNklsQXRNalUySWl3aWEybGtJam9pTTFsT1pEbEdibmc1U214NVVGWlpkMmRYUmtVek4wVXpSM2RKTUdWSGJFTkxPSGRHYkZkNFIyWndUU0lzSW5naU9pSkdiM1paTWpGTVFVRlBWR3huTFcwdFRtVkxWMmhhUlV3MVlVWnlibEl3ZFdOS2FrUTFWRXR3UjNWbklpd2llU0k2SWtOeVJrcG1SMVJrVURJNVNrcGpZM0JSV0hWNVRVOHpiMmgwZW5KVWNWQjZRbEJDU1ZSWmFqQnZaMEVpZlEiLCJzdWIiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0pqY25ZaU9pSkZaREkxTlRFNUlpd2lhMmxrSWpvaVEyVTVNbGczUVhVMVRuQlRWV0ZoWlU5VFl6TkpSMjlDTFVacFNUTmtaMjFYT1Y5c2NGTldja3hrYXlJc0luZ2lPaUpIY1VabWRqbFNjemh1TlRrMk4wSXlVR3g0TW1wbFduRnlOWFZxVkRoM2RYWk5XbEpIYlZoeGVFSkZJbjAiLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVmVyaWZpYWJsZUF0dGVzdGF0aW9uIiwiVmVyaWZpYWJsZUlkIl0sImNyZWRlbnRpYWxTY2hlbWEiOnsiaWQiOiJodHRwczovL2FwaS5wcmVwcm9kLmVic2kuZXUvdHJ1c3RlZC1zY2hlbWFzLXJlZ2lzdHJ5L3YxL3NjaGVtYXMvMHhiNzdmODUxNmE5NjU2MzFiNGYxOTdhZDU0YzY1YTllMmY5OTM2ZWJmYjc2YmFlNDkwNmQzMzc0NGRiY2M2MGJhIiwidHlwZSI6IkZ1bGxKc29uU2NoZW1hVmFsaWRhdG9yMjAyMSJ9LCJjcmVkZW50aWFsU3ViamVjdCI6eyJjdXJyZW50QWRkcmVzcyI6WyIxIEJvdWxldmFyZCBkZSBsYSBMaWJlcnTDqSwgNTk4MDAgTGlsbGUiXSwiZGF0ZU9mQmlydGgiOiIxOTkzLTA0LTA4IiwiZmFtaWx5TmFtZSI6IkRPRSIsImZpcnN0TmFtZSI6IkphbmUiLCJnZW5kZXIiOiJGRU1BTEUiLCJpZCI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpQUzFBaUxDSmpjbllpT2lKRlpESTFOVEU1SWl3aWEybGtJam9pUTJVNU1sZzNRWFUxVG5CVFZXRmhaVTlUWXpOSlIyOUNMVVpwU1ROa1oyMVhPVjlzY0ZOV2NreGtheUlzSW5naU9pSkhjVVptZGpsU2N6aHVOVGsyTjBJeVVHeDRNbXBsV25GeU5YVnFWRGgzZFhaTldsSkhiVmh4ZUVKRkluMCIsIm5hbWVBbmRGYW1pbHlOYW1lQXRCaXJ0aCI6IkphbmUgRE9FIiwicGVyc29uYWxJZGVudGlmaWVyIjoiMDkwNDAwODA4NEgiLCJwbGFjZU9mQmlydGgiOiJMSUxMRSwgRlJBTkNFIn0sImV2aWRlbmNlIjpbeyJkb2N1bWVudFByZXNlbmNlIjpbIlBoeXNpY2FsIl0sImV2aWRlbmNlRG9jdW1lbnQiOlsiUGFzc3BvcnQiXSwic3ViamVjdFByZXNlbmNlIjoiUGh5c2ljYWwiLCJ0eXBlIjpbIkRvY3VtZW50VmVyaWZpY2F0aW9uIl0sInZlcmlmaWVyIjoiZGlkOmVic2k6MkE5Qlo5U1VlNkJhdGFjU3B2czFWNUNkakh2THBRN2JFc2kySmI2TGRIS25ReGFOIn1dLCJpZCI6InVybjp1dWlkOmZiMDliYTNkLWI3M2QtNDlhZC05ZDg4LTNkMDE4ODc4ZWE4MyIsImlzc3VlZCI6IjIwMjEtMDgtMzFUMDA6MDA6MDBaIiwiaXNzdWVyIjoiZGlkOmp3azpleUpyZEhraU9pSkZReUlzSW1OeWRpSTZJbEF0TWpVMklpd2lhMmxrSWpvaU0xbE9aRGxHYm5nNVNteDVVRlpaZDJkWFJrVXpOMFV6UjNkSk1HVkhiRU5MT0hkR2JGZDRSMlp3VFNJc0luZ2lPaUpHYjNaWk1qRk1RVUZQVkd4bkxXMHRUbVZMVjJoYVJVdzFZVVp5YmxJd2RXTktha1ExVkV0d1IzVm5JaXdpZVNJNklrTnlSa3BtUjFSa1VESTVTa3BqWTNCUldIVjVUVTh6YjJoMGVuSlVjVkI2UWxCQ1NWUlphakJ2WjBFaWZRIiwidmFsaWRGcm9tIjoiMjAyMS0wOC0zMVQwMDowMDowMFoiLCJpc3N1YW5jZURhdGUiOiIyMDI0LTA4LTIxVDA5OjExOjMzLjI0NjM4OTA0MVoifSwianRpIjoidXJuOnV1aWQ6ZmIwOWJhM2QtYjczZC00OWFkLTlkODgtM2QwMTg4NzhlYTgzIiwiaWF0IjoxNzI0MjMxNDkzLCJuYmYiOjE3MjQyMzE0OTN9.OsPTKQC6tvD6TtxeoCd8FtaIThYrYeYRjCHzDXkmgTUjRw78faT9R2bljT03ncrb0YstK0EmjM9D5lwqMZ9ZIA",
    "addedOn": "2024-08-21T09:11:33.284Z",
    "format": "jwt_vc"
  }
]

Using the result of the previous request, we can now display the credential(s) to the user (owner of the wallet) for confirming the exchange with the verifier.

4. Share Credential(s) With Verifier

Now that we have identified the credentials to share and received confirmation from the wallet owner, we are ready to reply to the verifier's presentation request. However, before we can do so we need to resolve the original presentation request using the following endpoint /exchange/resolvePresentationRequest

4.1. Resolve Presentation Request

CURL

Endpoint:/wallet-api/wallet/{wallet}/exchange/resolvePresentationRequest| API Reference

Example Request

curl -X 'POST' \
  'https://wallet.walt.id/wallet-api/wallet/{wallet_id}/exchange/resolvePresentationRequest' \
  -H 'accept: text/plain' \
  -H 'Content-Type: text/plain' \
  -d 'openid4vp://authorize?response_type=vp_token&client_id=https%3A%2F%2Fverifier.portal.walt.id%2Fopenid4vc%2Fverify&response_mode=direct_post&state=V3pp6GFoSro6&presentation_definition_uri=https%3A%2F%2Fverifier.portal.walt.id%2Fopenid4vc%2Fpd%2FV3pp6GFoSro6&client_id_scheme=redirect_uri&client_metadata=%7B%22authorization_encrypted_response_alg%22%3A%22ECDH-ES%22%2C%22authorization_encrypted_response_enc%22%3A%22A256GCM%22%7D&nonce=0c4e8d3c-1017-4a38-b6d2-468e4a2c13f8&response_uri=https%3A%2F%2Fverifier.portal.walt.id%2Fopenid4vc%2Fverify%2FV3pp6GFoSro6'

Path Parameters

  • wallet_id - Extract needed wallet id parameter via the /wallet-api/wallet/accounts/wallets endpoint.

Body

The original presentation request received from the verifier.

openid4vp://authorize?
response_type=vp_token&
client_id=https%3A%2F%2Fverifier.portal.walt.id%2Fopenid4vc%2Fverify&
response_mode=direct_post&
state=V3pp6GFoSro6&
presentation_definition_uri=https%3A%2F%2Fverifier.portal.walt.id%2Fopenid4vc%2Fpd%2FV3pp6GFoSro6&
client_id_scheme=redirect_uri&
client_metadata=%7B%22authorization_encrypted_response_alg%22%3A%22ECDH-ES%22%2C%22authorization_encrypted_response_enc%22%3A%22A256GCM%22%7D&
nonce=0c4e8d3c-1017-4a38-b6d2-468e4a2c13f8&
response_uri=https%3A%2F%2Fverifier.portal.walt.id%2Fopenid4vc%2Fverify%2FV3pp6GFoSro6

Body Parameters

The original presentation request received from the verifier.


Example Response

openid4vp://authorize?response_type=vp_token&client_id=https%3A%2F%2Fverifier.portal.walt.id%2Fopenid4vc%2Fverify&response_mode=direct_post&state=V3pp6GFoSro6&presentation_definition=%7B%22id%22%3A%22GgLtLahfRjuK%22%2C%22input_descriptors%22%3A%5B%7B%22id%22%3A%22VerifiableId%22%2C%22format%22%3A%7B%22jwt_vc_json%22%3A%7B%22alg%22%3A%5B%22EdDSA%22%5D%7D%7D%2C%22constraints%22%3A%7B%22fields%22%3A%5B%7B%22path%22%3A%5B%22%24.type%22%5D%2C%22filter%22%3A%7B%22type%22%3A%22string%22%2C%22pattern%22%3A%22VerifiableId%22%7D%7D%5D%7D%7D%5D%7D&presentation_definition_uri=https%3A%2F%2Fverifier.portal.walt.id%2Fopenid4vc%2Fpd%2FV3pp6GFoSro6&client_id_scheme=redirect_uri&client_metadata=%7B%22authorization_encrypted_response_alg%22%3A%22ECDH-ES%22%2C%22authorization_encrypted_response_enc%22%3A%22A256GCM%22%7D&nonce=0c4e8d3c-1017-4a38-b6d2-468e4a2c13f8&response_uri=https%3A%2F%2Fverifier.portal.walt.id%2Fopenid4vc%2Fverify%2FV3pp6GFoSro6

4.2. Fulfill Presentation Request

W3C/mDL Credentials
SD-JWTs
CURL

Endpoint:/wallet-api/wallet/{wallet}/exchange/usePresentationRequest| API Reference

Example Request

curl -X 'POST' \
  'https://wallet.walt.id/wallet-api/wallet/{wallet_id}/exchange/usePresentationRequest' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "did": "did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5Iiwia2lkIjoiQ2U5Mlg3QXU1TnBTVWFhZU9TYzNJR29CLUZpSTNkZ21XOV9scFNWckxkayIsIngiOiJHcUZmdjlSczhuNTk2N0IyUGx4MmplWnFyNXVqVDh3dXZNWlJHbVhxeEJFIn0",
  "presentationRequest": "openid4vp://authorize?response_type=vp_token&client_id=https%3A%2F%2Fverifier.portal.walt.id%2Fopenid4vc%2Fverify&response_mode=direct_post&state=pXZz119vn5SG&presentation_definition=%7B%22id%22%3A%222k6OTMoo2wbU%22%2C%22input_descriptors%22%3A%5B%7B%22id%22%3A%22VerifiableId%22%2C%22format%22%3A%7B%22jwt_vc_json%22%3A%7B%22alg%22%3A%5B%22EdDSA%22%5D%7D%7D%2C%22constraints%22%3A%7B%22fields%22%3A%5B%7B%22path%22%3A%5B%22%24.type%22%5D%2C%22filter%22%3A%7B%22type%22%3A%22string%22%2C%22pattern%22%3A%22VerifiableId%22%7D%7D%5D%7D%7D%5D%7D&presentation_definition_uri=https%3A%2F%2Fverifier.portal.walt.id%2Fopenid4vc%2Fpd%2FpXZz119vn5SG&client_id_scheme=redirect_uri&client_metadata=%7B%22authorization_encrypted_response_alg%22%3A%22ECDH-ES%22%2C%22authorization_encrypted_response_enc%22%3A%22A256GCM%22%7D&nonce=d319f72a-cadf-4b15-827f-8ed0e9f755c0&response_uri=https%3A%2F%2Fverifier.portal.walt.id%2Fopenid4vc%2Fverify%2FpXZz119vn5SG",
  "selectedCredentials": [
    "urn:uuid:fb09ba3d-b73d-49ad-9d88-3d018878ea83"
  ]
}'

Path Parameters

  • wallet_id - Extract needed wallet id parameter via the /wallet-api/wallet/accounts/wallets endpoint.

Body

{
  "did": "did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5Iiwia2lkIjoiQ2U5Mlg3QXU1TnBTVWFhZU9TYzNJR29CLUZpSTNkZ21XOV9scFNWckxkayIsIngiOiJHcUZmdjlSczhuNTk2N0IyUGx4MmplWnFyNXVqVDh3dXZNWlJHbVhxeEJFIn0",
  "presentationRequest": "openid4vp://authorize?response_type=vp_token&client_id=https%3A%2F%2Fverifier.portal.walt.id%2Fopenid4vc%2Fverify&response_mode=direct_post&state=pXZz119vn5SG&presentation_definition=%7B%22id%22%3A%222k6OTMoo2wbU%22%2C%22input_descriptors%22%3A%5B%7B%22id%22%3A%22VerifiableId%22%2C%22format%22%3A%7B%22jwt_vc_json%22%3A%7B%22alg%22%3A%5B%22EdDSA%22%5D%7D%7D%2C%22constraints%22%3A%7B%22fields%22%3A%5B%7B%22path%22%3A%5B%22%24.type%22%5D%2C%22filter%22%3A%7B%22type%22%3A%22string%22%2C%22pattern%22%3A%22VerifiableId%22%7D%7D%5D%7D%7D%5D%7D&presentation_definition_uri=https%3A%2F%2Fverifier.portal.walt.id%2Fopenid4vc%2Fpd%2FpXZz119vn5SG&client_id_scheme=redirect_uri&client_metadata=%7B%22authorization_encrypted_response_alg%22%3A%22ECDH-ES%22%2C%22authorization_encrypted_response_enc%22%3A%22A256GCM%22%7D&nonce=d319f72a-cadf-4b15-827f-8ed0e9f755c0&response_uri=https%3A%2F%2Fverifier.portal.walt.id%2Fopenid4vc%2Fverify%2FpXZz119vn5SG",
  "selectedCredentials": [
    "urn:uuid:fb09ba3d-b73d-49ad-9d88-3d018878ea83"
  ]
}

Body Parameters

  • did: String - DID owning the credential. Used for signature generation.
  • presentationRequest: String - response from previous call to resolvePresentationRequest
  • selectedCredentials: Array - list of credential id's to share which we received from the call to find matching credentials
  • disclosures: (optional) Object - This object specifies which selectively disclosable fields of a credential should be shared with the verifier. It is applicable only if the credential to be shared contains selective disclosure attributes. The object uses the credential ID as the key(s) and an array of the disclosures of the fields to be disclosed by credential as values.
    Example:
     "disclosures": {
       "urn:uuid:fb09ba3d-b73d-49ad-9d88-3d018878ea83": [
         "WyJiZ1I5OTdnRUVRNEU5bFpXNEhwVjRRPT0iLCJuYW1lIiwiSkZGIHggdmMtZWR1IFBsdWdGZXN0IDMgSW50ZXJvcGVyYWJpbGl0eSJd"
      ],
      "urn:uuid:fb0234-b7234d-55ad-25d88-3d018878ea83": [
         "WyJiZ1I5OTdnRUVRNEU5bFpXNEhwVjRRPT0iLCJuYW1lIiwiSkZGIHggdmMtZWR1IFBsdWdGZXN0IDMgSW50ZXJvcGVyYWJpbGl0eSJd"
      ]
    }
    

    Explanation:
    • Key: The unique identifier (ID) of the credential to be shared.
    • Value: An array containing the disclosures of credential fields that should be disclosed. This structure allows you to specify which parts of a credential are shared on a per-credential basis, enhancing privacy and control over the information disclosed.

Example Response

If the verifier provided a redirect URI we will receive it as a response.

{
  "redirectUri": null
}