From 3da24dbcadc3bf863b21f2e2533d070d23604889 Mon Sep 17 00:00:00 2001 From: bnonni Date: Sun, 1 Sep 2024 20:35:34 -0400 Subject: [PATCH] Web5 agent/register did service endpoints (#741) Add option to HdIdentityValue.initialize() to pass dwnEndpoints to allow the registration of the dwn did service duing DidDht.create() Enables the ability to use the parent, agent did to connect to a remote DWN for connectedDid recovery Can bypass the agentDid-->connectedDids pattern for use on the server side, if desired --- packages/agent/src/hd-identity-vault.ts | 55 ++++++++++++++++--------- packages/api/src/web5.ts | 10 ++--- packages/user-agent/src/user-agent.ts | 14 ++++++- web5-spec | 2 +- 4 files changed, 52 insertions(+), 29 deletions(-) diff --git a/packages/agent/src/hd-identity-vault.ts b/packages/agent/src/hd-identity-vault.ts index 4357ea60d..484344d5e 100644 --- a/packages/agent/src/hd-identity-vault.ts +++ b/packages/agent/src/hd-identity-vault.ts @@ -1,5 +1,6 @@ import type { Jwk } from '@web5/crypto'; import type { KeyValueStore } from '@web5/common'; +import { DidDhtCreateOptions } from '@web5/dids'; import { HDKey } from 'ed25519-keygen/hdkey'; import { BearerDid, DidDht } from '@web5/dids'; @@ -36,6 +37,15 @@ export type HdIdentityVaultInitializeParams = { * during the initialization process. */ recoveryPhrase?: string; + + /** + * Optional dwnEndpoints to register didService endpoints during HdIdentityVault initialization + * + * The dwnEndpoints are used to register a DWN endpoint during DidDht.create(). This allows the + * agent to properly recover connectedDids from DWN. Also, this pattern can be used on the server + * side in place of the agentDid-->connectedDids pattern. + */ + dwnEndpoints?: string[]; }; /** @@ -352,7 +362,7 @@ export class HdIdentityVault implements IdentityVault<{ InitializeResult: string * @returns A promise that resolves with the recovery phrase used during the initialization, which * should be securely stored by the user. */ - public async initialize({ password, recoveryPhrase }: + public async initialize({ password, recoveryPhrase, dwnEndpoints }: HdIdentityVaultInitializeParams ): Promise { /** @@ -498,28 +508,33 @@ export class HdIdentityVault implements IdentityVault<{ InitializeResult: string // created it will use the derived keys. const deterministicKeyGenerator = new DeterministicKeyGenerator(); await deterministicKeyGenerator.addPredefinedKeys({ - privateKeys: [ identityPrivateKey, signingPrivateKey] + privateKeys: [identityPrivateKey, signingPrivateKey] }); // Create the DID using the derived identity, signing, and encryption keys. - const did = await DidDht.create({ - keyManager : deterministicKeyGenerator, - options : { - verificationMethods: [ - { - algorithm : 'Ed25519', - id : 'sig', - purposes : ['assertionMethod', 'authentication'] - }, - // TODO: Enable this once DID DHT supports X25519 keys. - // { - // algorithm : 'X25519', - // id : 'enc', - // purposes : ['keyAgreement'] - // } - ] - } - }); + const options = { + verificationMethods: [ + { + algorithm : 'Ed25519', + id : 'sig', + purposes : ['assertionMethod', 'authentication'] + }, + ] + } as DidDhtCreateOptions; + + if(dwnEndpoints && !!dwnEndpoints.length) { + options.services = [ + { + id : 'dwn', + type : 'DecentralizedWebNode', + serviceEndpoint : dwnEndpoints, + enc : '#enc', + sig : '#sig', + } + ]; + } + + const did = await DidDht.create({ keyManager: deterministicKeyGenerator, options }); /** * STEP 6: Convert the DID to portable format and store it in the data store as a diff --git a/packages/api/src/web5.ts b/packages/api/src/web5.ts index b93e857bf..6fc5d5d8f 100644 --- a/packages/api/src/web5.ts +++ b/packages/api/src/web5.ts @@ -276,9 +276,12 @@ export class Web5 { ); } + // Use the specified DWN endpoints or the latest TBD hosted DWN + const serviceEndpointNodes = techPreview?.dwnEndpoints ?? didCreateOptions?.dwnEndpoints ?? ['https://dwn.tbddev.org/beta']; + // Initialize, if necessary, and start the agent. if (await userAgent.firstLaunch()) { - recoveryPhrase = await userAgent.initialize({ password, recoveryPhrase }); + recoveryPhrase = await userAgent.initialize({ password, recoveryPhrase, dwnEndpoints: serviceEndpointNodes }); } await userAgent.start({ password }); // Attempt to retrieve the connected Identity if it exists. @@ -348,9 +351,6 @@ export class Web5 { // since we are creating a new identity, we will want to register sync for the created Did registerSync = true; - // Use the specified DWN endpoints or the latest TBD hosted DWN - const serviceEndpointNodes = techPreview?.dwnEndpoints ?? didCreateOptions?.dwnEndpoints ?? ['https://dwn.tbddev.org/beta']; - // Generate a new Identity for the end-user. identity = await userAgent.identity.create({ didMethod : 'dht', @@ -397,8 +397,6 @@ export class Web5 { delegateDid = identity.metadata.connectedDid ? identity.did.uri : undefined; if (registration !== undefined) { // If a registration object is passed, we attempt to register the AgentDID and the ConnectedDID with the DWN endpoints provided - const serviceEndpointNodes = techPreview?.dwnEndpoints ?? didCreateOptions?.dwnEndpoints; - try { for (const dwnEndpoint of serviceEndpointNodes) { // check if endpoint needs registration diff --git a/packages/user-agent/src/user-agent.ts b/packages/user-agent/src/user-agent.ts index 9568959fb..492620b27 100644 --- a/packages/user-agent/src/user-agent.ts +++ b/packages/user-agent/src/user-agent.ts @@ -53,6 +53,16 @@ export type AgentInitializeParams = { * If omitted, a new phrase is generated, which should be securely recorded for future recovery needs. */ recoveryPhrase?: string; + + /** + * Optional dwnEndpoints to register didService endpoints during Web5UserAgent initialization + * + * The dwnEndpoints are used to register DWN endpoints against the agent DID created during + * Web5UserAgent.initialize() => DidDht.create(). This allows the + * agent to properly recover connectedDids from DWN. Also, this pattern can be used on the server + * side in place of the agentDid-->connectedDids pattern. + */ + dwnEndpoints?: string[]; }; export type AgentStartParams = { @@ -202,9 +212,9 @@ export class Web5UserAgent { + public async initialize({ password, recoveryPhrase, dwnEndpoints }: AgentInitializeParams): Promise { // Initialize the Agent vault. - recoveryPhrase = await this.vault.initialize({ password, recoveryPhrase }); + recoveryPhrase = await this.vault.initialize({ password, recoveryPhrase, dwnEndpoints }); return recoveryPhrase; } diff --git a/web5-spec b/web5-spec index 7c4e7fc52..a582f4757 160000 --- a/web5-spec +++ b/web5-spec @@ -1 +1 @@ -Subproject commit 7c4e7fc5206ee3f50c05383e61be0046623f7dc9 +Subproject commit a582f4757c00f8797985a756729e6e6c7407bc13