Skip to content

Commit

Permalink
Merge branch 'main' into okstatus-feat
Browse files Browse the repository at this point in the history
  • Loading branch information
Toheeb-Ojuolape authored Oct 21, 2024
2 parents b13603f + 5120f6f commit 5de24a2
Show file tree
Hide file tree
Showing 44 changed files with 1,589 additions and 111 deletions.
9 changes: 9 additions & 0 deletions .changeset/brave-cameras-reply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@web5/agent": minor
"@web5/dids": minor
"@web5/identity-agent": minor
"@web5/proxy-agent": minor
"@web5/user-agent": minor
---

Ability to Update a DID
9 changes: 9 additions & 0 deletions .changeset/fair-pillows-notice.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@web5/agent": patch
"@web5/dids": patch
"@web5/identity-agent": patch
"@web5/proxy-agent": patch
"@web5/user-agent": patch
---

Add ability to update DWN Endpoints
5 changes: 5 additions & 0 deletions .changeset/lemon-bees-yawn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@web5/agent": patch
---

Added parameter for app display name for dynamic rendering in the wallet during web5 connect flow
8 changes: 8 additions & 0 deletions .changeset/many-suns-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@web5/agent": patch
"@web5/identity-agent": patch
"@web5/proxy-agent": patch
"@web5/user-agent": patch
---

Add `getProtocolRole` util
5 changes: 5 additions & 0 deletions .changeset/slimy-mayflies-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@web5/api": patch
---

Ensure protocolRole is maintained between query/read and subscribe/read.
5 changes: 5 additions & 0 deletions .changeset/smooth-weeks-serve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@web5/api": patch
---

Added parameter for app display name for dynamic rendering in the wallet during web5 connect flow
5 changes: 5 additions & 0 deletions .changeset/yellow-schools-fail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@web5/common": minor
---

Added a logger.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"@changesets/cli": "^2.27.5",
"@npmcli/package-json": "5.0.0",
"@typescript-eslint/eslint-plugin": "7.9.0",
"@web5/dwn-server": "0.4.10",
"@web5/dwn-server": "0.6.0",
"audit-ci": "^7.0.1",
"eslint-plugin-mocha": "10.4.3",
"globals": "^13.24.0",
Expand Down Expand Up @@ -60,4 +60,4 @@
"rollup@>=4.0.0 <4.22.4": ">=4.22.4"
}
}
}
}
4 changes: 2 additions & 2 deletions packages/agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
"dependencies": {
"@noble/ciphers": "0.5.3",
"@scure/bip39": "1.2.2",
"@tbd54566975/dwn-sdk-js": "0.4.7",
"@tbd54566975/dwn-sdk-js": "0.5.1",
"@web5/common": "workspace:*",
"@web5/crypto": "workspace:*",
"@web5/dids": "workspace:*",
Expand Down Expand Up @@ -110,4 +110,4 @@
"sinon": "18.0.0",
"typescript": "5.1.6"
}
}
}
29 changes: 26 additions & 3 deletions packages/agent/src/agent-did-resolver-cache.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { DidResolutionResult, DidResolverCache, DidResolverCacheLevel, DidResolverCacheLevelParams } from '@web5/dids';
import { Web5PlatformAgent } from './types/agent.js';
import { logger } from '@web5/common';


