Advanced VC

Onboarding Emma to a Virtual Company, issuing custom credentials, and utilizing dynamic verification policies.

In this tutorial, we will expand on the "My First VC" tutorial by now onboarding Emma to a virtual company. During the onboarding, Emma will receive an EmployeeID credential, which will be based on a custom credential template using the status property. With that, you learn about two more concepts; Custom credential templates and the status property. The credential will also have Emma's role as a property, which can then be checked with a dynamic verification policy, another new concept.

Where we start:

Using the walt.id SSI-Kit's REST API, we'll streamline access control within the company by

Running the SSI-Kit

Make sure you have Docker or a JDK 16 build environment including Gradle installed on your machine

Pull the docker container directly from docker hub and run the project

docker run -p 7000-7004:7000-7004 -itv $(pwd)/data:/app/data waltid/ssikit serve -b 0.0.0.0

This will create a folder called data in your current directory as storage for the VC, DIDs, Keys and other things which need to be stored in order to provide all the functionality.

Now with the SSI-Kit up and running, we can create the DID for the company, establishing its Digital Identity.

Establishing a Digital Identity (DID)

You have two options for creating a DID: executing a curl command or visiting the swagger docs. The custodian endpoint should be visible in the terminal after you have run the serve command, visit the endpoint to see the swagger docs.

curl -X 'POST' \
  'http://localhost:7002/did/create' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "method": "{method}"
}'

Body Parameters method: [string] method of the did. Value can be one of key, web, ebsi, iota, cheqd, jwk Example

curl -X 'POST' \
  'http://localhost:7002/did/create' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "method": "key"
}'

Create one DID for the company using the command above. In case you don't have done the My First VC tutorial and still have the DID for Emma, create another one for her. Make sure to save both, as we will be needing them later for the issuance of the EmployeID. You can also use the list DID endpoint to see all previously created DIDs.

Creating an EmployeeID Template

The SSI-Kit supports the creation of credential templates, which are blueprints that establish reusable data structures based on which credentials can be issued.

Let's create one for an EmployeeID. The template will include the following properties:

  • id: Identifier for the employee

  • name: Name of the employee

  • role: Role or designation in the company

  • joiningDate: Date of joining the company

{
  "type": ["VerifiableCredential", "EmployeeID"],
  "credentialSubject": {
    "id": "",
    "name": "",
    "role": "",
    "joiningDate": ""
  }
}

Saving the credential template

curl -X 'POST' \
  'http://127.0.0.1:7001/v1/templates/{id}' \
  -H 'accept: text/plain' \
  -H 'Content-Type: application/json' \
  -d '{credentialTemplate}'

Path Parameters

  • id: The name for the credential template, which we will later reference during issuance. e.g. EmployeeID

Body Use the JSON structure from above as the body. Example

curl -X 'POST' \
  'http://127.0.0.1:7001/v1/templates/EmployeeID' \
  -H 'accept: text/plain' \
  -H 'Content-Type: application/json' \
  -d '{
    "type": ["VerifiableCredential", "EmployeeID"],
    "credentialSubject": {
        "id": "",
        "name": "",
        "role": "",
        "joiningDate": ""
    }
}'

We can streamline the process of issuing a credential to Emma by utilizing the EmployeeID credential template. However, we also have the opportunity to further simplify the process using an additional feature: prefilling credential properties. This will be particularly beneficial as Emma likely won't be the only one receiving the Engineer role assignment. By creating a template where the Engineer role is prefilled, we can improve the issuance process.

EngineerEmployeeID Template

{
    "type": ["VerifiableCredential", "EmployeeID"],
    "credentialSubject": {
        "id": "",
        "name": "",
        "role": "Engineer",
        "joiningDate": ""
    }
}

You can now also save this template and use it later for the issuance of our VC, or use the one we created before and provide the value for the role attribute during issuance.

Issuing a Verifiable Credential to Emma

In order to issue Emma's EmployeeID VC, we will use one of the credential templates. Additionally, we will include a credential status so that we have the option to revoke the credential if Emma leaves the company without needing to remove it from her ownership. For more information on credential status and the types we support, read our guide.

curl -X 'POST' \
  'http://localhost:7001/v1/credentials/issue' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "templateId": "{template type}",
  "config": {
    "issuerDid": "{issuerDid}",
    "subjectDid": "{subjectDid}",
    "proofType": "{proofType}",
    "statusType": "{statusType}"
  },
  "credentialData": {
    "id": "{subjectDID}",
    "name": "{name}",
    "role": "{role}",
    "joiningDate": "{joiningDate}"
  }
}'

