How to Issue Mobile Driver’s Licenses (ISO/IEC 18013-5 mDLs) via OID4VCI with walt.id

This guide provides a comprehensive walkthrough for issuing an mDL credential based on the ISO/IEC 18103-5 standard using the walt.id Issuer API. The issuance process will utilize the OID4VCI protocol.

Setup

See how to access to the issuer API below.

Preparing for Issuance: Key Components

The mobile driver's license (mDL) ecosystem builds on established PKI principles that employ X.509 digital certificates, while introducing domain-specific roles and data models. Its two main components are:

  1. IACA — Issuing Authority Certification Authority: This is an authority that serves as the root of trust vouching for authorized credential issuers and is represented by a self-signed X.509 digital certificate that is published to verifiers.
  2. DS — Document Signer (Credential Issuer): This is the entity that issues and cryptographically signs mDL credential(s). It holds a private key and an X.509 digital certificate issued by an IACA. Typically, DS certificates are short-lived and are rotated periodically.
  3. The mDL Claims: The mDL is a verifiable credential that contains claims about the subject (holder), such as name, birthdate and driving privileges. The ISO/IEC 18103-5 standard document defines a set of mandatory fields (e.g., family_name, birth_date) that must be included in every mDL, as well as a set of optional fields (e.g., age_over_NN attestations, height, weight, sex).

Example mDL Credential in JSON format:

{
  "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, 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"
}

Note: mDLs are encoded in CBOR, which is a binary format and is shown in JSON representation above for readability purposes. Claims values that are encoded as JSON integer arrays, e.g., the portrait claim's value, represent byte arrays.

Issuing an mDL

In this section, we'll walk through the steps required to successfully issue an mDL by generating signing keys (for the IACA & the DS) which we store ourselves. For production environments, we recommend using an external KMS provider for key management due to the enhanced security. Learn more about the different types of keys and the storage options here.

Note: At the moment, you can only use secp256r1 keys to onboard IACAs and DSs and to issue mDLs.

Step 1: Onboard an Issuing Authority Certification Authority (IACA)

Every mobile driver’s license ecosystem starts with a trusted root certificate of an IACA. The IACA serves as the cryptographic authority that issues DS certificates that are deemed trustworthy.

🔧 What this step does

  • Create a self-signed X.509 root certificate (CA=true).
  • Encode in it identity info (e.g., country, organization) for the IACA.
  • Include optional extensions (e.g., IACA alternative names, CRL distribution URIs).

IACA Onboarding Request

CURL

Endpoint: /onboard/iso-mdl/iacas | API Reference

Example Request

curl -X 'POST' \
  'http://0.0.0.0:7002/onboard/iso-mdl/iacas' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "certificateData": {
    "country": "US",
    "commonName": "Example IACA",
    "issuerAlternativeNameConf": {
      "uri": "https://iaca.example.com"
    }
  }
}'

Body

{
  "certificateData": {
    "country": "US",
    "commonName": "Example IACA",
    "issuerAlternativeNameConf": {
      "uri": "https://iaca.example.com"
    }
  }
}

Body Parameters

  • certificateData: JSON - Data that will be encoded in the generated certificate of the IACA.
    • country: String - Two-letter ISO 3166-1 alpha-2 country code (e.g., US, DE).
    • commonName: String - Human-readable name of the issuing authority (e.g., Ministry of Transport).
      • issuerAlternativeNameConf: JSON - Metadata for the IACA; can include uri and/or email.
        • uri: String - Uniform resource identifier regarding the IACA's contact information.
        • email: String - RFC822 name for contacting the IACA.

Example Response

The IACA onboarding endpoint will return an object containing the generated signing key of the IACA in JWK format, the PEM-encoded, JSON stringified self-signed X.509 certificate of the IACA and an object that contains the data that is encoded in the generated certificate (for convenience - useful for the DS onboadrding endpoint below).

