This is a walk through of the VC issuance process from the perspective of the issuer. It is using a manual approach to issue verifiable credentials and is not intended to be used in production applications. We are providing this for educational purposes only.
Narrative
Alice starts a new job at Acme and she'd like a Verifiable Credential (VC) proving her current employment status.
To request a VC, Alice logs into Acme's internal employee portal and clicks Employment Verification. Clicking the button will invoke the VC issuance process via the SSI Service Acme is hosting.
Once the process has completed, a new Employment Status VC will be sent to Alice's identity wallet.
Steps to Issue a Verifiable Credential
- Clone & Run SSI Service
- Create Decentralized Identifier (DID) for Credential Issuer (Acme)
- Create or obtain DID for Credential Subject (Alice)
- Create Credential Schema
SSI Service Setup
a. Clone and Run SSI Service
Locally clone the SSI Service repo:
git clone https://github.com/TBD54566975/ssi-service.git
The SSI Service is packaged as a Docker container and a Docker Compose file is included to make it simple to run the service locally. First make sure you have Docker downloaded and running on your desktop:
Switch to the build folder:
cd ssi-service/build
Run the Docker Compose file:
docker-compose up
If you'd like to confirm the SSI service and sub-services are functioning, check the health and readiness endpoints:
curl localhost:8080/health
The following response should be returned:
{"status":"OK"}
curl localhost:8080/readiness
{
"status": {
"status": "ready",
"message": "all service ready"
},
"serviceStatuses": {
"credential": {
"status": "ready"
},
"did": {
"status": "ready"
},
"schema": {
"status": "ready"
}
}
}
b. Create Decentralized Identifier for Credential Issuer (Acme)
In the world of Self-Sovereign Identity (SSI), Decentralized Identifiers (DIDs) are used to identify any subject (e.g., a person, organization, thing, data model, abstract entity, etc.). In this scenario, both Acme and Alice are represented by their DIDs in the Verifiable Credential we create in step two.
A DID is a W3C standard for a globally unique identifier that does not require a centralized registration authority. In contrast to typical, federated identifiers, DIDs have been designed so that they may be decoupled from centralized registries, identity providers, and certificate authorities.
Standard format of a DID:
To start the employment verification credential issuance flow we need to create a DID for Acme. We'll do so via the following command:
curl -X PUT -d '{"keyType":"Ed25519"}' localhost:8080/v1/dids/key
Each DID has one or more keys, and each key has a type. Some keytypes give you different properties and can be used for different purposes. Some keys are better for signing, others for encryption. Some are even provided by the government (NIST). There are variations on how they're constructed to give different security properties. You can use any number of DID types but we recommend Ed25519 as it's sufficient for most use cases.
The following response should be returned:
{
"did": {
"id": "did:key:z6MkpEQY4FCCtJEVpZ6gGK541fYWynH2ya7D1RikTGfdydCF",
"verificationMethod": [
{
"id": "#z6MkpEQY4FCCtJEVpZ6gGK541fYWynH2ya7D1RikTGfdydCF",
"type": "Ed25519VerificationKey2018",
"controller": "did:key:z6MkpEQY4FCCtJEVpZ6gGK541fYWynH2ya7D1RikTGfdydCF",
"publicKeyBase58": "An9VTzwmYkk2i4Fyak7DAZzXAD1BZgrrKQopczhd4QQs"
}
],
"authentication": [["#z6MkpEQY4FCCtJEVpZ6gGK541fYWynH2ya7D1RikTGfdydCF"]],
"assertionMethod": [["#z6MkpEQY4FCCtJEVpZ6gGK541fYWynH2ya7D1RikTGfdydCF"]],
"keyAgreement": [["#z6MkpEQY4FCCtJEVpZ6gGK541fYWynH2ya7D1RikTGfdydCF"]],
"capabilityDelegation": [
["#z6MkpEQY4FCCtJEVpZ6gGK541fYWynH2ya7D1RikTGfdydCF"]
]
},
"privateKeyBase58": "4eMKcoDwfrdxf851Yg4xs8kyGPy6GFXc7kUrsq3jhtQUnGrVnM3qgKxE59DSMJfSFcH6zL4yZ183WTYmwf2iJx5Z",
"keyType": "Ed25519"
}
As you can see the DID returned here is using key as its DID method. Learn more about other DID methods here.
Important: Make sure to copy Acme's DID for the next step.
c. Create DID for Credential Subject (Alice)
Here's a generic DID you can use for Alice for testing purposes:
did:key:z6MkqcFHFXqzsYyDYrEUA2pVCfQGJz2rYoCZy5WWszzSW3o6
However, if you'd prefer to create a new DID for Alice, run:
curl -X PUT -d '{"keyType":"Ed25519"}' localhost:8080/v1/dids/key
d. Create Credential Schema
A Credential Schema is a document that defines the structure of a VC. It's based on the json schema and shows which properties the issuer is going to use to create the VC.
The employedAt
property is a timestamp data type to define the date and time someone was employed at Acme.
Let's create a Credential Schema for Alice's Employment Status VC, using Acme's DID as the author:
curl -X PUT -d '{
"author": "did:key:z6MkpEQY4FCCtJEVpZ6gGK541fYWynH2ya7D1RikTGfdydCF",
"name": "Acme",
"schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Employee Status VC",
"type": "object",
"properties": {
"id": {
"type": "string"
},
"givenName": {
"type": "string"
},
"employedAt": {
"type": "string"
}
},
"additionalProperties": false
},
"sign": false
}' localhost:8080/v1/schemas
The following response should be returned:
{
"id": "b28feb61-e0b8-454a-86ed-d487a46e8584",
"schema": {
"type": "https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json",
"version": "1.0",
"id": "b28feb61-e0b8-454a-86ed-d487a46e8584",
"name": "Acme",
"author": "did:key:z6MkpEQY4FCCtJEVpZ6gGK541fYWynH2ya7D1RikTGfdydCF",
"authored": "2022-12-09T18:04:06Z",
"schema": {
"$id": "26dfd04a-39e1-461f-af04-f1f92a8783f2",
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"description": "Employee Status VC",
"properties": {
"employedAt": {
"type": "string"
},
"givenName": {
"type": "string"
},
"id": {
"type": "string"
}
},
"required": [],
"type": "object"
}
Important: Make sure to copy your Schema ID for the next step.
Create Verifiable Credential
Now we have all three objects needed to create a VC:
- Acme's DID (issuer)
- Alice's DID (subject)
- Schema ID
To create the VC, run:
curl -X PUT -d '{
"data": {
"givenName": "Alice",
"employedAt": "2022-08-20T13:20:10.000+0000"
},
"issuer": "did:key:z6MkpEQY4FCCtJEVpZ6gGK541fYWynH2ya7D1RikTGfdydCF",
"subject": "did:key:z6MkqcFHFXqzsYyDYrEUA2pVCfQGJz2rYoCZy5WWszzSW3o6",
"@context": "https://www.w3.org/2018/credentials/v1",
"expiry": "2051-10-05T14:48:00.000Z",
"schema": "b28feb61-e0b8-454a-86ed-d487a46e8584"
}' http://localhost:8080/v1/credentials
The following response should be returned:
{
"credential": {
"@context": ["https://www.w3.org/2018/credentials/v1"],
"id": "2b5b0cfb-5023-4dc4-ae98-1cb94c65a22c",
"type": ["VerifiableCredential"],
"issuer": "did:key:z6MkpEQY4FCCtJEVpZ6gGK541fYWynH2ya7D1RikTGfdydCF",
"issuanceDate": "2022-12-09T18:41:10Z",
"expirationDate": "2051-10-05T14:48:00.000Z",
"credentialSubject": {
"employedAt": "2022-08-20T13:20:10.000+0000",
"givenName": "Alice",
"id": "did:key:z6MkqcFHFXqzsYyDYrEUA2pVCfQGJz2rYoCZy5WWszzSW3o6"
},
"credentialSchema": {
"id": "b28feb61-e0b8-454a-86ed-d487a46e8584",
"type": "JsonSchemaValidator2018"
}
},
"credentialJwt": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa3Y0MVQ5ZHMzWm5ncEpxcGpjYjVBOXpUeHFIN1FqOHA5bm81TVk2MzViZFRaIiwidHlwIjoiSldUIn0.eyJleHAiOjI1ODAxMzAwODAsImlzcyI6ImRpZDprZXk6ejZNa3Y0MVQ5ZHMzWm5ncEpxcGpjYjVBOXpUeHFIN1FqOHA5bm81TVk2MzViZFRaIiwianRpIjoiMmI1YjBjZmItNTAyMy00ZGM0LWFlOTgtMWNiOTRjNjVhMjJjIiwibmJmIjoxNjcwNjExMjcwLCJzdWIiOiJkaWQ6a2V5Ono2TWtxY0ZIRlhxenNZeURZckVVQTJwVkNmUUdKejJyWW9DWnk1V1dzenpTVzNvNiIsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sImlkIjoiMmI1YjBjZmItNTAyMy00ZGM0LWFlOTgtMWNiOTRjNjVhMjJjIiwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJpc3N1ZXIiOiJkaWQ6a2V5Ono2TWt2NDFUOWRzM1puZ3BKcXBqY2I1QTl6VHhxSDdRajhwOW5vNU1ZNjM1YmRUWiIsImlzc3VhbmNlRGF0ZSI6IjIwMjItMTItMDlUMTg6NDE6MTBaIiwiZXhwaXJhdGlvbkRhdGUiOiIyMDUxLTEwLTA1VDE0OjQ4OjAwLjAwMFoiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJlbXBsb3llZEF0IjoiMjAyMi0wOC0yMFQxMzoyMDoxMC4wMDArMDAwMCIsImdpdmVuTmFtZSI6IkFsaWNlIiwiaWQiOiJkaWQ6a2V5Ono2TWtxY0ZIRlhxenNZeURZckVVQTJwVkNmUUdKejJyWW9DWnk1V1dzenpTVzNvNiJ9LCJjcmVkZW50aWFsU2NoZW1hIjp7ImlkIjoiYjI4ZmViNjEtZTBiOC00NTRhLTg2ZWQtZDQ4N2E0NmU4NTg0IiwidHlwZSI6Ikpzb25TY2hlbWFWYWxpZGF0b3IyMDE4In19fQ.LxcBOYC9DzqUXcpNRnW29BSg2QrHWNb98tm4h8Agz-MuoCHaOfJ2_sVat9ChyU8d9XYtIf6A4elr8JVE6hERBw"
}
Note: the value for credentialJwt can be decoded using a tool like jwt.io.
If Alice has an identity wallet she will be able to store the credential and use it however she wishes.