What you'll learn
- Create verification requests for mDL credentials with specific claim requirements
- Configure trusted IACA root certificates for mDL verification
- Retrieve verification status, policy results and inspect presented mDL credentials
Learn how to verify ISO/IEC 18013-7 mobile driver's licenses (mDL) via OID4VP using walt.id's verifier API, including specifying claims, configuring trusted root CAs, and retrieving verification results.
What you'll learn
Relevant concepts
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.
After you have provided the required information, the Verifier API:
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.
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
mdoc-openid4vp://.direct_post.jwt.$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$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=1234567890POST including the whole presentation
result. See Verification Status Policies ResponsestatusCallbackUri is protected, you can use
the statusCallbackApiKey to authenticate.
The provided key will be appended as Authorization Header to the request.state value which gets created for each verification request with your
own.Body Parameters
Below are the possible credential types and their respective object structures:
mso_mdoc.
{ "doc_type": "org.iso.18013.5.1.mDL", "format": "mso_mdoc" }
VerifiableID, VerifiableDiploma). This maps to the type
attribute in W3C credentials.jwt_vc_json.
{ "type": "ProofOfResidence", "format": "jwt_vc_json" }
vct
attribute in the SD-JWT VC credential.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
Full Examples
{
"type": "ProofOfResidence",
"format": "jwt_vc_json",
"policies": [
"schema",
{
"policy": "webhook",
"args": "https://example.org/abc/xyz"
}
],
"id": "test123"
}
ISO_18013_7_MDOC.Once the Verifier API receives the presented mDL from the holder, it will apply the following checks:
IssuerAuth structure.DeviceAuth structure.IssuerSignedItem received from the holder has not been tampered with.doc_type.ValidityInfo structure.Using the URL returned by the verification request, you can fulfill the request using the hosted wallet by walt.id.
To present the mDL via the walt.id Wallet:
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: */*'
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
The verificationResult field will be true if all verifications were successful, otherwise it will be false.
After a successful presentation session (verificationResult is true), you can retrieve a decoded and formatted
view of all mDLs presented by the holder.
Two view modes are available:
simple - Go-to choice for most cases.verbose - Useful for debugging, auditing, and other detailed technical information.Endpoint: /openid4vc/session/{id}/presented-credentials | API Reference
Path Parameters
id (required)- The identifier of the presentation session whose credentials should be retrieved.Query Parameters
viewMode (optional) - Controls how detailed the response will be. Available values are simple and verbose
(defaults to simple).Response Body
viewMode - Echoed request value (or the default if it was not specified).credentialsByFormat : A map where each key is a presentation format (jwt_vc_json, sd_jwt_vc, mso_mdoc)
and the value is an array of decoded presented credentials, formatted as JSON objects and whose properties vary
according to the value of viewMode:mso_mdoc in simple view mode:version - The version of the device response structure.status - Integer value where a value of 0 corresponds to the OK status message.documents - An array of decoded mDocs (only one entry in this case that contains the mDL) where each entry
is a JSON object that is composed of the following properties:
docType - The document type of the mDoc, which in this case will have a value of org.iso.18013.5.1.mDL.nameSpaces - An associative array between the requested data elements and the namespaces (identifiers)
they belong to.certificateChain - The x5chain element in the header of the IssuerAuth Cose_Sign1 structure.validityInfo - JSON object that contains information related to the validity of the Mobile Security
Object (MSO) and its signature. It is composed of the following properties:
signed - Timestamp of when the MSO was signed.validFrom - Timestamp before which the MSO should not be considered valid.validUntil - Timestamp after which the MSO should not be considered valid.expectedUpdate - Optionally included timestamp by the document signer (issuer) at which the MSO is
expected to be re-signed and the data elements will be potentially updated.deviceKey - JWK-formatted device key.mso_mdoc in verbose view mode:raw - The base64 URL-encoded device response structure.version - The version of the device response structure.status - Integer value where a value of 0 corresponds to the OK status message.documents - An array of verbosely decoded mDocs (only one entry in this case that contains the mDL) where
each entry is a JSON object that is composed of the following properties:
docType - The document type of the mDoc, which in this case will have a value of org.iso.18013.5.1.mDL.issuerSigned - JSON object that is composed of the following properties:
nameSpaces - An associative array between the requested data elements and the namespaces (identifiers)
they belong to.issuerAuth - JSON object that is composed of the following properties:
x5c - The x5chain element in the header of the IssuerAuth Cose_Sign1 structure.algorithm - The COSE Algorithm ID.protectedHeader - The protected header of the IssuerAuth Cose_Sign1 structure.payload - JSON object that provides a verbosely decoded version of the MSO and is composed of the
following properties:
docType - The document type of the mDoc, which in this case will have a value of org.iso.18013.5.1.mDL.version - The version of the mobile security object structure.digestAlgorithm - Identifier of the digest algorithm used.valueDigests - Digests of all data elements per namespace.validityInfo - JSON object that contains information related to the validity of the Mobile Security
Object (MSO) and its signature (defined above).deviceKeyInfo - JSON object that contains information related to the device key and is composed of
the following properties:
deviceKey - JWK-formatted device key.keyInfo - Optional structure that may contain extra information about the key.keyAuthorizations - Contains all the elements the device key may sign or MAC (optional field).status - Optional structure that may be used to provide MSO revocation information that is compared
to the information in an externally hosted MSO revocation list. This structure is provided as a JSON
object.deviceSigned - JSON object that is composed of the following properties:
nameSpaces - Contains the returned data elements as part of their corresponding namespaces.deviceAuth - Contains either the device signature or the device MAC element.In the following we provide an example that involves the presentation of all mandatory claims of an mDL.
Example Request
curl -X 'GET' \
'http://0.0.0.0:7003/openid4vc/session/{id}/presented-credentials' \
-H 'accept: application/json'
Example Response
{
"credentialsByFormat": {
"mso_mdoc": [
{
"type": "mso_mdoc_view_simple",
"version": "1.0",
"status": 0,
"documents": [
{
"docType": "org.iso.18013.5.1.mDL",
"nameSpaces": {
"org.iso.18013.5.1": {
"family_name": "Doe",
"given_name": "John",
"birth_date": "1986-03-22",
"issue_date": "2019-10-20",
"expiry_date": "2024-10-20",
"issuing_country": "AT",
"issuing_authority": "AT DMV",
"document_number": 123456789,
"portrait": [
141,
182,
121,
111,
238,
50,
120,
94,
54,
111,
113,
13,
241,
12,
12
],
"driving_privileges": [
{
"vehicle_category_code": "A",
"issue_date": "2018-08-09",
"expiry_date": "2024-10-20"
},
{
"vehicle_category_code": "B",
"issue_date": "2017-02-23",
"expiry_date": "2024-10-20"
}
],
"un_distinguishing_sign": "AT"
}
},
"certificateChain": [
"-----BEGIN CERTIFICATE-----\nMIICCTCCAbCgAwIBAgIUfqyiArJZoX7M61/473UAVi2/UpgwCgYIKoZIzj0EAwIwKDELMAkGA1UEBhMCQVQxGTAXBgNVBAMMEFdhbHRpZCBUZXN0IElBQ0EwHhcNMjUwNjAyMDY0MTEzWhcNMjYwOTAyMDY0MTEzWjAzMQswCQYDVQQGEwJBVDEkMCIGA1UEAwwbV2FsdGlkIFRlc3QgRG9jdW1lbnQgU2lnbmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPzp6eVSAdXERqAp8q8OuDEhl2ILGAaoaQXTJ2sD2g5Xp3CFQDMrMpR/SQ0jt/jTOqExk1PRzjQ79aKpIsJM1mqOBrDCBqTAfBgNVHSMEGDAWgBTxCn2nWMrE70qXb614U14BweY2azAdBgNVHQ4EFgQUx5qkOLC4lpl1xpYZGmF9HLxtp0gwDgYDVR0PAQH/BAQDAgeAMBoGA1UdEgQTMBGGD2h0dHBzOi8vd2FsdC5pZDAVBgNVHSUBAf8ECzAJBgcogYxdBQECMCQGA1UdHwQdMBswGaAXoBWGE2h0dHBzOi8vd2FsdC5pZC9jcmwwCgYIKoZIzj0EAwIDRwAwRAIgHTap3c6yCUNhDVfZWBPMKj9dCWZbrME03kh9NJTbw1ECIAvVvuGll9O21eR16SkJHHAA1pPcovhcTvF9fz9cc66M\n-----END CERTIFICATE-----"
],
"validityInfo": {
"signed": "2025-07-16T07:28:10.396597247Z",
"validFrom": "2025-07-16T07:28:10.396597357Z",
"validUntil": "2026-07-16T07:28:10.396597407Z"
},
"deviceKey": {
"kty": "EC",
"crv": "P-256",
"x": "wDAbXvruEhxgchNp_M-lCaTU-oVbk4iz90wBah3RoN0",
"y": "WHY287BApRNQkUrNQVEDUZUID5W0D8gi01EZpHG7e6I"
}
}
]
}
]
},
"viewMode": "simple"
}
On this page