Verifying Verifiable Credentials via OID4VC

This guide provides a comprehensive walkthrough for verifying a Verifiable Credential based on the W3C standard using the walt.id verifier API. The verification process will utilize the OID4VCI protocol, an extension of OpenID, facilitating secure and standardized communication between identities.

Verifiable Credential (VC): A digital equivalent of physical credentials such as a driver's license or university degree. VCs are cryptographically secure, privacy-respecting, and instantly verifiable.

OID4VCI: A protocol specifying how parties can issue VCs and present these credentials in a way that's consistent and secure across platforms ensuring interoperability.

Preparing the Verification

First, determine the type of Verifiable Credential to be verified. Although this guide focuses on a "VerifiableDiploma," you can use any credential type compliant with the W3C standard.

The verification process will be as follows:

  1. Specify the credential type(s) to request from a user and the verification policies to be applied to the credential( s).
  2. Optionally provide a success and failure redirect URL, which the user will be redirected to after the verification process is completed.

After you have provided the required information:

  1. The API generates a Presentation Definition.
  2. API returns a URL which can passed to OIDC-compliant wallet to fulfill the request.

If you have provided a success or failure redirect URL, the user will be redirected to that URL. You can then access the verification results by using the id of the verification session, which can be found in the URL generated by the API, as well as in the query or path parameters of the redirect URL.

Example Verification Request 1 - Basic

For this example, we will only do a simple verification, where we request one credential type, a Verifiable Diploma, and let the system apply the default signature policy to verify the validity of the signature of the presented credential. Optionally we can provide a success and failure redirect URL, which the user will be redirected to after the verification.

CURL
curl -X 'POST' \
  'https://verifier.portal.walt-test.cloud/openid4vc/verify' \
  -H 'accept: */*' \
  -H 'authorizeBaseUrl: openid4vp://authorize' \
  -H 'responseMode: direct_post' \
  -H 'successRedirectUri: https://example.com/success?id=$id' \
  -H 'errorRedirectUri: https://example.com/error?id=$id' \
  -H 'statusCallbackUri: https://example.com/verificationResult' \
  -H 'statusCallbackApiKey: myAPIKey' \
  -H 'stateId: myUniqueStateValue' \
  -H 'Content-Type: application/json' \
  -d '{
  "request_credentials": [
    "VerifiableDiploma"
  ]
}'

Parameters

  • authorizeBaseUrl - is used to modify the start of the OID4VC request URL. If you are using the cross-device flow, where you will display the URL as a QR code, you can leave the value as openid4vp://authorize or if you don't know the wallet the user will be using to claim the credential. If you are using the same device flow, where you already know the user's wallet and want the user to be able to go directly to it, you can use the wallet URL path that is able to receive an OIDC request as a query parameter. Our wallet for example can receive OID4VC requests here https://wallet.walt.id//wallet-api/wallet/{wallet}/exchange/useOfferRequest.
  • responseMode - should be direct_post as the other options are not yet supported.
  • successRedirectUri (optional) - is used to redirect the user if verification is successful. You can use the $id placeholder to get access to the id of verification session in your application in order to retrieve the verification results. E.g. /success?id=$id will be replaced with /success?id=1234567890
  • errorRedirectUri (optional) - is used to redirect the user if verification is unsuccessful. You can use the $id placeholder to get access to the id of verification session in your application in order to retrieve the verification results. E.g. /error?id=$id will be replaced with /error?id=1234567890
  • statusCallbackUri (optional) - URL that should be called when the presentation request has been fulfilled by a wallet. The request sent will be a POST including the whole presentation result. See Verification Status Policies Response
  • statusCallbackApiKey (optional) - If the endpoint you provide via statusCallbackUri is protected, you can use the statusCallbackApiKey to authenticate. The provided key will be appended as Authorization Header to the request.
  • stateId (optional) - overwrite the unique state value which gets created for each verification request with your own.

Body

  • request_credentials - parameter requires a list of credential types to request from the user.

Example Verification Request 2 - Defining VC/VP Policies

In this example, we're not only specifying the types of credentials to verify, but also defining specific policies that should be executed upon their verification. These include Verifiable Credential (VC) policies applied to credentials, or Verifiable Presentation (VP) policies applied to the VP. The default policy applied for both VP and VC(s) is the signature policy. A list of available policies can be found here.

It's important to note that VC policies, once defined, are applied globally - that is, they are enacted on all requested credentials within the scope of the verification. In a subsequent section, we'll delve into how to customize and apply specific policies to individual credentials.

We will now specify these in the verification request alongside the request_credentials parameter (previously explained) using vc_policies and vp_policies. These represent lists of policies - either one, or both, can be provided. The structure for both is identical.

