@web5/agent
Version:
278 lines • 14.4 kB
JavaScript
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