{
  "iacaKey": {
    "type": "jwk",
    "jwk": {
      "kty": "EC",
      "d": "u-UvsghdzpSXv5HmG5ngvm4Dv8yyRYw9fKA6mdp1KWs",
      "crv": "P-256",
      "kid": "R_E_QZ-Ea6etoAdWfUHSjjexRYz447ffnnfIO9kxn_Y",
      "x": "n_b1GmZTSEhioK3z8MGqcb7nxXqyjFaLR-OfKOnspwU",
      "y": "nGRVvuHTtEAZ1HjgdLaLZnYxrkiRV_e4V2Wz0qVWa-M"
    }
  },
  "certificatePEM": "-----BEGIN CERTIFICATE-----\nMIIBtTCCAVqgAwIBAgIUNlgkpoam39UxORhMNRkwuFzD9pQwCgYIKoZIzj0EAwIwJDELMAkGA1UEBhMCVVMxFTATBgNVBAMMDEV4YW1wbGUgSUFDQTAeFw0yNTA1MjgxMjIzMDFaFw00MDA1MjQxMjIzMDFaMCQxCzAJBgNVBAYTAlVTMRUwEwYDVQQDDAxFeGFtcGxlIElBQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASf9vUaZlNISGKgrfPwwapxvufFerKMVotH458o6eynBZxkVb7h07RAGdR44HS2i2Z2Ma5IkVf3uFdls9KlVmvjo2owaDAdBgNVHQ4EFgQUjCMRsfolTeK5Ds6MqOWj5Nx01BQwEgYDVR0TAQH/BAgwBgEB/wIBADAjBgNVHRIEHDAahhhodHRwczovL2lhY2EuZXhhbXBsZS5jb20wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMCA0kAMEYCIQCnUfp3OyxcaPCT34SQ4dTNyNN0qgxKWpWIDeUXkrs7HwIhALFYrMrINeAats4ZWRxZMK6bykb9dcOwkmBCv96MoZVi\n-----END CERTIFICATE-----\n",
  "certificateData": {
    "country": "US",
    "commonName": "Example IACA",
    "issuerAlternativeNameConf": {
      "uri": "https://iaca.example.com"
    }
  }
}

Defaults:

  • A local (jwk) secp256r1 key is automatically created.
  • Validity defaults to 15 years, unless otherwise specified.

As we've used the local (jwk) key type, it's important to note that we need to save the returned values ourselves for future reference. The API doesn't save any information about created keys.

Remember to save the value of the IACA's PEM-encoded X.509 certificate (certificatePEM field in the response) as it is required for verification of mDLs.

Step 2: Onboard a Document Signer

Document signers are the entities responsible for signing mDL credentials. These entities are identified by an X.509 digital certificate that is signed by an IACA and contains, among others, a special purpose extension that marks it as valid for mDL issuance.

🔧 What this step does

  • Issue a DS certificate signed by the input IACA private key.
  • Embed an Extended Key Usage (EKU) value marking it as suitable for mDL signing.
  • Include CRL distribution info and various other optional fields.

DS Onboarding Request

CURL

Endpoint: /onboard/iso-mdl/document-signers | API Reference

Example Request

curl -X 'POST' \
  'http://0.0.0.0:7002/onboard/iso-mdl/document-signers' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "iacaSigner": {
    "iacaKey": {
      "type": "jwk",
      "jwk": {
        "kty": "EC",
        "crv": "P-256",
        "kid": "R_E_QZ-Ea6etoAdWfUHSjjexRYz447ffnnfIO9kxn_Y",
        "x": "n_b1GmZTSEhioK3z8MGqcb7nxXqyjFaLR-OfKOnspwU",
        "y": "nGRVvuHTtEAZ1HjgdLaLZnYxrkiRV_e4V2Wz0qVWa-M",
        "d": "u-UvsghdzpSXv5HmG5ngvm4Dv8yyRYw9fKA6mdp1KWs"
      }
    },
    "certificateData": {
      "country": "US",
      "commonName": "Example IACA",
      "issuerAlternativeNameConf": {
        "uri": "https://iaca.example.com"
      }
    }
  },
  "certificateData": {
    "country": "US",
    "commonName": "Example DS",
    "crlDistributionPointUri": "https://iaca.example.com/crl"
  }
}'