VC and VP policies are formatted as lists: the policy can be represented as a string if no arguments are required, or as an object if arguments are needed.

Example Polices

[
  "signature",
  // policy without argument
  "expired",
  // policy without argument
  "not-before",
  // policy without argument
  {
    "policy": "webhook",
    "args": "https://example.org/abc/xyz"
  }
  // policy with argument
]

Initiation Request

CURL
curl -X 'POST' \
  'https://verifier.portal.walt.id/openid4vc/verify' \
  -H 'accept: */*' \
  -H 'authorizeBaseUrl: openid4vp://authorize' \
  -H 'responseMode: direct_post' \
  -H 'successRedirectUri: https://example.com/success?id=$id' \
  -H 'errorRedirectUri: https://example.com/error?id=$id' \
  -H 'statusCallbackUri: https://example.com/verificationResult' \
  -H 'statusCallbackApiKey: myAPIKey' \
  -H 'stateId: myUniqueStateValue' \
  -H 'Content-Type: application/json' \
  -d '{
      "vp_policies": ["signature", "expired", "not-before"],
      "vc_policies": ["signature", "expired", "not-before",
       {"policy": "webhook", "args": "https://example.org/abc/xyz"}],
      "request_credentials": ["VerifiableDiploma"]
   }'

Parameters

  • authorizeBaseUrl - is used to modify the start of the OID4VC request URL. If you are using the cross-device flow, where you will display the URL as a QR code, you can leave the value as openid4vp://authorize or if you don't know the wallet the user will be using to claim the credential. If you are using the same device flow, where you already know the user's wallet and want the user to be able to go directly to it, you can use the wallet URL path that is able to receive an OIDC request as a query parameter. Our wallet for example can receive OID4VC requests here https://wallet.walt.id//wallet-api/wallet/{wallet}/exchange/useOfferRequest.
  • responseMode - should be direct_post as the other options are not yet supported.
  • successRedirectUri (optional) - is used to redirect the user if verification is successful. You can use the $id placeholder to get access to the id of verification session in your application in order to retrieve the verification results. E.g. /success?id=$id will be replaced with /success?id=1234567890
  • errorRedirectUri (optional) - is used to redirect the user if verification is unsuccessful. You can use the $id placeholder to get access to the id of verification session in your application in order to retrieve the verification results. E.g. /error?id=$id will be replaced with /error?id=1234567890
  • statusCallbackUri (optional) - URL that should be called when the presentation request has been fulfilled by a wallet. The request sent will be a POST including the whole presentation result. See Verification Status Policies Response
  • statusCallbackApiKey (optional) - If the endpoint you provide via statusCallbackUri is protected, you can use the statusCallbackApiKey to authenticate. The provided key will be appended as Authorization Header to the request.
  • stateId (optional) - overwrite the unique state value which gets created for each verification request with your own.

Upon execution, the system generates a URL, which can be shared directly with users or displayed as a QR code.

Example Verification Request 3: Applying Policies to Specific Credentials

In this example, which builds up on the previous ones, we'll demonstrate how to apply specific policies to a single credential in the verification process. This is achieved by specifying the required credential type, not merely as a string, but as an object within therequest_credentials array. This object includes the credential's name and an array of policies that should be applied.

Here's how it looks in practice:

{
  "vp_policies": [
    "signature",
    "expired"
  ],
  "vc_policies": [
    "signature",
    "expired"
  ],
  "request_credentials": [
    "VerifiableId",
    "ProofOfResidence",
    {
      "credential": "OpenBadgeCredential",
      "policies": [
        "signature",
        {
          "policy": "3.test",
          "args": {
            "type": "object"
          }
        }
      ]
    }
  ]
}

In this code snippet, we specifically tailored policies for the "OpenBadgeCredential" type. It's included within the request_credentials array as an enriched object, composed of its type ("OpenBadgeCredential") and an array of its associated policies. These include the "signature" policy and an additional "test" policy, exclusive to this credential and requiring an argument - "type" in this instance.

Meanwhile, the other requested credentials, "VerifiableId" and "ProofOfResidence", are still represented as simple strings, meaning they will be verified against the globally defined "vp_policies" and "vc_policies", as no unique policies are outlined for them.

In summary, enhancing a credential's representation from a mere string to a more complex object within the request_credentials array empowers you with the ability to define distinct policies on individual credentials as required.

Initiation Request

