UNPKG

@microsoft/useragent-sdk

Version:

SDK for building decentralized identity wallets and enterprise agents.

136 lines 6.57 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * 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