Body Parameters

  • templateId: [string] The identifier of the template used for issuing a Verifiable Credential. In our case this would be the EmployeeID or EngineerEmployeeID template.

  • config: [object] Contains configuration parameters for the issuance process.

    • issuerDid: [string] The DID of the entity issuing the credential (University).

    • subjectDid: [string] The DID of the entity receiving the credential (Emma).

    • proofType: [string] Specifies the format and cryptographic algorithm used for the digital signature of the Verifiable Credential. E.g. LD_PROOF

    • statusType: [statusType] Specifies if the credential should be issued with status and the type of the status. Options StatusList2021Entry or SimpleCredentialStatus2022

  • credentialData: [object] Contains the actual data of the credential being issued.

    • credentialSubject: [object] Holds the information about the employee as described in the EmployeeID credential template.

The id in the credential subject we don't need to provide as this will be prefilled automatically with the DID of Emma.

Example EmployeeID:

curl -X 'POST' \
  'http://localhost:7001/v1/credentials/issue' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "templateId": "EmployeeID",
  "config": {
    "issuerDid": "did:key:z6MkmE21F2dAHkM71tnYdBEGaSvnPpxBFZYi2wtzYbTwqvgK",
    "subjectDid": "did:key:z6MkmE21F2dAHkM71tnYdBEGaSvnPpxBFZYi2wtzYbTwqvgK",
    "proofType": "LD_PROOF",
    "statusType": "StatusList2021Entry"
  },
  "credentialData": {
    "name": "Emma",
    "role": "Engineer",
    "joiningDate": "2023-06-28"
  }
}'

Example EngineerEmployeeID:

curl -X 'POST' \
  'http://localhost:7001/v1/credentials/issue' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "templateId": "EngineerEmployeeID",
  "config": {
    "issuerDid": "did:key:z6MkmE21F2dAHkM71tnYdBEGaSvnPpxBFZYi2wtzYbTwqvgK",
    "subjectDid": "did:key:z6MkmE21F2dAHkM71tnYdBEGaSvnPpxBFZYi2wtzYbTwqvgK",
    "proofType": "LD_PROOF",
    "statusType": "StatusList2021Entry"
  },
  "credentialData": {
    "name": "Emma",
    "joiningDate": "2023-06-28"
  }
}'

Now we can use the issued VC in the company campus verification system.

Authenticating with Door Access System

To successfully authorize access, our authentication system needs to verify four crucial elements regarding the Verifiable Credential presented during the verification process:

  1. Confirm the signature of the Verifiable Credential to ensure it's legitimate.

  2. Ascertain the credential's validity by validating its current status, ensuring it has not expired or been revoked.

  3. Verify that the issuer is indeed our company, thereby eliminating any fraudulent issuers.

  4. Evaluate whether the role of the employee, as indicated on the credential, authorizes them to access the desired area.

To facilitate successful verification, we will use Verification Policies. For the initial two cases, which are relatively common, we can utilize our built-in policies, namely SignaturePolicy and CredentialStatusPolicy. These policies essentially verify that the signature is authentic and that the status is still valid.

For the additional cases, we will create custom policies, leveraging the Open Policy Agent (OPA) Engine and the REGO language. You can learn more about it here.

Please install the Open Policy Agent as described here, to ensure seamless verification.

Creating Custom Policies - IsCompany

package system

default main = false

main {
    input.credentialData.issuer == {issuerDID}
}

Make sure to replace {issuerDID} with the DID of your issuer (company)

Creating Custom Policies - IsRole

This custom policy holds greater flexibility, as it accepts an argument containing a 'role' property. This property is then cross-verified with the role present in the Verifiable Credential. Alternatively, the role can be hardcoded, and the policy can be renamed accordingly, such as IsEngineer, similar to the IsCompany policy.

package system

default main = false

main {
    input.parameter.role == input.credentialData.credentialSubject.role
}

Saving the policies

curl -X 'POST' \
  'http://127.0.0.1:7003/v1/create/{{policyName}}?update=true&downloadPolicy=true' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "IsCompany",
    "description": "Test",
    "input": {},
    "policy": "package system

default main = false

main {
    input.credentialData.issuer == \"did:key:z6MkrFtzYS5RW748vvAjARZ4CYjBMveV9LYfSAFov439Bm8Z\"
}",
}
",
    "dataPath": "$",
    "policyQuery": "data.system.main",
    "policyEngine": "OPA",
    "applyToVC": true,
    "applyToVP": true
}'

Path parameters:

  • policyName: [string] Name of the policy, e.g. IsCompany

Query parameters:

  • update: [boolean] Specifies if existing policy with same name should be overridden (if mutable)

  • downloadPolicy: [boolean] When using an URL to reference the to created policy. Downloads and/or saves the policy definition locally, rather than keeping the reference to the original URL\

Body

