Identity hub tutorial
Updated: June 2, 2020
Identity hubs expose a set of interfaces for reading and writing data to a DID's personal data store. This article describes the steps necessary for sending your first identity hub requests using the NodeJS identity hub SDK from the Decentralized Identity Foundation.
In this article, we'll demonstrate how to access an identity hub data store by doing three things:
- Write a "to-do" list item to a user's identity hub.
- Query for all the to-do list items in a user's identity hub.
- Read one of the user's to-do list items from the identity hub.
The complete code for this tutorial can be found in the hub SDK GitHub repository.
Prerequisites
To complete this tutorial, you will need:
- NPM and NodeJS 8 or later installed on your machine.
Register a DID
Before you can proceed with this article, you'll need a DID that represents a test user, and an associated RSA private key in the PEM format. If you don't have one already, the steps for registering a DID are described in this registration article.
Important
This tutorial currently uses the test
DID method and RSA as a default crypto algorithm. This tutorial is in the process of being migrated to the ion-test
method and elliptic curve cryptography. The tutorial will currently not work with ion-test
DIDs.
A user always has complete access to their own identity hub. In this article, we'll assume your app has access to the user's private key. Having access to such a key allows us to read, write, and modify any data in an identity hub.
Install the hub SDK
DIF's identity hub SDK for NodeJS is available on NPM for download:
npm install @decentralized-identity/hub-sdk-js@0.1.2
You'll also need to install a few other dependencies:
npm install pem-jwk
npm install node-fetch
npm install @decentralized-identity/did-common-typescript@0.1.16
npm install @decentralized-identity/did-auth-jose@0.1.11
Once you've installed the SDK and its dependencies, create a javascript file like sample-app.js
to use for the remainder of the tutorial. Since we're going to await
a few functions, place all code below inside an async
function, like so:
// sample-app.js
(async () => {
// all code goes here
})();
Import all required packages for the tutorial as follows:
var path = require('path');
var fs = require('fs');
var pemJwk = require('pem-jwk');
var fetch = require('node-fetch');
var RsaPrivateKey = require('@decentralized-identity/did-auth-jose/dist/lib/crypto/rsa/RsaPrivateKey');
var DidCommon = require('@decentralized-identity/did-common-typescript');
var HubSdk = require('@decentralized-identity/hub-sdk-js');
Configure the hub SDK
The hub SDK requires a few parameters to get started. First, the hub SDK requires an HTTP based universal resolver endpoint to use for discovering DIDs. You can use our DID discovery API:
const HTTP_RESOLVER = 'https://beta.discover.did.microsoft.com/';
The hub SDK also needs a DID and a private key to use for signing requests and decrypting responses. Typically, you would use a DID and private key that represents your application. But in this tutorial, we'll use the user's DID and private key from the DID you registered above:
const USER_DID = 'did:test:b31c6552-8b63-4c80-bbc0-06a0c41d1104'; // The DID you registered
const KEY_ID = 'key-1'; // The key ID you used during DID registration
// Read private key from file system and convert to JWK with correct key ID
const privatePem = fs.readFileSync(path.resolve(__dirname, './private.pem'), 'ascii');
const PRIVATE_KEY = pemJwk.pem2jwk(privatePem);
PRIVATE_KEY.kid = KEY_ID;
const privateKey = new RsaPrivateKey.default({
id: `${USER_DID}#${KEY_ID}`,
type: 'RsaVerificationKey2018',
publicKeyJwk: PRIVATE_KEY
});
Establish a hub session
A hub session represents a connection between your app and a user's identity hub. To establish a session, you first need to get the user's DID, which can then be used to look up details about the user's hub.
The details of how to discover a hub's DID and endpoints are described in our detailed service API tutorial.
For now, we'll simplify things and assume the following values:
const HUB_ENDPOINT = 'https://beta.personal.hub.microsoft.com/';
const HUB_DID = 'did:test:hub.id';
With this information, you can now create a new hub session for your test user:
const session = new HubSdk.HubSession({
hubEndpoint: HUB_ENDPOINT,
hubDid: HUB_DID,
resolver: new DidCommon.HttpResolver({ resolverUrl: HTTP_RESOLVER, fetch: fetch}),
clientDid: USER_DID,
clientPrivateKey: privateKey,
targetDid: USER_DID
});
This session can be used to read and write data in the target user's identity hub.
Writing data
Every piece of data in identity hubs is modeled as a series of commits. To learn more about commits, refer to our hub overview page.
To write data, you first need to construct and sign a commit:
// Create a commit
const commit = new HubSdk.Commit({
// The protected headers of the commit
protected: {
committed_at: (new Date()).toISOString(),
iss: USER_DID,
sub: USER_DID,
interface: 'Collections',
context: 'http://schema.org',
type: 'TodoItem',
operation: 'create',
commit_strategy: 'basic',
},
// The data to write
payload: {
"@context": "http://schema.org",
"@type": "TodoItem",
"text": "Get milk from the grocery store",
"status": "incomplete"
}
});
// Sign the commit with the user's private key
const signer = new HubSdk.RsaCommitSigner({
did: USER_DID,
key: privateKey,
});
const signedCommit = await signer.sign(commit);
The values that must be included in the commit headers are the following:
Header | Description |
---|---|
interface |
Must be one of Collections , Profile , Actions , or Permissions . |
context |
The context of the object. This, along with type , indicates the schema of the object that is being created. |
type |
The type of the object. This, along with context , indicates the schema of the object that is being created. |
operation |
Must be one of create , update , or delete |
committed_at |
The time at which the commit is taking place. |
commit_strategy |
Must be basic . See the hub overview for more details. |
sub |
The DID of the user whose hub is being accessed; in other words, the hub owner's DID. |
Once the commit has been signed, you can send it to the user's identity hub to be stored:
try {
// Send the commit to the user's hub
const commitRequest = new HubSdk.HubWriteRequest(signedCommit);
const commitResponse = await session.send(commitRequest);
} catch (e) {
console.error(e);
}
Details on the possible errors that may occur during a hub write request are available in the hub API reference.
Querying for data
Identity hubs also allow you to query for objects that are relevant to your application or service. The simplest query is to list all the objects in an identity hub of a certain kind:
var objects;
try {
const queryRequest = new HubSdk.HubObjectQueryRequest({
interface: 'Collections',
context: 'http://schema.org',
type: 'TodoItem',
});
const queryResponse = await session.send(queryRequest);
objects = queryResponse.getObjects();
} catch (e) {
console.error(e);
}
This query returns a reference to all to-do items that are currently stored in the user's identity hub. Details on the possible errors that may occur during a hub object query request are available in the hub API reference.
Reading an object
The query above does not return the contents of the data, it only returns metadata about the object.
To get the contents of each to-do item, you need to read all commits for each to-do item. The commits for each object can then be resolved to determine the current value of each to-do:
objects.forEach(async (objectMetadata) => {
// See the hub overview for details on hub commits
if (objectMetadata.commit_strategy !== 'basic') {
throw new Error('Currently only the basic commit strategy is supported.');
}
try {
// Request the commits for a specific object by ID
const commitQueryRequest = new HubSdk.HubCommitQueryRequest({
object_id: [objectMetadata.id],
});
// Get the commits from the identity hub
const commitQueryResponse = await session.send(commitQueryRequest);
const commits = commitQueryResponse.getCommits();
// Resolve the commits into the current value for the object
const strategy = new HubSdk.CommitStrategyBasic();
const todo = await strategy.resolveObject(commits);
// Output the final state for the object
console.log(`${todo.text}: ${todo.status}`);
} catch (e) {
console.error(e);
}
});
Details on the possible errors that may occur during a commit query request are available in the hub API reference.
Congratulations! You now know how to read and write data to a user's personal identity hub. You can now build applications and features that integrate with identity hubs to give users control over their sensitive data and respect user's privacy. If you have any questions or suggestions on how this document could be improved, please reach out to us.
The complete code for this tutorial can be found in the hub SDK GitHub repository.
See something missing? We'd love your feedback and input on the Verifiable Credentials preview. Please contact us. When you use Microsoft DID Services, you agree to the DID Preview Agreement and the Microsoft Privacy Statement.