How to Verify Mobile Driver’s Licenses (ISO/IEC 18013-7 mDL) via OID4VP with walt.id

This guide provides a comprehensive walkthrough for verifying an mDL based on the ISO/IEC 18103-7 standard using the walt.id Verifier API. The verification process will utilize the OID4VP protocol.

mDL: A digital equivalent of physical driver's license.

OID4VP: A protocol specifying how parties can present verifiable credentials in a way that's consistent and secure across platforms ensuring interoperability.

Verification Process

  1. Set mDL as the credential type to request from a user.
  2. Specify the claims from the mDL that are subject to presentation.
  3. 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, the Verifier API:

  1. Generates an appropriate Presentation Definition.
  2. Returns a URL which can passed to a 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

CURL
curl -X 'POST' \
  'https://verifier.portal.test.waltid.cloud/openid4vc/verify' \
  -H 'accept: */*' \
  -H 'authorizeBaseUrl: mdoc-openid4vp://' \
  -H 'responseMode: direct_post.jwt' \
  -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": [
    {
      "id": "mDL-request",
      "input_descriptor": {
        "id": "org.iso.18013.5.1.mDL",
        "format": {
          "mso_mdoc": {
            "alg": [
              "ES256"
            ]
          }
        },
        "constraints": {
          "fields": [
            {
              "path": [
                "$['org.iso.18013.5.1']['birth_date']"
              ],
              "intent_to_retain": false
            },
            {
              "path": [
                "$['org.iso.18013.5.1']['issue_date']"
              ],
              "intent_to_retain": false
            },
            {
              "path": [
                "$['org.iso.18013.5.1']['expiry_date']"
              ],
              "intent_to_retain": false
            }
          ],
          "limit_disclosure": "required"
        }
      }
    }
  ],
  "trusted_root_cas": [
    "-----BEGIN CERTIFICATE-----\nMIIBtDCCAVmgAwIBAgIUAOXLkeu9penFRno6oDcOBgT1odYwCgYIKoZIzj0EAwIwKDELMAkGA1UEBhMCQVQxGTAXBgNVBAMMEFdhbHRpZCBUZXN0IElBQ0EwHhcNMjUwNjAyMDYzOTQ0WhcNNDAwNTI5MDYzOTQ0WjAoMQswCQYDVQQGEwJBVDEZMBcGA1UEAwwQV2FsdGlkIFRlc3QgSUFDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABAZGrRN7Oeanhn7MOaGU6HhaCt8ZMySk/nRHefLbRq8lChr+PS6JqpCJ503sEvByXzPDgPsp0urKg/y0E+F7q9+jYTBfMB0GA1UdDgQWBBTxCn2nWMrE70qXb614U14BweY2azASBgNVHRMBAf8ECDAGAQH/AgEAMBoGA1UdEgQTMBGGD2h0dHBzOi8vd2FsdC5pZDAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwIDSQAwRgIhAOM37BjC48KhsSlU6mdJwlTLrad9VzlXVKc1GmjoCNm1AiEAkFRJalpz62QCOby9l7Vkq0LAdWVKiFMd0DmSxjsdT2U=\n-----END CERTIFICATE-----\n"
  ],
  "openid_profile": "ISO_18013_7_MDOC"
}'

