@enbox/auth 0.6.34 → 0.6.36

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.
@@ -14,19 +14,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
14
14
  step((generator = generator.apply(thisArg, _arguments || [])).next());
15
15
  });
16
16
  };
17
- import { IdentityProtocolDefinition, JwkProtocolDefinition } from '@enbox/agent';
18
17
  import { DEFAULT_DWN_ENDPOINTS } from '../types.js';
19
18
  import { registerWithDwnEndpoints } from '../registration.js';
20
19
  import { createDefaultIdentity, ensureVaultReady, finalizeSession, registerSyncScopeForIdentity, resolveIdentityDids, startSyncIfEnabled } from './lifecycle.js';
21
- const AGENT_DID_SYNC_PROTOCOLS = [
22
- IdentityProtocolDefinition.protocol,
23
- JwkProtocolDefinition.protocol,
24
- ];
20
+ import { recoverIdentitiesFromRemote, registerAgentDidForSync } from './recovery.js';
25
21
  /**
26
22
  * Import (or recover) an identity from a BIP-39 recovery phrase.
27
23
  *
28
24
  * This re-initializes the vault with the given phrase and password,
29
- * recovering the agent DID and all derived keys.
25
+ * recovering the agent DID and all derived keys. If the recovery phrase
26
+ * was previously used to create identities, they are pulled from the
27
+ * remote DWN. Otherwise a new default identity is created.
30
28
  */
