@enbox/auth 0.6.0 → 0.6.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/dist/esm/auth-manager.js +147 -5
- package/dist/esm/auth-manager.js.map +1 -1
- package/dist/esm/connect/lifecycle.js +145 -2
- package/dist/esm/connect/lifecycle.js.map +1 -1
- package/dist/esm/connect/local.js +19 -5
- package/dist/esm/connect/local.js.map +1 -1
- package/dist/esm/connect/restore.js +22 -8
- package/dist/esm/connect/restore.js.map +1 -1
- package/dist/esm/connect/wallet.js +24 -137
- package/dist/esm/connect/wallet.js.map +1 -1
- package/dist/esm/index.js +9 -15
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/permissions.js +41 -0
- package/dist/esm/permissions.js.map +1 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/wallet-connect-client.js.map +1 -1
- package/dist/types/auth-manager.d.ts +70 -6
- package/dist/types/auth-manager.d.ts.map +1 -1
- package/dist/types/connect/lifecycle.d.ts +49 -2
- package/dist/types/connect/lifecycle.d.ts.map +1 -1
- package/dist/types/connect/local.d.ts +6 -1
- package/dist/types/connect/local.d.ts.map +1 -1
- package/dist/types/connect/restore.d.ts.map +1 -1
- package/dist/types/connect/wallet.d.ts +1 -15
- package/dist/types/connect/wallet.d.ts.map +1 -1
- package/dist/types/index.d.ts +10 -16
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/permissions.d.ts +18 -0
- package/dist/types/permissions.d.ts.map +1 -0
- package/dist/types/types.d.ts +148 -1
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/wallet-connect-client.d.ts +1 -4
- package/dist/types/wallet-connect-client.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/auth-manager.ts +167 -6
- package/src/connect/lifecycle.ts +170 -4
- package/src/connect/local.ts +20 -5
- package/src/connect/restore.ts +25 -9
- package/src/connect/wallet.ts +26 -146
- package/src/index.ts +16 -16
- package/src/permissions.ts +48 -0
- package/src/types.ts +164 -1
- package/src/wallet-connect-client.ts +1 -4
package/src/connect/lifecycle.ts
CHANGED
|
@@ -14,12 +14,16 @@
|
|
|
14
14
|
* @internal
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import type {
|
|
17
|
+
import type { PortableDid } from '@enbox/dids';
|
|
18
|
+
import type { BearerIdentity, DwnDataEncodedRecordsWriteMessage, DwnMessagesPermissionScope, DwnRecordsPermissionScope, EnboxUserAgent } from '@enbox/agent';
|
|
18
19
|
|
|
19
20
|
import type { AuthEventEmitter } from '../events.js';
|
|
20
21
|
import type { PasswordProvider } from '../password-provider.js';
|
|
21
22
|
import type { IdentityInfo, RegistrationOptions, StorageAdapter, SyncOption } from '../types.js';
|
|
22
23
|
|
|
24
|
+
import { Convert } from '@enbox/common';
|
|
25
|
+
import { DwnInterface, DwnPermissionGrant } from '@enbox/agent';
|
|
26
|
+
|
|
23
27
|
import { AuthSession } from '../identity-session.js';
|
|
24
28
|
import { DEFAULT_DWN_ENDPOINTS, INSECURE_DEFAULT_PASSWORD, STORAGE_KEYS } from '../types.js';
|
|
25
29
|
|
|
@@ -239,6 +243,166 @@ export function resolveIdentityDids(
|
|
|
239
243
|
return { connectedDid, delegateDid };
|
|
240
244
|
}
|
|
241
245
|
|
|
246
|
+
// ─── processConnectedGrants ─────────────────────────────────────
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Process connected grants by storing them in the local DWN as the owner.
|
|
250
|
+
*
|
|
251
|
+
* This is the agent-level equivalent of `Enbox.processConnectedGrants()`.
|
|
252
|
+
* It stores each grant, signed as owner, and returns the deduplicated
|
|
253
|
+
* list of protocol URIs represented by the grants.
|
|
254
|
+
*
|
|
255
|
+
* @internal
|
|
256
|
+
*/
|
|
257
|
+
export async function processConnectedGrants(params: {
|
|
258
|
+
agent: EnboxUserAgent;
|
|
259
|
+
delegateDid: string;
|
|
260
|
+
grants: DwnDataEncodedRecordsWriteMessage[];
|
|
261
|
+
}): Promise<string[]> {
|
|
262
|
+
const { agent, delegateDid, grants } = params;
|
|
263
|
+
const connectedProtocols = new Set<string>();
|
|
264
|
+
|
|
265
|
+
for (const grantMessage of grants) {
|
|
266
|
+
const grant = DwnPermissionGrant.parse(grantMessage);
|
|
267
|
+
|
|
268
|
+
// Store the grant as the owner of the DWN so the delegateDid
|
|
269
|
+
// can use it when impersonating the connectedDid.
|
|
270
|
+
const { encodedData, ...rawMessage } = grantMessage;
|
|
271
|
+
const dataStream = new Blob([Convert.base64Url(encodedData).toUint8Array() as BlobPart]);
|
|
272
|
+
|
|
273
|
+
const { reply } = await agent.processDwnRequest({
|
|
274
|
+
store : true,
|
|
275
|
+
author : delegateDid,
|
|
276
|
+
target : delegateDid,
|
|
277
|
+
messageType : DwnInterface.RecordsWrite,
|
|
278
|
+
signAsOwner : true,
|
|
279
|
+
rawMessage,
|
|
280
|
+
dataStream,
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
if (reply.status.code !== 202) {
|
|
284
|
+
throw new Error(
|
|
285
|
+
`[@enbox/auth] Failed to process connected grant: ${reply.status.detail}`
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const protocol = (grant.scope as DwnMessagesPermissionScope | DwnRecordsPermissionScope).protocol;
|
|
290
|
+
if (protocol) {
|
|
291
|
+
connectedProtocols.add(protocol);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return [...connectedProtocols];
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ─── importDelegateAndSetupSync ─────────────────────────────────
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Import a delegated DID, process its grants, register sync, and pull.
|
|
302
|
+
*
|
|
303
|
+
* This is the shared post-connect lifecycle used by both the DWeb Connect
|
|
304
|
+
* and relay WalletConnect flows. On failure, the imported identity is
|
|
305
|
+
* cleaned up before re-throwing.
|
|
306
|
+
*
|
|
307
|
+
* @internal
|
|
308
|
+
*/
|
|
309
|
+
export async function importDelegateAndSetupSync(params: {
|
|
310
|
+
userAgent: EnboxUserAgent;
|
|
311
|
+
delegatePortableDid: PortableDid;
|
|
312
|
+
connectedDid: string;
|
|
313
|
+
delegateGrants: DwnDataEncodedRecordsWriteMessage[];
|
|
314
|
+
flowName: string;
|
|
315
|
+
}): Promise<BearerIdentity> {
|
|
316
|
+
const { userAgent, delegatePortableDid, connectedDid, delegateGrants, flowName } = params;
|
|
317
|
+
|
|
318
|
+
let identity: BearerIdentity | undefined;
|
|
319
|
+
try {
|
|
320
|
+
identity = await userAgent.identity.import({
|
|
321
|
+
portableIdentity: {
|
|
322
|
+
portableDid : delegatePortableDid,
|
|
323
|
+
metadata : {
|
|
324
|
+
connectedDid,
|
|
325
|
+
name : 'Default',
|
|
326
|
+
uri : delegatePortableDid.uri,
|
|
327
|
+
tenant : userAgent.agentDid.uri,
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const connectedProtocols = await processConnectedGrants({
|
|
333
|
+
agent : userAgent,
|
|
334
|
+
delegateDid : delegatePortableDid.uri,
|
|
335
|
+
grants : delegateGrants,
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
await userAgent.sync.registerIdentity({
|
|
339
|
+
did : connectedDid,
|
|
340
|
+
options : {
|
|
341
|
+
delegateDid : delegatePortableDid.uri,
|
|
342
|
+
protocols : connectedProtocols,
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
await userAgent.sync.sync('pull');
|
|
347
|
+
|
|
348
|
+
return identity;
|
|
349
|
+
} catch (error: unknown) {
|
|
350
|
+
if (identity) {
|
|
351
|
+
try {
|
|
352
|
+
await userAgent.did.delete({
|
|
353
|
+
didUri : identity.did.uri,
|
|
354
|
+
tenant : identity.metadata.tenant,
|
|
355
|
+
deleteKey : true,
|
|
356
|
+
});
|
|
357
|
+
} catch { /* best effort */ }
|
|
358
|
+
|
|
359
|
+
try {
|
|
360
|
+
await userAgent.identity.delete({ didUri: identity.did.uri });
|
|
361
|
+
} catch { /* best effort */ }
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
365
|
+
throw new Error(`[@enbox/auth] ${flowName} failed: ${message}`);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// ─── finalizeDelegateSession ────────────────────────────────────
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Build an `AuthSession` for a delegated connect flow (DWeb Connect or
|
|
373
|
+
* relay WalletConnect). Starts sync and persists delegate/connected DID
|
|
374
|
+
* markers.
|
|
375
|
+
*
|
|
376
|
+
* @internal
|
|
377
|
+
*/
|
|
378
|
+
export async function finalizeDelegateSession(params: {
|
|
379
|
+
userAgent: EnboxUserAgent;
|
|
380
|
+
emitter: AuthEventEmitter;
|
|
381
|
+
storage: StorageAdapter;
|
|
382
|
+
identity: BearerIdentity;
|
|
383
|
+
connectedDid: string;
|
|
384
|
+
delegateDid: string;
|
|
385
|
+
sync: SyncOption | undefined;
|
|
386
|
+
}): Promise<AuthSession> {
|
|
387
|
+
const { userAgent, emitter, storage, identity, connectedDid, delegateDid, sync } = params;
|
|
388
|
+
|
|
389
|
+
startSyncIfEnabled(userAgent, sync);
|
|
390
|
+
|
|
391
|
+
return finalizeSession({
|
|
392
|
+
userAgent,
|
|
393
|
+
emitter,
|
|
394
|
+
storage,
|
|
395
|
+
connectedDid,
|
|
396
|
+
delegateDid,
|
|
397
|
+
identityName : identity.metadata.name,
|
|
398
|
+
identityConnectedDid : identity.metadata.connectedDid,
|
|
399
|
+
extraStorageKeys : {
|
|
400
|
+
[STORAGE_KEYS.DELEGATE_DID] : delegateDid,
|
|
401
|
+
[STORAGE_KEYS.CONNECTED_DID] : connectedDid,
|
|
402
|
+
},
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
|
|
242
406
|
// ─── finalizeSession ────────────────────────────────────────────
|
|
243
407
|
|
|
244
408
|
/**
|
|
@@ -267,7 +431,7 @@ export async function finalizeSession(params: {
|
|
|
267
431
|
connectedDid: string;
|
|
268
432
|
delegateDid?: string;
|
|
269
433
|
recoveryPhrase?: string;
|
|
270
|
-
identityName
|
|
434
|
+
identityName?: string;
|
|
271
435
|
identityConnectedDid?: string;
|
|
272
436
|
emitIdentityAdded?: boolean;
|
|
273
437
|
extraStorageKeys?: Record<string, string>;
|
|
@@ -295,9 +459,11 @@ export async function finalizeSession(params: {
|
|
|
295
459
|
}
|
|
296
460
|
}
|
|
297
461
|
|
|
462
|
+
// When identityName is undefined, no user identity exists (agent-only session).
|
|
463
|
+
// Build an IdentityInfo with the agent DID as a fallback.
|
|
298
464
|
const identityInfo: IdentityInfo = {
|
|
299
465
|
didUri : connectedDid,
|
|
300
|
-
name : identityName,
|
|
466
|
+
name : identityName ?? 'Agent',
|
|
301
467
|
connectedDid : identityConnectedDid,
|
|
302
468
|
};
|
|
303
469
|
|
|
@@ -309,7 +475,7 @@ export async function finalizeSession(params: {
|
|
|
309
475
|
identity : identityInfo,
|
|
310
476
|
});
|
|
311
477
|
|
|
312
|
-
if (emitIdentityAdded) {
|
|
478
|
+
if (emitIdentityAdded && identityName !== undefined) {
|
|
313
479
|
emitter.emit('identity-added', { identity: identityInfo });
|
|
314
480
|
}
|
|
315
481
|
|
package/src/connect/local.ts
CHANGED
|
@@ -18,8 +18,13 @@ import { createDefaultIdentity, ensureVaultReady, finalizeSession, resolveIdenti
|
|
|
18
18
|
/**
|
|
19
19
|
* Execute the local connect flow.
|
|
20
20
|
*
|
|
21
|
-
* - On first launch: initializes the vault
|
|
21
|
+
* - On first launch: initializes the vault. Identity creation is opt-in via
|
|
22
|
+
* `options.createIdentity: true`.
|
|
22
23
|
* - On subsequent launches: unlocks the vault and reconnects to the existing identity.
|
|
24
|
+
*
|
|
25
|
+
* When no identities exist and `createIdentity` is not `true`, the session
|
|
26
|
+
* is returned with the **agent DID** as the connected DID. This allows apps to
|
|
27
|
+
* manage identity creation separately from vault setup.
|
|
23
28
|
*/
|
|
24
29
|
export async function localConnect(
|
|
25
30
|
ctx: FlowContext,
|
|
@@ -33,6 +38,7 @@ export async function localConnect(
|
|
|
33
38
|
|
|
34
39
|
const sync = options.sync ?? ctx.defaultSync;
|
|
35
40
|
const dwnEndpoints = options.dwnEndpoints ?? ctx.defaultDwnEndpoints ?? DEFAULT_DWN_ENDPOINTS;
|
|
41
|
+
const shouldCreateIdentity = options.createIdentity === true;
|
|
36
42
|
|
|
37
43
|
// Initialize vault on first launch and start the agent.
|
|
38
44
|
const recoveryPhrase = await ensureVaultReady({
|
|
@@ -55,12 +61,21 @@ export async function localConnect(
|
|
|
55
61
|
let identity = identities[0];
|
|
56
62
|
let isNewIdentity = false;
|
|
57
63
|
|
|
58
|
-
if (!identity) {
|
|
64
|
+
if (!identity && shouldCreateIdentity) {
|
|
59
65
|
isNewIdentity = true;
|
|
60
66
|
identity = await createDefaultIdentity(userAgent, dwnEndpoints, options.metadata?.name ?? 'Default');
|
|
61
67
|
}
|
|
62
68
|
|
|
63
|
-
|
|
69
|
+
// When no identity exists (createIdentity: false on first launch), use the
|
|
70
|
+
// agent DID as the session's connected DID. The session is still valid but
|
|
71
|
+
// operates in the agent's context rather than a user identity's context.
|
|
72
|
+
const connectedDid = identity
|
|
73
|
+
? resolveIdentityDids(identity).connectedDid
|
|
74
|
+
: userAgent.agentDid.uri;
|
|
75
|
+
|
|
76
|
+
const delegateDid = identity
|
|
77
|
+
? resolveIdentityDids(identity).delegateDid
|
|
78
|
+
: undefined;
|
|
64
79
|
|
|
65
80
|
// Register with DWN endpoints (if registration options are provided).
|
|
66
81
|
if (ctx.registration) {
|
|
@@ -95,7 +110,7 @@ export async function localConnect(
|
|
|
95
110
|
connectedDid,
|
|
96
111
|
delegateDid,
|
|
97
112
|
recoveryPhrase,
|
|
98
|
-
identityName : identity
|
|
99
|
-
identityConnectedDid : identity
|
|
113
|
+
identityName : identity?.metadata.name,
|
|
114
|
+
identityConnectedDid : identity?.metadata.connectedDid,
|
|
100
115
|
});
|
|
101
116
|
}
|
package/src/connect/restore.ts
CHANGED
|
@@ -86,22 +86,38 @@ export async function restoreSession(
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
// Start sync.
|
|
90
|
+
startSyncIfEnabled(userAgent, ctx.defaultSync);
|
|
91
|
+
|
|
89
92
|
if (!identity) {
|
|
90
|
-
// No identity found —
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
// No identity found — this is valid for agent-only sessions created
|
|
94
|
+
// with `createIdentity: false`. Restore a session using the agent DID.
|
|
95
|
+
// If the active identity stored was the agent DID, this is an
|
|
96
|
+
// intentional agent-only session rather than stale data.
|
|
97
|
+
const isAgentOnlySession = activeIdentityDid === userAgent.agentDid.uri;
|
|
98
|
+
|
|
99
|
+
if (!isAgentOnlySession) {
|
|
100
|
+
// Truly stale session data — clean up and bail.
|
|
101
|
+
await storage.remove(STORAGE_KEYS.PREVIOUSLY_CONNECTED);
|
|
102
|
+
await storage.remove(STORAGE_KEYS.ACTIVE_IDENTITY);
|
|
103
|
+
await storage.remove(STORAGE_KEYS.DELEGATE_DID);
|
|
104
|
+
await storage.remove(STORAGE_KEYS.CONNECTED_DID);
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return finalizeSession({
|
|
109
|
+
userAgent,
|
|
110
|
+
emitter,
|
|
111
|
+
storage,
|
|
112
|
+
connectedDid : userAgent.agentDid.uri,
|
|
113
|
+
emitIdentityAdded : false,
|
|
114
|
+
});
|
|
96
115
|
}
|
|
97
116
|
|
|
98
117
|
const { connectedDid, delegateDid } = resolveIdentityDids(
|
|
99
118
|
identity, storedDelegateDid ?? undefined,
|
|
100
119
|
);
|
|
101
120
|
|
|
102
|
-
// Start sync.
|
|
103
|
-
startSyncIfEnabled(userAgent, ctx.defaultSync);
|
|
104
|
-
|
|
105
121
|
// Persist session info, build AuthSession, and emit lifecycle events.
|
|
106
122
|
// Session restore does not emit `identity-added` (identity was already added in the original flow).
|
|
107
123
|
return finalizeSession({
|
package/src/connect/wallet.ts
CHANGED
|
@@ -7,68 +7,17 @@
|
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import type { DwnDataEncodedRecordsWriteMessage, DwnMessagesPermissionScope, DwnRecordsPermissionScope, EnboxUserAgent } from '@enbox/agent';
|
|
11
|
-
|
|
12
10
|
import type { AuthSession } from '../identity-session.js';
|
|
13
11
|
import type { FlowContext } from './lifecycle.js';
|
|
14
12
|
import type { WalletConnectOptions } from '../types.js';
|
|
15
13
|
|
|
16
|
-
import {
|
|
14
|
+
import { DEFAULT_DWN_ENDPOINTS } from '../types.js';
|
|
17
15
|
import { registerWithDwnEndpoints } from '../registration.js';
|
|
18
16
|
import { WalletConnect } from '../wallet-connect-client.js';
|
|
19
|
-
import {
|
|
20
|
-
import { DwnInterface, DwnPermissionGrant } from '@enbox/agent';
|
|
21
|
-
import { ensureVaultReady, finalizeSession, resolvePassword, startSyncIfEnabled } from './lifecycle.js';
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Process connected grants by storing them in the local DWN as the owner.
|
|
25
|
-
*
|
|
26
|
-
* This is the agent-level equivalent of `Enbox.processConnectedGrants()`.
|
|
27
|
-
* It stores each grant, signed as owner, and returns the deduplicated
|
|
28
|
-
* list of protocol URIs represented by the grants.
|
|
29
|
-
*
|
|
30
|
-
* @internal
|
|
31
|
-
*/
|
|
32
|
-
export async function processConnectedGrants(params: {
|
|
33
|
-
agent: EnboxUserAgent;
|
|
34
|
-
delegateDid: string;
|
|
35
|
-
grants: DwnDataEncodedRecordsWriteMessage[];
|
|
36
|
-
}): Promise<string[]> {
|
|
37
|
-
const { agent, delegateDid, grants } = params;
|
|
38
|
-
const connectedProtocols = new Set<string>();
|
|
39
|
-
|
|
40
|
-
for (const grantMessage of grants) {
|
|
41
|
-
const grant = DwnPermissionGrant.parse(grantMessage);
|
|
17
|
+
import { ensureVaultReady, finalizeDelegateSession, importDelegateAndSetupSync, resolvePassword } from './lifecycle.js';
|
|
42
18
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const { encodedData, ...rawMessage } = grantMessage;
|
|
46
|
-
const dataStream = new Blob([Convert.base64Url(encodedData).toUint8Array() as BlobPart]);
|
|
47
|
-
|
|
48
|
-
const { reply } = await agent.processDwnRequest({
|
|
49
|
-
store : true,
|
|
50
|
-
author : delegateDid,
|
|
51
|
-
target : delegateDid,
|
|
52
|
-
messageType : DwnInterface.RecordsWrite,
|
|
53
|
-
signAsOwner : true,
|
|
54
|
-
rawMessage,
|
|
55
|
-
dataStream,
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
if (reply.status.code !== 202) {
|
|
59
|
-
throw new Error(
|
|
60
|
-
`[@enbox/auth] Failed to process connected grant: ${reply.status.detail}`
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const protocol = (grant.scope as DwnMessagesPermissionScope | DwnRecordsPermissionScope).protocol;
|
|
65
|
-
if (protocol) {
|
|
66
|
-
connectedProtocols.add(protocol);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return [...connectedProtocols];
|
|
71
|
-
}
|
|
19
|
+
// Re-export for backward compatibility — processConnectedGrants moved to lifecycle.ts.
|
|
20
|
+
export { processConnectedGrants } from './lifecycle.js';
|
|
72
21
|
|
|
73
22
|
/**
|
|
74
23
|
* Execute the wallet connect flow.
|
|
@@ -94,16 +43,9 @@ export async function walletConnect(
|
|
|
94
43
|
// Ensure the agent is initialized and started before the relay flow.
|
|
95
44
|
const isFirstLaunch = await userAgent.firstLaunch();
|
|
96
45
|
const password = await resolvePassword(ctx, undefined, isFirstLaunch);
|
|
97
|
-
|
|
98
|
-
await ensureVaultReady({
|
|
99
|
-
userAgent,
|
|
100
|
-
emitter,
|
|
101
|
-
password,
|
|
102
|
-
isFirstLaunch,
|
|
103
|
-
});
|
|
46
|
+
await ensureVaultReady({ userAgent, emitter, password, isFirstLaunch });
|
|
104
47
|
|
|
105
48
|
// Run the Enbox Connect relay flow.
|
|
106
|
-
// permissionRequests are already agent-level ConnectPermissionRequest objects.
|
|
107
49
|
const result = await WalletConnect.initClient({
|
|
108
50
|
displayName : options.displayName,
|
|
109
51
|
connectServerUrl : options.connectServerUrl,
|
|
@@ -117,93 +59,31 @@ export async function walletConnect(
|
|
|
117
59
|
throw new Error('[@enbox/auth] Wallet connect flow was cancelled or returned no result.');
|
|
118
60
|
}
|
|
119
61
|
|
|
62
|
+
// Import delegate DID, process grants, and set up sync.
|
|
120
63
|
const { delegatePortableDid, connectedDid, delegateGrants } = result;
|
|
64
|
+
const identity = await importDelegateAndSetupSync({
|
|
65
|
+
userAgent, delegatePortableDid, connectedDid, delegateGrants,
|
|
66
|
+
flowName: 'Wallet connect',
|
|
67
|
+
});
|
|
121
68
|
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
tenant : userAgent.agentDid.uri,
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
// Process the connected grants using agent primitives.
|
|
138
|
-
const connectedProtocols = await processConnectedGrants({
|
|
139
|
-
agent : userAgent,
|
|
140
|
-
delegateDid : delegatePortableDid.uri,
|
|
141
|
-
grants : delegateGrants,
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
// Register with DWN endpoints (if registration options are provided).
|
|
145
|
-
if (ctx.registration) {
|
|
146
|
-
const dwnEndpoints = ctx.defaultDwnEndpoints ?? DEFAULT_DWN_ENDPOINTS;
|
|
147
|
-
await registerWithDwnEndpoints(
|
|
148
|
-
{
|
|
149
|
-
userAgent : userAgent,
|
|
150
|
-
dwnEndpoints,
|
|
151
|
-
agentDid : userAgent.agentDid.uri,
|
|
152
|
-
connectedDid,
|
|
153
|
-
storage : storage,
|
|
154
|
-
},
|
|
155
|
-
ctx.registration,
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Register sync for the connected identity.
|
|
160
|
-
await userAgent.sync.registerIdentity({
|
|
161
|
-
did : connectedDid,
|
|
162
|
-
options : {
|
|
163
|
-
delegateDid : delegatePortableDid.uri,
|
|
164
|
-
protocols : connectedProtocols,
|
|
69
|
+
// Register with DWN endpoints (if registration options are provided).
|
|
70
|
+
if (ctx.registration) {
|
|
71
|
+
const dwnEndpoints = ctx.defaultDwnEndpoints ?? DEFAULT_DWN_ENDPOINTS;
|
|
72
|
+
await registerWithDwnEndpoints(
|
|
73
|
+
{
|
|
74
|
+
userAgent,
|
|
75
|
+
dwnEndpoints,
|
|
76
|
+
agentDid: userAgent.agentDid.uri,
|
|
77
|
+
connectedDid,
|
|
78
|
+
storage,
|
|
165
79
|
},
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
// Pull down existing messages from the connected DID's DWN.
|
|
169
|
-
await userAgent.sync.sync('pull');
|
|
170
|
-
} catch (error: unknown) {
|
|
171
|
-
// Clean up on failure.
|
|
172
|
-
if (identity) {
|
|
173
|
-
try {
|
|
174
|
-
await userAgent.did.delete({
|
|
175
|
-
didUri : identity.did.uri,
|
|
176
|
-
tenant : identity.metadata.tenant,
|
|
177
|
-
deleteKey : true,
|
|
178
|
-
});
|
|
179
|
-
} catch { /* best effort */ }
|
|
180
|
-
|
|
181
|
-
try {
|
|
182
|
-
await userAgent.identity.delete({ didUri: identity.did.uri });
|
|
183
|
-
} catch { /* best effort */ }
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
187
|
-
throw new Error(`[@enbox/auth] Wallet connect failed: ${message}`);
|
|
80
|
+
ctx.registration,
|
|
81
|
+
);
|
|
188
82
|
}
|
|
189
83
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
// Persist session info, build AuthSession, and emit lifecycle events.
|
|
196
|
-
return finalizeSession({
|
|
197
|
-
userAgent,
|
|
198
|
-
emitter,
|
|
199
|
-
storage,
|
|
200
|
-
connectedDid,
|
|
201
|
-
delegateDid,
|
|
202
|
-
identityName : identity.metadata.name,
|
|
203
|
-
identityConnectedDid : identity.metadata.connectedDid,
|
|
204
|
-
extraStorageKeys : {
|
|
205
|
-
[STORAGE_KEYS.DELEGATE_DID] : delegateDid,
|
|
206
|
-
[STORAGE_KEYS.CONNECTED_DID] : connectedDid,
|
|
207
|
-
},
|
|
84
|
+
// Finalize session.
|
|
85
|
+
return finalizeDelegateSession({
|
|
86
|
+
userAgent, emitter, storage, identity,
|
|
87
|
+
connectedDid, delegateDid: delegatePortableDid.uri, sync,
|
|
208
88
|
});
|
|
209
89
|
}
|
package/src/index.ts
CHANGED
|
@@ -5,30 +5,23 @@
|
|
|
5
5
|
* in both browser and CLI environments. Depends only on `@enbox/agent`
|
|
6
6
|
* and can be used standalone or consumed by `@enbox/api`.
|
|
7
7
|
*
|
|
8
|
-
* @example Standalone auth
|
|
8
|
+
* @example Standalone auth (wallet app)
|
|
9
9
|
* ```ts
|
|
10
10
|
* import { AuthManager } from '@enbox/auth';
|
|
11
11
|
*
|
|
12
12
|
* const auth = await AuthManager.create({ sync: '15s' });
|
|
13
|
-
* const session = await auth.
|
|
14
|
-
*
|
|
15
|
-
* // session.agent — the authenticated Enbox agent
|
|
16
|
-
* // session.did — the connected DID URI
|
|
13
|
+
* const session = await auth.connectLocal({ password: userPin });
|
|
17
14
|
* ```
|
|
18
15
|
*
|
|
19
|
-
* @example
|
|
16
|
+
* @example Dapp with browser connect handler
|
|
20
17
|
* ```ts
|
|
21
18
|
* import { AuthManager } from '@enbox/auth';
|
|
22
|
-
* import {
|
|
23
|
-
*
|
|
24
|
-
* const auth = await AuthManager.create({ sync: '15s' });
|
|
25
|
-
* const session = await auth.connect();
|
|
19
|
+
* import { BrowserConnectHandler } from '@enbox/browser';
|
|
26
20
|
*
|
|
27
|
-
* const
|
|
28
|
-
*
|
|
29
|
-
* connectedDid: session.did,
|
|
30
|
-
* delegateDid: session.delegateDid,
|
|
21
|
+
* const auth = await AuthManager.create({
|
|
22
|
+
* connectHandler: BrowserConnectHandler(),
|
|
31
23
|
* });
|
|
24
|
+
* const session = await auth.connect({ protocols: [NotesProtocol] });
|
|
32
25
|
* ```
|
|
33
26
|
*
|
|
34
27
|
* @packageDocumentation
|
|
@@ -47,10 +40,11 @@ export type { PasswordContext } from './password-provider.js';
|
|
|
47
40
|
// without a direct @enbox/agent dependency.
|
|
48
41
|
export { EnboxUserAgent, HdIdentityVault } from '@enbox/agent';
|
|
49
42
|
|
|
50
|
-
//
|
|
43
|
+
// Connect helpers
|
|
51
44
|
export { processConnectedGrants } from './connect/wallet.js';
|
|
45
|
+
export { normalizeProtocolRequests } from './permissions.js';
|
|
52
46
|
export { WalletConnect } from './wallet-connect-client.js';
|
|
53
|
-
export type {
|
|
47
|
+
export type { ProtocolPermissionOptions, WalletConnectClientOptions } from './wallet-connect-client.js';
|
|
54
48
|
|
|
55
49
|
// Registration token storage helpers
|
|
56
50
|
export { loadTokensFromStorage, saveTokensToStorage } from './registration.js';
|
|
@@ -77,8 +71,12 @@ export type {
|
|
|
77
71
|
AuthManagerOptions,
|
|
78
72
|
AuthSessionInfo,
|
|
79
73
|
AuthState,
|
|
74
|
+
ConnectHandler,
|
|
75
|
+
ConnectOptions,
|
|
80
76
|
ConnectPermissionRequest,
|
|
77
|
+
ConnectResult,
|
|
81
78
|
DisconnectOptions,
|
|
79
|
+
HandlerConnectOptions,
|
|
82
80
|
HeadlessConnectOptions,
|
|
83
81
|
IdentityInfo,
|
|
84
82
|
IdentityVaultBackup,
|
|
@@ -86,7 +84,9 @@ export type {
|
|
|
86
84
|
ImportFromPortableOptions,
|
|
87
85
|
LocalConnectOptions,
|
|
88
86
|
LocalDwnStrategy,
|
|
87
|
+
Permission,
|
|
89
88
|
PortableIdentity,
|
|
89
|
+
ProtocolRequest,
|
|
90
90
|
ProviderAuthParams,
|
|
91
91
|
ProviderAuthResult,
|
|
92
92
|
RegistrationOptions,
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission request normalization utilities.
|
|
3
|
+
*
|
|
4
|
+
* Converts simplified `ProtocolRequest` entries (just a protocol definition
|
|
5
|
+
* or `{ definition, permissions }`) into agent-level `ConnectPermissionRequest`
|
|
6
|
+
* objects used by connect handlers.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { ConnectPermissionRequest, DwnProtocolDefinition } from '@enbox/agent';
|
|
13
|
+
|
|
14
|
+
import type { ProtocolRequest } from './types.js';
|
|
15
|
+
|
|
16
|
+
import { DEFAULT_PERMISSIONS } from './types.js';
|
|
17
|
+
import { WalletConnect } from './wallet-connect-client.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Normalize simplified `ProtocolRequest[]` into agent-level
|
|
21
|
+
* `ConnectPermissionRequest[]`.
|
|
22
|
+
*/
|
|
23
|
+
export function normalizeProtocolRequests(
|
|
24
|
+
protocols: ProtocolRequest[] | undefined,
|
|
25
|
+
): ConnectPermissionRequest[] {
|
|
26
|
+
if (!protocols || protocols.length === 0) { return []; }
|
|
27
|
+
|
|
28
|
+
return protocols.map((entry) => {
|
|
29
|
+
let definition: DwnProtocolDefinition;
|
|
30
|
+
let permissions: string[];
|
|
31
|
+
|
|
32
|
+
if ('protocol' in entry && 'types' in entry && 'structure' in entry) {
|
|
33
|
+
// Bare protocol definition — use default permissions.
|
|
34
|
+
definition = entry as DwnProtocolDefinition;
|
|
35
|
+
permissions = [...DEFAULT_PERMISSIONS];
|
|
36
|
+
} else {
|
|
37
|
+
// Object with explicit permissions.
|
|
38
|
+
const explicit = entry as { definition: DwnProtocolDefinition; permissions: string[] };
|
|
39
|
+
definition = explicit.definition;
|
|
40
|
+
permissions = explicit.permissions;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return WalletConnect.createPermissionRequestForProtocol({
|
|
44
|
+
definition,
|
|
45
|
+
permissions: permissions as Parameters<typeof WalletConnect.createPermissionRequestForProtocol>[0]['permissions'],
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|