{
    "name": "MyCustomPolicy",
    "description": "Test",
    "input": {},
    "policy": "package system

               default main = false

               main {
                 input.credentialData.issuer == \"did:key:z6MkrFtzYS5RW748vvAjARZ4CYjBMveV9LYfSAFov439Bm8Z\"
    }",
    "dataPath": "$",
    "policyQuery": "data.system.main",
    "policyEngine": "OPA",
    "applyToVC": true,
    "applyToVP": true
}
  • name: [string] Policy name, must not conflict with existing policies

  • description: [string] Optional policy description

  • input: [JSON] Input JSON object for rego query, which can be overridden/extended on verification. Can be a JSON string or JSON file

  • policy: [URL, REGO] Whole Policy or URL to policy definition.

  • dataPath: [JSON path] JSON path to the data in the credential which should be verified, default: "$" (whole credential object)

  • policyQuery: [string] The query string in the policy engine language. Defaults to "data.system.main".

  • policyEngine: [string] Policy engine type, default: OPA. Options, OPA

  • applyToVC: [boolean] Apply/Don't apply to verifiable credentials (default: apply)

  • applyToVP: [boolean] Apply/Don't apply to verifiable presentation (default: don't apply)

Using the endpoint above, you can save both policies.

Verifying the credential with all policies

Now with the custom policies created, we will use those and the two predefined ones to verify Emma's credential and enable access.

curl -X 'POST' \
  'http://127.0.0.1:7003/v1/verify' \
  -H 'accept: application/json' \
  -H 'Content-Type: text/plain' \
  -d '{
    "policies": [
        {
            "policy": "SignaturePolicy"
        },
        {
            "policy": "CredentialStatusPolicy"
        },
        {
            "policy": "IsRole",
            "argument": {"role": "Engineer"}
        },
        {
            "policy": "IsCompany"
        }
    ],
    "credentials": [{
  "type" : [ "VerifiableCredential", "EngineerEmployeeID" ],
  "@context" : [ "https://www.w3.org/2018/credentials/v1", "https://w3id.org/security/suites/jws-2020/v1" ],
  "id" : "urn:uuid:ae9cf1f3-8609-4685-9928-5073b6990af4",
  "issuer" : "did:key:z6MkrFtzYS5RW748vvAjARZ4CYjBMveV9LYfSAFov439Bm8Z",
  "issuanceDate" : "2023-06-28T13:55:15Z",
  "issued" : "2023-06-28T13:55:15Z",
  "validFrom" : "2023-06-28T13:55:15Z",
  "credentialSubject" : {
    "id" : "did:key:z6MkrFtzYS5RW748vvAjARZ4CYjBMveV9LYfSAFov439Bm8Z",
    "name" : "Emma",
    "role" : "Engineer",
    "joiningDate" : "2023-06-28"
  },
  "credentialStatus" : {
    "id" : "http://127.0.0.1:7001/v1/credentials/status/revocation#2",
    "statusListCredential" : "http://127.0.0.1:7001/v1/credentials/status/revocation",
    "statusListIndex" : "2",
    "statusPurpose" : "revocation",
    "type" : "StatusList2021Entry"
  },
  "proof" : {
    "type" : "JsonWebSignature2020",
    "created" : "2023-06-28T13:55:15Z",
    "verificationMethod" : "did:key:z6MkrFtzYS5RW748vvAjARZ4CYjBMveV9LYfSAFov439Bm8Z#z6MkrFtzYS5RW748vvAjARZ4CYjBMveV9LYfSAFov439Bm8Z",
    "jws" : "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFZERTQSJ9.._uFLlXHpNgFsUJFwahcIh_VeLsps8n5ZaifMMvAMqmOkESWN1eoJNNkGfAYY_R185ia-qkKRKwb2rsNzPUcvAA"
  }
}]
}
'

Body

{
    "policies": [
        {
            "policy": "SignaturePolicy"
        },
        {
            "policy": "CredentialStatusPolicy"
        },
        {
            "policy": "IsRole",
            "argument": {"role": "Engineer"}
        },
        {
            "policy": "IsCompany"
        }
    ],
    "credentials": [
      {... credential ... }
    ]
}
  • policies: [array] A list of policy definitions to verify against

    • policy: [string] The name/id of the policy

    • argument: [JSON] The argument needed by the policy (optional)

  • credentials: [array] An array of credentials in JWT, or LD_PROOF format

Congratulations, you've reached the finish line! 🎉 You have skillfully utilized an array of features to bring this use case to life. Now, you have the opportunity to explore further and deepen your understanding of the concepts introduced today. Happy building!

Next Steps

  • Credential Templates - Dive deeper into credential templates and what you can do with them

  • Credential Status - Learn more about the credential status property and what is enables.

  • Verification Policies - Explore our pre-build templates and learn more about how to build and leverage custom ones using OPA (Open Policy Agent) and the Rego language.

Last updated