Body

{
  "iacaSigner": {
    "iacaKey": {
      "type": "jwk",
      "jwk": {
        "kty": "EC",
        "crv": "P-256",
        "kid": "R_E_QZ-Ea6etoAdWfUHSjjexRYz447ffnnfIO9kxn_Y",
        "x": "n_b1GmZTSEhioK3z8MGqcb7nxXqyjFaLR-OfKOnspwU",
        "y": "nGRVvuHTtEAZ1HjgdLaLZnYxrkiRV_e4V2Wz0qVWa-M",
        "d": "u-UvsghdzpSXv5HmG5ngvm4Dv8yyRYw9fKA6mdp1KWs"
      }
    },
    "certificateData": {
      "country": "US",
      "commonName": "Example IACA",
      "issuerAlternativeNameConf": {
        "uri": "https://iaca.example.com"
      }
    }
  },
  "certificateData": {
    "country": "US",
    "commonName": "Example DS",
    "crlDistributionPointUri": "https://iaca.example.com/crl"
  }
}

Body Parameters

  • iacaSigner: JSON - Object containing necessary data related to the signing IACA.
    • iacaKey: JSON - The JSON serialized signing key of the issuing IACA - used as is from the its respective onboarding endpoint.
    • certificateData: JSON - The data that is encoded in the IACA's X.509 certificate - used as is from its respective onboarding endpoint.
  • certificateData: JSON - Data that will be encoded in the generated certificate of the DS.
    • country: String - Two-letter ISO 3166-1 alpha-2 country code (e.g., US, DE).
    • commonName: String - Human-readable name of the issuing authority (e.g., Ministry of Transport).
    • crlDistributionPointUri: String - URL where the relevant certificate revocation list (CRL) is published.

Requirements

  • Must provide the IACA's signing key and certificate data (as obtained from the IACA onboarding endpoint).
  • Countries and state/province fields must match between IACA and DS.
  • CRL URI is mandatory for DS onboarding.

Example Response

The DS onboarding endpoint will return an object containing the generated signing key of the DS in JWK format, the PEM-encoded, JSON stringified X.509 certificate of the DS that is signed by the IACA signing key in the request and an object that contains the data that is encoded in the generated certificate of the DS (for convenience).

{
  "documentSignerKey": {
    "type": "jwk",
    "jwk": {
      "kty": "EC",
      "d": "ZSHgIcRvbwV9s224kHUaFqkEPShCAdwXocGl_w3M42Q",
      "crv": "P-256",
      "kid": "pX99OZjL2iNqM7OMkE1r1rYyuAObvPntewcDHdc2bMM",
      "x": "GWKpdL3jPoPJ5wKgSA-jxS2jgp-ZUDE6sIQbeB86vF0",
      "y": "F3xAwH96_xVciV7mFQslU_eRQgP-5pSZiNf8bjMoGfo"
    }
  },
  "certificatePEM": "-----BEGIN CERTIFICATE-----\nMIICCDCCAa2gAwIBAgIUDo8kr194t6sttt6KL3YcnMtcaYYwCgYIKoZIzj0EAwIwJDELMAkGA1UEBhMCVVMxFTATBgNVBAMMDEV4YW1wbGUgSUFDQTAeFw0yNTA1MjkwNzE4MzlaFw0yNjA4MjkwNzE4MzlaMCIxCzAJBgNVBAYTAlVTMRMwEQYDVQQDDApFeGFtcGxlIERTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGWKpdL3jPoPJ5wKgSA+jxS2jgp+ZUDE6sIQbeB86vF0XfEDAf3r/FVyJXuYVCyVT95FCA/7mlJmI1/xuMygZ+qOBvjCBuzAfBgNVHSMEGDAWgBSMIxGx+iVN4rkOzoyo5aPk3HTUFDAdBgNVHQ4EFgQU7S49LSeg/e0onfT44FVbL/rSKnswDgYDVR0PAQH/BAQDAgeAMCMGA1UdEgQcMBqGGGh0dHBzOi8vaWFjYS5leGFtcGxlLmNvbTAVBgNVHSUBAf8ECzAJBgcogYxdBQECMC0GA1UdHwQmMCQwIqAgoB6GHGh0dHBzOi8vaWFjYS5leGFtcGxlLmNvbS9jcmwwCgYIKoZIzj0EAwIDSQAwRgIhAMuSq75BPBXXBWGtIMd57fhRqpKf3Yzl3ldDdoQsK2xEAiEA/dmWLMLiJPV3UzmQS5MUHtn611z0VlL/k3YAdaVJ51c=\n-----END CERTIFICATE-----\n",
  "certificateData": {
    "country": "US",
    "commonName": "Example DS",
    "crlDistributionPointUri": "https://iaca.example.com/crl"
  }
}

