@enbox/auth 0.5.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 +240 -171
- package/dist/esm/auth-manager.js.map +1 -1
- package/dist/esm/connect/import.js +131 -0
- package/dist/esm/connect/import.js.map +1 -0
- package/dist/esm/connect/lifecycle.js +378 -0
- package/dist/esm/connect/lifecycle.js.map +1 -0
- package/dist/esm/connect/local.js +105 -0
- package/dist/esm/connect/local.js.map +1 -0
- package/dist/esm/connect/restore.js +117 -0
- package/dist/esm/connect/restore.js.map +1 -0
- package/dist/esm/connect/wallet.js +80 -0
- package/dist/esm/connect/wallet.js.map +1 -0
- package/dist/esm/{flows/dwn-discovery.js → discovery.js} +2 -2
- package/dist/esm/discovery.js.map +1 -0
- package/dist/esm/index.js +13 -19
- 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/{flows/dwn-registration.js → registration.js} +2 -2
- package/dist/esm/registration.js.map +1 -0
- package/dist/esm/types.js +4 -0
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/wallet-connect-client.js +188 -0
- package/dist/esm/wallet-connect-client.js.map +1 -0
- package/dist/types/auth-manager.d.ts +89 -11
- package/dist/types/auth-manager.d.ts.map +1 -1
- package/dist/types/connect/import.d.ts +25 -0
- package/dist/types/connect/import.d.ts.map +1 -0
- package/dist/types/connect/lifecycle.d.ts +199 -0
- package/dist/types/connect/lifecycle.d.ts.map +1 -0
- package/dist/types/connect/local.d.ts +23 -0
- package/dist/types/connect/local.d.ts.map +1 -0
- package/dist/types/connect/restore.d.ts +18 -0
- package/dist/types/connect/restore.d.ts.map +1 -0
- package/dist/types/connect/wallet.d.ts +21 -0
- package/dist/types/connect/wallet.d.ts.map +1 -0
- package/dist/types/{flows/dwn-discovery.d.ts → discovery.d.ts} +3 -3
- package/dist/types/discovery.d.ts.map +1 -0
- package/dist/types/index.d.ts +14 -19
- 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/{flows/dwn-registration.d.ts → registration.d.ts} +2 -2
- package/dist/types/registration.d.ts.map +1 -0
- package/dist/types/types.d.ts +154 -4
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/wallet-connect-client.d.ts +86 -0
- package/dist/types/wallet-connect-client.d.ts.map +1 -0
- package/package.json +9 -5
- package/src/auth-manager.ts +258 -191
- package/src/connect/import.ts +148 -0
- package/src/connect/lifecycle.ts +487 -0
- package/src/connect/local.ts +116 -0
- package/src/connect/restore.ts +133 -0
- package/src/connect/wallet.ts +89 -0
- package/src/{flows/dwn-discovery.ts → discovery.ts} +4 -3
- package/src/index.ts +20 -19
- package/src/permissions.ts +48 -0
- package/src/{flows/dwn-registration.ts → registration.ts} +2 -2
- package/src/types.ts +171 -4
- package/src/wallet-connect-client.ts +275 -0
- package/dist/esm/flows/dwn-discovery.js.map +0 -1
- package/dist/esm/flows/dwn-registration.js.map +0 -1
- package/dist/esm/flows/import-identity.js +0 -177
- package/dist/esm/flows/import-identity.js.map +0 -1
- package/dist/esm/flows/local-connect.js +0 -158
- package/dist/esm/flows/local-connect.js.map +0 -1
- package/dist/esm/flows/session-restore.js +0 -125
- package/dist/esm/flows/session-restore.js.map +0 -1
- package/dist/esm/flows/wallet-connect.js +0 -200
- package/dist/esm/flows/wallet-connect.js.map +0 -1
- package/dist/esm/vault/vault-manager.js +0 -95
- package/dist/esm/vault/vault-manager.js.map +0 -1
- package/dist/types/flows/dwn-discovery.d.ts.map +0 -1
- package/dist/types/flows/dwn-registration.d.ts.map +0 -1
- package/dist/types/flows/import-identity.d.ts +0 -35
- package/dist/types/flows/import-identity.d.ts.map +0 -1
- package/dist/types/flows/local-connect.d.ts +0 -31
- package/dist/types/flows/local-connect.d.ts.map +0 -1
- package/dist/types/flows/session-restore.d.ts +0 -29
- package/dist/types/flows/session-restore.d.ts.map +0 -1
- package/dist/types/flows/wallet-connect.d.ts +0 -44
- package/dist/types/flows/wallet-connect.d.ts.map +0 -1
- package/dist/types/vault/vault-manager.d.ts +0 -57
- package/dist/types/vault/vault-manager.d.ts.map +0 -1
- package/src/flows/import-identity.ts +0 -219
- package/src/flows/local-connect.ts +0 -192
- package/src/flows/session-restore.ts +0 -155
- package/src/flows/wallet-connect.ts +0 -226
- package/src/vault/vault-manager.ts +0 -89
package/src/auth-manager.ts
CHANGED
|
@@ -6,26 +6,19 @@
|
|
|
6
6
|
* @module
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
import type { BearerIdentity, PortableIdentity } from '@enbox/agent';
|
|
11
|
-
|
|
12
|
-
import { AuthEventEmitter } from './events.js';
|
|
13
|
-
import { AuthSession } from './identity-session.js';
|
|
14
|
-
import { createDefaultStorage } from './storage/storage.js';
|
|
15
|
-
import { discoverLocalDwn } from './flows/dwn-discovery.js';
|
|
16
|
-
import { localConnect } from './flows/local-connect.js';
|
|
17
|
-
import { restoreSession } from './flows/session-restore.js';
|
|
18
|
-
import { STORAGE_KEYS } from './types.js';
|
|
19
|
-
import { VaultManager } from './vault/vault-manager.js';
|
|
20
|
-
import { walletConnect } from './flows/wallet-connect.js';
|
|
9
|
+
import type { BearerIdentity, HdIdentityVault, PortableIdentity } from '@enbox/agent';
|
|
21
10
|
|
|
11
|
+
import type { FlowContext } from './connect/lifecycle.js';
|
|
22
12
|
import type { PasswordProvider } from './password-provider.js';
|
|
23
13
|
import type {
|
|
24
14
|
AuthEvent,
|
|
25
15
|
AuthEventHandler,
|
|
26
16
|
AuthManagerOptions,
|
|
27
17
|
AuthState,
|
|
18
|
+
ConnectHandler,
|
|
19
|
+
ConnectOptions,
|
|
28
20
|
DisconnectOptions,
|
|
21
|
+
HandlerConnectOptions,
|
|
29
22
|
HeadlessConnectOptions,
|
|
30
23
|
IdentityInfo,
|
|
31
24
|
ImportFromPhraseOptions,
|
|
@@ -38,7 +31,20 @@ import type {
|
|
|
38
31
|
SyncOption,
|
|
39
32
|
WalletConnectOptions,
|
|
40
33
|
} from './types.js';
|
|
41
|
-
|
|
34
|
+
|
|
35
|
+
import { EnboxUserAgent } from '@enbox/agent';
|
|
36
|
+
|
|
37
|
+
import { AuthEventEmitter } from './events.js';
|
|
38
|
+
import { AuthSession } from './identity-session.js';
|
|
39
|
+
import { createDefaultStorage } from './storage/storage.js';
|
|
40
|
+
import { discoverLocalDwn } from './discovery.js';
|
|
41
|
+
import { localConnect } from './connect/local.js';
|
|
42
|
+
import { normalizeProtocolRequests } from './permissions.js';
|
|
43
|
+
import { restoreSession } from './connect/restore.js';
|
|
44
|
+
import { STORAGE_KEYS } from './types.js';
|
|
45
|
+
import { walletConnect } from './connect/wallet.js';
|
|
46
|
+
import { ensureVaultReady, finalizeDelegateSession, importDelegateAndSetupSync, resolveIdentityDids, resolvePassword, startSyncIfEnabled } from './connect/lifecycle.js';
|
|
47
|
+
import { importFromPhrase, importFromPortable } from './connect/import.js';
|
|
42
48
|
|
|
43
49
|
/**
|
|
44
50
|
* The primary entry point for authentication and identity management.
|
|
@@ -77,7 +83,6 @@ export class AuthManager {
|
|
|
77
83
|
private _userAgent: EnboxUserAgent;
|
|
78
84
|
private _emitter: AuthEventEmitter;
|
|
79
85
|
private _storage: StorageAdapter;
|
|
80
|
-
private _vault: VaultManager;
|
|
81
86
|
private _session: AuthSession | undefined;
|
|
82
87
|
private _state: AuthState = 'uninitialized';
|
|
83
88
|
private _isConnecting = false;
|
|
@@ -89,6 +94,7 @@ export class AuthManager {
|
|
|
89
94
|
private _defaultSync?: SyncOption;
|
|
90
95
|
private _defaultDwnEndpoints?: string[];
|
|
91
96
|
private _registration?: RegistrationOptions;
|
|
97
|
+
private _connectHandler?: ConnectHandler;
|
|
92
98
|
|
|
93
99
|
/**
|
|
94
100
|
* The local DWN server endpoint discovered during `create()`, if any.
|
|
@@ -102,24 +108,24 @@ export class AuthManager {
|
|
|
102
108
|
userAgent: EnboxUserAgent;
|
|
103
109
|
emitter: AuthEventEmitter;
|
|
104
110
|
storage: StorageAdapter;
|
|
105
|
-
vault: VaultManager;
|
|
106
111
|
defaultPassword?: string;
|
|
107
112
|
passwordProvider?: PasswordProvider;
|
|
108
113
|
defaultSync?: SyncOption;
|
|
109
114
|
defaultDwnEndpoints?: string[];
|
|
110
115
|
registration?: RegistrationOptions;
|
|
111
116
|
localDwnEndpoint?: string;
|
|
117
|
+
connectHandler?: ConnectHandler;
|
|
112
118
|
}) {
|
|
113
119
|
this._userAgent = params.userAgent;
|
|
114
120
|
this._emitter = params.emitter;
|
|
115
121
|
this._storage = params.storage;
|
|
116
|
-
this._vault = params.vault;
|
|
117
122
|
this._defaultPassword = params.defaultPassword;
|
|
118
123
|
this._passwordProvider = params.passwordProvider;
|
|
119
124
|
this._defaultSync = params.defaultSync;
|
|
120
125
|
this._defaultDwnEndpoints = params.defaultDwnEndpoints;
|
|
121
126
|
this._registration = params.registration;
|
|
122
127
|
this._localDwnEndpoint = params.localDwnEndpoint;
|
|
128
|
+
this._connectHandler = params.connectHandler;
|
|
123
129
|
}
|
|
124
130
|
|
|
125
131
|
/**
|
|
@@ -159,24 +165,22 @@ export class AuthManager {
|
|
|
159
165
|
localDwnEndpoint,
|
|
160
166
|
});
|
|
161
167
|
|
|
162
|
-
const vault = new VaultManager(userAgent.vault, emitter);
|
|
163
|
-
|
|
164
168
|
const manager = new AuthManager({
|
|
165
169
|
userAgent,
|
|
166
170
|
emitter,
|
|
167
171
|
storage,
|
|
168
|
-
vault,
|
|
169
172
|
defaultPassword : options.password,
|
|
170
173
|
passwordProvider : options.passwordProvider,
|
|
171
174
|
defaultSync : options.sync,
|
|
172
175
|
defaultDwnEndpoints : options.dwnEndpoints,
|
|
173
176
|
registration : options.registration,
|
|
174
177
|
localDwnEndpoint,
|
|
178
|
+
connectHandler : options.connectHandler,
|
|
175
179
|
});
|
|
176
180
|
|
|
177
181
|
// Determine initial state.
|
|
178
|
-
if (await vault.isInitialized()) {
|
|
179
|
-
manager._setState(vault.isLocked ? 'locked' : 'unlocked');
|
|
182
|
+
if (await userAgent.vault.isInitialized()) {
|
|
183
|
+
manager._setState(userAgent.vault.isLocked() ? 'locked' : 'unlocked');
|
|
180
184
|
} else {
|
|
181
185
|
manager._setState('uninitialized');
|
|
182
186
|
}
|
|
@@ -187,40 +191,75 @@ export class AuthManager {
|
|
|
187
191
|
// ─── Connection flows ──────────────────────────────────────────
|
|
188
192
|
|
|
189
193
|
/**
|
|
190
|
-
*
|
|
194
|
+
* Connect to a wallet or create a local session.
|
|
195
|
+
*
|
|
196
|
+
* This is the primary entry point for dapps. It routes to the
|
|
197
|
+
* appropriate flow based on the options:
|
|
198
|
+
*
|
|
199
|
+
* **Handler-based connect** (dapps): Delegates credential acquisition
|
|
200
|
+
* to a {@link ConnectHandler}. Triggered when `protocols` or
|
|
201
|
+
* `connectHandler` is provided.
|
|
202
|
+
*
|
|
203
|
+
* **Local connect** (wallets / CLI): Creates or unlocks a local vault.
|
|
204
|
+
* Triggered when `password`, `createIdentity`, or `recoveryPhrase`
|
|
205
|
+
* is provided.
|
|
206
|
+
*
|
|
207
|
+
* In both cases, `connect()` first attempts to restore a previous
|
|
208
|
+
* session. If a valid session exists, it is returned immediately
|
|
209
|
+
* without any user interaction.
|
|
210
|
+
*
|
|
211
|
+
* @example Dapp (browser)
|
|
212
|
+
* ```ts
|
|
213
|
+
* import { BrowserConnectHandler } from '@enbox/browser';
|
|
214
|
+
*
|
|
215
|
+
* const auth = await AuthManager.create({
|
|
216
|
+
* connectHandler: BrowserConnectHandler(),
|
|
217
|
+
* });
|
|
218
|
+
* const session = await auth.connect({
|
|
219
|
+
* protocols: [NotesProtocol],
|
|
220
|
+
* });
|
|
221
|
+
* ```
|
|
191
222
|
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
223
|
+
* @example Wallet / CLI
|
|
224
|
+
* ```ts
|
|
225
|
+
* const session = await auth.connect({
|
|
226
|
+
* password: userPin,
|
|
227
|
+
* createIdentity: true,
|
|
228
|
+
* });
|
|
229
|
+
* ```
|
|
194
230
|
*
|
|
195
|
-
* @param options -
|
|
231
|
+
* @param options - Connection options. The shape determines the flow.
|
|
196
232
|
* @returns An active AuthSession.
|
|
197
233
|
* @throws If a connection attempt is already in progress.
|
|
234
|
+
* @throws If handler-based connect is attempted without a handler.
|
|
198
235
|
*/
|
|
199
|
-
async connect(options?:
|
|
200
|
-
this.
|
|
201
|
-
|
|
236
|
+
async connect(options?: ConnectOptions): Promise<AuthSession> {
|
|
237
|
+
return this._withConnect(async () => {
|
|
238
|
+
// 1. Try to restore a previous session first.
|
|
239
|
+
const restored = await restoreSession(this._flowContext());
|
|
240
|
+
if (restored) { return restored; }
|
|
241
|
+
|
|
242
|
+
// 2. Route to the appropriate flow.
|
|
243
|
+
if (this._isLocalConnect(options)) {
|
|
244
|
+
return localConnect(this._flowContext(), options as LocalConnectOptions);
|
|
245
|
+
}
|
|
202
246
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
userAgent : this._userAgent,
|
|
207
|
-
emitter : this._emitter,
|
|
208
|
-
storage : this._storage,
|
|
209
|
-
defaultPassword : this._defaultPassword,
|
|
210
|
-
passwordProvider : this._passwordProvider,
|
|
211
|
-
defaultSync : this._defaultSync,
|
|
212
|
-
defaultDwnEndpoints : this._defaultDwnEndpoints,
|
|
213
|
-
registration : this._registration,
|
|
214
|
-
},
|
|
215
|
-
options,
|
|
216
|
-
);
|
|
247
|
+
return this._handlerConnect(options as HandlerConnectOptions | undefined);
|
|
248
|
+
});
|
|
249
|
+
}
|
|
217
250
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
251
|
+
/**
|
|
252
|
+
* Create or reconnect a local identity (explicit local connect).
|
|
253
|
+
*
|
|
254
|
+
* Use this when you explicitly want the local vault flow, bypassing
|
|
255
|
+
* auto-detection. This is the preferred method for wallet apps.
|
|
256
|
+
*
|
|
257
|
+
* @param options - Local connect options.
|
|
258
|
+
* @returns An active AuthSession.
|
|
259
|
+
* @throws If a connection attempt is already in progress.
|
|
260
|
+
*/
|
|
261
|
+
async connectLocal(options?: LocalConnectOptions): Promise<AuthSession> {
|
|
262
|
+
return this._withConnect(() => localConnect(this._flowContext(), options));
|
|
224
263
|
}
|
|
225
264
|
|
|
226
265
|
/**
|
|
@@ -235,50 +274,7 @@ export class AuthManager {
|
|
|
235
274
|
* @throws If a connection attempt is already in progress.
|
|
236
275
|
*/
|
|
237
276
|
async walletConnect(options: WalletConnectOptions): Promise<AuthSession> {
|
|
238
|
-
this.
|
|
239
|
-
this._isConnecting = true;
|
|
240
|
-
|
|
241
|
-
try {
|
|
242
|
-
// Ensure the agent is initialized and started before wallet connect.
|
|
243
|
-
const isFirstLaunch = await this._userAgent.firstLaunch();
|
|
244
|
-
let password = this._defaultPassword;
|
|
245
|
-
|
|
246
|
-
if (!password && this._passwordProvider) {
|
|
247
|
-
try {
|
|
248
|
-
password = await this._passwordProvider.getPassword({
|
|
249
|
-
reason: isFirstLaunch ? 'create' : 'unlock',
|
|
250
|
-
});
|
|
251
|
-
} catch {
|
|
252
|
-
// Provider failed — fall through to insecure default.
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
password ??= 'insecure-static-phrase';
|
|
257
|
-
|
|
258
|
-
if (isFirstLaunch) {
|
|
259
|
-
await this._userAgent.initialize({ password });
|
|
260
|
-
}
|
|
261
|
-
await this._userAgent.start({ password });
|
|
262
|
-
this._emitter.emit('vault-unlocked', {});
|
|
263
|
-
|
|
264
|
-
const session = await walletConnect(
|
|
265
|
-
{
|
|
266
|
-
userAgent : this._userAgent,
|
|
267
|
-
emitter : this._emitter,
|
|
268
|
-
storage : this._storage,
|
|
269
|
-
defaultSync : this._defaultSync,
|
|
270
|
-
defaultDwnEndpoints : this._defaultDwnEndpoints,
|
|
271
|
-
registration : this._registration,
|
|
272
|
-
},
|
|
273
|
-
options,
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
this._session = session;
|
|
277
|
-
this._setState('connected');
|
|
278
|
-
return session;
|
|
279
|
-
} finally {
|
|
280
|
-
this._isConnecting = false;
|
|
281
|
-
}
|
|
277
|
+
return this._withConnect(() => walletConnect(this._flowContext(), options));
|
|
282
278
|
}
|
|
283
279
|
|
|
284
280
|
/**
|
|
@@ -288,28 +284,7 @@ export class AuthManager {
|
|
|
288
284
|
* recovering the identity on this device.
|
|
289
285
|
*/
|
|
290
286
|
async importFromPhrase(options: ImportFromPhraseOptions): Promise<AuthSession> {
|
|
291
|
-
this.
|
|
292
|
-
this._isConnecting = true;
|
|
293
|
-
|
|
294
|
-
try {
|
|
295
|
-
const session = await importFromPhrase(
|
|
296
|
-
{
|
|
297
|
-
userAgent : this._userAgent,
|
|
298
|
-
emitter : this._emitter,
|
|
299
|
-
storage : this._storage,
|
|
300
|
-
defaultSync : this._defaultSync,
|
|
301
|
-
defaultDwnEndpoints : this._defaultDwnEndpoints,
|
|
302
|
-
registration : this._registration,
|
|
303
|
-
},
|
|
304
|
-
options,
|
|
305
|
-
);
|
|
306
|
-
|
|
307
|
-
this._session = session;
|
|
308
|
-
this._setState('connected');
|
|
309
|
-
return session;
|
|
310
|
-
} finally {
|
|
311
|
-
this._isConnecting = false;
|
|
312
|
-
}
|
|
287
|
+
return this._withConnect(() => importFromPhrase(this._flowContext(), options));
|
|
313
288
|
}
|
|
314
289
|
|
|
315
290
|
/**
|
|
@@ -318,28 +293,7 @@ export class AuthManager {
|
|
|
318
293
|
* The portable identity contains the DID's private keys and metadata.
|
|
319
294
|
*/
|
|
320
295
|
async importFromPortable(options: ImportFromPortableOptions): Promise<AuthSession> {
|
|
321
|
-
this.
|
|
322
|
-
this._isConnecting = true;
|
|
323
|
-
|
|
324
|
-
try {
|
|
325
|
-
const session = await importFromPortable(
|
|
326
|
-
{
|
|
327
|
-
userAgent : this._userAgent,
|
|
328
|
-
emitter : this._emitter,
|
|
329
|
-
storage : this._storage,
|
|
330
|
-
defaultSync : this._defaultSync,
|
|
331
|
-
defaultDwnEndpoints : this._defaultDwnEndpoints,
|
|
332
|
-
registration : this._registration,
|
|
333
|
-
},
|
|
334
|
-
options,
|
|
335
|
-
);
|
|
336
|
-
|
|
337
|
-
this._session = session;
|
|
338
|
-
this._setState('connected');
|
|
339
|
-
return session;
|
|
340
|
-
} finally {
|
|
341
|
-
this._isConnecting = false;
|
|
342
|
-
}
|
|
296
|
+
return this._withConnect(() => importFromPortable(this._flowContext(), options));
|
|
343
297
|
}
|
|
344
298
|
|
|
345
299
|
/**
|
|
@@ -353,17 +307,7 @@ export class AuthManager {
|
|
|
353
307
|
this._isConnecting = true;
|
|
354
308
|
|
|
355
309
|
try {
|
|
356
|
-
const session = await restoreSession(
|
|
357
|
-
{
|
|
358
|
-
userAgent : this._userAgent,
|
|
359
|
-
emitter : this._emitter,
|
|
360
|
-
storage : this._storage,
|
|
361
|
-
defaultPassword : this._defaultPassword,
|
|
362
|
-
passwordProvider : this._passwordProvider,
|
|
363
|
-
defaultSync : this._defaultSync,
|
|
364
|
-
},
|
|
365
|
-
options,
|
|
366
|
-
);
|
|
310
|
+
const session = await restoreSession(this._flowContext(), options);
|
|
367
311
|
|
|
368
312
|
if (session) {
|
|
369
313
|
this._session = session;
|
|
@@ -400,10 +344,10 @@ export class AuthManager {
|
|
|
400
344
|
*/
|
|
401
345
|
async connectHeadless(options?: HeadlessConnectOptions): Promise<AuthSession> {
|
|
402
346
|
let password = options?.password ?? this._defaultPassword;
|
|
347
|
+
const isFirstLaunch = await this._userAgent.firstLaunch();
|
|
403
348
|
|
|
404
349
|
// Try the password provider if no explicit password.
|
|
405
350
|
if (!password && this._passwordProvider) {
|
|
406
|
-
const isFirstLaunch = await this._userAgent.firstLaunch();
|
|
407
351
|
password = await this._passwordProvider.getPassword({
|
|
408
352
|
reason: isFirstLaunch ? 'create' : 'unlock',
|
|
409
353
|
});
|
|
@@ -416,13 +360,13 @@ export class AuthManager {
|
|
|
416
360
|
);
|
|
417
361
|
}
|
|
418
362
|
|
|
419
|
-
// Unlock the vault (initialise on first launch).
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
363
|
+
// Unlock the vault (initialise on first launch, always start).
|
|
364
|
+
await ensureVaultReady({
|
|
365
|
+
userAgent : this._userAgent,
|
|
366
|
+
emitter : this._emitter,
|
|
367
|
+
password,
|
|
368
|
+
isFirstLaunch,
|
|
369
|
+
});
|
|
426
370
|
|
|
427
371
|
// Find the active identity.
|
|
428
372
|
const identities = await this._userAgent.identity.list();
|
|
@@ -437,8 +381,7 @@ export class AuthManager {
|
|
|
437
381
|
: undefined
|
|
438
382
|
) ?? identities[0];
|
|
439
383
|
|
|
440
|
-
const connectedDid
|
|
441
|
-
const delegateDid = identity.metadata.connectedDid ? identity.did.uri : undefined;
|
|
384
|
+
const { connectedDid, delegateDid } = resolveIdentityDids(identity);
|
|
442
385
|
|
|
443
386
|
const identityInfo: IdentityInfo = {
|
|
444
387
|
didUri : connectedDid,
|
|
@@ -484,15 +427,14 @@ export class AuthManager {
|
|
|
484
427
|
const did = this._session?.did;
|
|
485
428
|
|
|
486
429
|
// 1. Stop sync.
|
|
487
|
-
|
|
488
|
-
await (this._userAgent as any).sync.stopSync(timeout);
|
|
489
|
-
}
|
|
430
|
+
await this._userAgent.sync.stopSync(timeout);
|
|
490
431
|
|
|
491
432
|
// 2. Clear the session (but keep storage markers for restore).
|
|
492
433
|
this._session = undefined;
|
|
493
434
|
|
|
494
|
-
// 3. Lock the vault
|
|
495
|
-
await this.
|
|
435
|
+
// 3. Lock the vault.
|
|
436
|
+
await this._userAgent.vault.lock();
|
|
437
|
+
this._emitter.emit('vault-locked', {});
|
|
496
438
|
|
|
497
439
|
// 4. Transition state.
|
|
498
440
|
this._setState('locked');
|
|
@@ -517,9 +459,7 @@ export class AuthManager {
|
|
|
517
459
|
|
|
518
460
|
// Stop sync.
|
|
519
461
|
if (this._session) {
|
|
520
|
-
|
|
521
|
-
await (this._userAgent as any).sync.stopSync(timeout);
|
|
522
|
-
}
|
|
462
|
+
await this._userAgent.sync.stopSync(timeout);
|
|
523
463
|
}
|
|
524
464
|
|
|
525
465
|
this._session = undefined;
|
|
@@ -591,33 +531,28 @@ export class AuthManager {
|
|
|
591
531
|
const did = this._session?.did;
|
|
592
532
|
|
|
593
533
|
// 1. Stop sync.
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
} catch {
|
|
599
|
-
// Best-effort — don't block shutdown on sync errors.
|
|
600
|
-
}
|
|
534
|
+
try {
|
|
535
|
+
await this._userAgent.sync.stopSync(timeout);
|
|
536
|
+
} catch {
|
|
537
|
+
// Best-effort — don't block shutdown on sync errors.
|
|
601
538
|
}
|
|
602
539
|
|
|
603
540
|
// 2. Clear the active session.
|
|
604
541
|
this._session = undefined;
|
|
605
542
|
|
|
606
|
-
// 3. Lock the vault
|
|
543
|
+
// 3. Lock the vault.
|
|
607
544
|
try {
|
|
608
|
-
await this.
|
|
545
|
+
await this._userAgent.vault.lock();
|
|
546
|
+
this._emitter.emit('vault-locked', {});
|
|
609
547
|
} catch {
|
|
610
548
|
// Vault may already be locked or uninitialised — safe to ignore.
|
|
611
549
|
}
|
|
612
550
|
|
|
613
551
|
// 4. Close the sync engine (releases LevelDB handles, timers).
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
} catch {
|
|
619
|
-
// Best-effort.
|
|
620
|
-
}
|
|
552
|
+
try {
|
|
553
|
+
await this._userAgent.sync.close();
|
|
554
|
+
} catch {
|
|
555
|
+
// Best-effort.
|
|
621
556
|
}
|
|
622
557
|
|
|
623
558
|
// 5. Close the storage adapter (e.g. LevelDB session store).
|
|
@@ -673,8 +608,7 @@ export class AuthManager {
|
|
|
673
608
|
throw new Error(`[@enbox/auth] Identity not found: ${didUri}`);
|
|
674
609
|
}
|
|
675
610
|
|
|
676
|
-
const connectedDid
|
|
677
|
-
const delegateDid = identity.metadata.connectedDid ? identity.did.uri : undefined;
|
|
611
|
+
const { connectedDid, delegateDid } = resolveIdentityDids(identity);
|
|
678
612
|
|
|
679
613
|
// Persist the switch.
|
|
680
614
|
await this._storage.set(STORAGE_KEYS.PREVIOUSLY_CONNECTED, 'true');
|
|
@@ -699,10 +633,7 @@ export class AuthManager {
|
|
|
699
633
|
// Already registered — safe to ignore.
|
|
700
634
|
}
|
|
701
635
|
|
|
702
|
-
|
|
703
|
-
const syncInterval = sync ?? (syncMode === 'live' ? '5m' : '2m');
|
|
704
|
-
this._userAgent.sync.startSync({ mode: syncMode, interval: syncInterval })
|
|
705
|
-
.catch((err: unknown) => console.error('[@enbox/auth] Sync failed:', err));
|
|
636
|
+
startSyncIfEnabled(this._userAgent, sync);
|
|
706
637
|
}
|
|
707
638
|
|
|
708
639
|
this._session = new AuthSession({
|
|
@@ -768,9 +699,9 @@ export class AuthManager {
|
|
|
768
699
|
|
|
769
700
|
// ─── Vault ─────────────────────────────────────────────────────
|
|
770
701
|
|
|
771
|
-
/** Access the vault
|
|
772
|
-
get vault():
|
|
773
|
-
return this.
|
|
702
|
+
/** Access the underlying identity vault for lock/unlock/backup operations. */
|
|
703
|
+
get vault(): HdIdentityVault {
|
|
704
|
+
return this._userAgent.vault;
|
|
774
705
|
}
|
|
775
706
|
|
|
776
707
|
// ─── Events ────────────────────────────────────────────────────
|
|
@@ -800,7 +731,7 @@ export class AuthManager {
|
|
|
800
731
|
|
|
801
732
|
/** Whether the vault is currently locked. */
|
|
802
733
|
get isLocked(): boolean {
|
|
803
|
-
return this.
|
|
734
|
+
return this._userAgent.vault.isLocked();
|
|
804
735
|
}
|
|
805
736
|
|
|
806
737
|
/** Whether a connection attempt is in progress. */
|
|
@@ -826,6 +757,142 @@ export class AuthManager {
|
|
|
826
757
|
|
|
827
758
|
// ─── Private helpers ───────────────────────────────────────────
|
|
828
759
|
|
|
760
|
+
/**
|
|
761
|
+
* Determine whether the given options indicate a local connect flow.
|
|
762
|
+
*
|
|
763
|
+
* Local connect is indicated by the presence of `password`,
|
|
764
|
+
* `createIdentity`, or `recoveryPhrase` — signals that the caller
|
|
765
|
+
* is managing its own vault/identity lifecycle. In non-browser
|
|
766
|
+
* environments, local connect is the fallback.
|
|
767
|
+
*/
|
|
768
|
+
private _isLocalConnect(options?: ConnectOptions): boolean {
|
|
769
|
+
const o = (options ?? {}) as Record<string, unknown>;
|
|
770
|
+
|
|
771
|
+
// If any local-connect-specific keys are present, it's definitely local.
|
|
772
|
+
const hasLocalSignals = (
|
|
773
|
+
o.password !== undefined ||
|
|
774
|
+
o.createIdentity !== undefined ||
|
|
775
|
+
o.recoveryPhrase !== undefined ||
|
|
776
|
+
o.dwnEndpoints !== undefined ||
|
|
777
|
+
o.metadata !== undefined
|
|
778
|
+
);
|
|
779
|
+
if (hasLocalSignals) { return true; }
|
|
780
|
+
|
|
781
|
+
// If any handler-connect signals are present, use the handler flow.
|
|
782
|
+
const hasHandlerSignals = (
|
|
783
|
+
o.protocols !== undefined ||
|
|
784
|
+
o.connectHandler !== undefined
|
|
785
|
+
);
|
|
786
|
+
if (hasHandlerSignals) { return false; }
|
|
787
|
+
|
|
788
|
+
// No explicit signals → default to local connect.
|
|
789
|
+
// Callers that want handler-based connect must provide protocols
|
|
790
|
+
// or a connectHandler.
|
|
791
|
+
return true;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* Run a handler-based (delegated) connect flow.
|
|
796
|
+
*
|
|
797
|
+
* 1. Initialize the vault (agent-only, no identity).
|
|
798
|
+
* 2. Normalize protocol permission requests.
|
|
799
|
+
* 3. Delegate to the connect handler for credential acquisition.
|
|
800
|
+
* 4. Import the delegate DID, process grants, set up sync.
|
|
801
|
+
* 5. Finalize and return the AuthSession.
|
|
802
|
+
*/
|
|
803
|
+
private async _handlerConnect(
|
|
804
|
+
options?: HandlerConnectOptions,
|
|
805
|
+
): Promise<AuthSession> {
|
|
806
|
+
const ctx = this._flowContext();
|
|
807
|
+
const { userAgent, emitter, storage } = ctx;
|
|
808
|
+
const sync = options?.sync ?? ctx.defaultSync;
|
|
809
|
+
|
|
810
|
+
if (sync === 'off') {
|
|
811
|
+
throw new Error(
|
|
812
|
+
'[@enbox/auth] Sync must be enabled for delegated connect. ' +
|
|
813
|
+
'Remove sync: "off" or set an interval like "15s".'
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// 1. Initialize vault (agent-only, no identity).
|
|
818
|
+
const isFirstLaunch = await userAgent.firstLaunch();
|
|
819
|
+
const password = await resolvePassword(ctx, undefined, isFirstLaunch);
|
|
820
|
+
await ensureVaultReady({ userAgent, emitter, password, isFirstLaunch });
|
|
821
|
+
|
|
822
|
+
// 2. Normalize protocol requests.
|
|
823
|
+
const permissionRequests = normalizeProtocolRequests(options?.protocols);
|
|
824
|
+
|
|
825
|
+
// 3. Resolve the handler.
|
|
826
|
+
const handler = options?.connectHandler ?? this._connectHandler;
|
|
827
|
+
if (!handler) {
|
|
828
|
+
throw new Error(
|
|
829
|
+
'[@enbox/auth] No connect handler provided. ' +
|
|
830
|
+
'Install @enbox/browser and pass BrowserConnectHandler(), ' +
|
|
831
|
+
'or provide a custom ConnectHandler.'
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// 4. Delegate to the handler.
|
|
836
|
+
const result = await handler.requestAccess({ permissionRequests });
|
|
837
|
+
if (!result) {
|
|
838
|
+
throw new Error('[@enbox/auth] Connect was denied or cancelled by the user.');
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// 5. Import delegate DID, process grants, set up sync.
|
|
842
|
+
const { delegatePortableDid, connectedDid, delegateGrants } = result;
|
|
843
|
+
const identity = await importDelegateAndSetupSync({
|
|
844
|
+
userAgent, delegatePortableDid, connectedDid, delegateGrants,
|
|
845
|
+
flowName: 'Connect',
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
// 6. Finalize session.
|
|
849
|
+
return finalizeDelegateSession({
|
|
850
|
+
userAgent, emitter, storage, identity,
|
|
851
|
+
connectedDid, delegateDid: delegatePortableDid.uri, sync,
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* Build a `FlowContext` from the manager's current state.
|
|
857
|
+
*
|
|
858
|
+
* Replaces the 5 manual inline context constructions that were
|
|
859
|
+
* previously duplicated across `connect()`, `walletConnect()`,
|
|
860
|
+
* `importFromPhrase()`, `importFromPortable()`, and `restoreSession()`.
|
|
861
|
+
*/
|
|
862
|
+
private _flowContext(): FlowContext {
|
|
863
|
+
return {
|
|
864
|
+
userAgent : this._userAgent,
|
|
865
|
+
emitter : this._emitter,
|
|
866
|
+
storage : this._storage,
|
|
867
|
+
defaultPassword : this._defaultPassword,
|
|
868
|
+
passwordProvider : this._passwordProvider,
|
|
869
|
+
defaultSync : this._defaultSync,
|
|
870
|
+
defaultDwnEndpoints : this._defaultDwnEndpoints,
|
|
871
|
+
registration : this._registration,
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* Template for connection flows that follow the guard → try/finally → setState pattern.
|
|
877
|
+
*
|
|
878
|
+
* Consolidates the duplicated concurrency guard, `_isConnecting` flag management,
|
|
879
|
+
* session assignment, and state transition across `connect()`, `walletConnect()`,
|
|
880
|
+
* `importFromPhrase()`, and `importFromPortable()`.
|
|
881
|
+
*/
|
|
882
|
+
private async _withConnect(fn: () => Promise<AuthSession>): Promise<AuthSession> {
|
|
883
|
+
this._guardConcurrency();
|
|
884
|
+
this._isConnecting = true;
|
|
885
|
+
|
|
886
|
+
try {
|
|
887
|
+
const session = await fn();
|
|
888
|
+
this._session = session;
|
|
889
|
+
this._setState('connected');
|
|
890
|
+
return session;
|
|
891
|
+
} finally {
|
|
892
|
+
this._isConnecting = false;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
829
896
|
private _setState(state: AuthState): void {
|
|
830
897
|
if (state === this._state) {return;}
|
|
831
898
|
const previous = this._state;
|