|
| 1 | +// Copyright 2020-2023 IOTA Stiftung |
| 2 | +// SPDX-License-Identifier: Apache-2.0 |
| 3 | + |
| 4 | +import { |
| 5 | + CredentialV2, |
| 6 | + EdDSAJwsVerifier, |
| 7 | + FailFast, |
| 8 | + JwsSignatureOptions, |
| 9 | + JwtCredentialValidationOptions, |
| 10 | + JwtCredentialValidator, |
| 11 | +} from "@iota/identity-wasm/node"; |
| 12 | +import { IotaClient } from "@iota/iota-sdk/client"; |
| 13 | +import { createDocumentForNetwork, getFundedClient, getMemstorage, NETWORK_URL } from "../util"; |
| 14 | + |
| 15 | +/** |
| 16 | + * This example shows how to create a Verifiable Credential and validate it. |
| 17 | + * In this example, Alice takes the role of the subject, while we also have an issuer. |
| 18 | + * The issuer signs a UniversityDegreeCredential type verifiable credential with Alice's name and DID. |
| 19 | + * This Verifiable Credential can be verified by anyone, allowing Alice to take control of it and share it with whomever they please. |
| 20 | + */ |
| 21 | +export async function createVC() { |
| 22 | + // create new client to connect to IOTA network |
| 23 | + const iotaClient = new IotaClient({ url: NETWORK_URL }); |
| 24 | + const network = await iotaClient.getChainIdentifier(); |
| 25 | + |
| 26 | + // Create an identity for the issuer with one verification method `key-1`, and publish DID document for it. |
| 27 | + const issuerStorage = getMemstorage(); |
| 28 | + const issuerClient = await getFundedClient(issuerStorage); |
| 29 | + const [unpublishedIssuerDocument, issuerFragment] = await createDocumentForNetwork(issuerStorage, network); |
| 30 | + const { output: issuerIdentity } = await issuerClient |
| 31 | + .createIdentity(unpublishedIssuerDocument) |
| 32 | + .finish() |
| 33 | + .buildAndExecute(issuerClient); |
| 34 | + const issuerDocument = issuerIdentity.didDocument(); |
| 35 | + |
| 36 | + // Create an identity for the holder, and publish DID document for it, in this case also the subject. |
| 37 | + const aliceStorage = getMemstorage(); |
| 38 | + const aliceClient = await getFundedClient(aliceStorage); |
| 39 | + const [unpublishedAliceDocument] = await createDocumentForNetwork(aliceStorage, network); |
| 40 | + const { output: aliceIdentity } = await aliceClient |
| 41 | + .createIdentity(unpublishedAliceDocument) |
| 42 | + .finish() |
| 43 | + .buildAndExecute(aliceClient); |
| 44 | + const aliceDocument = aliceIdentity.didDocument(); |
| 45 | + |
| 46 | + // Create a credential subject indicating the degree earned by Alice, linked to their DID. |
| 47 | + const subject = { |
| 48 | + id: aliceDocument.id(), |
| 49 | + name: "Alice", |
| 50 | + degreeName: "Bachelor of Science and Arts", |
| 51 | + degreeType: "BachelorDegree", |
| 52 | + GPA: "4.0", |
| 53 | + }; |
| 54 | + |
| 55 | + // Create an unsigned `UniversityDegree` credential for Alice |
| 56 | + const unsignedVc = new CredentialV2({ |
| 57 | + id: "https://example.edu/credentials/3732", |
| 58 | + type: "UniversityDegreeCredential", |
| 59 | + issuer: issuerDocument.id(), |
| 60 | + credentialSubject: subject, |
| 61 | + }); |
| 62 | + |
| 63 | + // Create signed JWT credential. |
| 64 | + const credentialJwt = await issuerDocument.createCredentialJwt( |
| 65 | + issuerStorage, |
| 66 | + issuerFragment, |
| 67 | + unsignedVc, |
| 68 | + new JwsSignatureOptions(), |
| 69 | + ); |
| 70 | + console.log(`Credential JWT > ${credentialJwt.toString()}`); |
| 71 | + |
| 72 | + // Before sending this credential to the holder the issuer wants to validate that some properties |
| 73 | + // of the credential satisfy their expectations. |
| 74 | + |
| 75 | + // Validate the credential's signature, the credential's semantic structure, |
| 76 | + // check that the issuance date is not in the future and that the expiration date is not in the past. |
| 77 | + // Note that the validation returns an object containing the decoded credential. |
| 78 | + const decoded_credential = new JwtCredentialValidator(new EdDSAJwsVerifier()).validateV2( |
| 79 | + credentialJwt, |
| 80 | + issuerDocument, |
| 81 | + new JwtCredentialValidationOptions(), |
| 82 | + FailFast.FirstError, |
| 83 | + ); |
| 84 | + |
| 85 | + // Since `validate` did not throw any errors we know that the credential was successfully validated. |
| 86 | + console.log(`VC successfully validated`); |
| 87 | + |
| 88 | + // The issuer is now sure that the credential they are about to issue satisfies their expectations. |
| 89 | + // Note that the credential is NOT published to the IOTA Tangle. It is sent and stored off-chain. |
| 90 | + console.log(`Issued credential: ${JSON.stringify(decoded_credential.intoCredential(), null, 2)}`); |
| 91 | +} |
0 commit comments