diff --git a/bindings/wasm/examples/README.md b/bindings/wasm/examples/README.md index 40c523d213..a168436e11 100644 --- a/bindings/wasm/examples/README.md +++ b/bindings/wasm/examples/README.md @@ -4,6 +4,16 @@ This folder provides code examples for you to learn how the IOTA Identity WASM bindings can be used in JavaScript. +If you are writing code against the test network then, most function calls will need to include information about the +network, since this is not automatically inferred from the arguments in all cases currently. + +We recommend that you **always** use a `CLIENT_CONFIG` parameter that you define when calling any functions that take a +`ClientConfig` object. This will ensure that all the API calls use a consistent node and network throughout. If you +mismatch the network across calls you will encounter errors. + +A `ClientConfig` is a record consisting of two string fields: `network` and `node`. There is an example client config +that can be found in the `config.js` file. + You can run each example using ``` @@ -18,12 +28,12 @@ npm run example:node -- create_did The following examples are currently available: -| # | Name | Information | -| :--: | :----------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- | -| 1 | [create_did](create_did.js) | Generates and publishes a DID Document, the fundamental building block for decentralized identity. | -| 2 | [manipulate_did](manipulate_did.js) | Add verification methods and service endpoints to a DID Document and update an already existing DID Document. | -| 3 | [resolution](resolution.js) | Resolves an existing DID to return the latest DID Document. | -| 4 | [create_vc](create_vc.js) | Generates and publishes subject and issuer DID Documents, then creates a Verifiable Credential (VC) specifying claims about the subject, and verifies it. | -| 5 | [revocation](revocation.js) | Remove a verification method from the Issuers DID Document, making the Verifiable Credential it signed unable to verify, effectively revoking the VC. | -| 6 | [create_vp](create_vp.js) | Create a Verifiable Presentation, the data model for sharing VCs, out of a Verifiable Credential and verifies it. | -| 7 | [merkle_key](merkle_key.js) | Adds a MerkleKeyCollection verification method to an Issuers DID Document and signs a Verifiable Credential with the key on index 0. Afterwards the key on index 0 is deactivated, making the Verifiable Credential fail its verification. | +| # | Name | Information | +| :-: | :---------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | [create_did](create_did.js) | Generates and publishes a DID Document, the fundamental building block for decentralized identity. | +| 2 | [manipulate_did](manipulate_did.js) | Add verification methods and service endpoints to a DID Document and update an already existing DID Document. | +| 3 | [resolution](resolution.js) | Resolves an existing DID to return the latest DID Document. | +| 4 | [create_vc](create_vc.js) | Generates and publishes subject and issuer DID Documents, then creates a Verifiable Credential (VC) specifying claims about the subject, and verifies it. | +| 5 | [revocation](revocation.js) | Remove a verification method from the Issuers DID Document, making the Verifiable Credential it signed unable to verify, effectively revoking the VC. | +| 6 | [create_vp](create_vp.js) | Create a Verifiable Presentation, the data model for sharing VCs, out of a Verifiable Credential and verifies it. | +| 7 | [merkle_key](merkle_key.js) | Adds a MerkleKeyCollection verification method to an Issuers DID Document and signs a Verifiable Credential with the key on index 0. Afterwards the key on index 0 is deactivated, making the Verifiable Credential fail its verification. | diff --git a/bindings/wasm/examples/config.js b/bindings/wasm/examples/config.js index 818f8b8941..e54b97aa9d 100644 --- a/bindings/wasm/examples/config.js +++ b/bindings/wasm/examples/config.js @@ -1,9 +1,7 @@ +/* @type {{network: string, node: string}} */ const CLIENT_CONFIG = { network: "main", node: "https://chrysalis-nodes.iota.org:443", } -const EXPLORER_URL = "https://explorer.iota.org/mainnet/transaction"; - exports.CLIENT_CONFIG = CLIENT_CONFIG; -exports.EXPLORER_URL = EXPLORER_URL; diff --git a/bindings/wasm/examples/create_did.js b/bindings/wasm/examples/create_did.js index 9b184428f2..ab74321ff1 100644 --- a/bindings/wasm/examples/create_did.js +++ b/bindings/wasm/examples/create_did.js @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 const { Document, KeyType, publish } = require('../node/identity_wasm') -const { CLIENT_CONFIG, EXPLORER_URL } = require('./config') +const { logExplorerUrl } = require('./explorer_util') /* This example shows a basic introduction on how to create a basic DID Document and upload it to the Tangle. @@ -10,20 +10,20 @@ const { CLIENT_CONFIG, EXPLORER_URL } = require('./config') The keypair becomes part of the DID Document in order to prove a link between the DID and the published DID Document. That same keypair should be used to sign the original DID Document. */ -async function createIdentity() { - //Create a DID Document (an identity). - const { doc, key } = new Document(KeyType.Ed25519) +async function createIdentity(clientConfig) { + // Create a DID Document (an identity). + const { doc, key } = new Document(KeyType.Ed25519, clientConfig.network); - //Sign the DID Document with the generated key + // Sign the DID Document with the generated key. doc.sign(key); - //Publish the Identity to the IOTA Network, this may take a few seconds to complete Proof-of-Work. - const messageId = await publish(doc.toJSON(), CLIENT_CONFIG); + // Publish the Identity to the IOTA Network, this may take a few seconds to complete Proof-of-Work. + const messageId = await publish(doc.toJSON(), clientConfig); - //Log the results - console.log(`Identity Creation: ${EXPLORER_URL}/${messageId}`); + // Log the results. + logExplorerUrl("Identity Creation:", clientConfig.network, messageId); - //Return the results + // Return the results. return {key, doc, messageId}; } diff --git a/bindings/wasm/examples/create_vc.js b/bindings/wasm/examples/create_vc.js index 2061e39052..7e3a135c66 100644 --- a/bindings/wasm/examples/create_vc.js +++ b/bindings/wasm/examples/create_vc.js @@ -4,18 +4,19 @@ const { VerifiableCredential, checkCredential } = require('../node/identity_wasm') const { createIdentity } = require('./create_did'); const { manipulateIdentity } = require('./manipulate_did'); -const { CLIENT_CONFIG } = require('./config') /* This example shows how to create a Verifiable Credential and validate it. In this example, alice takes the role of the subject, while we also have an issuer. The issuer signs a UniversityDegreeCredential type verifiable credential with Alice's name and DID. This Verifiable Credential can be verified by anyone, allowing Alice to take control of it and share it with whoever they please. + + @param {{network: string, node: string}} clientConfig */ -async function createVC() { - //Creates new identities (See "create_did" and "manipulate_did" examples) - const alice = await createIdentity(); - const issuer = await manipulateIdentity(); +async function createVC(clientConfig) { + // Creates new identities (See "create_did" and "manipulate_did" examples) + const alice = await createIdentity(clientConfig); + const issuer = await manipulateIdentity(clientConfig); // Prepare a credential subject indicating the degree earned by Alice let credentialSubject = { @@ -34,15 +35,15 @@ async function createVC() { credentialSubject, }); - //Sign the credential with the Issuer's newKey + // Sign the credential with the Issuer's newKey const signedVc = issuer.doc.signCredential(unsignedVc, { method: issuer.doc.id.toString()+"#newKey", public: issuer.newKey.public, secret: issuer.newKey.secret, }); - //Check if the credential is verifiable - const result = await checkCredential(signedVc.toString(), CLIENT_CONFIG); + // Check if the credential is verifiable. + const result = await checkCredential(signedVc.toString(), clientConfig); console.log(`VC verification result: ${result.verified}`); return {alice, issuer, signedVc}; diff --git a/bindings/wasm/examples/create_vp.js b/bindings/wasm/examples/create_vp.js index 79523e8afb..796cc65bad 100644 --- a/bindings/wasm/examples/create_vp.js +++ b/bindings/wasm/examples/create_vp.js @@ -3,16 +3,17 @@ const { VerifiablePresentation, checkPresentation } = require('../node/identity_wasm') const { createVC } = require('./create_VC'); -const { CLIENT_CONFIG } = require('./config') /* This example shows how to create a Verifiable Presentation and validate it. A Verifiable Presentation is the format in which a (collection of) Verifiable Credential(s) gets shared. It is signed by the subject, to prove control over the Verifiable Credential with a nonce or timestamp. + + @param {{network: string, node: string}} clientConfig */ -async function createVP() { +async function createVP(clientConfig) { // Creates new identities (See "createVC" example) - const {alice, issuer, signedVc} = await createVC(); + const {alice, issuer, signedVc} = await createVC(clientConfig); // Create a Verifiable Presentation from the Credential - signed by Alice's key // TODO: Sign with a challenge @@ -24,7 +25,7 @@ async function createVP() { }) // Check the validation status of the Verifiable Presentation - const result = await checkPresentation(signedVp.toString(), CLIENT_CONFIG); + const result = await checkPresentation(signedVp.toString(), clientConfig); console.log(`VP verification result: ${result.verified}`); } diff --git a/bindings/wasm/examples/explorer_util.js b/bindings/wasm/examples/explorer_util.js new file mode 100644 index 0000000000..856f2c3bdf --- /dev/null +++ b/bindings/wasm/examples/explorer_util.js @@ -0,0 +1,15 @@ +// Copyright 2020-2021 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +/* + Write out the Targle Explorer URL given the network and message ID, with the given preamble. + + @param {!string} preamble + @param {!string} network + @param {!string} messageId +*/ +function logExplorerUrl(preamble, network, messageId) { + console.log(`${preamble} https://explorer.iota.org/${network}net/transaction/${messageId}`); +} + +exports.logExplorerUrl = logExplorerUrl; diff --git a/bindings/wasm/examples/manipulate_did.js b/bindings/wasm/examples/manipulate_did.js index 43116f9816..11a68b4b0c 100644 --- a/bindings/wasm/examples/manipulate_did.js +++ b/bindings/wasm/examples/manipulate_did.js @@ -3,7 +3,7 @@ const { KeyPair, KeyType, publish, VerificationMethod, Service } = require('../node/identity_wasm') const { createIdentity } = require('./create_did'); -const { CLIENT_CONFIG, EXPLORER_URL } = require('./config'); +const { logExplorerUrl } = require('./explorer_util'); /* This example shows how to add more to an existing DID Document. @@ -12,10 +12,12 @@ const { CLIENT_CONFIG, EXPLORER_URL } = require('./config'); The services provide metadata around the identity via URIs. These can be URLs, but can also emails or IOTA indices. An important detail to note is the previousMessageId. This is an important field as it links the new DID Document to the old DID Document, creating a chain. Without setting this value, the new DID Document won't get used during resolution of the DID! + + @param {{network: string, node: string}} clientConfig */ -async function manipulateIdentity() { +async function manipulateIdentity(clientConfig) { //Creates a new identity (See "create_did" example) - let { key, doc, messageId } = await createIdentity(); + let { key, doc, messageId } = await createIdentity(clientConfig); //Add a new VerificationMethod with a new KeyPair const newKey = new KeyPair(KeyType.Ed25519); @@ -37,14 +39,14 @@ async function manipulateIdentity() { */ doc.previousMessageId = messageId; - //Sign the DID Document with the appropriate key + // Sign the DID Document with the appropriate key. doc.sign(key); - //Publish the Identity to the IOTA Network, this may take a few seconds to complete Proof-of-Work. - const nextMessageId = await publish(doc.toJSON(), CLIENT_CONFIG); + // Publish the Identity to the IOTA Network, this may take a few seconds to complete Proof-of-Work. + const nextMessageId = await publish(doc.toJSON(), clientConfig); - //Log the results - console.log(`Identity Update: ${EXPLORER_URL}/${nextMessageId}`); + // Log the results. + logExplorerUrl("Identity Update:", clientConfig.network, messageId); return {key, newKey, doc, nextMessageId}; } diff --git a/bindings/wasm/examples/merkle_key.js b/bindings/wasm/examples/merkle_key.js index 4a56b13436..fc5d7f891b 100644 --- a/bindings/wasm/examples/merkle_key.js +++ b/bindings/wasm/examples/merkle_key.js @@ -3,7 +3,7 @@ const { Digest, checkCredential, KeyType, publish, VerifiableCredential, VerificationMethod, KeyCollection } = require('../node/identity_wasm') const { createIdentity } = require('./create_did'); -const { EXPLORER_URL, CLIENT_CONFIG } = require('./config') +const { logExplorerUrl } = require('./explorer_util') /* This example shows how to sign/revoke verifiable credentials on scale. @@ -11,11 +11,13 @@ const { EXPLORER_URL, CLIENT_CONFIG } = require('./config') This MerkleKeyCollection can be created as a collection of a power of 2 amount of keys. Every key should be used once by the issuer for signing a verifiable credential. When the verifiable credential must be revoked, the issuer revokes the index of the revoked key. + + @param {{network: string, node: string}} clientConfig */ -async function merkleKey() { +async function merkleKey(clientConfig) { //Creates new identities (See "create_did" example) - const alice = await createIdentity(); - const issuer = await createIdentity(); + const alice = await createIdentity(clientConfig); + const issuer = await createIdentity(clientConfig); //Add a Merkle Key Collection Verification Method with 8 keys (Must be a power of 2) const keys = new KeyCollection(KeyType.Ed25519, 8); @@ -27,8 +29,8 @@ async function merkleKey() { issuer.doc.sign(issuer.key); //Publish the Identity to the IOTA Network and log the results, this may take a few seconds to complete Proof-of-Work. - const nextMessageId = await publish(issuer.doc.toJSON(), CLIENT_CONFIG); - console.log(`Identity Update: ${EXPLORER_URL}/${nextMessageId}`); + const nextMessageId = await publish(issuer.doc.toJSON(), clientConfig); + logExplorerUrl("Identity Update:", clientConfig.network, nextMessageId); // Prepare a credential subject indicating the degree earned by Alice let credentialSubject = { @@ -56,17 +58,17 @@ async function merkleKey() { }); //Check the verifiable credential - const result = await checkCredential(signedVc.toString(), CLIENT_CONFIG); + const result = await checkCredential(signedVc.toString(), clientConfig); console.log(`VC verification result: ${result.verified}`); // The Issuer would like to revoke the credential (and therefore revokes key 0) issuer.doc.revokeMerkleKey(method.id.toString(), 0); issuer.doc.previousMessageId = nextMessageId; - const revokeMessageId = await publish(issuer.doc.toJSON(), CLIENT_CONFIG); - console.log(`Identity Update: ${EXPLORER_URL}/${revokeMessageId}`); + const revokeMessageId = await publish(issuer.doc.toJSON(), clientConfig); + logExplorerUrl("Identity Update:", clientConfig.network, revokeMessageId); //Check the verifiable credential - const newResult = await checkCredential(signedVc.toString(), CLIENT_CONFIG); + const newResult = await checkCredential(signedVc.toString(), clientConfig); console.log(`VC verification result: ${newResult.verified}`); } diff --git a/bindings/wasm/examples/node.js b/bindings/wasm/examples/node.js index 00e8981994..80915080cc 100644 --- a/bindings/wasm/examples/node.js +++ b/bindings/wasm/examples/node.js @@ -8,6 +8,7 @@ const { createVC } = require('./create_vc'); const { createVP } = require('./create_vp'); const { revokeVC } = require('./revocation'); const { merkleKey } = require('./merkle_key'); +const { CLIENT_CONFIG } = require('./config') async function main() { //Check if an example is mentioned @@ -19,19 +20,19 @@ async function main() { let argument = process.argv[2]; switch(argument) { case 'create_did': - return await createIdentity(); + return await createIdentity(CLIENT_CONFIG); case 'manipulate_did': - return await manipulateIdentity(); + return await manipulateIdentity(CLIENT_CONFIG); case 'resolution': - return await resolution(); + return await resolution(CLIENT_CONFIG); case 'create_vc': - return await createVC(); + return await createVC(CLIENT_CONFIG); case 'revocation': - return await revokeVC(); + return await revokeVC(CLIENT_CONFIG); case 'create_vp': - return await createVP(); + return await createVP(CLIENT_CONFIG); case 'merkle_key': - return await merkleKey(); + return await merkleKey(CLIENT_CONFIG); default: throw 'Unknown example name'; } diff --git a/bindings/wasm/examples/resolution.js b/bindings/wasm/examples/resolution.js index c6b8781d68..692311fc61 100644 --- a/bindings/wasm/examples/resolution.js +++ b/bindings/wasm/examples/resolution.js @@ -3,17 +3,18 @@ const { resolve } = require('../node/identity_wasm') const { manipulateIdentity } = require('./manipulate_did'); -const { CLIENT_CONFIG } = require('./config'); /* A short example to show how to resolve a DID. This returns the latest DID Document. + + @param {{network: string, node: string}} clientConfig */ -async function resolution() { - //Creates a new identity, that also is updated (See "manipulate_did" example) +async function resolution(clientConfig) { + // Creates a new identity, that also is updated (See "manipulate_did" example). const result = await manipulateIdentity(); - //Resolve a DID - return await resolve(result.doc.id.toString(), CLIENT_CONFIG); + // Resolve a DID. + return await resolve(result.doc.id.toString(), clientConfig); } exports.resolution = resolution; diff --git a/bindings/wasm/examples/revocation.js b/bindings/wasm/examples/revocation.js index 0dc6f38063..4f908aafcd 100644 --- a/bindings/wasm/examples/revocation.js +++ b/bindings/wasm/examples/revocation.js @@ -3,7 +3,7 @@ const { DID, checkCredential, publish } = require('../node/identity_wasm') const { createVC } = require('./create_VC'); -const { EXPLORER_URL, CLIENT_CONFIG } = require('./config') +const { logExplorerUrl } = require('./explorer_util') /* This example shows how to revoke a verifiable credential. @@ -11,22 +11,31 @@ const { EXPLORER_URL, CLIENT_CONFIG } = require('./config') As such, the Verifiable Credential can no longer be validated. This would invalidate every Verifiable Credential signed with the same public key, therefore the issuer would have to sign every VC with a different key. Have a look at the Merkle Key example on how to do that practically. + + Note that this example uses the "main" network, if you are writing code against the test network then most function + calls will need to include information about the network, since this is not automatically inferred from the + arguments in all cases currently. + + We recommend that you ALWAYS using a CLIENT_CONFIG parameter that you define when calling any functions that take a + ClientConfig object. This will ensure that all the API calls use a consistent node and network. + + @param {{network: string, node: string}} clientConfig */ -async function revoke() { +async function revoke(clientConfig) { //Creates new identities (See "create_did" and "manipulate_did" examples) - const {alice, issuer, signedVc} = await createVC(); + const {alice, issuer, signedVc} = await createVC(clientConfig); //Remove the public key that signed the VC - effectively revoking the VC as it will no longer be able to verify issuer.doc.removeMethod(DID.parse(issuer.doc.id.toString()+"#newKey")); issuer.doc.previousMessageId = issuer.nextMessageId; issuer.doc.sign(issuer.key); - const MessageId = await publish(issuer.doc, CLIENT_CONFIG); + const messageId = await publish(issuer.doc, clientConfig); //Log the resulting Identity update - console.log(`Identity Update: ${EXPLORER_URL}/${MessageId}`); + logExplorerUrl("Identity Update:", clientConfig.network, messageId); //Check the verifiable credential - const result = await checkCredential(signedVc.toString(), CLIENT_CONFIG); + const result = await checkCredential(signedVc.toString(), clientConfig); console.log(`VC verification result: ${result.verified}`); }