Defaults:

  • A local (jwk) secp256r1 key is automatically created.
  • Validity defaults to 457 days, unless otherwise specified.

As we've used the local (jwk) key type, it's important to note that we need to save the returned values ourselves for future reference. The API doesn't save any information about created keys.

Remember to save the value of the DS PEM-encoded X.509 certificate (certificatePEM field in the response) as it is required for the next (issuance) step.

Step 3: Issue the mDL

To issue mDLs, we will use the document signers key (obtained previously).

To facilitate the issuance of the mDL from us (the document signer, or issuer) to the holder, we will utilize the OID4VCI protocol. In particular, we will be generating a credential offer URL that can be accepted by any compliant wallet to receive the credential.

The credential offer URL specifies the credentials to be issued. This includes details such as the URL of the issuer and information about the credential's format and type.

mDL Issuance Request

CURL

Endpoint:/openid4vc/mdoc/issue | API Reference

Example Request

curl -X 'POST' \
  'https://issuer.portal.test.waltid.cloud/openid4vc/mdoc/issue' \
  -H 'accept: */*' \
  -H 'statusCallbackUri: https://example.com/$id' \
  -H 'Content-Type: application/json' \
  -d '{
  "issuerKey": {
    "type": "jwk",
    "jwk": {
      "kty": "EC",
      "d": "-wSIL_tMH7-mO2NAfHn03I8ZWUHNXVzckTTb96Wsc1s",
      "crv": "P-256",
      "kid": "sW5yv0UmZ3S0dQuUrwlR9I3foREBHHFwXhGJGqGEVf0",
      "x": "Pzp6eVSAdXERqAp8q8OuDEhl2ILGAaoaQXTJ2sD2g5U",
      "y": "6dwhUAzKzKUf0kNI7f40zqhMZNT0c40O_WiqSLCTNZo"
    }
  },
  "credentialConfigurationId": "org.iso.18013.5.1.mDL",
  "mdocData": {
    "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 ],
      "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"
    }
  },
  "x5Chain": [
    "-----BEGIN CERTIFICATE-----\nMIICCTCCAbCgAwIBAgIUfqyiArJZoX7M61/473UAVi2/UpgwCgYIKoZIzj0EAwIwKDELMAkGA1UEBhMCQVQxGTAXBgNVBAMMEFdhbHRpZCBUZXN0IElBQ0EwHhcNMjUwNjAyMDY0MTEzWhcNMjYwOTAyMDY0MTEzWjAzMQswCQYDVQQGEwJBVDEkMCIGA1UEAwwbV2FsdGlkIFRlc3QgRG9jdW1lbnQgU2lnbmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPzp6eVSAdXERqAp8q8OuDEhl2ILGAaoaQXTJ2sD2g5Xp3CFQDMrMpR/SQ0jt/jTOqExk1PRzjQ79aKpIsJM1mqOBrDCBqTAfBgNVHSMEGDAWgBTxCn2nWMrE70qXb614U14BweY2azAdBgNVHQ4EFgQUx5qkOLC4lpl1xpYZGmF9HLxtp0gwDgYDVR0PAQH/BAQDAgeAMBoGA1UdEgQTMBGGD2h0dHBzOi8vd2FsdC5pZDAVBgNVHSUBAf8ECzAJBgcogYxdBQECMCQGA1UdHwQdMBswGaAXoBWGE2h0dHBzOi8vd2FsdC5pZC9jcmwwCgYIKoZIzj0EAwIDRwAwRAIgHTap3c6yCUNhDVfZWBPMKj9dCWZbrME03kh9NJTbw1ECIAvVvuGll9O21eR16SkJHHAA1pPcovhcTvF9fz9cc66M\n-----END CERTIFICATE-----\n"
  ]
}'

