Issue W3C Credentials (JWT / SD-JWT) via OID4VC
This guide provides a walkthrough for issuing a Verifiable Credential based on the W3C standard with a JWT or SD-JWT signature using the walt.id Enterprise issuer API. The issuance 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 credentials and present them in a way that's consistent and secure across platforms ensuring interoperability.
Enterprise Service Dependencies
To issue a credential, you need to have the following enterprise services setup:
- Issuer Service - Have a running issuer service. Setup instructions can be found here.
- KMS Service - Have a running KMS service. Setup instructions can be found here.
- DID Service - Have a running DID service. Setup instructions can be found here.
Preparing for Issuance: Key Components
Before issuing a verifiable credential, you'll need:
- Raw Credential Data: The information you want to issue as a VC, typically following a specific template or schema.
- Signing Key & DID: A cryptographic key used to sign the credential, confirming its authenticity and integrity during verification and a correlated DID.
During the setup of issuer service, you have the option to specify which credential types the issuer will
support. It
is important to ensure that the credential type you wish to issue is included in this selection. For the purposes of
this guide, we will focus on using a W3C UniversityDegree
credential.
Example Credential: Verifiable University Degree
{
// The contextual information required for data integrity and 1.api
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"id": "http://example.gov/credentials/3732",
// The credential's unique identifier
"type": [
"VerifiableCredential",
"UniversityDegree"
// Specifies the kind of credential being issued
],
"issuer": {
"id": "did:web:vc.transmute.world"
// The issuer's unique identifier (DID)
},
"issuanceDate": "2020-03-10T04:24:12.164Z",
// When the credential was issued
"credentialSubject": {
// Information about the credential's recipient and the degree earned
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"degree": {
"type": "BachelorDegree",
"name": "Bachelor of Science and Arts"
}
}
}
Note: Fields like issuer
, issuanceDate
, and credentialSubject.did
are dynamic and will be populated with
actual
data upon issuance.
Issuing a Credential
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.
Step 1: Get a Signing Key & Issuer DID
Please use the KMS service to create a key and the DID service to generate a DID for that key and have them at hand for the next section.
Step 2: Issue the Credential
Now, we'll issue a verifiable credential using the key and DID obtained. You can choose to either issue the W3C credential as JWT or SD-JWT, which are two different signature types. JWT referring to a JSON-Web-Token and SD-JWT to a Selective-Disclosure JSON Web Token.
Learn more about Selective Disclosure here. In short, it enables your users to only reveal a subset of the claims in a credential to a verifier when suitable. This increases privacy, reduces the risk of identity theft and other types of fraud.
To facilitate the issuance of the credential 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 credential will be signed with the provided key and the chosen signature type (JWT, SD-JWT).
- The information about the singed credential will be embedded into the OID Credential Offer URL, which we can send off to our users to claim the credential(s).
You can receive updates on your issuance progress, including notifications for successfully claimed credentials, by providing the callback header parameter.
Endpoint: /v1/{target}/issuer-service-api/credentials/issue
| API Reference
Example Request
curl -X 'POST' \
'https://{orgID}.enterprise-sandbox.waltid.dev/v1/{target}/issuer-service-api/credentials/issue' \
-H 'accept: */*' \
-H 'Authorization: Bearer {yourToken}' \
-H 'statusCallbackUri: https://example.com/$id' \
-H 'Content-Type: application/json' \
-d '{
"issuerKeyId": "waltid.tenant1.kms1.key1",
"issuerDid": "did:key:z6MkjoRhq1jSNJdLiruSXrFFxagqrztZaXHqHGUTKJbcNywp",
"authenticationMethod": "PRE_AUTHORIZED",
"credentialConfigurationId": "UniversityDegree_jwt_vc_json",
"credentialData": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"id": "http://example.gov/credentials/3732",
"type": [
"VerifiableCredential",
"UniversityDegree"
],
"issuer": {
"id": "did:web:vc.transmute.world"
},
"issuanceDate": "2020-03-10T04:24:12.164Z",
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"degree": {
"type": "BachelorDegree",
"name": "Bachelor of Science and Arts"
}
}
},
"mapping": {
"id": "<uuid>",
"issuer": {
"id": "<issuerDid>"
},
"credentialSubject": {
"id": "<subjectDid>"
},
"issuanceDate": "<timestamp>",
"expirationDate": "<timestamp-in:365d>"
}
}'
Path Parameters
orgID
: - When performing operations within an organization, it is essential to use the organization's Base URL or another valid host alias. For example, if your organization is namedtest
, your default Base URL will betest.enterprise-sandbox.walt.dev
when using the sandbox environment.target
: resourceIdentifier - The target indicates the organization + tenant + issuer service with which to execute the credential issuance ({organizationID}.{tenantID}.{issuerServiceID}
), e.g.waltid.tenant1.issuer1
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
{
"issuerKeyId": "waltid.tenant1.kms1.key1",
"issuerDid": "did:key:z6MkjoRhq1jSNJdLiruSXrFFxagqrztZaXHqHGUTKJbcNywp",
"authenticationMethod": "PRE_AUTHORIZED",
"credentialConfigurationId": "UniversityDegree_jwt_vc_json",
"credentialData": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"id": "http://example.gov/credentials/3732",
"type": [
"VerifiableCredential",
"UniversityDegree"
],
"issuer": {
"id": "did:web:vc.transmute.world"
},
"issuanceDate": "2020-03-10T04:24:12.164Z",
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"degree": {
"type": "BachelorDegree",
"name": "Bachelor of Science and Arts"
}
}
},
"mapping": {
"id": "<uuid>",
"issuer": {
"id": "<issuerDid>"
},
"credentialSubject": {
"id": "<subjectDid>"
},
"issuanceDate": "<timestamp>",
"expirationDate": "<timestamp-in:365d>"
}
}
Body Parameters
issuerKeyId
: resourceIdentifier - The keyID of a key stored in a KMS service under the same tenant. E.g.waltid.tenant1.kms1.key1
issuerDid
: String - The DID related to the provided key.credentialConfigurationId
: String - Reference to a specific credential configuration the issuer supports. Configured during issuer service setup.credentialData
: JSON - A credential data structure to sign (W3C compliant). A list of predefined valid structures can be found here.mapping
(optional): JSON - The mapping object that allows for dynamic value insertion via data functions, executed at the time when the credentials is claimed. This feature enables personalized credentials based on real-time data. Learn more about it and see a list of supported data functions here.authenticationMethod
: (optional) String - Defines which OIDC4VC exchange flow is used (pre-auth or full-auth). If no value is provided, it will default to pre-auth flow indicated asPRE_AUTHORIZED
. The parameter options are:
Expand To Learn More About The Options
PWD
- used for authorization code flow with username/password authentication with external auth serverID_TOKEN
- used for authorization code flow with id_token authentication. When this method is used an authorization request is sent to the wallet, which generates and issues the ID_TOKEN using its DID ( Decentralized Identifier). The issuer then verifies the ID_TOKEN to confirm the holder's identity and DID.VP_TOKEN
- used for authorization code flow with vp_token(OIDC4VP). With this method, the receiver of the credential must be authenticated by presenting the requested credential. The credential which must be presented can be configured using thevpRequestedValue
as explained below. The policies which are applied to the presented credential are 'signature,' 'expired,' and 'not-before.' Learn more about policies here.NONE
- used for authorization code flow with none authentication methodPRE_AUTHORIZED
- used for pre-authorizated code flow
Additional parameters required for selected auth method:
For "ID_TOKEN" and "VP_TOKEN":
useJar
: (Optional) Boolean - used for using JAR OAuth specification in the id/vp_token requests. If omitted, the default value is true.
For VP_TOKEN:
vpRequestedValue
: String - Specifies the requested credential type value for the VP token. E.g. "VerifiableId"vpProfile
: (Optional) String - Specifies the profile of the VP request. Available Profiles: DEFAULT: For W3C OpenID4VP, ISO_18013_7_MDOC: For MDOC OpenID4VP, EBSIV3: For EBSI V3 Compliant VP. If omitted, the default value is DEFAULT
Example Response
The issuer endpoint will respond with Credential Offer URL.
Plain Response
openid-credential-offer://issuer.portal.walt.id/?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fissuer.portal.walt.id%22%2C%22credentials%22%3A%5B%7B%22format%22%3A%22jwt_vc_json%22%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegree%22%5D%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww.w3.org%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww.w3.org%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegree%22%5D%7D%7D%5D%2C%22grants%22%3A%7B%22authorization_code%22%3A%7B%22issuer_state%22%3A%220431b78c-cd94-4f50-bfdf-e24d436c0cf6%22%7D%2C%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22eyJhbGciOiJFZERTQSJ9.eyJzdWIiOiIwNDMxYjc4Yy1jZDk0LTRmNTAtYmZkZi1lMjRkNDM2YzBjZjYiLCJpc3MiOiJodHRwczovL2lzc3Vlci5wb3J0YWwud2FsdC5pZCIsImF1ZCI6IlRPS0VOIn0.NorG7GtjmA-HXMJfUzU9vfnshcIgFY0oYQb8qJjDfORPoNxuurgySSOIDKFi7Z4XJmC-oJZnM0Nbb0NUd57cDA%22%2C%22user_pin_required%22%3Afalse%7D%7D%7D
Decoded
openid-credential-offer://issuer.portal.walt.id/?
credential_offer=
{
"credential_issuer": "https://issuer.portal.walt.id",
"credentials": [
{
"format": "jwt_vc_json",
"types": [
"VerifiableCredential",
"UniversityDegree"
],
"credential_definition": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"types": [
"VerifiableCredential",
"UniversityDegree"
]
}
}
],
"grants": {
"authorization_code": {
"issuer_state": "0431b78c-cd94-4f50-bfdf-e24d436c0cf6"
},
"urn:ietf:params:oauth:grant-type:pre-authorized_code": {
"pre-authorized_code": "eyJhbGciOiJFZERTQSJ9.eyJzdWIiOiIwNDMxYjc4Yy1jZDk0LTRmNTAtYmZkZi1lMjRkNDM2YzBjZjYiLCJpc3MiOiJodHRwczovL2lzc3Vlci5wb3J0YWwud2FsdC5pZCIsImF1ZCI6IlRPS0VOIn0.NorG7GtjmA-HXMJfUzU9vfnshcIgFY0oYQb8qJjDfORPoNxuurgySSOIDKFi7Z4XJmC-oJZnM0Nbb0NUd57cDA",
"user_pin_required": false
}
}
}
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 W3C-compliant Verifiable Credential using OID4VCI! 🎉