@microsoft/useragent-sdk
Version:
SDK for building decentralized identity wallets and enterprise agents.
136 lines • 6.57 kB
JavaScript
;
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
require('es6-promise').polyfill();
const base64url_1 = require("base64url");
require("isomorphic-fetch");
const Identifier_1 = require("../Identifier");
const IdentifierDocument_1 = require("../IdentifierDocument");
const UserAgentError_1 = require("../UserAgentError");
const Multihash_1 = require("./Multihash");
const RegistrarCryptoOptions_1 = require("./RegistrarCryptoOptions");
const JoseProtocol_1 = require("../crypto/protocols/jose/JoseProtocol");
const cloneDeep = require('lodash/fp/cloneDeep');
/**
* Registrar implementation for the Sidetree (ION) network
*/
class SidetreeRegistrar {
/**
* Constructs a new instance of the Sidetree registrar
* @param url to the registration endpoint at the registrar
* @param options to configure the registrar.
*/
constructor(url, options) {
this.url = url;
// Set options. Stringify to avoid circular exception during serialization of this object.
if (!(options && options.keyStore)) {
throw new UserAgentError_1.default('options and options.keyStore need to be defined');
}
this.options = options;
this.cryptoFactory = options.cryptoFactory;
this.registrarCryptoOptions = options.registrar ? options.registrar.registrarCryptoOptions : new RegistrarCryptoOptions_1.default(this.cryptoFactory, new JoseProtocol_1.default());
// Format the url
this.url = `${url.replace(/\/?$/, '/')}`;
this.timeoutInMilliseconds =
1000 *
(!options || !options.timeoutInSeconds
? 30
: options.timeoutInSeconds);
}
/**
* Prepare the document for registration
* @param document Document to format
*/
prepareDocForRegistration(document) {
delete document.id;
return document;
}
/**
* Registers the identifier document on the ledger
* returning the identifier generated by the registrar.
* @param identifierDocument to register.
* @param keyReference Reference to the identifier for the signing key.
*/
async register(identifierDocument, keyReference) {
return new Promise(async (resolve, reject) => {
const timer = setTimeout(() => reject(new UserAgentError_1.default('Fetch timed out.')), this.timeoutInMilliseconds);
// prepare document for registration
identifierDocument = this.prepareDocForRegistration(identifierDocument);
let bodyString = JSON.stringify(identifierDocument);
console.debug(bodyString);
// registration with signed message for bodyString
const encodedBodyString = await this.sign(keyReference, bodyString);
const fetchOptions = {
method: 'POST',
body: encodedBodyString,
headers: {
'Content-Type': 'application/json',
'Content-Length': encodedBodyString.length.toString()
}
};
// Now call the actual fetch with the updated options
const response = await fetch(this.url, fetchOptions);
// Got a response so clear the timer
clearTimeout(timer);
if (!response.ok) {
let msg;
if (response.body) {
msg = response.body.read().toString();
;
}
else {
msg = `Status ${response.status}`;
}
const error = new UserAgentError_1.default(`Failed to register the identifier document: ${msg}`);
reject(error);
return;
}
const responseJson = IdentifierDocument_1.default.fromJSON(await response.json());
const identifier = new Identifier_1.default(responseJson, this.options);
resolve(identifier);
});
}
/**
* Sign the registration payload
* @param keyReference Reference to signature key
* @param bodyString original payload to sign
*/
async sign(keyReference, bodyString) {
const strategy = this.registrarCryptoOptions.protectionStrategy.payloadSigningStrategy;
const options = strategy.protectionOptions;
const protocol = options.payloadProtection;
const signature = await protocol.sign(keyReference, Buffer.from(bodyString), strategy.serializationFormat, options);
return protocol.serialize(signature, strategy.serializationFormat, options);
}
/**
* Uses the specified input to create a basic Sidetree
* compliant identifier document and then hashes the document
* in accordance with the Sidetree protocol specification
* to generate and return the identifier.
*
* @param identifierDocument for which to generate the identifier.
*/
async generateIdentifier(identifierDocument) {
if (!Array.isArray(identifierDocument.publicKeys) || identifierDocument.publicKeys.length === 0) {
throw new UserAgentError_1.default('At least one public key must be specified in the identifier document.');
}
// The genesis document is used for generating the hash,
// but we need to ensure that the id property of the document
// if specified is removed beforehand.
const genesisDocument = cloneDeep(identifierDocument);
genesisDocument.id = undefined;
// Hash the document JSON
const documentBuffer = Buffer.from(JSON.stringify(genesisDocument));
const hashedDocument = Multihash_1.default.hash(documentBuffer, 18);
const encodedDocument = base64url_1.default.encode(hashedDocument);
// Now update the identifier property in
// the genesis document
genesisDocument.id = `did:ion:test:${encodedDocument}`;
return new Identifier_1.default(genesisDocument);
}
}
exports.default = SidetreeRegistrar;
//# sourceMappingURL=SidetreeRegistrar.js.map