Header Parameters

  • statusCallbackUri: URL - Receive updates on the created issuance process, e.g. when a credential was successfully claimed. The parameter expects a URL which can accept a JSON POST request. The URL can also hold a $id, which will be replaced by the issuance session id. For example: https://myurl.com/$id, https://myurl.com or https://myurl.com/test/$id

    Expand To Learn More

    Body

    The data send to the provided URL will contain a JSON body:

    • id : String - the issuance session id
    • type: String - the event type
    • data: JsonObject - the data for the event

    Event Types

    Possible events (event types) and their data are:

    • resolved_credential_offer with the credential offer as JSON (in our Web Wallet: called when the issuance offer is entered into the wallet, but not processing / accepted yet)
    • requested_token with the issuance request for the token as json object (called for the token required to receive the credentials)

    Credential issuance (called for every credential that's issued (= requested from wallet))

    • jwt_issue with jwt being the issued jwt
    • sdjwt_issue with sdjwt being the issued sdjwt
    • batch_jwt_issue with jwt being the issued jwt
    • batch_sdjwt_issue with sdjwt being the issued sdjwt
    • generated_mdoc with mdoc being the CBOR (HEX) of the signed mdoc

    To allow for secure business logic flows, if a callback URL is set, and it cannot be reached, the issuance will not commence further (after that point). If no callback URL is set, the issuance logic does not change in any way.

Body

{
  "issuerKey": {
    "type": "jwk",
    "jwk": {
      "kty": "EC",
      "d": "-wSIL_tMH7-mO2NAfHn03I8ZWUHNXVzckTTb96Wsc1s",
      "crv": "P-256",
      "kid": "sW5yv0UmZ3S0dQuUrwlR9I3foREBHHFwXhGJGqGEVf0",
      "x": "Pzp6eVSAdXERqAp8q8OuDEhl2ILGAaoaQXTJ2sD2g5U",
      "y": "6dwhUAzKzKUf0kNI7f40zqhMZNT0c40O_WiqSLCTNZo"
    }
  },
  "credentialConfigurationId": "org.iso.18013.5.1.mDL",
  "mdocData": {
    "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 ],
      "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"
    }
  },
  "x5Chain": [
    "-----BEGIN CERTIFICATE-----\nMIICCTCCAbCgAwIBAgIUfqyiArJZoX7M61/473UAVi2/UpgwCgYIKoZIzj0EAwIwKDELMAkGA1UEBhMCQVQxGTAXBgNVBAMMEFdhbHRpZCBUZXN0IElBQ0EwHhcNMjUwNjAyMDY0MTEzWhcNMjYwOTAyMDY0MTEzWjAzMQswCQYDVQQGEwJBVDEkMCIGA1UEAwwbV2FsdGlkIFRlc3QgRG9jdW1lbnQgU2lnbmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPzp6eVSAdXERqAp8q8OuDEhl2ILGAaoaQXTJ2sD2g5Xp3CFQDMrMpR/SQ0jt/jTOqExk1PRzjQ79aKpIsJM1mqOBrDCBqTAfBgNVHSMEGDAWgBTxCn2nWMrE70qXb614U14BweY2azAdBgNVHQ4EFgQUx5qkOLC4lpl1xpYZGmF9HLxtp0gwDgYDVR0PAQH/BAQDAgeAMBoGA1UdEgQTMBGGD2h0dHBzOi8vd2FsdC5pZDAVBgNVHSUBAf8ECzAJBgcogYxdBQECMCQGA1UdHwQdMBswGaAXoBWGE2h0dHBzOi8vd2FsdC5pZC9jcmwwCgYIKoZIzj0EAwIDRwAwRAIgHTap3c6yCUNhDVfZWBPMKj9dCWZbrME03kh9NJTbw1ECIAvVvuGll9O21eR16SkJHHAA1pPcovhcTvF9fz9cc66M\n-----END CERTIFICATE-----\n"
  ]
}

