UNPKG

@microsoft/useragent-sdk

Version:

SDK for building decentralized identity wallets and enterprise agents.

218 lines (184 loc) 7.41 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { IHubError, IHubResponse, HubErrorCode } from '@decentralized-identity/hub-common-js'; import HubRequest from './requests/HubRequest'; import HubError from './HubError'; import HubCommitWriteRequest from './requests/HubCommitWriteRequest'; import HubWriteResponse from './responses/HubWriteResponse'; import HubObjectQueryRequest from './requests/HubObjectQueryRequest'; import HubObjectQueryResponse from './responses/HubObjectQueryResponse'; import HubCommitQueryRequest from './requests/HubCommitQueryRequest'; import HubCommitQueryResponse from './responses/HubCommitQueryResponse'; import nodeFetch, { Request, RequestInit } from 'node-fetch'; import Identifier from '../Identifier'; import DidProtocol, { DidProtocolOptions } from '../crypto/protocols/did/DidProtocol'; import HttpResolver from '../resolvers/HttpResolver'; import IResolver from '../resolvers/IResolver'; import UserAgentError from '../UserAgentError'; /** * Options for instantiating a new Hub session. */ export class HubSessionOptions { /** * The DID of the client, i.e the identity of the user/app using this SDK. */ client?: Identifier; /** * The reference to the private key to use for signing when communicating with the Hub. Must be * registered in the DID document of the clientDid. */ signingKeyReference: string | undefined; /** * The reference to the private key to use for decrypting the hub communication. Must be * registered in the DID document of the clientDid. */ encryptionKeyReference: string | undefined; /** * The Identfier of the owner of the Hub with which we will be communicating. */ hubOwner?: Identifier; /** * The Identifier of the Hub, for addressing request envelopes. */ hubId: string = 'did:test:hub.id'; /** * The HTTPS endpoint of the Hub. */ hubEndpoint: string = 'https://beta.hub.microsoft.com'; } /** * Represents a communication session with a particular Hub instance. */ export default class HubSession { private client?: Identifier; private hubId: string; private hubEndpoint: string; private hubOwner?: Identifier; private signingKeyReference: string | undefined; private encryptionKeyReference: string | undefined; constructor(options: HubSessionOptions) { this.client = options.client; this.hubId = options.hubId; this.hubEndpoint = options.hubEndpoint; this.hubOwner = options.hubOwner; this.signingKeyReference = options.signingKeyReference; this.encryptionKeyReference = options.encryptionKeyReference; } /** * Sends the given request to the Hub instance, and returns the associated response. * * @param request An instance or subclass of HubRequest to be sent. */ send(request: HubCommitWriteRequest): Promise<HubWriteResponse>; send(request: HubObjectQueryRequest): Promise<HubObjectQueryResponse>; send(request: HubCommitQueryRequest): Promise<HubCommitQueryResponse>; async send(request: HubRequest): Promise<any> { const rawRequestJson = await request.getRequestJson(); if (!this.client || !this.hubOwner) { throw new HubError({ error_code: HubErrorCode.NotFound, developer_message: `Identifiers not Specified`, }); } rawRequestJson.iss = this.client.id; rawRequestJson.aud = this.hubId; rawRequestJson.sub = this.hubOwner.id; //rawRequestJson.commit = rawRequestJson.commit.serialize(); const rawRequestString = JSON.stringify(rawRequestJson); const response = await this.makeRequest(rawRequestString); let responseObject: IHubResponse<string>; try { responseObject = response; } catch (e) { throw new HubError({ error_code: HubErrorCode.ServerError, developer_message: `Unexpected error decoding JSON response: ${e.message}`, inner_error: e, }); } return HubSession.mapResponseToObject(responseObject); } /** * Sends a raw (string) request body to the Hub and receives a response. * * @param message The raw request body to send. * @param accessToken The access token to include in the request, if any. */ private async makeRequest(message: string): Promise<any> { if (!this.client || !this.hubOwner) { throw new HubError({ error_code: HubErrorCode.AuthenticationFailed, developer_message: `Client Identifier not specified in options`, }); } let resolver: IResolver; if (this.client.options && this.client.options.resolver) { resolver = this.client.options.resolver; } else { resolver = new HttpResolver('https://beta.discover.did.microsoft.com/'); } // await this.authentication.getAuthenticatedRequest(message, this.hubDid, accessToken); const didProtocolOptions: DidProtocolOptions = { sender: this.client, resolver }; const didProtocol = new DidProtocol(didProtocolOptions); if (!this.signingKeyReference) { throw new UserAgentError('Signing key reference is not defined'); } const request = await didProtocol.signAndEncrypt(message, this.hubId); const res = await this.callFetch(this.hubEndpoint, { method: 'POST', body: request, headers: { 'Content-Type': 'application/jwt', 'Content-Length': request.length.toString() }, }); if (res.status !== 200) { const errorResponse = await res.json(); throw new HubError(errorResponse); } const response = await res.buffer(); return didProtocol.decryptAndVerify(<string>this.encryptionKeyReference, response); } /** * Fetch API wrapper, to allow unit testing. * * @param url The URL to make a request to. * @param init Request initialization details. */ private async callFetch(url: string | Request, init?: RequestInit) { return nodeFetch(url, init); } /** * Mapping of known response types. */ private static responseTypes = { WriteResponse: HubWriteResponse, ObjectQueryResponse: HubObjectQueryResponse, CommitQueryResponse: HubCommitQueryResponse, }; /** * Transforms a JSON blob returned by the Hub into a subclass of HubResponse, based on the `@type` * field of the response. * * @param response The Hub response to be transformed. */ private static mapResponseToObject(response: IHubResponse<string>) { const responseTypeString = response['@type']; const responseType = (<any> HubSession.responseTypes)[responseTypeString]; if (responseType) { return new responseType(response); } if (response['@type'] === 'ErrorResponse') { throw new HubError(<IHubError> <any> response); } throw new HubError({ error_code: HubErrorCode.NotImplemented, developer_message: `Unexpected response type ${responseTypeString}`, }); } }