Issuing Verifiable Credentials via OID4VC

This guide provides a comprehensive walkthrough for issuing a Verifiable Credential based on the W3C standard using the walt.id 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.

Setup

See how to access to the issuer API below.

Preparing for Issuance: Key Components

Before issuing a verifiable credential, you'll need:

  1. Raw Credential Data: The information you want to issue as a VC, typically following a specific template or schema.
  2. Signing Key: A cryptographic key used to sign the credential, confirming its authenticity and integrity during verification.

The walt.id's issuer API supports various W3C credential types, allowing you to define custom credentials or select predefined schemas from our credential repository. For this example we will issue a Verifiable University Degree, though the process is the same for other credentials.

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",
    "UniversityDegreeCredential"
    // 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 local signing key using the wallet-api a verifiable credential. For production environments, we recommend using TSE keys due to their enhanced security. Learn more about TSE keys here (coming soon)

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 credential in JWK format with every operation. With the walt.id enterprise license, we offer pre-build integrations with popular KMS providers like Hashicorp Vault, Oracle KMS and more.

To create keys for signing credentials, you can use the wallet-api and choose from the following algorithms: ed25519, secp256k1, secp256r1, or RSA. The selected key can then be exported as a JWK. Alternatively, any key in JWK format that is generated using one of these algorithms can also be used for signing. To create keys, follow the guide below.

  • Guide - Create & Export Key via Wallet-API

Step 2: Issue the Credential

Now, we'll issue a verifiable credential using the key obtained or any other supported JWK. 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.

  1. The credential will be signed with the provided key and the chosen signature type (JWT, SD-JWT).
  2. The signed credential will be embedded into the OID Credential Offer URL, which we can send off to our users to claim the credential(s).
JWT
SD-JWT

API Reference

CURL

Endpoint: /openid4vc/jwt/issue

Example Request

curl -X 'POST' \
  'https://issuer.portal.walt.id/openid4vc/jwt/issue' \
  -H 'accept: text/plain' \
  -H 'Content-Type: application/json' \
  -d '{
  "issuanceKey": {
    "type": "local",
    "jwk": "{\"kty\":\"OKP\",\"d\":\"mDhpwaH6JYSrD2Bq7Cs-pzmsjlLj4EOhxyI-9DM1mFI\",\"crv\":\"Ed25519\",\"kid\":\"Vzx7l5fh56F3Pf9aR3DECU5BwfrY6ZJe05aiWYWzan8\",\"x\":\"T3T4-u1Xz3vAV2JwPNxWfs4pik_JLiArz_WTCvrCFUM\"}"
  },
  "issuerDid": "did:key:z6MkjoRhq1jSNJdLiruSXrFFxagqrztZaXHqHGUTKJbcNywp",
  "vc": {
    "@context": [
      "https://www.w3.org/2018/credentials/v1",
      "https://www.w3.org/2018/credentials/examples/v1"
    ],
    "id": "http://example.gov/credentials/3732",
    "type": [
      "VerifiableCredential",
      "UniversityDegreeCredential"
    ],
    "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

{
  "issuanceKey": {
    "type": "local",
    "jwk": "{\"kty\":\"OKP\",\"d\":\"mDhpwaH6JYSrD2Bq7Cs-pzmsjlLj4EOhxyI-9DM1mFI\",\"crv\":\"Ed25519\",\"kid\":\"Vzx7l5fh56F3Pf9aR3DECU5BwfrY6ZJe05aiWYWzan8\",\"x\":\"T3T4-u1Xz3vAV2JwPNxWfs4pik_JLiArz_WTCvrCFUM\"}"
  },
  "issuerDid": "did:key:z6MkjoRhq1jSNJdLiruSXrFFxagqrztZaXHqHGUTKJbcNywp",
  "vc": {
    "@context": [
      "https://www.w3.org/2018/credentials/v1",
      "https://www.w3.org/2018/credentials/examples/v1"
    ],
    "id": "http://example.gov/credentials/3732",
    "type": [
      "VerifiableCredential",
      "UniversityDegreeCredential"
    ],
    "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

  • issuanceKey: JSON - A JWK to sign the credential with. Supported algorithms: ed25519, secp256k1, secp256r1, or RSA. Format: {"type": "local", "jwk": "Here JSON Web Key Object"}
  • issuerDid: String - The DID related to the provided key.
  • vc: 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.

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%22UniversityDegreeCredential%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%22UniversityDegreeCredential%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", 
        "UniversityDegreeCredential"
      ],
      "credential_definition": {
        "@context": [
          "https://www.w3.org/2018/credentials/v1", 
          "https://www.w3.org/2018/credentials/examples/v1"
        ],
        "types": [
          "VerifiableCredential", 
          "UniversityDegreeCredential"
        ]
      }
    }
  ],
  "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! 🎉