Body Parameters

  • issuerKey: JSON - A JWK or reference object to a key stored in an external KMS to sign the mDL with. Supported algorithms: secp256r1.
    JWK Format: {"type": "jwk", "jwk": Here JSON Web Key Object}. Can be provided as String Or JWK object to " issuerKey".
    KMS Key: Please refer to the Key Management Section and the KMS you want to use for more details on the structure of the reference object.
  • credentialConfigurationId: String - org.iso.18013.5.1.mDL
  • mdocData: JSON - Claims to be added to the mDL. In the provided example above, all mandatory claims are included.
  • x5Chain: JSON Array - Must contain a single entry that contains the PEM-encoded, JSON stringified X.509 certificate of the DS (as output by the respective onboarding endpoint).

Example Response

The issuer endpoint will respond with Credential Offer URL.

Plain Response

openid-credential-offer://issuer.portal.test.waltid.cloud/draft13/?credential_offer_uri=https%3A%2F%2Fissuer.portal.test.waltid.cloud%2Fdraft13%2FcredentialOffer%3Fid%3D52b19ff5-5b42-423a-ad16-63099760baea

Decoded

{
  "credential_issuer": "https://issuer.portal.test.waltid.cloud/draft13",
  "grants": {
    "urn:ietf:params:oauth:grant-type:pre-authorized_code": {
      "pre-authorized_code": "eyJ0eXBlIjoiand0IiwiYWxnIjoiRVMyNTYiLCJraWQiOiJ0R3pBbUthdE0tbzlicDM1Y291aDZUUXhxZHpVeFgzQWwtcU9iVXhZelh3In0.eyJzdWIiOiI1MmIxOWZmNS01YjQyLTQyM2EtYWQxNi02MzA5OTc2MGJhZWEiLCJpc3MiOiJodHRwczovL2lzc3Vlci5wb3J0YWwudGVzdC53YWx0aWQuY2xvdWQvZHJhZnQxMyIsImF1ZCI6IlRPS0VOIn0.aAgMH0XTq2YVTpsSGtwPIkinTai6Hr0iJyBttDOljPD2WV2kJolZR35K9hiqpz4ZBwGqUbHjmpYaywQqxb8Wfg"
    }
  },
  "credential_configuration_ids": [
    "org.iso.18013.5.1.mDL"
  ]
}

Step 4: Receive the Credential Offer

The created credential offer can now be embedded into a QR code for users to scan with their mobile wallet or pasted manually into the credential offer field of our web wallet.

Try It Out: Use our web wallet for a practical demonstration. After logging in, click the 'request credential' button and paste the received Offer URL into the text field below the camera.

🎉 Congratulations, you've issued a mDL using OID4VCI! 🎉

Last updated on June 24, 2025