diff --git a/examples/wallet-connect.html b/examples/wallet-connect.html index eb3e813ab..35d0e8369 100644 --- a/examples/wallet-connect.html +++ b/examples/wallet-connect.html @@ -128,35 +128,16 @@

Success

}, }; - const scopes = [ - { - interface: "Records", - method: "Write", - protocol: "http://profile-protocol.xyz", - }, - { - interface: "Records", - method: "Query", - protocol: "http://profile-protocol.xyz", - }, - { - interface: "Records", - method: "Read", - protocol: "http://profile-protocol.xyz", - }, - ]; + const permissionRequests = Web5.requestPermissions([{ + definition: profileProtocol, + }]); try { const { delegateDid } = await Web5.connect({ walletConnectOptions: { walletUri: "web5://connect", connectServerUrl: "http://localhost:3000/connect", - permissionRequests: [ - { - protocolDefinition: profileProtocol, - permissionScopes: scopes, - }, - ], + permissionRequests: permissionRequests, onWalletUriReady: generateQRCode, validatePin: async () => { goToPinScreen(); diff --git a/packages/agent/src/connect.ts b/packages/agent/src/connect.ts index a3d671185..abe89333b 100644 --- a/packages/agent/src/connect.ts +++ b/packages/agent/src/connect.ts @@ -1,13 +1,15 @@ -import { CryptoUtils } from '@web5/crypto'; -import { DwnPermissionScope, DwnProtocolDefinition, DwnRecordsPermissionScope } from './index.js'; + +import type { PushedAuthResponse } from './oidc.js'; +import type { DwnPermissionScope, DwnProtocolDefinition, Web5ConnectAuthResponse } from './index.js'; + import { - Web5ConnectAuthResponse, Oidc, - type PushedAuthResponse, } from './oidc.js'; import { pollWithTtl } from './utils.js'; -import { DidJwk } from '@web5/dids'; + import { Convert } from '@web5/common'; +import { CryptoUtils } from '@web5/crypto'; +import { DidJwk } from '@web5/dids'; import { DwnInterfaceName, DwnMethodName } from '@tbd54566975/dwn-sdk-js'; /** @@ -183,9 +185,9 @@ export type ConnectPermissionRequest = { permissionScopes: DwnPermissionScope[]; }; -export type PermissionRequest = 'write' | 'read' | 'delete' | 'query' | 'subscribe'; +export type Permission = 'write' | 'read' | 'delete' | 'query' | 'subscribe'; -function requestPermissionsForProtocol(definition: DwnProtocolDefinition, permissions: PermissionRequest[]): ConnectPermissionRequest { +function requestPermissionsForProtocol(definition: DwnProtocolDefinition, permissions: Permission[]): ConnectPermissionRequest { const requests: DwnPermissionScope[] = []; // In order to enable sync, we must request permissions for `MessagesQuery`, `MessagesRead` and `MessagesSubscribe` diff --git a/packages/agent/src/oidc.ts b/packages/agent/src/oidc.ts index 2952d55b3..169c40ff1 100644 --- a/packages/agent/src/oidc.ts +++ b/packages/agent/src/oidc.ts @@ -12,13 +12,13 @@ import { concatenateUrl } from './utils.js'; import { xchacha20poly1305 } from '@noble/ciphers/chacha'; import type { ConnectPermissionRequest } from './connect.js'; import { DidDocument, DidJwk, PortableDid, type BearerDid } from '@web5/dids'; -import { AgentDwnApi } from './dwn-api.js'; import type { PermissionScope, RecordsWriteMessage, } from '@tbd54566975/dwn-sdk-js'; import { DwnInterface, DwnProtocolDefinition } from './types/dwn.js'; import { AgentPermissionsApi } from './permissions-api.js'; +import type { Web5Agent } from './types/agent.js'; /** * Sent to an OIDC server to authorize a client. Allows clients @@ -614,11 +614,11 @@ function encryptAuthResponse({ async function createPermissionGrants( selectedDid: string, delegateBearerDid: BearerDid, - dwn: AgentDwnApi, + agent: Web5Agent, scopes: PermissionScope[], ) { - const permissionsApi = new AgentPermissionsApi(dwn); + const permissionsApi = new AgentPermissionsApi({ agent }); // TODO: cleanup all grants if one fails by deleting them from the DWN: https://github.com/TBD54566975/web5-js/issues/849 const permissionGrants = await Promise.all( @@ -638,19 +638,17 @@ async function createPermissionGrants( const { encodedData, ...rawMessage } = grant.message; const data = Convert.base64Url(encodedData).toUint8Array(); - const params = { + const { reply } = await agent.sendDwnRequest({ author : selectedDid, target : selectedDid, messageType : DwnInterface.RecordsWrite, dataStream : new Blob([data]), rawMessage, - }; - - const sent = await dwn.sendRequest(params); + }); - if (sent.reply.status.code !== 202) { + if (reply.status.code !== 202) { throw new Error( - `Could not send the message. Error details: ${sent.reply.status.detail}` + `Could not send the message. Error details: ${reply.status.detail}` ); } @@ -668,10 +666,10 @@ async function createPermissionGrants( */ async function prepareProtocols( selectedDid: string, - agentDwnApi: AgentDwnApi, + agent: Web5Agent, protocolDefinition: DwnProtocolDefinition ) { - const queryMessage = await agentDwnApi.processRequest({ + const queryMessage = await agent.processDwnRequest({ author : selectedDid, messageType : DwnInterface.ProtocolsQuery, target : selectedDid, @@ -679,7 +677,7 @@ async function prepareProtocols( }); if (queryMessage.reply.status.code === 404) { - const configureMessage = await agentDwnApi.processRequest({ + const configureMessage = await agent.processDwnRequest({ author : selectedDid, messageType : DwnInterface.ProtocolsConfigure, target : selectedDid, @@ -707,7 +705,7 @@ async function submitAuthResponse( selectedDid: string, authRequest: Web5ConnectAuthRequest, randomPin: string, - agentDwnApi: AgentDwnApi, + agent: Web5Agent, ) { const delegateBearerDid = await DidJwk.create(); const delegatePortableDid = await delegateBearerDid.export(); @@ -716,8 +714,8 @@ async function submitAuthResponse( // TODO: validate to make sure the scopes and definition are assigned to the same protocol const { protocolDefinition, permissionScopes } = permissionRequest; - await prepareProtocols(selectedDid, agentDwnApi, protocolDefinition); - const permissionGrants = await Oidc.createPermissionGrants(selectedDid, delegateBearerDid, agentDwnApi, permissionScopes); + await prepareProtocols(selectedDid, agent, protocolDefinition); + const permissionGrants = await Oidc.createPermissionGrants(selectedDid, delegateBearerDid, agent, permissionScopes); return permissionGrants; }); diff --git a/packages/agent/tests/connect.spec.ts b/packages/agent/tests/connect.spec.ts index ee0aa5f15..6bb5d36a0 100644 --- a/packages/agent/tests/connect.spec.ts +++ b/packages/agent/tests/connect.spec.ts @@ -288,7 +288,7 @@ describe('web5 connect', function () { const results = await Oidc.createPermissionGrants( providerIdentity.did.uri, delegateBearerDid, - testHarness.agent.dwn, + testHarness.agent, permissionScopes ); const scopesRequestedPlusTwoDefaultScopes = permissionScopes.length; @@ -390,7 +390,7 @@ describe('web5 connect', function () { selectedDid, authRequest, randomPin, - testHarness.agent.dwn + testHarness.agent ); expect(fetchSpy.calledOnce).to.be.true; }); diff --git a/packages/api/src/web5.ts b/packages/api/src/web5.ts index 112579de1..0f45bcbb7 100644 --- a/packages/api/src/web5.ts +++ b/packages/api/src/web5.ts @@ -6,11 +6,14 @@ import type { BearerIdentity, + ConnectPermissionRequest, DelegateGrant, DwnDataEncodedRecordsWriteMessage, DwnMessagesPermissionScope, + DwnProtocolDefinition, DwnRecordsPermissionScope, HdIdentityVault, + Permission, WalletConnectOptions, Web5Agent, } from '@web5/agent'; @@ -480,4 +483,14 @@ export class Web5 { // we expect that any connected protocols will include MessagesQuery and MessagesRead grants that will allow it to sync return [...connectedProtocols]; } + + /** + * Creates a connect permissions request for specific protocols. + * If no permissions are explicitly provided, the default is all permissions ('read', 'write', 'delete', 'query', 'subscribe'). + */ + static requestPermissions(requests: { definition: DwnProtocolDefinition, permissions?: Permission[] }[]): ConnectPermissionRequest[] { + return requests.map(({ definition, permissions }) => WalletConnect.requestPermissionsForProtocol(definition, permissions ?? [ + 'read', 'write', 'delete', 'query', 'subscribe' + ])); + } }