Skip to content

Commit

Permalink
Web5 agent/register did service endpoints (#741)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
bnonni authored Sep 2, 2024
1 parent d643160 commit 3da24db
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 29 deletions.
55 changes: 35 additions & 20 deletions packages/agent/src/hd-identity-vault.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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[];
};

/**
Expand Down Expand Up @@ -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<string> {
/**
Expand Down Expand Up @@ -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<DeterministicKeyGenerator>;

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
Expand Down
10 changes: 4 additions & 6 deletions packages/api/src/web5.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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
Expand Down
14 changes: 12 additions & 2 deletions packages/user-agent/src/user-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -202,9 +212,9 @@ export class Web5UserAgent<TKeyManager extends AgentKeyManager = LocalKeyManager
* cryptographic keys for the vault. If a recovery phrase is not provided, a new recovery phrase
* will be generated and returned. The password should be chosen and entered by the end-user.
*/
public async initialize({ password, recoveryPhrase }: AgentInitializeParams): Promise<string> {
public async initialize({ password, recoveryPhrase, dwnEndpoints }: AgentInitializeParams): Promise<string> {
// Initialize the Agent vault.
recoveryPhrase = await this.vault.initialize({ password, recoveryPhrase });
recoveryPhrase = await this.vault.initialize({ password, recoveryPhrase, dwnEndpoints });

return recoveryPhrase;
}
Expand Down
2 changes: 1 addition & 1 deletion web5-spec

0 comments on commit 3da24db

Please sign in to comment.