31
29
  export function importFromPhrase(ctx, options) {
32
30
  return __awaiter(this, void 0, void 0, function* () {
@@ -45,51 +43,64 @@ export function importFromPhrase(ctx, options) {
45
43
  recoveryPhrase,
46
44
  dwnEndpoints,
47
45
  });
48
- // The recovery phrase re-derives the same agent DID,
49
- // but the user identity might not exist yet — create one if needed.
50
- const identities = yield userAgent.identity.list();
51
- let identity = identities[0];
52
- let isNewIdentity = false;
53
- if (!identity) {
54
- isNewIdentity = true;
55
- identity = yield createDefaultIdentity(userAgent, dwnEndpoints);
56
- }
57
- const { connectedDid, delegateDid } = resolveIdentityDids(identity);
58
- // Register with DWN endpoints (if registration options are provided).
46
+ // Register agent DID as tenant and for sync — prerequisites for recovery.
59
47
  if (ctx.registration) {
60
48
  yield registerWithDwnEndpoints({
61
- userAgent: userAgent,
49
+ userAgent,
62
50
  dwnEndpoints,
63
51
  agentDid: userAgent.agentDid.uri,
64
- connectedDid,
52
+ connectedDid: userAgent.agentDid.uri,
65
53
  secretStore: userAgent.secrets,
66
- storage: storage,
54
+ storage,
67
55
  }, ctx.registration);
68
56
  }
69
- // Register sync. For delegate identities, always repair the registration
70
- // (derive scope from active grants — revoked grants must not remain in a
71
- // stale registration), regardless of whether the identity was just
72
- // created or restored from storage. For local identities, register
73
- // `protocols: 'all'` only on first creation; a pre-existing local
74
- // identity was already registered during its initial flow.
75
- if (delegateDid) {
76
- yield registerSyncScopeForIdentity({ userAgent, connectedDid, delegateDid });
77
- }
78
- else if (isNewIdentity && sync !== 'off') {
79
- yield registerSyncScopeForIdentity({ userAgent, connectedDid });
80
- }
81
- // Register the agent DID for sync (same rationale as vaultConnect).
82
57
  if (sync !== 'off') {
58
+ yield registerAgentDidForSync(userAgent);
59
+ }
60
+ // Try to recover identities from the remote DWN before falling back
61
+ // to creating a new one.
62
+ let identities = yield userAgent.identity.list();
63
+ let identity = identities[0];
64
+ let isNewIdentity = false;
65
+ if (!identity && sync !== 'off') {
83
66
  try {
84
- yield userAgent.sync.registerIdentity({
85
- did: userAgent.agentDid.uri,
86
- options: { protocols: AGENT_DID_SYNC_PROTOCOLS },
87
- });
67
+ identities = yield recoverIdentitiesFromRemote({ userAgent, dwnEndpoints, registration: ctx.registration, storage });
68
+ identity = identities[0];
88
69
  }
89
- catch (_d) {
90
- // Already registered from a previous session.
70
+ catch (err) {
71
+ console.warn('[@enbox/auth] Seed phrase recovery failed:', err);
91
72
  }
92
73
  }
74
+ if (!identity) {
75
+ isNewIdentity = true;
76
+ identity = yield createDefaultIdentity(userAgent, dwnEndpoints);
77
+ }
78
+ const { connectedDid, delegateDid } = resolveIdentityDids(identity);
79
+ // Register sync for the identity DID.
80
+ if (isNewIdentity) {
81
+ // New identity: register as tenant first, then for sync.
82
+ if (ctx.registration) {
83
+ yield registerWithDwnEndpoints({
84
+ userAgent,
85
+ dwnEndpoints,
86
+ agentDid: userAgent.agentDid.uri,
87
+ connectedDid,
88
+ secretStore: userAgent.secrets,
89
+ storage,
90
+ }, ctx.registration);
91
+ }
92
+ if (delegateDid) {
93
+ yield registerSyncScopeForIdentity({ userAgent, connectedDid, delegateDid });
94
+ }
95
+ else if (sync !== 'off') {
96
+ yield registerSyncScopeForIdentity({ userAgent, connectedDid });
97
+ }
98
+ }
99
+ else if (delegateDid) {
100
+ // Pre-existing delegate identity: repair sync scope so revoked
101
+ // grants don't remain in a stale registration.
102
+ yield registerSyncScopeForIdentity({ userAgent, connectedDid, delegateDid });
103
+ }
93
104
  // Start sync.
94
105
  yield startSyncIfEnabled(userAgent, sync);
95
106
  // Persist session info, build AuthSession, and emit lifecycle events.
@@ -1 +1 @@
1
- {"version":3,"file":"import.js","sourceRoot":"","sources":["../../../src/connect/import.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"import.js","sourceRoot":"","sources":["../../../src/connect/import.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;;;;;;;;;;AAMH,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,eAAe,EAAE,4BAA4B,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACjK,OAAO,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAErF;;;;;;;GAOG;AACH,MAAM,UAAgB,gBAAgB,CACpC,GAAgB,EAChB,OAAgC;;;QAEhC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC;QAC5C,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;QAC7C,MAAM,IAAI,GAAG,MAAA,OAAO,CAAC,IAAI,mCAAI,GAAG,CAAC,WAAW,CAAC;QAC7C,MAAM,YAAY,GAAG,MAAA,MAAA,OAAO,CAAC,YAAY,mCAAI,GAAG,CAAC,mBAAmB,mCAAI,qBAAqB,CAAC;QAE9F,qEAAqE;QACrE,MAAM,aAAa,GAAG,MAAM,SAAS,CAAC,WAAW,EAAE,CAAC;QACpD,MAAM,gBAAgB,CAAC;YACrB,SAAS;YACT,OAAO;YACP,QAAQ;YACR,aAAa;YACb,cAAc;YACd,YAAY;SACb,CAAC,CAAC;QAEH,0EAA0E;QAC1E,IAAI,GAAG,CAAC,YAAY,EAAE,CAAC;YACrB,MAAM,wBAAwB,CAC5B;gBACE,SAAS;gBACT,YAAY;gBACZ,QAAQ,EAAO,SAAS,CAAC,QAAQ,CAAC,GAAG;gBACrC,YAAY,EAAG,SAAS,CAAC,QAAQ,CAAC,GAAG;gBACrC,WAAW,EAAI,SAAS,CAAC,OAAO;gBAChC,OAAO;aACR,EACD,GAAG,CAAC,YAAY,CACjB,CAAC;QACJ,CAAC;QACD,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;YACnB,MAAM,uBAAuB,CAAC,SAAS,CAAC,CAAC;QAC3C,CAAC;QAED,oEAAoE;QACpE,yBAAyB;QACzB,IAAI,UAAU,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACjD,IAAI,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,aAAa,GAAG,KAAK,CAAC;QAE1B,IAAI,CAAC,QAAQ,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;YAChC,IAAI,CAAC;gBACH,UAAU,GAAG,MAAM,2BAA2B,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,CAAC,YAAY,EAAE,OAAO,EAAE,CAAC,CAAC;gBACrH,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;YAC3B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,IAAI,CAAC,4CAA4C,EAAE,GAAG,CAAC,CAAC;YAClE,CAAC;QACH,CAAC;QAED,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,aAAa,GAAG,IAAI,CAAC;YACrB,QAAQ,GAAG,MAAM,qBAAqB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QAClE,CAAC;QAED,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;QAEpE,sCAAsC;QACtC,IAAI,aAAa,EAAE,CAAC;YAClB,yDAAyD;YACzD,IAAI,GAAG,CAAC,YAAY,EAAE,CAAC;gBACrB,MAAM,wBAAwB,CAC5B;oBACE,SAAS;oBACT,YAAY;oBACZ,QAAQ,EAAM,SAAS,CAAC,QAAQ,CAAC,GAAG;oBACpC,YAAY;oBACZ,WAAW,EAAG,SAAS,CAAC,OAAO;oBAC/B,OAAO;iBACR,EACD,GAAG,CAAC,YAAY,CACjB,CAAC;YACJ,CAAC;YACD,IAAI,WAAW,EAAE,CAAC;gBAChB,MAAM,4BAA4B,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC,CAAC;YAC/E,CAAC;iBAAM,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;gBAC1B,MAAM,4BAA4B,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,CAAC;YAClE,CAAC;QACH,CAAC;aAAM,IAAI,WAAW,EAAE,CAAC;YACvB,+DAA+D;YAC/D,+CAA+C;YAC/C,MAAM,4BAA4B,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC,CAAC;QAC/E,CAAC;QAED,cAAc;QACd,MAAM,kBAAkB,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QAE1C,sEAAsE;QACtE,OAAO,eAAe,CAAC;YACrB,SAAS;YACT,OAAO;YACP,OAAO;YACP,YAAY;YACZ,WAAW;YACX,YAAY,EAAW,QAAQ,CAAC,QAAQ,CAAC,IAAI;YAC7C,oBAAoB,EAAG,QAAQ,CAAC,QAAQ,CAAC,YAAY;SACtD,CAAC,CAAC;IACL,CAAC;CAAA;AAED;;;;;GAKG;AACH,MAAM,UAAgB,kBAAkB,CACtC,GAAgB,EAChB,OAAkC;;;QAElC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC;QAC5C,MAAM,IAAI,GAAG,MAAA,OAAO,CAAC,IAAI,mCAAI,GAAG,CAAC,WAAW,CAAC;QAE7C,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC/C,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;SAC3C,CAAC,CAAC;QAEH,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;QAEpE,sEAAsE;QACtE,+EAA+E;QAC/E,IAAI,GAAG,CAAC,YAAY,EAAE,CAAC;YACrB,MAAM,YAAY,GAAG,MAAA,GAAG,CAAC,mBAAmB,mCAAI,qBAAqB,CAAC;YACtE,MAAM,wBAAwB,CAC5B;gBACE,SAAS,EAAK,SAAS;gBACvB,YAAY;gBACZ,QAAQ,EAAM,SAAS,CAAC,QAAQ,CAAC,GAAG;gBACpC,YAAY;gBACZ,WAAW,EAAG,SAAS,CAAC,OAAO;gBAC/B,OAAO,EAAO,OAAO;aACtB,EACD,GAAG,CAAC,YAAY,CACjB,CAAC;QACJ,CAAC;QAED,sEAAsE;QACtE,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,4BAA4B,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC,CAAC;QAC/E,CAAC;aAAM,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;YAC1B,MAAM,4BAA4B,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,CAAC;QAClE,CAAC;QAED,MAAM,kBAAkB,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QAE1C,sEAAsE;QACtE,OAAO,eAAe,CAAC;YACrB,SAAS;YACT,OAAO;YACP,OAAO;YACP,YAAY;YACZ,WAAW;YACX,YAAY,EAAW,QAAQ,CAAC,QAAQ,CAAC,IAAI;YAC7C,oBAAoB,EAAG,QAAQ,CAAC,QAAQ,CAAC,YAAY;SACtD,CAAC,CAAC;IACL,CAAC;CAAA"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Seed phrase recovery helpers.
3
+ *
4
+ * When a vault is re-derived from a recovery phrase on a new device,
5
+ * the local DWN is empty. This module provides the building blocks
6
+ * for pulling identity metadata, keys, and user data from the remote
7
+ * DWN in a controlled two-phase sequence.
8
+ *
9
+ * @module
10
+ * @internal
11
+ */
12
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
13
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
14
+ return new (P || (P = Promise))(function (resolve, reject) {
15
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
16
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
17
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
18
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
19
+ });
20
+ };
21
+ import { IdentityProtocolDefinition, JwkProtocolDefinition } from '@enbox/agent';
22
+ import { registerWithDwnEndpoints } from '../registration.js';
23
+ /**
24
+ * Internal protocols that store recovery-critical data in the agent DID's DWN.
25
+ * Syncing these ensures that seed phrase recovery can pull identity metadata,
26
+ * portable DIDs, and encrypted private keys from the remote.
27
+ */
28
+ export const AGENT_DID_SYNC_PROTOCOLS = [
29
+ IdentityProtocolDefinition.protocol,
30
+ JwkProtocolDefinition.protocol,
31
+ ];
32
+ /**
33
+ * Register the agent DID for sync with the recovery-critical protocols.
34
+ *
35
+ * This is a prerequisite for both normal operation (pushing identity
36
+ * metadata to the remote) and seed phrase recovery (pulling it back).
37
+ * Silently succeeds if the agent DID is already registered.
38
+ */
39
+ export function registerAgentDidForSync(userAgent) {
40
+ return __awaiter(this, void 0, void 0, function* () {
41
+ try {
42
+ yield userAgent.sync.registerIdentity({
43
+ did: userAgent.agentDid.uri,
44
+ options: { protocols: AGENT_DID_SYNC_PROTOCOLS },
45
+ });
46
+ }
47
+ catch (_a) {
48
+ // Already registered from a previous session.
49
+ }
50
+ });
51
+ }
52
+ /**
53
+ * Recover identities and their data from remote DWN endpoints.
54
+ *
55
+ * Assumes the agent DID is already registered for sync (via
56
+ * {@link registerAgentDidForSync}) and as a DWN tenant.
57
+ *
58
+ * Phase 1 — pull identity metadata and DID private keys stored in the
59
+ * agent DID's DWN.
60
+ *
61
+ * Phase 2 — register each recovered identity DID as a tenant and for
62
+ * sync, then pull their profile data, protocol configurations, and
63
+ * records.
64
+ *
65
+ * Returns the recovered identities, or an empty array if the remote
66
+ * had nothing (e.g. first-ever setup with a pre-generated phrase).
67
+ */
68
+ export function recoverIdentitiesFromRemote(params) {
69
+ return __awaiter(this, void 0, void 0, function* () {
70
+ var _a;
71
+ const { userAgent, dwnEndpoints, registration, storage } = params;
72
+ const agentDid = userAgent.agentDid.uri;
73
+ // Phase 1: pull identity metadata + encrypted DID keys.
74
+ yield userAgent.sync.sync('pull');
75
+ const identities = yield userAgent.identity.list();
76
+ if (identities.length === 0) {
77
+ return [];
78
+ }
79
+ // Register each recovered identity DID as a DWN tenant, then for sync.
80
+ // Tenant registration must come first — sync('pull') issues
81
+ // MessagesSync which requires the DID to be a recognised tenant.
82
+ for (const identity of identities) {
83
+ const did = (_a = identity.metadata.connectedDid) !== null && _a !== void 0 ? _a : identity.did.uri;
84
+ if (registration) {
85
+ try {
86
+ yield registerWithDwnEndpoints({ userAgent, dwnEndpoints, agentDid, connectedDid: did, secretStore: userAgent.secrets, storage }, registration);
87
+ }
88
+ catch (_b) {
89
+ // Best effort — the DID may already be registered, or the
90
+ // endpoint may be temporarily unreachable.
91
+ }
92
+ }
93
+ try {
94
+ yield userAgent.sync.registerIdentity({ did, options: { protocols: 'all' } });
95
+ }
96
+ catch (_c) {
97
+ // Already registered from a previous session.
98
+ }
99
+ }
100
+ // Phase 2: pull profile data, protocol configurations, and records.
101
+ yield userAgent.sync.sync('pull');
102
+ return userAgent.identity.list();
103
+ });
104
+ }
105
+ //# sourceMappingURL=recovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery.js","sourceRoot":"","sources":["../../../src/connect/recovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;;;;;;;;;;AAIH,OAAO,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAIjF,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAE9D;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAA0B;IAC7D,0BAA0B,CAAC,QAAQ;IACnC,qBAAqB,CAAC,QAAQ;CAC/B,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAgB,uBAAuB,CAAC,SAAyB;;QACrE,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,IAAI,CAAC,gBAAgB,CAAC;gBACpC,GAAG,EAAO,SAAS,CAAC,QAAQ,CAAC,GAAG;gBAChC,OAAO,EAAG,EAAE,SAAS,EAAE,wBAAwB,EAAE;aAClD,CAAC,CAAC;QACL,CAAC;QAAC,WAAM,CAAC;YACP,8CAA8C;QAChD,CAAC;IACH,CAAC;CAAA;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAgB,2BAA2B,CAAC,MAKjD;;;QACC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;QAClE,MAAM,QAAQ,GAAG,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC;QAExC,wDAAwD;QACxD,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAElC,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,uEAAuE;QACvE,4DAA4D;QAC5D,iEAAiE;QACjE,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;YAClC,MAAM,GAAG,GAAG,MAAA,QAAQ,CAAC,QAAQ,CAAC,YAAY,mCAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;YAE/D,IAAI,YAAY,EAAE,CAAC;gBACjB,IAAI,CAAC;oBACH,MAAM,wBAAwB,CAC5B,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,EAAE,WAAW,EAAE,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,EACjG,YAAY,CACb,CAAC;gBACJ,CAAC;gBAAC,WAAM,CAAC;oBACP,0DAA0D;oBAC1D,2CAA2C;gBAC7C,CAAC;YACH,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,SAAS,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;YAChF,CAAC;YAAC,WAAM,CAAC;gBACP,8CAA8C;YAChD,CAAC;QACH,CAAC;QAED,oEAAoE;QACpE,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAElC,OAAO,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IACnC,CAAC;CAAA"}
@@ -17,26 +17,20 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
17
17
  step((generator = generator.apply(thisArg, _arguments || [])).next());
18
18
  });
19
19
  };
20
- import { IdentityProtocolDefinition, JwkProtocolDefinition } from '@enbox/agent';
21
20
  import { applyLocalDwnDiscovery } from '../discovery.js';
22
21
  import { DEFAULT_DWN_ENDPOINTS } from '../types.js';
23
22
  import { registerWithDwnEndpoints } from '../registration.js';
24
23
  import { createDefaultIdentity, ensureVaultReady, finalizeSession, resolveIdentityDids, resolvePassword, startSyncIfEnabled } from './lifecycle.js';
25
- /**
26
- * Internal protocols that store recovery-critical data in the agent DID's DWN.
27
- * These must be synced so that seed phrase recovery can pull identity metadata,
28
- * portable DIDs, and encrypted private keys from the remote DWN.
29
- */
30
- const AGENT_DID_SYNC_PROTOCOLS = [
31
- IdentityProtocolDefinition.protocol,
32
- JwkProtocolDefinition.protocol,
33
- ];
24
+ import { recoverIdentitiesFromRemote, registerAgentDidForSync } from './recovery.js';
34
25
  /**
35
26
  * Execute the vault connect flow.
36
27
  *
37
28
  * - On first launch: initializes the vault. Identity creation is opt-in via
38
29
  * `options.createIdentity: true`.
39
30
  * - On subsequent launches: unlocks the vault and reconnects to the existing identity.
31
+ * - On recovery: when `recoveryPhrase` is provided on a fresh vault, pulls
32
+ * identities and their data from the remote DWN before falling back to
33
+ * identity creation.
40
34
  *
41
35
  * When no identities exist and `createIdentity` is not `true`, the session
42
36
  * is returned with the **agent DID** as the connected DID. This allows apps to
@@ -66,10 +60,38 @@ export function vaultConnect(ctx_1) {
66
60
  if (!userAgent.dwn.isRemoteMode) {
67
61
  yield applyLocalDwnDiscovery(userAgent, storage, emitter);
68
62
  }
69
- // Find or create the user identity.
70
- const identities = yield userAgent.identity.list();
63
+ // Register the agent DID as a DWN tenant and for sync early — both are
64
+ // prerequisites for seed phrase recovery and for normal push/pull of
65
+ // identity metadata after identity creation.
66
+ if (ctx.registration) {
67
+ yield registerWithDwnEndpoints({
68
+ userAgent,
69
+ dwnEndpoints,
70
+ agentDid: userAgent.agentDid.uri,
71
+ connectedDid: userAgent.agentDid.uri,
72
+ secretStore: userAgent.secrets,
73
+ storage,
74
+ }, ctx.registration);
75
+ }
76
+ if (sync !== 'off') {
77
+ yield registerAgentDidForSync(userAgent);
78
+ }
79
+ // Find existing identities.
80
+ let identities = yield userAgent.identity.list();
71
81
  let identity = identities[0];
72
82
  let isNewIdentity = false;
83
+ // Seed phrase recovery: when a recovery phrase was provided on a fresh
84
+ // vault and no identities exist locally, pull them from the remote DWN.
85
+ if (!identity && isFirstLaunch && options.recoveryPhrase && sync !== 'off') {
86
+ try {
87
+ identities = yield recoverIdentitiesFromRemote({ userAgent, dwnEndpoints, registration: ctx.registration, storage });
88
+ identity = identities[0];
89
+ }
90
+ catch (err) {
91
+ console.warn('[@enbox/auth] Seed phrase recovery failed:', err);
92
+ }
93
+ }
94
+ // Create a default identity if none were found or recovered.
73
95
  if (!identity && shouldCreateIdentity) {
74
96
  isNewIdentity = true;
75
97
  identity = yield createDefaultIdentity(userAgent, dwnEndpoints, (_e = (_d = options.metadata) === null || _d === void 0 ? void 0 : _d.name) !== null && _e !== void 0 ? _e : 'Default');
@@ -83,38 +105,26 @@ export function vaultConnect(ctx_1) {
83
105
  const delegateDid = identity
84
106
  ? resolveIdentityDids(identity).delegateDid
85
107
  : undefined;
86
- // Register with DWN endpoints (if registration options are provided).
87
- if (ctx.registration) {
108
+ // Register the new identity DID as a tenant and for sync.
109
+ // Tenant registration must come before sync registration — with live
110
+ // sync active, registerIdentity hot-adds a subscription that needs
111
+ // the DID to be a recognised tenant on the remote DWN.
112
+ if (isNewIdentity && ctx.registration) {
88
113
  yield registerWithDwnEndpoints({
89
- userAgent: userAgent,
114
+ userAgent,
90
115
  dwnEndpoints,
91
116
  agentDid: userAgent.agentDid.uri,
92
117
  connectedDid,
93
118
  secretStore: userAgent.secrets,
94
- storage: storage,
119
+ storage,
95
120
  }, ctx.registration);
96
121
  }
97
- // Register sync for new identities.
98
122
  if (isNewIdentity && sync !== 'off') {
99
123
  yield userAgent.sync.registerIdentity({
100
124
  did: connectedDid,
101
125
  options: { delegateDid, protocols: 'all' },
102
126
  });
103
127
  }
104
- // Register the agent DID for sync so that identity metadata, portable
105
- // DIDs, and encrypted private keys are pushed to the remote DWN. Without
106
- // this, seed phrase recovery on a new device has nothing to pull.
107
- if (sync !== 'off') {
108
- try {
109
- yield userAgent.sync.registerIdentity({
110
- did: userAgent.agentDid.uri,
111
- options: { protocols: AGENT_DID_SYNC_PROTOCOLS },
112
- });
113
- }
114
- catch (_f) {
115
- // Already registered from a previous session — no action needed.
116
- }
117
- }
118
128
  // Start sync.
119
129
  yield startSyncIfEnabled(userAgent, sync);
120
130
  // Persist session info, build AuthSession, and emit lifecycle events.
@@ -1 +1 @@
1
- {"version":3,"file":"vault.js","sourceRoot":"","sources":["../../../src/connect/vault.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"vault.js","sourceRoot":"","sources":["../../../src/connect/vault.ts"],"names":[],"mappings":"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"}
@@ -12,7 +12,9 @@ import type { ImportFromPhraseOptions, ImportFromPortableOptions } from '../type
12
12
  * Import (or recover) an identity from a BIP-39 recovery phrase.
13
13
  *
14
14
  * This re-initializes the vault with the given phrase and password,
15
- * recovering the agent DID and all derived keys.
15
+ * recovering the agent DID and all derived keys. If the recovery phrase
16
+ * was previously used to create identities, they are pulled from the
17
+ * remote DWN. Otherwise a new default identity is created.
16
18
  */
17
19
  export declare function importFromPhrase(ctx: FlowContext, options: ImportFromPhraseOptions): Promise<AuthSession>;
18
20
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../../src/connect/import.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAatF;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,WAAW,CAAC,CAkFtB;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,WAAW,CAAC,CA8CtB"}
1
+ {"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../../src/connect/import.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAOtF;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,WAAW,CAAC,CAiGtB;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,WAAW,CAAC,CA8CtB"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Seed phrase recovery helpers.
3
+ *
4
+ * When a vault is re-derived from a recovery phrase on a new device,
5
+ * the local DWN is empty. This module provides the building blocks
6
+ * for pulling identity metadata, keys, and user data from the remote
7
+ * DWN in a controlled two-phase sequence.
8
+ *
9
+ * @module
10
+ * @internal
11
+ */
12
+ import type { BearerIdentity, EnboxUserAgent } from '@enbox/agent';
13
+ import type { RegistrationOptions, StorageAdapter } from '../types.js';
14
+ /**
15
+ * Internal protocols that store recovery-critical data in the agent DID's DWN.
16
+ * Syncing these ensures that seed phrase recovery can pull identity metadata,
17
+ * portable DIDs, and encrypted private keys from the remote.
18
+ */
19
+ export declare const AGENT_DID_SYNC_PROTOCOLS: [string, ...string[]];
20
+ /**
21
+ * Register the agent DID for sync with the recovery-critical protocols.
22
+ *
23
+ * This is a prerequisite for both normal operation (pushing identity
24
+ * metadata to the remote) and seed phrase recovery (pulling it back).
25
+ * Silently succeeds if the agent DID is already registered.
26
+ */
27
+ export declare function registerAgentDidForSync(userAgent: EnboxUserAgent): Promise<void>;
28
+ /**
29
+ * Recover identities and their data from remote DWN endpoints.
30
+ *
31
+ * Assumes the agent DID is already registered for sync (via
32
+ * {@link registerAgentDidForSync}) and as a DWN tenant.
33
+ *
34
+ * Phase 1 — pull identity metadata and DID private keys stored in the
35
+ * agent DID's DWN.
36
+ *
37
+ * Phase 2 — register each recovered identity DID as a tenant and for
38
+ * sync, then pull their profile data, protocol configurations, and
39
+ * records.
40
+ *
41
+ * Returns the recovered identities, or an empty array if the remote
42
+ * had nothing (e.g. first-ever setup with a pre-generated phrase).
43
+ */
44
+ export declare function recoverIdentitiesFromRemote(params: {
45
+ userAgent: EnboxUserAgent;
46
+ dwnEndpoints: string[];
47
+ registration?: RegistrationOptions;
48
+ storage: StorageAdapter;
49
+ }): Promise<BearerIdentity[]>;
50
+ //# sourceMappingURL=recovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../../../src/connect/recovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAInE,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAIvE;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAG1D,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAAC,SAAS,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAStF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,2BAA2B,CAAC,MAAM,EAAE;IACxD,SAAS,EAAE,cAAc,CAAC;IAC1B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,OAAO,EAAE,cAAc,CAAC;CACzB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAyC5B"}
@@ -17,6 +17,9 @@ import type { VaultConnectOptions } from '../types.js';
17
17
  * - On first launch: initializes the vault. Identity creation is opt-in via
18
18
  * `options.createIdentity: true`.
19
19
  * - On subsequent launches: unlocks the vault and reconnects to the existing identity.
20
+ * - On recovery: when `recoveryPhrase` is provided on a fresh vault, pulls
21
+ * identities and their data from the remote DWN before falling back to
22
+ * identity creation.
20
23
  *
21
24
  * When no identities exist and `createIdentity` is not `true`, the session
22
25
  * is returned with the **agent DID** as the connected DID. This allows apps to
@@ -1 +1 @@
1
- {"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../../../src/connect/vault.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAmBvD;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,WAAW,EAChB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,WAAW,CAAC,CAmGtB"}
1
+ {"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../../../src/connect/vault.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAQvD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,WAAW,EAChB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,WAAW,CAAC,CAsHtB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enbox/auth",
3
- "version": "0.6.34",
3
+ "version": "0.6.36",
4
4
  "description": "Headless authentication and identity management SDK for Enbox",
5
5
  "type": "module",
6
6
  "main": "./dist/esm/index.js",
@@ -60,7 +60,7 @@
60
60
  "bun": ">=1.0.0"
61
61
  },
62
62
  "dependencies": {
63
- "@enbox/agent": "0.7.2",
63
+ "@enbox/agent": "0.7.3",
64
64
  "@enbox/common": "0.1.1",
65
65
  "@enbox/crypto": "0.1.1",
66
66
  "@enbox/dids": "0.1.1",
@@ -10,22 +10,18 @@ import type { AuthSession } from '../identity-session.js';
10
10
  import type { FlowContext } from './lifecycle.js';
11
11
  import type { ImportFromPhraseOptions, ImportFromPortableOptions } from '../types.js';
12
12
 
13
- import { IdentityProtocolDefinition, JwkProtocolDefinition } from '@enbox/agent';
14
-
15
13
  import { DEFAULT_DWN_ENDPOINTS } from '../types.js';
16
14
  import { registerWithDwnEndpoints } from '../registration.js';
17
15
  import { createDefaultIdentity, ensureVaultReady, finalizeSession, registerSyncScopeForIdentity, resolveIdentityDids, startSyncIfEnabled } from './lifecycle.js';
18
-
19
- const AGENT_DID_SYNC_PROTOCOLS: [string, ...string[]] = [
20
- IdentityProtocolDefinition.protocol,
21
- JwkProtocolDefinition.protocol,
22
- ];
16
+ import { recoverIdentitiesFromRemote, registerAgentDidForSync } from './recovery.js';
23
17
 
24
18
  /**
25
19
  * Import (or recover) an identity from a BIP-39 recovery phrase.
26
20
  *
27
21
  * This re-initializes the vault with the given phrase and password,
28
- * recovering the agent DID and all derived keys.
22
+ * recovering the agent DID and all derived keys. If the recovery phrase
23
+ * was previously used to create identities, they are pulled from the
24
+ * remote DWN. Otherwise a new default identity is created.
29
25
  */
30
26
  export async function importFromPhrase(
31
27
  ctx: FlowContext,
@@ -47,58 +43,73 @@ export async function importFromPhrase(
47
43
  dwnEndpoints,
48
44
  });
49
45
 
50
- // The recovery phrase re-derives the same agent DID,
51
- // but the user identity might not exist yet — create one if needed.
52
- const identities = await userAgent.identity.list();
53
- let identity = identities[0];
54
- let isNewIdentity = false;
55
-
56
- if (!identity) {
57
- isNewIdentity = true;
58
- identity = await createDefaultIdentity(userAgent, dwnEndpoints);
59
- }
60
-
61
- const { connectedDid, delegateDid } = resolveIdentityDids(identity);
62
-
63
- // Register with DWN endpoints (if registration options are provided).
46
+ // Register agent DID as tenant and for sync — prerequisites for recovery.
64
47
  if (ctx.registration) {
65
48
  await registerWithDwnEndpoints(
66
49
  {
67
- userAgent : userAgent,
50
+ userAgent,
68
51
  dwnEndpoints,
69
- agentDid : userAgent.agentDid.uri,
70
- connectedDid,
71
- secretStore : userAgent.secrets,
72
- storage : storage,
52
+ agentDid : userAgent.agentDid.uri,
53
+ connectedDid : userAgent.agentDid.uri,
54
+ secretStore : userAgent.secrets,
55
+ storage,
73
56
  },
74
57
  ctx.registration,
75
58
  );
76
59
  }
77
-
78
- // Register sync. For delegate identities, always repair the registration
79
- // (derive scope from active grants — revoked grants must not remain in a
80
- // stale registration), regardless of whether the identity was just
81
- // created or restored from storage. For local identities, register
82
- // `protocols: 'all'` only on first creation; a pre-existing local
83
- // identity was already registered during its initial flow.
84
- if (delegateDid) {
85
- await registerSyncScopeForIdentity({ userAgent, connectedDid, delegateDid });
86
- } else if (isNewIdentity && sync !== 'off') {
87
- await registerSyncScopeForIdentity({ userAgent, connectedDid });
60
+ if (sync !== 'off') {
61
+ await registerAgentDidForSync(userAgent);
88
62
  }
89
63
 
90
- // Register the agent DID for sync (same rationale as vaultConnect).
91
- if (sync !== 'off') {
64
+ // Try to recover identities from the remote DWN before falling back
65
+ // to creating a new one.
66
+ let identities = await userAgent.identity.list();
67
+ let identity = identities[0];
68
+ let isNewIdentity = false;
69
+
70
+ if (!identity && sync !== 'off') {
92
71
  try {
93
- await userAgent.sync.registerIdentity({
94
- did : userAgent.agentDid.uri,
95
- options : { protocols: AGENT_DID_SYNC_PROTOCOLS },
96
- });
97
- } catch {
98
- // Already registered from a previous session.
72
+ identities = await recoverIdentitiesFromRemote({ userAgent, dwnEndpoints, registration: ctx.registration, storage });
73
+ identity = identities[0];
74
+ } catch (err) {
75
+ console.warn('[@enbox/auth] Seed phrase recovery failed:', err);
99
76
  }
100
77
  }
101
78
 
79
+ if (!identity) {
80
+ isNewIdentity = true;
81
+ identity = await createDefaultIdentity(userAgent, dwnEndpoints);
82
+ }
83
+
84
+ const { connectedDid, delegateDid } = resolveIdentityDids(identity);
85
+
86
+ // Register sync for the identity DID.
87
+ if (isNewIdentity) {
88
+ // New identity: register as tenant first, then for sync.
89
+ if (ctx.registration) {
90
+ await registerWithDwnEndpoints(
91
+ {
92
+ userAgent,
93
+ dwnEndpoints,
94
+ agentDid : userAgent.agentDid.uri,
95
+ connectedDid,
96
+ secretStore : userAgent.secrets,
97
+ storage,
98
+ },
99
+ ctx.registration,
100
+ );
101
+ }
102
+ if (delegateDid) {
103
+ await registerSyncScopeForIdentity({ userAgent, connectedDid, delegateDid });
104
+ } else if (sync !== 'off') {
105
+ await registerSyncScopeForIdentity({ userAgent, connectedDid });
106
+ }
107
+ } else if (delegateDid) {
108
+ // Pre-existing delegate identity: repair sync scope so revoked
109
+ // grants don't remain in a stale registration.
110
+ await registerSyncScopeForIdentity({ userAgent, connectedDid, delegateDid });
111
+ }
112
+
102
113
  // Start sync.
103
114
  await startSyncIfEnabled(userAgent, sync);
104
115
 
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Seed phrase recovery helpers.
3
+ *
4
+ * When a vault is re-derived from a recovery phrase on a new device,
5
+ * the local DWN is empty. This module provides the building blocks
6
+ * for pulling identity metadata, keys, and user data from the remote
7
+ * DWN in a controlled two-phase sequence.
8
+ *
9
+ * @module
10
+ * @internal
11
+ */
12
+
13
+ import type { BearerIdentity, EnboxUserAgent } from '@enbox/agent';
14
+
15
+ import { IdentityProtocolDefinition, JwkProtocolDefinition } from '@enbox/agent';
16
+
17
+ import type { RegistrationOptions, StorageAdapter } from '../types.js';
18
+
19
+ import { registerWithDwnEndpoints } from '../registration.js';
20
+
21
+ /**
22
+ * Internal protocols that store recovery-critical data in the agent DID's DWN.
23
+ * Syncing these ensures that seed phrase recovery can pull identity metadata,
24
+ * portable DIDs, and encrypted private keys from the remote.
25
+ */
26
+ export const AGENT_DID_SYNC_PROTOCOLS: [string, ...string[]] = [
27
+ IdentityProtocolDefinition.protocol,
28
+ JwkProtocolDefinition.protocol,
29
+ ];
30
+
31
+ /**
32
+ * Register the agent DID for sync with the recovery-critical protocols.
33
+ *
34
+ * This is a prerequisite for both normal operation (pushing identity
35
+ * metadata to the remote) and seed phrase recovery (pulling it back).
36
+ * Silently succeeds if the agent DID is already registered.
37
+ */
38
+ export async function registerAgentDidForSync(userAgent: EnboxUserAgent): Promise<void> {
39
+ try {
40
+ await userAgent.sync.registerIdentity({
41
+ did : userAgent.agentDid.uri,
42
+ options : { protocols: AGENT_DID_SYNC_PROTOCOLS },
43
+ });
44
+ } catch {
45
+ // Already registered from a previous session.
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Recover identities and their data from remote DWN endpoints.
51
+ *
52
+ * Assumes the agent DID is already registered for sync (via
53
+ * {@link registerAgentDidForSync}) and as a DWN tenant.
54
+ *
55
+ * Phase 1 — pull identity metadata and DID private keys stored in the
56
+ * agent DID's DWN.
57
+ *
58
+ * Phase 2 — register each recovered identity DID as a tenant and for
59
+ * sync, then pull their profile data, protocol configurations, and
60
+ * records.
61
+ *
62
+ * Returns the recovered identities, or an empty array if the remote
63
+ * had nothing (e.g. first-ever setup with a pre-generated phrase).
64
+ */
65
+ export async function recoverIdentitiesFromRemote(params: {
66
+ userAgent: EnboxUserAgent;
67
+ dwnEndpoints: string[];
68
+ registration?: RegistrationOptions;
69
+ storage: StorageAdapter;
70
+ }): Promise<BearerIdentity[]> {
71
+ const { userAgent, dwnEndpoints, registration, storage } = params;
72
+ const agentDid = userAgent.agentDid.uri;
73
+
74
+ // Phase 1: pull identity metadata + encrypted DID keys.
75
+ await userAgent.sync.sync('pull');
76
+
77
+ const identities = await userAgent.identity.list();
78
+ if (identities.length === 0) {
79
+ return [];
80
+ }
81
+
82
+ // Register each recovered identity DID as a DWN tenant, then for sync.
83
+ // Tenant registration must come first — sync('pull') issues
84
+ // MessagesSync which requires the DID to be a recognised tenant.
85
+ for (const identity of identities) {
86
+ const did = identity.metadata.connectedDid ?? identity.did.uri;
87
+
88
+ if (registration) {
89
+ try {
90
+ await registerWithDwnEndpoints(
91
+ { userAgent, dwnEndpoints, agentDid, connectedDid: did, secretStore: userAgent.secrets, storage },
92
+ registration,
93
+ );
94
+ } catch {
95
+ // Best effort — the DID may already be registered, or the
96
+ // endpoint may be temporarily unreachable.
97
+ }
98
+ }
99
+
100
+ try {
101
+ await userAgent.sync.registerIdentity({ did, options: { protocols: 'all' } });
102
+ } catch {
103
+ // Already registered from a previous session.
104
+ }
105
+ }
106
+
107
+ // Phase 2: pull profile data, protocol configurations, and records.
108
+ await userAgent.sync.sync('pull');
109
+
110
+ return userAgent.identity.list();
111
+ }
@@ -13,22 +13,11 @@ import type { AuthSession } from '../identity-session.js';
13
13
  import type { FlowContext } from './lifecycle.js';
14
14
  import type { VaultConnectOptions } from '../types.js';
15
15
 
16
- import { IdentityProtocolDefinition, JwkProtocolDefinition } from '@enbox/agent';
17
-
18
16
  import { applyLocalDwnDiscovery } from '../discovery.js';
19
17
  import { DEFAULT_DWN_ENDPOINTS } from '../types.js';
20
18
  import { registerWithDwnEndpoints } from '../registration.js';
21
19
  import { createDefaultIdentity, ensureVaultReady, finalizeSession, resolveIdentityDids, resolvePassword, startSyncIfEnabled } from './lifecycle.js';
22
-
23
- /**
24
- * Internal protocols that store recovery-critical data in the agent DID's DWN.
25
- * These must be synced so that seed phrase recovery can pull identity metadata,
26
- * portable DIDs, and encrypted private keys from the remote DWN.
27
- */
28
- const AGENT_DID_SYNC_PROTOCOLS: [string, ...string[]] = [
29
- IdentityProtocolDefinition.protocol,
30
- JwkProtocolDefinition.protocol,
31
- ];
20
+ import { recoverIdentitiesFromRemote, registerAgentDidForSync } from './recovery.js';
32
21
 
33
22
  /**
34
23
  * Execute the vault connect flow.
@@ -36,6 +25,9 @@ const AGENT_DID_SYNC_PROTOCOLS: [string, ...string[]] = [
36
25
  * - On first launch: initializes the vault. Identity creation is opt-in via
37
26
  * `options.createIdentity: true`.
38
27
  * - On subsequent launches: unlocks the vault and reconnects to the existing identity.
28
+ * - On recovery: when `recoveryPhrase` is provided on a fresh vault, pulls
29
+ * identities and their data from the remote DWN before falling back to
30
+ * identity creation.
39
31
  *
40
32
  * When no identities exist and `createIdentity` is not `true`, the session
41
33
  * is returned with the **agent DID** as the connected DID. This allows apps to
@@ -71,11 +63,43 @@ export async function vaultConnect(
71
63
  await applyLocalDwnDiscovery(userAgent, storage, emitter);
72
64
  }
73
65
 
74
- // Find or create the user identity.
75
- const identities = await userAgent.identity.list();
66
+ // Register the agent DID as a DWN tenant and for sync early — both are
67
+ // prerequisites for seed phrase recovery and for normal push/pull of
68
+ // identity metadata after identity creation.
69
+ if (ctx.registration) {
70
+ await registerWithDwnEndpoints(
71
+ {
72
+ userAgent,
73
+ dwnEndpoints,
74
+ agentDid : userAgent.agentDid.uri,
75
+ connectedDid : userAgent.agentDid.uri,
76
+ secretStore : userAgent.secrets,
77
+ storage,
78
+ },
79
+ ctx.registration,
80
+ );
81
+ }
82
+ if (sync !== 'off') {
83
+ await registerAgentDidForSync(userAgent);
84
+ }
85
+
86
+ // Find existing identities.
87
+ let identities = await userAgent.identity.list();
76
88
  let identity = identities[0];
77
89
  let isNewIdentity = false;
78
90
 
91
+ // Seed phrase recovery: when a recovery phrase was provided on a fresh
92
+ // vault and no identities exist locally, pull them from the remote DWN.
93
+ if (!identity && isFirstLaunch && options.recoveryPhrase && sync !== 'off') {
94
+ try {
95
+ identities = await recoverIdentitiesFromRemote({ userAgent, dwnEndpoints, registration: ctx.registration, storage });
96
+ identity = identities[0];
97
+ } catch (err) {
98
+ console.warn('[@enbox/auth] Seed phrase recovery failed:', err);
99
+ }
100
+ }
101
+
102
+ // Create a default identity if none were found or recovered.
79
103
  if (!identity && shouldCreateIdentity) {
80
104
  isNewIdentity = true;
81
105
  identity = await createDefaultIdentity(userAgent, dwnEndpoints, options.metadata?.name ?? 'Default');
@@ -92,22 +116,23 @@ export async function vaultConnect(
92
116
  ? resolveIdentityDids(identity).delegateDid
93
117
  : undefined;
94
118
 
95
- // Register with DWN endpoints (if registration options are provided).
96
- if (ctx.registration) {
119
+ // Register the new identity DID as a tenant and for sync.
120
+ // Tenant registration must come before sync registration — with live
121
+ // sync active, registerIdentity hot-adds a subscription that needs
122
+ // the DID to be a recognised tenant on the remote DWN.
123
+ if (isNewIdentity && ctx.registration) {
97
124
  await registerWithDwnEndpoints(
98
125
  {
99
- userAgent : userAgent,
126
+ userAgent,
100
127
  dwnEndpoints,
101
128
  agentDid : userAgent.agentDid.uri,
102
129
  connectedDid,
103
130
  secretStore : userAgent.secrets,
104
- storage : storage,
131
+ storage,
105
132
  },
106
133
  ctx.registration,
107
134
  );
108
135
  }
109
-
110
- // Register sync for new identities.
111
136
  if (isNewIdentity && sync !== 'off') {
112
137
  await userAgent.sync.registerIdentity({
113
138
  did : connectedDid,
@@ -115,20 +140,6 @@ export async function vaultConnect(
115
140
  });
116
141
  }
117
142
 
118
- // Register the agent DID for sync so that identity metadata, portable
119
- // DIDs, and encrypted private keys are pushed to the remote DWN. Without
120
- // this, seed phrase recovery on a new device has nothing to pull.
121
- if (sync !== 'off') {
122
- try {
123
- await userAgent.sync.registerIdentity({
124
- did : userAgent.agentDid.uri,
125
- options : { protocols: AGENT_DID_SYNC_PROTOCOLS },
126
- });
127
- } catch {
128
- // Already registered from a previous session — no action needed.
129
- }
130
- }
131
-
132
143
  // Start sync.
133
144
  await startSyncIfEnabled(userAgent, sync);
134
145