CURL
curl -X 'POST' \
  'https://verifier.portal.walt.id/openid4vc/verify' \
  -H 'accept: */*' \
  -H 'authorizeBaseUrl: openid4vp://authorize' \
  -H 'responseMode: direct_post' \
  -H 'successRedirectUri: https://example.com/success?id=$id' \
  -H 'errorRedirectUri: https://example.com/error?id=$id' \
  -H 'statusCallbackUri: https://example.com/verificationResult' \
  -H 'statusCallbackApiKey: myAPIKey' \
  -H 'stateId: myUniqueStateValue' \
  -H 'Content-Type: application/json' \
  -d '{
  "vp_policies": ["signature", "expired"],
  "vc_policies": ["signature", "expired"],
  "request_credentials": [
    "VerifiableId",
    "ProofOfResidence",
    {
      "credential": "OpenBadgeCredential",
      "policies": [
        "signature",
        {"policy": "webhook", "args": "https://example.org/abc/xyz"}]
      ]
    }
  ]
}'

Parameters

  • authorizeBaseUrl - is used to modify the start of the OID4VC request URL. If you are using the cross-device flow, where you will display the URL as a QR code, you can leave the value as openid4vp://authorize or if you don't know the wallet the user will be using to claim the credential. If you are using the same device flow, where you already know the user's wallet and want the user to be able to go directly to it, you can use the wallet URL path that is able to receive an OIDC request as a query parameter. Our wallet for example can receive OID4VC requests here https://wallet.walt.id//wallet-api/wallet/{wallet}/exchange/useOfferRequest.
  • responseMode - should be direct_post as the other options are not yet supported.
  • successRedirectUri (optional) - is used to redirect the user if verification is successful. You can use the $id placeholder to get access to the id of verification session in your application in order to retrieve the verification results. E.g. /success?id=$id will be replaced with /success?id=1234567890
  • errorRedirectUri (optional) - is used to redirect the user if verification is unsuccessful. You can use the $id placeholder to get access to the id of verification session in your application in order to retrieve the verification results. E.g. /error?id=$id will be replaced with /error?id=1234567890
  • statusCallbackUri (optional) - URL that should be called when the presentation request has been fulfilled by a wallet. The request sent will be a POST including the whole presentation result. See Verification Status Policies Response
  • statusCallbackApiKey (optional) - If the endpoint you provide via statusCallbackUri is protected, you can use the statusCallbackApiKey to authenticate. The provided key will be appended as Authorization Header to the request.
  • stateId (optional) - overwrite the unique state value which gets created for each verification request with your own.

Upon execution, the system generates a URL, which can be shared directly with users or displayed as a QR code.

Example Verification Request 4: Implementing a Custom Presentation Definition

In this example, we'll explore how to provide your own presentation definition within the verification request. With that you will overwrite the one generated by the system.

You can specify your custom presentation definition via the presentation_definition key, see example blow.

Example

{
  "vp_policies": [
    ...
  ],
  "vc_policies": [
    ...
  ],
  "request_credentials": [
    "VerifiableId"
  ],
  "presentation_definition": {
    "id": "<automatically assigned>",
    "input_descriptors": [
      {
        "id": "VerifiableId",
        "format": {
          "jwt_vc_json": {
            "alg": [
              "EdDSA"
            ]
          }
        },
        "constraints": {
          "fields": [
            {
              "path": [
                "$.type"
              ],
              "filter": {
                "type": "string",
                "pattern": "VerifiableId"
              }
            }
          ]
        }
      }
    ]
  }
}

Presenting the Credential via walt.id Wallet

Using the URL returned by the verification request, we can fulfill the request using the hosted wallet by walt.id. Either show the URL as QR code and scan it with a camera or provide the URL as is in the text field below the camera once you click on "Scan to receive or present credentials" in the web wallet credentials overview page in the top right corner.

Retrieving the Verification Status

After the user presents the credential(s), you can verify the status by performing the following call with the state value obtained from the URL query params you shared with the user previously.

Example

openid4vp://authorize?...state=a07bdb17-7d87-4965-9296-1adefcaaddd9...

Making the call to receive the verification result

curl -X 'GET' \
  'https://verifier.portal.walt.id/openid4vc/session/$state' \
  -H 'accept: */*'

Verification Status Policies Response

The response of the verification status call will contain the status of the verification policies applied to the credential(s) presented by the user. The policy results will be in the following format:

{
  "verificationResult": true,
  "policyResults": {
    "results": [
      {
        "credential": "VerifiableDiploma",
        "policies": [
          {
            "policy": "signature",
            "is_success": true
          }
        ]
      }
    ]
  }
}

The verificationResult field will be true if all policies were successful, otherwise it will be false.

The policyResults field will contain the results of the policies applied to each credential. The credential field will contain the name of the credential, and the policies field will contain the results of the policies applied to the credential. The policy field will contain the name of the policy, and the is_success field will contain the result of the policy.