Issue mDLs via OID4VC
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, an extension of OpenID, facilitating a secure exchange of credentials between issuers, holders and verifiers.
Setup
See how to access to the issuer API below.
- Deployed (Testing Only) - Use our deployed version for testing.
- Local - Run the API in your environment with our open-source setup.
Preparing for Issuance: Key Components
Before issuing the mDL credential, you'll need:
- The Claim Set: The claims you want to include in the mDL, those could range from Given Names, Date Of Birth, Height, Weight, Eye Colour and many more. The full list of standard compliant claims can be found in section 7.2 of the ISO/IEC 18103-5 standard document
- Signing Key: A cryptographic key used to sign the credential, confirming its authenticity and integrity during verification.
Example mDL Credential in JSON format:
{
"family_name": "Doe",
"given_name": "John",
"birth_date": "1980-01-01"
}
Note: mDLs are typically encoded in CBOR format, shown as JSON above for readability.
Issuing an mDL
In this section, we'll generate a signing key 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.
Step 1: Get a Signing Key
As the issuer API doesn't store any cryptographic key material by default. You need to provide the key used for signing the mDL in JWK format or a reference object that points to a key stored in an external KMS we support. At the moment, we support Hashicorp Vault and Oracle KMS. In a production environment, we recommend the usage of an external KMS provider to secure the key material.
To create keys for signing mDLs,
you can use the /onboard/issuer
endpoint. At the moment, you can only issue mDLs using the secp256r1
key algorithm. In the future others will also be supported. The DID generated by the endpoint is not needed for issuing
the mDL.
Please refer to our Key Management section to learn more about different options.
Create Key
Endpoint: /onboard/issuer
| API Reference
Example Request
curl -X 'POST' \
'http://0.0.0.0:7002/onboard/issuer' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"key": {
"backend": "jwk",
"keyType": "secp256r1"
},
"did": {
"method": "jwk"
}
}'
Body
{
"key": {
"backend": "jwk",
"keyType": "secp256r1"
},
"did": {
"method": "jwk"
}
}
Body Parameters
key
backend
: String - Specifies the storage type of key. It can bejwk
(manged by you),TSE
(managed by Hashicorp Vault) and others. Learn more about different types here.keyType
: String - the algorithm used to generate the key. For local, it can be ed25519, secp256k1, secp256r1, or RSA. For the other types and the supported algorithms, please go here.
did
:method
: String - Specifies the DID method. It can be key, jwk, web, cheqd.
Example Response
The onboard/issuer endpoint will return an object containing both the generated key in JWK format and the related DID.
{
"issuerKey": {
"type": "jwk",
"jwk": {
"kty": "EC",
"d": "J5KYuo0pbBnGtq_BENu__-9iE0ZFftdLK1Z7SreAu6I",
"crv": "P-256",
"kid": "bzo6SMsK2EMqYAV4FautEzeesrHVWYIw65uOzVUWPms",
"x": "mnFKBCL26GuAFCdtFSlgS9tO2rr0ZF9U66sHlM23eqw",
"y": "szq31hnWbiQmwaAUD-IkxaTQjRlFgO0L3z0dArwDyik"
}
},
"issuerDid": "did:key:zDnaet4APuXWCGttr3Yt9AWfNHQuZo2sUHpLfK9cQV5iCpjr3"
}
As we've used the 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.
Step 2: Issue the mDL
To issue mDLs, we will use the obtained key or any other supported JWK.
To facilitate the issuance of the mDL from us (the issuer) to the holder, we will utilise the OID4VCI protocol. In particular, we will be generating an OID4VC offer URL that can be accepted by any OID compliant wallet to receive credential(s).
The credential offer URL specifies the credentials to be issued. This includes details such as the URL of the issuer, the authorisation and token endpoints, and information about the credential's format, type, and scope.
When we execute the issuance command, two things will happen.
- The mDL will be signed with the provided key.
- The signed mDL will be embedded into the OID Credential Offer URL, which we can send off to our users to claim the credential(s).
Endpoint:/openid4vc/mdl/issue
| API Reference
Example Request
curl -X 'POST' \
'https://issuer.portal.walt.id/openid4vc/mdoc/issue' \
-H 'accept: */*' \
-H 'statusCallbackUri: https://example.com/$id' \
-H 'Content-Type: application/json' \
-d '{
"issuerKey": {
"type": "jwk",
"jwk": {
"kty": "EC",
"d": "J5KYuo0pbBnGtq_BENu__-9iE0ZFftdLK1Z7SreAu6I",
"crv": "P-256",
"kid": "bzo6SMsK2EMqYAV4FautEzeesrHVWYIw65uOzVUWPms",
"x": "mnFKBCL26GuAFCdtFSlgS9tO2rr0ZF9U66sHlM23eqw",
"y": "szq31hnWbiQmwaAUD-IkxaTQjRlFgO0L3z0dArwDyik"
}
},
"issuerDid": "",
"credentialConfigurationId": "org.iso.18013.5.1.mDL",
"mdocData": {
"org.iso.18013.5.1": {
"family_name": "Doe",
"given_name": "John",
"birth_date": "1980-01-01"
}
},
"x5Chain": [
"-----BEGIN CERTIFICATE-----\nMIIBRzCB7qADAgECAgg57ch6mnj5KjAKBggqhkjOPQQDAjAXMRUwEwYDVQQDDAxNRE9DIFJPT1QgQ0EwHhcNMjQwNTAyMTMxMzMwWhcNMjUwNTAyMTMxMzMwWjAbMRkwFwYDVQQDDBBNRE9DIFRlc3QgSXNzdWVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEG0RINBiF+oQUD3d5DGnegQuXenI29JDaMGoMvioKRBN53d4UazakS2unu8BnsEtxutS2kqRhYBPYk9RAriU3gaMgMB4wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwCgYIKoZIzj0EAwIDSAAwRQIhAI5wBBAA3ewqIwslhuzFn4rNFW9dkz2TY7xeImO7CraYAiAYhai1NzJ6abAiYg8HxcRdYpO4bu2Sej8E6CzFHK34Yw==\n-----END CERTIFICATE-----"
],
"trustedRootCAs": [
"-----BEGIN CERTIFICATE-----\nMIIBQzCB66ADAgECAgjbHnT+6LsrbDAKBggqhkjOPQQDAjAYMRYwFAYDVQQDDA1NRE9DIFJPT1QgQ1NQMB4XDTI0MDUwMjEzMTMzMFoXDTI0MDUwMzEzMTMzMFowFzEVMBMGA1UEAwwMTURPQyBST09UIENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWP0sG+CkjItZ9KfM3sLF+rLGb8HYCfnlsIH/NWJjiXkTx57ryDLYfTU6QXYukVKHSq6MEebvQPqTJT1blZ/xeKMgMB4wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwIDRwAwRAIgWM+JtnhdqbTzFD1S3byTvle0n/6EVALbkKCbdYGLn8cCICOoSETqwk1oPnJEEPjUbdR4txiNqkHQih8HKAQoe8t5\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
orhttps://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 idtype
: String - the event typedata
: 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
withjwt
being the issued jwtsdjwt_issue
withsdjwt
being the issued sdjwtbatch_jwt_issue
withjwt
being the issued jwtbatch_sdjwt_issue
withsdjwt
being the issued sdjwtgenerated_mdoc
withmdoc
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": "J5KYuo0pbBnGtq_BENu__-9iE0ZFftdLK1Z7SreAu6I",
"crv": "P-256",
"kid": "bzo6SMsK2EMqYAV4FautEzeesrHVWYIw65uOzVUWPms",
"x": "mnFKBCL26GuAFCdtFSlgS9tO2rr0ZF9U66sHlM23eqw",
"y": "szq31hnWbiQmwaAUD-IkxaTQjRlFgO0L3z0dArwDyik"
}
},
"issuerDid": "",
"credentialConfigurationId": "org.iso.18013.5.1.mDL",
"mdocData": {
"org.iso.18013.5.1": {
"family_name": "Doe",
"given_name": "John",
"birth_date": "1980-01-01"
}
},
"x5Chain": [
"-----BEGIN CERTIFICATE-----\nMIIBRzCB7qADAgECAgg57ch6mnj5KjAKBggqhkjOPQQDAjAXMRUwEwYDVQQDDAxNRE9DIFJPT1QgQ0EwHhcNMjQwNTAyMTMxMzMwWhcNMjUwNTAyMTMxMzMwWjAbMRkwFwYDVQQDDBBNRE9DIFRlc3QgSXNzdWVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEG0RINBiF+oQUD3d5DGnegQuXenI29JDaMGoMvioKRBN53d4UazakS2unu8BnsEtxutS2kqRhYBPYk9RAriU3gaMgMB4wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwCgYIKoZIzj0EAwIDSAAwRQIhAI5wBBAA3ewqIwslhuzFn4rNFW9dkz2TY7xeImO7CraYAiAYhai1NzJ6abAiYg8HxcRdYpO4bu2Sej8E6CzFHK34Yw==\n-----END CERTIFICATE-----"
],
"trustedRootCAs": [
"-----BEGIN CERTIFICATE-----\nMIIBQzCB66ADAgECAgjbHnT+6LsrbDAKBggqhkjOPQQDAjAYMRYwFAYDVQQDDA1NRE9DIFJPT1QgQ1NQMB4XDTI0MDUwMjEzMTMzMFoXDTI0MDUwMzEzMTMzMFowFzEVMBMGA1UEAwwMTURPQyBST09UIENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWP0sG+CkjItZ9KfM3sLF+rLGb8HYCfnlsIH/NWJjiXkTx57ryDLYfTU6QXYukVKHSq6MEebvQPqTJT1blZ/xeKMgMB4wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwIDRwAwRAIgWM+JtnhdqbTzFD1S3byTvle0n/6EVALbkKCbdYGLn8cCICOoSETqwk1oPnJEEPjUbdR4txiNqkHQih8HKAQoe8t5\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.mDLmdocData
: JSON - Claims to be added to the mDL.x5Chain
: (optional) - contains the certificate chain used to verify the issuer's digital signature.trustedRootCAs
: (optional) - contains trusted root certificate to validate the authenticity of the issuer's certificate chain.
Example Response
The issuer endpoint will respond with Credential Offer URL.
Plain Response
openid-credential-offer://issuer.potential.walt-test.cloud/?credential_offer_uri=https%3A%2F%2Fissuer.potential.walt-test.cloud%2Fopenid4vc%2FcredentialOffer%3Fid%3Df77ed4b0-fba3-4af2-ab82-3d400cfd1288
Decoded
{
"credential_issuer": "https://issuer.potential.walt-test.cloud",
"credential_configuration_ids": [
"org.iso.18013.5.1.mDL"
],
"grants": {
"authorization_code": {
"issuer_state": "f77ed4b0-fba3-4af2-ab82-3d400cfd1288"
},
"urn:ietf:params:oauth:grant-type:pre-authorized_code": {
"pre-authorized_code": "eyJhbGciOiJFZERTQSJ9.eyJzdWIiOiJmNzdlZDRiMC1mYmEzLTRhZjItYWI4Mi0zZDQwMGNmZDEyODgiLCJpc3MiOiJodHRwczovL2lzc3Vlci5wb3RlbnRpYWwud2FsdC10ZXN0LmNsb3VkIiwiYXVkIjoiVE9LRU4ifQ.1PCr61O0bX_LMvAKS4dqyM-EqsXuWhAL6vc_dd38KfpbDlDYXEjoiKVuwXMJ4UMcm2LJFRoS0FJ7MH2dfl30Bg"
}
}
}
Step 3: 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! 🎉