Header Parameters

  • authorizeBaseUrl - should be set mdoc-openid4vp://.
  • responseMode - must be set to direct_post.jwt.
  • 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 Parameters

  • request_credentials - An array of objects detailing the credentials to be requested from the user. Each object varies based on the type of credential being requested.
    Expand To Learn More

    Below are the possible credential types and their respective object structures:

    1. mDL Credential (ISO/IEC 18013-5)
    • doc_type: Specifies the type of credential (e.g., org.iso.18013.5.1.mDL). This maps to the doc_type attribute of the mdoc document.
    • format: Describes the format of the credential. For mDL credentials, this would be mso_mdoc.
      { "doc_type": "org.iso.18013.5.1.mDL", "format": "mso_mdoc" }
      
    1. W3C JWT Credential
    • type Specifies the type of credential (e.g., VerifiableID, VerifiableDiploma). This maps to the type attribute in W3C credentials.
    • format: Describes the format of the credential. For W3C JWT credentials, this would be jwt_vc_json.
      { "type": "ProofOfResidence", "format": "jwt_vc_json" }
      
    1. SD-JWT VC Credential (IETF Standard)
    • vct: Specifies the type of credential (e.g., https://issuer.com/identity_credential). This maps to the vct attribute in the SD-JWT VC credential.
    • format: Describes the format of the credential. For SD-JWT VC credentials, this would be vc+sd-jwt.: Describes the format of the credential. For SD-JWT VC credentials, this would be vc+sd-jwt.
    { "vct": "test.com/identity_credential", "format": "vc+sd-jwt" }
    

    Optional Parameters
    Next to describing the type and format of the credential, the objects also take an optional policies and id attribute

    • policies: An array of policies to apply to the specified credential. A list of all policies can be found here.
    • id: Used to set a specific id for the generated Presentation Definition. If not set, the verifier API auto-assigns a generated one.

    Full Examples

     {
      "type": "ProofOfResidence",
      "format": "jwt_vc_json",
      "policies": [
        "schema",
        {
          "policy": "webhook",
          "args": "https://example.org/abc/xyz"
        }
      ],
      "id": "test123"
     }
    
  • trusted_root_cas - List of trusted IACA root certificates used to verify the authenticity of the presented mDL credential.
  • openid_profile - should be set to ISO_18013_7_MDOC.

Once the Verifier API receives the presented mDL from the holder, it will apply the following checks:

  1. Validate the certificate of the document signer (credential issuer) included in the MSO header.
  2. Validate that the certificate of the document signer is issued by one of the designated trusted IACAs.
  3. Perform various other validity checks involving the certificates (e.g., validity period).
  4. Verify the digital signature of the IssuerAuth structure.
  5. Verify the digital signature of the DeviceAuth structure.
  6. Verify that every IssuerSignedItem received from the holder has not been tampered with.
  7. Verify the doc_type.
  8. Validate the elements in the MSO's ValidityInfo structure.

Presenting the mDL 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.test.waltid.cloud/openid4vc/session/$state' \
  -H 'accept: */*'

Verification Status Response

The response will contain, among others, the status of the verification following the submission of the presentation from the mDL holder. A sample verification status response (based on the sample presentation request introduced above) is as follows:

{
  "id": "6V0d0knr5cEk",
  "presentationDefinition": {
    "id": "2nQOQaPb7bi",
    "input_descriptors": [
      {
        "id": "org.iso.18013.5.1.mDL",
        "format": {
          "mso_mdoc": {
            "alg": [
              "ES256"
            ]
          }
        },
        "constraints": {
          "fields": [
            {
              "path": [
                "$['org.iso.18013.5.1']['birth_date']"
              ],
              "intent_to_retain": false
            },
            {
              "path": [
                "$['org.iso.18013.5.1']['issue_date']"
              ],
              "intent_to_retain": false
            },
            {
              "path": [
                "$['org.iso.18013.5.1']['expiry_date']"
              ],
              "intent_to_retain": false
            }
          ],
          "limit_disclosure": "required"
        }
      }
    ],
    "customParameters": {}
  },
  "tokenResponse": {
    "vp_token": "o2d2ZXJzaW9uYzEuMGlkb2N1bWVudHOBo2dkb2NUeXBldW9yZy5pc28uMTgwMTMuNS4xLm1ETGxpc3N1ZXJTaWduZWSiam5hbWVTcGFjZXOhcW9yZy5pc28uMTgwMTMuNS4xg9gYWFikaGRpZ2VzdElEAmZyYW5kb21Qw62gF77o6MEYazSd6hmS43FlbGVtZW50SWRlbnRpZmllcmpiaXJ0aF9kYXRlbGVsZW1lbnRWYWx1ZWoxOTg2LTAzLTIy2BhYWKRoZGlnZXN0SUQDZnJhbmRvbVBQWS2bHMQpgo6sQEcXyEGhcWVsZW1lbnRJZGVudGlmaWVyamlzc3VlX2RhdGVsZWxlbWVudFZhbHVlajIwMTktMTAtMjDYGFhZpGhkaWdlc3RJRARmcmFuZG9tUNSMY-SYGOzgghW85RIykexxZWxlbWVudElkZW50aWZpZXJrZXhwaXJ5X2RhdGVsZWxlbWVudFZhbHVlajIwMjQtMTAtMjBqaXNzdWVyQXV0aIRDoQEmoRghWQINMIICCTCCAbCgAwIBAgIUfqyiArJZoX7M61_473UAVi2_UpgwCgYIKoZIzj0EAwIwKDELMAkGA1UEBhMCQVQxGTAXBgNVBAMMEFdhbHRpZCBUZXN0IElBQ0EwHhcNMjUwNjAyMDY0MTEzWhcNMjYwOTAyMDY0MTEzWjAzMQswCQYDVQQGEwJBVDEkMCIGA1UEAwwbV2FsdGlkIFRlc3QgRG9jdW1lbnQgU2lnbmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPzp6eVSAdXERqAp8q8OuDEhl2ILGAaoaQXTJ2sD2g5Xp3CFQDMrMpR_SQ0jt_jTOqExk1PRzjQ79aKpIsJM1mqOBrDCBqTAfBgNVHSMEGDAWgBTxCn2nWMrE70qXb614U14BweY2azAdBgNVHQ4EFgQUx5qkOLC4lpl1xpYZGmF9HLxtp0gwDgYDVR0PAQH_BAQDAgeAMBoGA1UdEgQTMBGGD2h0dHBzOi8vd2FsdC5pZDAVBgNVHSUBAf8ECzAJBgcogYxdBQECMCQGA1UdHwQdMBswGaAXoBWGE2h0dHBzOi8vd2FsdC5pZC9jcmwwCgYIKoZIzj0EAwIDRwAwRAIgHTap3c6yCUNhDVfZWBPMKj9dCWZbrME03kh9NJTbw1ECIAvVvuGll9O21eR16SkJHHAA1pPcovhcTvF9fz9cc66MWQLb2BhZAtamZ3ZlcnNpb25jMS4wb2RpZ2VzdEFsZ29yaXRobWdTSEEtMjU2bHZhbHVlRGlnZXN0c6Fxb3JnLmlzby4xODAxMy41LjGrAFggNl8eLVqR74wd3QOna5iBSVtgkxUy1evZYyf-kzBAhUIBWCBqENuJTWRajrLgO4G0wCg_zbGVkYYngwbSuCM6pBDPEQJYIDgC-m4uZJFhvUiZqgSyjKZSnEg9xfKnOGiSq3kcH8EcA1ggjFwxgydoUg3FIablekLv1KPHumKUxnjSZDN4h8RgUZQEWCBfdbAm68joDWrtS97zTBSyOHSX6L8LNGycJxiKQWmWnQVYICX7ygfWVlNg5BffKs0oSWNPF1bXTB56p-bGJvo7cYEZBlgg7v7IxOCReToypDwxgg2Q_w7rXrU78o9p5sHajsne0NUHWCDChJxVL8ls1PB_WSUpY0LMGb4MfnaSwXsDQy5_TnKRoghYIJt90wUKMxmz1OctNn-YjFhg02-QomCoSJ2M_IPgh4aSCVggqgkamVdQM2GwLUCu8T-pTWKo9NhiHuMGPco3seU3SnQKWCBJgG1p_b_JO_CcXoi4VpIToNDZCKvO6m88jiMFK_4RJW1kZXZpY2VLZXlJbmZvoWlkZXZpY2VLZXmkAQIgASFYIAGMglQiByggcIffBlQbNiRFfsGh3vmPhekEgmpTsdpqIlggG10cWsZDdmK14nPpItsrs65J-EMMRlbXzDnCERQ2Ut1nZG9jVHlwZXVvcmcuaXNvLjE4MDEzLjUuMS5tRExsdmFsaWRpdHlJbmZvo2ZzaWduZWTAeB4yMDI1LTA2LTAzVDA1OjU0OjQxLjg2MTkwMzM3N1ppdmFsaWRGcm9twHgeMjAyNS0wNi0wM1QwNTo1NDo0MS44NjE5MDM5MThaanZhbGlkVW50aWzAeB4yMDI2LTA2LTAzVDA1OjU0OjQxLjg2MTkwMzk2OFpYQORvhnlGP5xw6xK8T-9fdlgerENfFDjiUwwaCH-AmXGq_kjqAF4LJo8nvkfJ9VzTinkPJO0SZpN7LmoNbL7bIitsZGV2aWNlU2lnbmVkompuYW1lU3BhY2Vz2BhBoGpkZXZpY2VBdXRooW9kZXZpY2VTaWduYXR1cmWEQ6EBJqEYIYD2WEAZqNCCNuDJDUlWUrjRzo9C5UylYEYpcdDmmQwgQetrwRTj14uDyw1jEYOFKdtGj1YPpdw30oM0fewG9StfAPEDZnN0YXR1cwA=",
    "presentation_submission": {
      "id": "2nQOQaPb7bi",
      "definition_id": "2nQOQaPb7bi",
      "descriptor_map": [
        {
          "id": "org.iso.18013.5.1.mDL",
          "format": "mso_mdoc",
          "path": "$",
          "path_nested": {
            "id": "org.iso.18013.5.1.mDL",
            "format": "mso_mdoc",
            "path": "$.documents[0]",
            "customParameters": {}
          }
        }
      ]
    },
    "state": "6V0d0knr5cEk"
  },
  "verificationResult": true
}

Body Parameters

  • id - Unique identifier for the presentation session.
  • presentationDefinition - The requirements that the holder must satisfy (e.g., credential types, fields, algorithms) in the context of this presentation session.
  • vp_token - The submitted verifiable presentation.
  • presentation_submission - Metadata showing how the presented credential maps to the expected structure..
  • verificationResult - Indicates whether the presentation was successfully verified.

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

Last updated on June 23, 2025