@enbox/api 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +585 -0
- package/dist/browser.js +2226 -0
- package/dist/browser.js.map +7 -0
- package/dist/browser.mjs +2226 -0
- package/dist/browser.mjs.map +7 -0
- package/dist/cjs/did-api.js +126 -0
- package/dist/cjs/did-api.js.map +1 -0
- package/dist/cjs/dwn-api.js +804 -0
- package/dist/cjs/dwn-api.js.map +1 -0
- package/dist/cjs/grant-revocation.js +183 -0
- package/dist/cjs/grant-revocation.js.map +1 -0
- package/dist/cjs/index.js +63 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/permission-grant.js +365 -0
- package/dist/cjs/permission-grant.js.map +1 -0
- package/dist/cjs/permission-request.js +272 -0
- package/dist/cjs/permission-request.js.map +1 -0
- package/dist/cjs/protocol.js +110 -0
- package/dist/cjs/protocol.js.map +1 -0
- package/dist/cjs/record.js +1127 -0
- package/dist/cjs/record.js.map +1 -0
- package/dist/cjs/subscription-util.js +86 -0
- package/dist/cjs/subscription-util.js.map +1 -0
- package/dist/cjs/utils.js +127 -0
- package/dist/cjs/utils.js.map +1 -0
- package/dist/cjs/vc-api.js +64 -0
- package/dist/cjs/vc-api.js.map +1 -0
- package/dist/cjs/web5.js +471 -0
- package/dist/cjs/web5.js.map +1 -0
- package/dist/esm/did-api.js +69 -0
- package/dist/esm/did-api.js.map +1 -0
- package/dist/esm/dwn-api.js +573 -0
- package/dist/esm/dwn-api.js.map +1 -0
- package/dist/esm/grant-revocation.js +109 -0
- package/dist/esm/grant-revocation.js.map +1 -0
- package/dist/esm/index.js +34 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/permission-grant.js +233 -0
- package/dist/esm/permission-grant.js.map +1 -0
- package/dist/esm/permission-request.js +166 -0
- package/dist/esm/permission-request.js.map +1 -0
- package/dist/esm/protocol.js +67 -0
- package/dist/esm/protocol.js.map +1 -0
- package/dist/esm/record.js +814 -0
- package/dist/esm/record.js.map +1 -0
- package/dist/esm/subscription-util.js +35 -0
- package/dist/esm/subscription-util.js.map +1 -0
- package/dist/esm/utils.js +120 -0
- package/dist/esm/utils.js.map +1 -0
- package/dist/esm/vc-api.js +30 -0
- package/dist/esm/vc-api.js.map +1 -0
- package/dist/esm/web5.js +281 -0
- package/dist/esm/web5.js.map +1 -0
- package/dist/types/did-api.d.ts +66 -0
- package/dist/types/did-api.d.ts.map +1 -0
- package/dist/types/dwn-api.d.ts +336 -0
- package/dist/types/dwn-api.d.ts.map +1 -0
- package/dist/types/grant-revocation.d.ts +66 -0
- package/dist/types/grant-revocation.d.ts.map +1 -0
- package/dist/types/index.d.ts +34 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/permission-grant.d.ts +157 -0
- package/dist/types/permission-grant.d.ts.map +1 -0
- package/dist/types/permission-request.d.ts +108 -0
- package/dist/types/permission-request.d.ts.map +1 -0
- package/dist/types/protocol.d.ts +59 -0
- package/dist/types/protocol.d.ts.map +1 -0
- package/dist/types/record.d.ts +441 -0
- package/dist/types/record.d.ts.map +1 -0
- package/dist/types/subscription-util.d.ts +19 -0
- package/dist/types/subscription-util.d.ts.map +1 -0
- package/dist/types/utils.d.ts +85 -0
- package/dist/types/utils.d.ts.map +1 -0
- package/dist/types/vc-api.d.ts +24 -0
- package/dist/types/vc-api.d.ts.map +1 -0
- package/dist/types/web5.d.ts +219 -0
- package/dist/types/web5.d.ts.map +1 -0
- package/package.json +111 -0
- package/src/did-api.ts +90 -0
- package/src/dwn-api.ts +952 -0
- package/src/grant-revocation.ts +124 -0
- package/src/index.ts +35 -0
- package/src/permission-grant.ts +327 -0
- package/src/permission-request.ts +214 -0
- package/src/protocol.ts +87 -0
- package/src/record.ts +1125 -0
- package/src/subscription-util.ts +42 -0
- package/src/utils.ts +128 -0
- package/src/vc-api.ts +30 -0
- package/src/web5.ts +516 -0
package/src/web5.ts
ADDED
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NOTE: Added reference types here to avoid a `pnpm` bug during build.
|
|
3
|
+
* https://github.com/TBD54566975/web5-js/pull/507
|
|
4
|
+
*/
|
|
5
|
+
/// <reference types="@enbox/dwn-sdk-js" />
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
BearerIdentity,
|
|
9
|
+
DwnDataEncodedRecordsWriteMessage,
|
|
10
|
+
DwnMessagesPermissionScope,
|
|
11
|
+
DwnProtocolDefinition,
|
|
12
|
+
DwnRecordsPermissionScope,
|
|
13
|
+
HdIdentityVault,
|
|
14
|
+
Permission,
|
|
15
|
+
WalletConnectOptions,
|
|
16
|
+
Web5Agent,
|
|
17
|
+
} from '@enbox/agent';
|
|
18
|
+
|
|
19
|
+
import { Web5UserAgent } from '@enbox/user-agent';
|
|
20
|
+
import { DwnInterface, DwnRegistrar, WalletConnect } from '@enbox/agent';
|
|
21
|
+
|
|
22
|
+
import { DidApi } from './did-api.js';
|
|
23
|
+
import { DwnApi } from './dwn-api.js';
|
|
24
|
+
import { VcApi } from './vc-api.js';
|
|
25
|
+
import { PermissionGrant } from './permission-grant.js';
|
|
26
|
+
|
|
27
|
+
/** Override defaults configured during the technical preview phase. */
|
|
28
|
+
export type TechPreviewOptions = {
|
|
29
|
+
/** Override default dwnEndpoints provided for technical preview. */
|
|
30
|
+
dwnEndpoints?: string[];
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** Override defaults for DID creation. */
|
|
34
|
+
export type DidCreateOptions = {
|
|
35
|
+
/** Override default dwnEndpoints provided during DID creation. */
|
|
36
|
+
dwnEndpoints?: string[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Represents a permission request for a protocol definition.
|
|
41
|
+
*/
|
|
42
|
+
export type ConnectPermissionRequest = {
|
|
43
|
+
/**
|
|
44
|
+
* The protocol definition for the protocol being requested.
|
|
45
|
+
*/
|
|
46
|
+
protocolDefinition: DwnProtocolDefinition;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The permissions being requested for the protocol. If none are provided, the default is to request all permissions.
|
|
50
|
+
*/
|
|
51
|
+
permissions?: Permission[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Options for connecting to a Web5 agent. This includes the ability to connect to an external wallet.
|
|
56
|
+
*
|
|
57
|
+
* NOTE: the returned `ConnectPermissionRequest` type is different to the `ConnectPermissionRequest` type in the `@enbox/agent` package.
|
|
58
|
+
*/
|
|
59
|
+
export type ConnectOptions = Omit<WalletConnectOptions, 'permissionRequests'> & {
|
|
60
|
+
/** The user friendly name of the client/app to be displayed when prompting end-user with permission requests. */
|
|
61
|
+
displayName: string;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The permissions that are being requested for the connected DID.
|
|
65
|
+
* This is used to create the {@link ConnectPermissionRequest} for the wallet connect flow.
|
|
66
|
+
*/
|
|
67
|
+
permissionRequests: ConnectPermissionRequest[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Optional overrides that can be provided when calling {@link Web5.connect}. */
|
|
71
|
+
export type Web5ConnectOptions = {
|
|
72
|
+
/**
|
|
73
|
+
* When specified, external wallet connect flow is triggered.
|
|
74
|
+
* This param currently will not work in apps that are currently connected.
|
|
75
|
+
* It must only be invoked at registration with a reset and empty DWN and agent.
|
|
76
|
+
*/
|
|
77
|
+
walletConnectOptions?: ConnectOptions;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Provide a {@link Web5Agent} implementation. Defaults to creating a local
|
|
81
|
+
* {@link Web5UserAgent} if one isn't provided
|
|
82
|
+
**/
|
|
83
|
+
agent?: Web5Agent;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Provide an instance of a {@link HdIdentityVault} implementation. Defaults to
|
|
87
|
+
* a LevelDB-backed store with an insecure, static unlock password if one
|
|
88
|
+
* isn't provided. To allow the app user to enter a secure password of
|
|
89
|
+
* their choosing, provide an initialized {@link HdIdentityVault} instance.
|
|
90
|
+
**/
|
|
91
|
+
agentVault?: HdIdentityVault;
|
|
92
|
+
|
|
93
|
+
/** Specify an existing DID to connect to. */
|
|
94
|
+
connectedDid?: string;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The Web5 app `password` is used to protect data on the device the application is running on.
|
|
98
|
+
*
|
|
99
|
+
* Only the end user should know this password: it should not be stored on the device or
|
|
100
|
+
* transmitted over the network.
|
|
101
|
+
*
|
|
102
|
+
* This password is crucial for the security of an identity vault that stores the local Agent's
|
|
103
|
+
* cryptographic keys and decentralized identifier (DID). The vault's content is encrypted using
|
|
104
|
+
* the password, making it accessible only to those who know the password.
|
|
105
|
+
*
|
|
106
|
+
* App users should be advised to use a strong, unique passphrase that is not shared across
|
|
107
|
+
* different services or applications. The password should be kept confidential and not be
|
|
108
|
+
* exposed to unauthorized entities. Losing the password may result in irreversible loss of
|
|
109
|
+
* access to the vault's contents.
|
|
110
|
+
*/
|
|
111
|
+
password?: string;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The `recoveryPhrase` is a unique, secure key for recovering the identity vault.
|
|
115
|
+
*
|
|
116
|
+
* This phrase is a series of 12 words generated securely and known only to the user. It plays a
|
|
117
|
+
* critical role in the security of the identity vault by enabling the recovery of the vault's
|
|
118
|
+
* contents, including cryptographic keys and the Agent's decentralized identifier (DID), across
|
|
119
|
+
* different devices or if the original device is compromised or lost.
|
|
120
|
+
*
|
|
121
|
+
* The recovery phrase is akin to a master key, as anyone with access to this phrase can restore
|
|
122
|
+
* and access the vault's contents. It’s combined with the app `password` to encrypt the vault's
|
|
123
|
+
* content.
|
|
124
|
+
*
|
|
125
|
+
* Unlike a password, the recovery phrase is not intended for regular use but as a secure backup
|
|
126
|
+
* method for vault recovery. Losing this phrase can result in permanent loss of access to the
|
|
127
|
+
* vault's contents, as it cannot be reset or retrieved if forgotten.
|
|
128
|
+
*
|
|
129
|
+
* Users should treat the recovery phrase with the highest level of security, ensuring it is
|
|
130
|
+
* never shared, stored online, or exposed to potential threats. It is the user's responsibility
|
|
131
|
+
* to keep this phrase safe to maintain the integrity and accessibility of their secured data. It
|
|
132
|
+
* is recommended to write it down and store it in a secure location, separate from the device and
|
|
133
|
+
* digital backups.
|
|
134
|
+
*/
|
|
135
|
+
recoveryPhrase?: string;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Enable synchronization of DWN records between local and remote DWNs.
|
|
139
|
+
* Sync defaults to running every 2 minutes and can be set to any value accepted by `ms()`.
|
|
140
|
+
* To disable sync set to 'off'.
|
|
141
|
+
*/
|
|
142
|
+
sync?: string;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Override defaults configured during the technical preview phase.
|
|
146
|
+
* See {@link TechPreviewOptions} for available options.
|
|
147
|
+
*/
|
|
148
|
+
techPreview?: TechPreviewOptions;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Override defaults configured options for creating a DID during connect.
|
|
152
|
+
* See {@link DidCreateOptions} for available options.
|
|
153
|
+
*/
|
|
154
|
+
didCreateOptions?: DidCreateOptions;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* If the `registration` option is provided, the agent DID and the connected DID will be registered with the DWN endpoints provided by `techPreview` or `didCreateOptions`.
|
|
158
|
+
*
|
|
159
|
+
* If registration fails, the `onFailure` callback will be called with the error.
|
|
160
|
+
* If registration is successful, the `onSuccess` callback will be called.
|
|
161
|
+
*/
|
|
162
|
+
registration? : {
|
|
163
|
+
/** Called when all of the DWN registrations are successful */
|
|
164
|
+
onSuccess: () => void;
|
|
165
|
+
/** Called when any of the DWN registrations fail */
|
|
166
|
+
onFailure: (error: any) => void;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Represents the result of the Web5 connection process, including the Web5 instance,
|
|
172
|
+
* the connected decentralized identifier (DID), and optionally the recovery phrase used
|
|
173
|
+
* during the agent's initialization.
|
|
174
|
+
*/
|
|
175
|
+
export type Web5ConnectResult = {
|
|
176
|
+
/** The Web5 instance, providing access to the agent, DID, DWN, and VC APIs. */
|
|
177
|
+
web5: Web5;
|
|
178
|
+
|
|
179
|
+
/** The DID that has been connected or created during the connection process. */
|
|
180
|
+
did: string;
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* The first time a Web5 agent is initialized, the recovery phrase that was used to generate the
|
|
184
|
+
* agent's DID and keys is returned. This phrase can be used to recover the agent's vault contents
|
|
185
|
+
* and should be stored securely by the user.
|
|
186
|
+
*/
|
|
187
|
+
recoveryPhrase?: string;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* The resulting did of a successful wallet connect. Only returned on success if
|
|
191
|
+
* {@link WalletConnectOptions} was provided.
|
|
192
|
+
*/
|
|
193
|
+
delegateDid?: string;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Parameters that are passed to Web5 constructor.
|
|
198
|
+
*
|
|
199
|
+
* @see {@link Web5ConnectOptions}
|
|
200
|
+
*/
|
|
201
|
+
export type Web5Params = {
|
|
202
|
+
/**
|
|
203
|
+
* A {@link Web5Agent} instance that handles DIDs, DWNs and VCs requests. The agent manages the
|
|
204
|
+
* user keys and identities, and is responsible to sign and verify messages.
|
|
205
|
+
*/
|
|
206
|
+
agent: Web5Agent;
|
|
207
|
+
|
|
208
|
+
/** The DID of the tenant under which all DID, DWN, and VC requests are being performed. */
|
|
209
|
+
connectedDid: string;
|
|
210
|
+
|
|
211
|
+
/** The DID that will be signing Web5 messages using grants from the connectedDid */
|
|
212
|
+
delegateDid?: string;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* The main Web5 API interface. It manages the creation of a DID if needed, the connection to the
|
|
217
|
+
* local DWN and all the web5 main foundational APIs such as VC, syncing, etc.
|
|
218
|
+
*/
|
|
219
|
+
export class Web5 {
|
|
220
|
+
/**
|
|
221
|
+
* A {@link Web5Agent} instance that handles DIDs, DWNs and VCs requests. The agent manages the
|
|
222
|
+
* user keys and identities, and is responsible to sign and verify messages.
|
|
223
|
+
*/
|
|
224
|
+
agent: Web5Agent;
|
|
225
|
+
|
|
226
|
+
/** Exposed instance to the DID APIs, allow users to create and resolve DIDs */
|
|
227
|
+
did: DidApi;
|
|
228
|
+
|
|
229
|
+
/** Exposed instance to the DWN APIs, allow users to read/write records */
|
|
230
|
+
dwn: DwnApi;
|
|
231
|
+
|
|
232
|
+
/** Exposed instance to the VC APIs, allow users to issue, present and verify VCs */
|
|
233
|
+
vc: VcApi;
|
|
234
|
+
|
|
235
|
+
constructor({ agent, connectedDid, delegateDid }: Web5Params) {
|
|
236
|
+
this.agent = agent;
|
|
237
|
+
this.did = new DidApi({ agent, connectedDid });
|
|
238
|
+
this.dwn = new DwnApi({ agent, connectedDid, delegateDid });
|
|
239
|
+
this.vc = new VcApi({ agent, connectedDid });
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Connects to a {@link Web5Agent}. Defaults to creating a local {@link Web5UserAgent} if one
|
|
244
|
+
* isn't provided.
|
|
245
|
+
*
|
|
246
|
+
* If `walletConnectOptions` are provided, a WalletConnect flow will be initiated to import a delegated DID from an external wallet.
|
|
247
|
+
* If there is a failure at any point during connecting and processing grants, all created DIDs and Identities as well as the provided grants
|
|
248
|
+
* will be cleaned up and an error thrown. This allows for subsequent Connect attempts to be made without any errors.
|
|
249
|
+
*
|
|
250
|
+
* @param options - Optional overrides that can be provided when calling {@link Web5.connect}.
|
|
251
|
+
* @returns A promise that resolves to a {@link Web5} instance and the connected DID.
|
|
252
|
+
*/
|
|
253
|
+
static async connect({
|
|
254
|
+
agent,
|
|
255
|
+
agentVault,
|
|
256
|
+
connectedDid,
|
|
257
|
+
password,
|
|
258
|
+
recoveryPhrase,
|
|
259
|
+
sync,
|
|
260
|
+
techPreview,
|
|
261
|
+
didCreateOptions,
|
|
262
|
+
registration,
|
|
263
|
+
walletConnectOptions,
|
|
264
|
+
}: Web5ConnectOptions = {}): Promise<Web5ConnectResult> {
|
|
265
|
+
let delegateDid: string | undefined;
|
|
266
|
+
if (agent === undefined) {
|
|
267
|
+
let registerSync = false;
|
|
268
|
+
// A custom Web5Agent implementation was not specified, so use default managed user agent.
|
|
269
|
+
const userAgent = await Web5UserAgent.create({ agentVault });
|
|
270
|
+
agent = userAgent;
|
|
271
|
+
|
|
272
|
+
// Warn the developer and application user of the security risks of using a static password.
|
|
273
|
+
if (password === undefined) {
|
|
274
|
+
password = 'insecure-static-phrase';
|
|
275
|
+
console.warn(
|
|
276
|
+
'%cSECURITY WARNING:%c ' +
|
|
277
|
+
'You have not set a password, which defaults to a static, guessable value. ' +
|
|
278
|
+
'This significantly compromises the security of your data. ' +
|
|
279
|
+
'Please configure a secure, unique password.',
|
|
280
|
+
'font-weight: bold; color: red;',
|
|
281
|
+
'font-weight: normal; color: inherit;'
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Use the specified DWN endpoints or the latest TBD hosted DWN
|
|
286
|
+
const serviceEndpointNodes = techPreview?.dwnEndpoints ?? didCreateOptions?.dwnEndpoints ?? ['https://enbox-production.up.railway.app'];
|
|
287
|
+
|
|
288
|
+
// Initialize, if necessary, and start the agent.
|
|
289
|
+
if (await userAgent.firstLaunch()) {
|
|
290
|
+
recoveryPhrase = await userAgent.initialize({ password, recoveryPhrase, dwnEndpoints: serviceEndpointNodes });
|
|
291
|
+
}
|
|
292
|
+
await userAgent.start({ password });
|
|
293
|
+
// Attempt to retrieve the connected Identity if it exists.
|
|
294
|
+
const connectedIdentity: BearerIdentity = await userAgent.identity.connectedIdentity();
|
|
295
|
+
let identity: BearerIdentity;
|
|
296
|
+
let connectedProtocols: string[] = [];
|
|
297
|
+
if (connectedIdentity) {
|
|
298
|
+
// if a connected identity is found, use it
|
|
299
|
+
// TODO: In the future, implement a way to re-connect an already connected identity and apply additional grants/protocols
|
|
300
|
+
identity = connectedIdentity;
|
|
301
|
+
} else if (walletConnectOptions) {
|
|
302
|
+
if (sync === 'off') {
|
|
303
|
+
// Currently we require sync to be enabled when using WalletConnect
|
|
304
|
+
// This is to ensure a connected app is not in a disjointed state from any other clients/app using the connectedDid
|
|
305
|
+
throw new Error('Sync must not be disabled when using WalletConnect');
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Since we are connecting a new identity, we will want to register sync for the connectedDid
|
|
309
|
+
registerSync = true;
|
|
310
|
+
|
|
311
|
+
// No connected identity found and connectOptions are provided, attempt to import a delegated DID from an external wallet
|
|
312
|
+
try {
|
|
313
|
+
const { permissionRequests, ...connectOptions } = walletConnectOptions;
|
|
314
|
+
const walletPermissionRequests = permissionRequests.map(({ protocolDefinition, permissions }) => WalletConnect.createPermissionRequestForProtocol({
|
|
315
|
+
definition : protocolDefinition,
|
|
316
|
+
permissions : permissions ?? [
|
|
317
|
+
'read', 'write', 'delete', 'query', 'subscribe'
|
|
318
|
+
]}
|
|
319
|
+
));
|
|
320
|
+
|
|
321
|
+
const { delegatePortableDid, connectedDid, delegateGrants } = await WalletConnect.initClient({
|
|
322
|
+
...connectOptions,
|
|
323
|
+
permissionRequests: walletPermissionRequests,
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// Import the delegated DID as an Identity in the User Agent.
|
|
327
|
+
// Setting the connectedDID in the metadata applies a relationship between the signer identity and the one it is impersonating.
|
|
328
|
+
identity = await userAgent.identity.import({ portableIdentity: {
|
|
329
|
+
portableDid : delegatePortableDid,
|
|
330
|
+
metadata : {
|
|
331
|
+
connectedDid,
|
|
332
|
+
name : 'Default',
|
|
333
|
+
uri : delegatePortableDid.uri,
|
|
334
|
+
tenant : agent.agentDid.uri,
|
|
335
|
+
}
|
|
336
|
+
}});
|
|
337
|
+
|
|
338
|
+
// Attempts to process the connected grants to be used by the delegateDID
|
|
339
|
+
// If the process fails, we want to clean up the identity
|
|
340
|
+
// the connected grants will return a de-duped array of protocol URIs that are used to register sync for those protocols
|
|
341
|
+
connectedProtocols = await this.processConnectedGrants({ agent, delegateDid: delegatePortableDid.uri, grants: delegateGrants });
|
|
342
|
+
} catch (error:any) {
|
|
343
|
+
// clean up the DID and Identity if import fails and throw
|
|
344
|
+
// TODO: Implement the ability to purge all of our messages as a tenant
|
|
345
|
+
await this.cleanUpIdentity({ identity, userAgent });
|
|
346
|
+
throw new Error(`Failed to connect to wallet: ${error.message}`);
|
|
347
|
+
}
|
|
348
|
+
} else {
|
|
349
|
+
// No connected identity found and no connectOptions provided, use local Identities
|
|
350
|
+
// Query the Agent's DWN tenant for identity records.
|
|
351
|
+
const identities = await userAgent.identity.list();
|
|
352
|
+
|
|
353
|
+
// If an existing identity is not found found, create a new one.
|
|
354
|
+
const existingIdentityCount = identities.length;
|
|
355
|
+
if (existingIdentityCount === 0) {
|
|
356
|
+
// since we are creating a new identity, we will want to register sync for the created Did
|
|
357
|
+
registerSync = true;
|
|
358
|
+
|
|
359
|
+
// Generate a new Identity for the end-user.
|
|
360
|
+
identity = await userAgent.identity.create({
|
|
361
|
+
didMethod : 'dht',
|
|
362
|
+
metadata : { name: 'Default' },
|
|
363
|
+
didOptions : {
|
|
364
|
+
services: [
|
|
365
|
+
{
|
|
366
|
+
id : 'dwn',
|
|
367
|
+
type : 'DecentralizedWebNode',
|
|
368
|
+
serviceEndpoint : serviceEndpointNodes,
|
|
369
|
+
enc : '#enc',
|
|
370
|
+
sig : '#sig',
|
|
371
|
+
}
|
|
372
|
+
],
|
|
373
|
+
verificationMethods: [
|
|
374
|
+
{
|
|
375
|
+
algorithm : 'Ed25519',
|
|
376
|
+
id : 'sig',
|
|
377
|
+
purposes : ['assertionMethod', 'authentication']
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
algorithm : 'secp256k1',
|
|
381
|
+
id : 'enc',
|
|
382
|
+
purposes : ['keyAgreement']
|
|
383
|
+
}
|
|
384
|
+
]
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
} else {
|
|
389
|
+
// If multiple identities are found, use the first one.
|
|
390
|
+
// TODO: Implement selecting a connectedDid from multiple identities
|
|
391
|
+
identity = identities[0];
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// If the stored identity has a connected DID, use it as the connected DID, otherwise use the identity's DID.
|
|
396
|
+
connectedDid = identity.metadata.connectedDid ?? identity.did.uri;
|
|
397
|
+
// If the stored identity has a connected DID, use the identity DID as the delegated DID, otherwise it is undefined.
|
|
398
|
+
delegateDid = identity.metadata.connectedDid ? identity.did.uri : undefined;
|
|
399
|
+
if (registration !== undefined) {
|
|
400
|
+
// If a registration object is passed, we attempt to register the AgentDID and the ConnectedDID with the DWN endpoints provided
|
|
401
|
+
try {
|
|
402
|
+
for (const dwnEndpoint of serviceEndpointNodes) {
|
|
403
|
+
// check if endpoint needs registration
|
|
404
|
+
const serverInfo = await userAgent.rpc.getServerInfo(dwnEndpoint);
|
|
405
|
+
if (serverInfo.registrationRequirements.length === 0) {
|
|
406
|
+
// no registration required
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// register the agent DID
|
|
411
|
+
await DwnRegistrar.registerTenant(dwnEndpoint, agent.agentDid.uri);
|
|
412
|
+
|
|
413
|
+
// register the connected Identity DID
|
|
414
|
+
await DwnRegistrar.registerTenant(dwnEndpoint, connectedDid);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// If no failures occurred, call the onSuccess callback
|
|
418
|
+
registration.onSuccess();
|
|
419
|
+
} catch(error) {
|
|
420
|
+
// for any failure, call the onFailure callback with the error
|
|
421
|
+
registration.onFailure(error);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Enable sync, unless explicitly disabled.
|
|
426
|
+
if (sync !== 'off') {
|
|
427
|
+
// First, register the user identity for sync.
|
|
428
|
+
// The connected protocols are used to register sync for only a subset of protocols from the connectedDid's DWN
|
|
429
|
+
|
|
430
|
+
if (registerSync) {
|
|
431
|
+
await userAgent.sync.registerIdentity({
|
|
432
|
+
did : connectedDid,
|
|
433
|
+
options : {
|
|
434
|
+
delegateDid,
|
|
435
|
+
protocols: connectedProtocols
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
if(walletConnectOptions !== undefined) {
|
|
440
|
+
// If we are using WalletConnect, we should do a one-shot sync to pull down any messages that are associated with the connectedDid
|
|
441
|
+
await userAgent.sync.sync('pull');
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Enable sync using the specified interval or default.
|
|
446
|
+
sync ??= '2m';
|
|
447
|
+
userAgent.sync.startSync({ interval: sync })
|
|
448
|
+
.catch((error: any) => {
|
|
449
|
+
console.error(`Sync failed: ${error}`);
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const web5 = new Web5({ agent, connectedDid, delegateDid });
|
|
455
|
+
|
|
456
|
+
return { web5, did: connectedDid, delegateDid, recoveryPhrase };
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Cleans up the DID, Keys and Identity. Primarily used by a failed WalletConnect import.
|
|
461
|
+
* Does not throw on error, but logs to console.
|
|
462
|
+
*/
|
|
463
|
+
private static async cleanUpIdentity({ identity, userAgent }:{
|
|
464
|
+
identity: BearerIdentity,
|
|
465
|
+
userAgent: Web5UserAgent
|
|
466
|
+
}): Promise<void> {
|
|
467
|
+
try {
|
|
468
|
+
// Delete the DID and the Associated Keys
|
|
469
|
+
await userAgent.did.delete({
|
|
470
|
+
didUri : identity.did.uri,
|
|
471
|
+
tenant : identity.metadata.tenant,
|
|
472
|
+
deleteKey : true,
|
|
473
|
+
});
|
|
474
|
+
} catch(error: any) {
|
|
475
|
+
console.error(`Failed to delete DID ${identity.did.uri}: ${error.message}`);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
try {
|
|
479
|
+
// Delete the Identity
|
|
480
|
+
await userAgent.identity.delete({ didUri: identity.did.uri });
|
|
481
|
+
} catch(error: any) {
|
|
482
|
+
console.error(`Failed to delete Identity ${identity.metadata.name}: ${error.message}`);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* A static method to process connected grants for a delegate DID.
|
|
488
|
+
*
|
|
489
|
+
* This will store the grants as the DWN owner to be used later when impersonating the connected DID.
|
|
490
|
+
*/
|
|
491
|
+
static async processConnectedGrants({ grants, agent, delegateDid }: {
|
|
492
|
+
grants: DwnDataEncodedRecordsWriteMessage[],
|
|
493
|
+
agent: Web5Agent,
|
|
494
|
+
delegateDid: string,
|
|
495
|
+
}): Promise<string[]> {
|
|
496
|
+
const connectedProtocols = new Set<string>();
|
|
497
|
+
for (const grantMessage of grants) {
|
|
498
|
+
// use the delegateDid as the connectedDid of the grant as they do not yet support impersonation/delegation
|
|
499
|
+
const grant = await PermissionGrant.parse({ connectedDid: delegateDid, agent, message: grantMessage });
|
|
500
|
+
// store the grant as the owner of the DWN, this will allow the delegateDid to use the grant when impersonating the connectedDid
|
|
501
|
+
const { status } = await grant.store(true);
|
|
502
|
+
if (status.code !== 202) {
|
|
503
|
+
throw new Error(`AgentDwnApi: Failed to process connected grant: ${status.detail}`);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const protocol = (grant.scope as DwnMessagesPermissionScope | DwnRecordsPermissionScope).protocol;
|
|
507
|
+
if (protocol) {
|
|
508
|
+
connectedProtocols.add(protocol);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// currently we return a de-duped set of protocols represented by these grants, this is used to register protocols for sync
|
|
513
|
+
// we expect that any connected protocols will include MessagesQuery and MessagesRead grants that will allow it to sync
|
|
514
|
+
return [...connectedProtocols];
|
|
515
|
+
}
|
|
516
|
+
}
|