UNPKG

@web5/agent

Version:
278 lines 14.4 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { BearerDid, Did, DidDht, UniversalResolver } from '@web5/dids'; import { InMemoryDidStore } from './store-did.js'; import { AgentDidResolverCache } from './agent-did-resolver-cache.js'; import { canonicalize } from '@web5/crypto'; export var DidInterface; (function (DidInterface) { DidInterface["Create"] = "Create"; // Deactivate = 'Deactivate', DidInterface["Resolve"] = "Resolve"; // Update = 'Update' })(DidInterface || (DidInterface = {})); export function isDidRequest(didRequest, messageType) { return didRequest.messageType === messageType; } /** * This API is used to manage and interact with DIDs within the Web5 Agent framework. * * If a DWN Data Store is used, the DID information is stored under DID's own tenant by default. * If a tenant property is passed, that tenant will be used to store the DID information. */ export class AgentDidApi extends UniversalResolver { constructor({ agent, didMethods, resolverCache, store }) { if (!didMethods) { throw new TypeError(`AgentDidApi: Required parameter missing: 'didMethods'`); } // Initialize the DID resolver with the given DID methods and resolver cache, or use a default // AgentDidResolverCache if none is provided. super({ didResolvers: didMethods, cache: resolverCache !== null && resolverCache !== void 0 ? resolverCache : new AgentDidResolverCache({ agent, location: 'DATA/AGENT/DID_CACHE' }) }); this._didMethods = new Map(); this._agent = agent; // If `store` is not given, use an in-memory store by default. this._store = store !== null && store !== void 0 ? store : new InMemoryDidStore(); for (const didMethod of didMethods) { this._didMethods.set(didMethod.methodName, didMethod); } } /** * Retrieves the `Web5PlatformAgent` execution context. * * @returns The `Web5PlatformAgent` instance that represents the current execution context. * @throws Will throw an error if the `agent` instance property is undefined. */ get agent() { if (this._agent === undefined) { throw new Error('AgentDidApi: Unable to determine agent execution context.'); } return this._agent; } set agent(agent) { this._agent = agent; // AgentDidResolverCache should set the agent if it is the type of cache being used if ('agent' in this.cache) { this.cache.agent = agent; } } create({ method, tenant, options, store }) { return __awaiter(this, void 0, void 0, function* () { // Get the DID method implementation, which also verifies the method is supported. const didMethod = this.getMethod(method); // Create the DID and store the generated keys in the Agent's key manager. const bearerDid = yield didMethod.create({ keyManager: this.agent.keyManager, options }); // pre-populate the resolution cache with the document and metadata yield this.cache.set(bearerDid.uri, { didDocument: bearerDid.document, didResolutionMetadata: {}, didDocumentMetadata: bearerDid.metadata }); // Persist the DID to the store, by default, unless the `store` option is set to false. if (store !== null && store !== void 0 ? store : true) { // Data stored in the Agent's DID store must be in PortableDid format. const { uri, document, metadata } = bearerDid; const portableDid = { uri, document, metadata }; // Unless an existing `tenant` is specified, a record that includes the DID's URI, document, // and metadata will be stored under a new tenant controlled by the newly created DID. yield this._store.set({ id: portableDid.uri, data: portableDid, agent: this.agent, tenant: tenant !== null && tenant !== void 0 ? tenant : portableDid.uri, preventDuplicates: false, useCache: true }); } return bearerDid; }); } export({ didUri, tenant }) { return __awaiter(this, void 0, void 0, function* () { // Attempt to retrieve the DID from the agent's DID store. const bearerDid = yield this.get({ didUri, tenant }); if (!bearerDid) { throw new Error(`AgentDidApi: Failed to export due to DID not found: ${didUri}`); } // If the DID was found, return the DID in a portable format, and if supported by the Agent's // key manager, the private key material. const portableDid = yield bearerDid.export(); return portableDid; }); } get({ didUri, tenant }) { return __awaiter(this, void 0, void 0, function* () { const portableDid = yield this._store.get({ id: didUri, agent: this.agent, tenant, useCache: true }); if (!portableDid) return undefined; const bearerDid = yield BearerDid.import({ portableDid, keyManager: this.agent.keyManager }); return bearerDid; }); } getSigningMethod({ didUri, methodId }) { return __awaiter(this, void 0, void 0, function* () { // Verify the DID method is supported. const parsedDid = Did.parse(didUri); if (!parsedDid) { throw new Error(`Invalid DID URI: ${didUri}`); } // Get the DID method implementation, which also verifies the method is supported. const didMethod = this.getMethod(parsedDid.method); // Resolve the DID document. const { didDocument, didResolutionMetadata } = yield this.resolve(didUri); if (!didDocument) { throw new Error(`DID resolution failed for '${didUri}': ${JSON.stringify(didResolutionMetadata)}`); } // Retrieve the method-specific verification method to be used for signing operations. const verificationMethod = yield didMethod.getSigningMethod({ didDocument, methodId }); return verificationMethod; }); } update({ tenant, portableDid, publish = true }) { return __awaiter(this, void 0, void 0, function* () { // Check if the DID exists in the store. const existingDid = yield this.get({ didUri: portableDid.uri, tenant: tenant !== null && tenant !== void 0 ? tenant : portableDid.uri }); if (!existingDid) { throw new Error(`AgentDidApi: Could not update, DID not found: ${portableDid.uri}`); } // If the document has not changed, abort the update. if (canonicalize(portableDid.document) === canonicalize(existingDid.document)) { throw new Error('AgentDidApi: No changes detected, update aborted'); } // If private keys are present in the PortableDid, import the key material into the Agent's key // manager. Validate that the key material for every verification method in the DID document is // present in the key manager. If no keys are present, this will fail. // NOTE: We currently do not delete the previous keys from the document. // TODO: Add support for deleting the keys no longer present in the document. const bearerDid = yield BearerDid.import({ keyManager: this.agent.keyManager, portableDid }); // Only the DID URI, document, and metadata are stored in the Agent's DID store. const { uri, document, metadata } = bearerDid; const portableDidWithoutKeys = { uri, document, metadata }; // pre-populate the resolution cache with the document and metadata yield this.cache.set(uri, { didDocument: document, didResolutionMetadata: {}, didDocumentMetadata: metadata }); yield this._store.set({ id: uri, data: portableDidWithoutKeys, agent: this.agent, tenant: tenant !== null && tenant !== void 0 ? tenant : uri, updateExisting: true, useCache: true }); if (publish) { const parsedDid = Did.parse(uri); // currently only supporting DHT as a publishable method. // TODO: abstract this into the didMethod class so that other publishable methods can be supported. if (parsedDid && parsedDid.method === 'dht') { yield DidDht.publish({ did: bearerDid }); } } return bearerDid; }); } import({ portableDid, tenant }) { return __awaiter(this, void 0, void 0, function* () { // If private keys are present in the PortableDid, import the key material into the Agent's key // manager. Validate that the key material for every verification method in the DID document is // present in the key manager. const bearerDid = yield BearerDid.import({ keyManager: this.agent.keyManager, portableDid }); // Only the DID URI, document, and metadata are stored in the Agent's DID store. const { uri, document, metadata } = bearerDid; const portableDidWithoutKeys = { uri, document, metadata }; // pre-populate the resolution cache with the document and metadata yield this.cache.set(uri, { didDocument: document, didResolutionMetadata: {}, didDocumentMetadata: metadata }); // Store the DID in the agent's DID store. // Unless an existing `tenant` is specified, a record that includes the DID's URI, document, // and metadata will be stored under a new tenant controlled by the imported DID. yield this._store.set({ id: portableDidWithoutKeys.uri, data: portableDidWithoutKeys, agent: this.agent, tenant: tenant !== null && tenant !== void 0 ? tenant : portableDidWithoutKeys.uri, preventDuplicates: true, useCache: true }); return bearerDid; }); } delete({ didUri, tenant, deleteKey = true }) { return __awaiter(this, void 0, void 0, function* () { const portableDid = yield this._store.get({ id: didUri, agent: this.agent, tenant, useCache: false }); if (!portableDid) { throw new Error('AgentDidApi: Could not delete, DID not found'); } // delete from the cache yield this.cache.delete(didUri); // Delete the data before deleting the associated keys. yield this._store.delete({ id: didUri, agent: this.agent, tenant }); if (deleteKey) { // Delete the keys associated with the DID // TODO: this could be made a static method on `BearerDid` class yield this.deleteKeys({ portableDid }); } }); } deleteKeys({ portableDid }) { return __awaiter(this, void 0, void 0, function* () { for (const verificationMethod of portableDid.document.verificationMethod || []) { if (!verificationMethod.publicKeyJwk) { continue; } // Compute the key URI of the verification method's public key. const keyUri = yield this.agent.keyManager.getKeyUri({ key: verificationMethod.publicKeyJwk }); yield this.agent.keyManager.deleteKey({ keyUri }); } }); } processRequest(request) { var _a; return __awaiter(this, void 0, void 0, function* () { // Process Create DID request. if (isDidRequest(request, DidInterface.Create)) { try { const bearerDid = yield this.create(Object.assign({}, request.messageParams)); const response = { result: { uri: bearerDid.uri, document: bearerDid.document, metadata: bearerDid.metadata, }, ok: true, status: { code: 201, message: 'Created' } }; return response; } catch (error) { return { ok: false, status: { code: 500, message: (_a = error.message) !== null && _a !== void 0 ? _a : 'Unknown error occurred' } }; } } // Process Resolve DID request. if (isDidRequest(request, DidInterface.Resolve)) { const { didUri, options } = request.messageParams; const resolutionResult = yield this.resolve(didUri, options); const response = { result: resolutionResult, ok: true, status: { code: 200, message: 'OK' } }; return response; } throw new Error(`AgentDidApi: Unsupported request type: ${request.messageType}`); }); } getMethod(methodName) { const didMethodApi = this._didMethods.get(methodName); if (didMethodApi === undefined) { throw new Error(`DID Method not supported: ${methodName}`); } return didMethodApi; } } //# sourceMappingURL=did-api.js.map