/**
Expand Down Expand Up @@ -47,11 +48,33 @@ export class AgentDidResolverCache extends DidResolverCacheLevel implements DidR
const cachedResult = JSON.parse(str);
if (!this._resolving.has(did) && Date.now() >= cachedResult.ttlMillis) {
this._resolving.set(did, true);
if (this.agent.agentDid.uri === did || 'undefined' !== typeof await this.agent.identity.get({ didUri: did })) {

// if a DID is stored in the DID Store, then we don't want to evict it from the cache until we have a successful resolution
// upon a successful resolution, we will update both the storage and the cache with the newly resolved Document.
const storedDid = await this.agent.did.get({ didUri: did, tenant: this.agent.agentDid.uri });
if ('undefined' !== typeof storedDid) {
try {
const result = await this.agent.did.resolve(did);
if (!result.didResolutionMetadata.error) {
this.set(did, result);

// if the resolution was successful, update the stored DID with the new Document
if (!result.didResolutionMetadata.error && result.didDocument) {

const portableDid = {
...storedDid,
document : result.didDocument,
metadata : result.didDocumentMetadata,
};

try {
// this will throw an error if the DID is not managed by the agent, or there is no difference between the stored and resolved DID
// We don't publish the DID in this case, as it was received by the resolver.
await this.agent.did.update({ portableDid, tenant: this.agent.agentDid.uri, publish: false });
} catch(error: any) {
// if the error is not due to no changes detected, log the error
if (error.message && !error.message.includes('No changes detected, update aborted')) {
logger.error(`Error updating DID: ${error.message}`);
}
}
}
} finally {
this._resolving.delete(did);
Expand Down
2 changes: 1 addition & 1 deletion packages/agent/src/bearer-identity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class BearerIdentity {
public async export(): Promise<PortableIdentity> {
return {
portableDid : await this.did.export(),
metadata : this.metadata
metadata : { ...this.metadata },
};
}
}
13 changes: 10 additions & 3 deletions packages/agent/src/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from './oidc.js';
import { pollWithTtl } from './utils.js';

import { Convert } from '@web5/common';
import { Convert, logger } from '@web5/common';
import { CryptoUtils } from '@web5/crypto';
import { DidJwk } from '@web5/dids';
import { DwnInterfaceName, DwnMethodName } from '@tbd54566975/dwn-sdk-js';
Expand All @@ -17,6 +17,7 @@ import { DwnInterfaceName, DwnMethodName } from '@tbd54566975/dwn-sdk-js';
* a did from a provider.
*/
async function initClient({
displayName,
connectServerUrl,
walletUri,
permissionRequests,
Expand Down Expand Up @@ -44,10 +45,12 @@ async function initClient({
const request = await Oidc.createAuthRequest({
client_id : clientDid.uri,
scope : 'openid did:jwk',
redirect_uri : callbackEndpoint,
// custom properties:
// code_challenge : codeChallengeBase64Url,
// code_challenge_method : 'S256',
permissionRequests : permissionRequests,
redirect_uri : callbackEndpoint,
displayName,
});

// Sign the Request Object using the Client DID's signing key.
Expand Down Expand Up @@ -91,6 +94,7 @@ async function initClient({

// a deeplink to a web5 compatible wallet. if the wallet scans this link it should receive
// a route to its web5 connect provider flow and the params of where to fetch the auth request.
logger.log(`Wallet URI: ${walletUri}`);
const generatedWalletUri = new URL(walletUri);
generatedWalletUri.searchParams.set('request_uri', parData.request_uri);
generatedWalletUri.searchParams.set(
Expand Down Expand Up @@ -133,7 +137,10 @@ async function initClient({
* a did from a provider.
*/
export type WalletConnectOptions = {
/** The URL of the intermediary server which relays messages between the client and provider */
/** The user friendly name of the client/app to be displayed when prompting end-user with permission requests. */
displayName: string;

/** The URL of the intermediary server which relays messages between the client and provider. */
connectServerUrl: string;

/**
Expand Down
55 changes: 54 additions & 1 deletion packages/agent/src/did-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ import type {
DidResolverCache,
} from '@web5/dids';

import { BearerDid, Did, UniversalResolver } from '@web5/dids';
import { BearerDid, Did, DidDht, UniversalResolver } from '@web5/dids';

import type { AgentDataStore } from './store-data.js';
import type { AgentKeyManager } from './types/key-manager.js';
import type { ResponseStatus, Web5PlatformAgent } from './types/agent.js';

import { InMemoryDidStore } from './store-did.js';
import { AgentDidResolverCache } from './agent-did-resolver-cache.js';
import { canonicalize } from '@web5/crypto';

export enum DidInterface {
Create = 'Create',
Expand Down Expand Up @@ -256,6 +257,58 @@ export class AgentDidApi<TKeyManager extends AgentKeyManager = AgentKeyManager>
return verificationMethod;
}

public async update({ tenant, portableDid, publish = true }: {
tenant?: string;
portableDid: PortableDid;
publish?: boolean;
}): Promise<BearerDid> {

// Check if the DID exists in the store.
const existingDid = await this.get({ didUri: portableDid.uri, tenant: 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 = await 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: PortableDid = { uri, document, metadata };

// pre-populate the resolution cache with the document and metadata
await this.cache.set(uri, { didDocument: document, didResolutionMetadata: { }, didDocumentMetadata: metadata });

await this._store.set({
id : uri,
data : portableDidWithoutKeys,
agent : this.agent,
tenant : 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') {
await DidDht.publish({ did: bearerDid });
}
}

return bearerDid;
}

public async import({ portableDid, tenant }: {
portableDid: PortableDid;
tenant?: string;
Expand Down
54 changes: 54 additions & 0 deletions packages/agent/src/identity-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import type { IdentityMetadata, PortableIdentity } from './types/identity.js';
import { BearerIdentity } from './bearer-identity.js';
import { isPortableDid } from './prototyping/dids/utils.js';
import { InMemoryIdentityStore } from './store-identity.js';
import { getDwnServiceEndpointUrls } from './utils.js';
import { PortableDid } from '@web5/dids';

export interface IdentityApiParams<TKeyManager extends AgentKeyManager> {
agent?: Web5PlatformAgent<TKeyManager>;
Expand Down Expand Up @@ -216,6 +218,58 @@ export class AgentIdentityApi<TKeyManager extends AgentKeyManager = AgentKeyMana
await this._store.delete({ id: didUri, agent: this.agent });
}

/**
* Returns the DWN endpoints for the given DID.
*
* @param didUri - The DID URI to get the DWN endpoints for.
* @returns An array of DWN endpoints.
* @throws An error if the DID is not found, or no DWN service exists.
*/
public getDwnEndpoints({ didUri }: { didUri: string; }): Promise<string[]> {
return getDwnServiceEndpointUrls(didUri, this.agent.did);
}

/**
* Sets the DWN endpoints for the given DID.
*
* @param didUri - The DID URI to set the DWN endpoints for.
* @param endpoints - The array of DWN endpoints to set.
* @throws An error if the DID is not found, or if an update cannot be performed.
*/
public async setDwnEndpoints({ didUri, endpoints }: { didUri: string; endpoints: string[] }): Promise<void> {
const bearerDid = await this.agent.did.get({ didUri });
if (!bearerDid) {
throw new Error(`AgentIdentityApi: Failed to set DWN endpoints due to DID not found: ${didUri}`);
}

const portableDid = await bearerDid.export();
const dwnService = portableDid.document.service?.find(service => service.id.endsWith('dwn'));
if (dwnService) {
// Update the existing DWN Service with the provided endpoints
dwnService.serviceEndpoint = endpoints;
} else {

// create a DWN Service to add to the DID document
const newDwnService = {
id : 'dwn',
type : 'DecentralizedWebNode',
serviceEndpoint : endpoints,
enc : '#enc',
sig : '#sig'
};

// if no other services exist, create a new array with the DWN service
if (!portableDid.document.service) {
portableDid.document.service = [newDwnService];
} else {
// otherwise, push the new DWN service to the existing services
portableDid.document.service.push(newDwnService);
}
}

await this.agent.did.update({ portableDid, tenant: this.agent.agentDid.uri });
}

/**
* Returns the connected Identity, if one is available.
*
Expand Down
Loading

0 comments on commit 5de24a2

Please sign in to comment.