@enbox/auth 0.3.1 → 0.5.0
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 +245 -5
- package/dist/esm/auth-manager.js.map +1 -1
- package/dist/esm/flows/dwn-discovery.js +96 -81
- package/dist/esm/flows/dwn-discovery.js.map +1 -1
- package/dist/esm/flows/dwn-registration.js +49 -3
- package/dist/esm/flows/dwn-registration.js.map +1 -1
- package/dist/esm/flows/import-identity.js +2 -0
- package/dist/esm/flows/import-identity.js.map +1 -1
- package/dist/esm/flows/local-connect.js +25 -8
- package/dist/esm/flows/local-connect.js.map +1 -1
- package/dist/esm/flows/session-restore.js +20 -4
- package/dist/esm/flows/session-restore.js.map +1 -1
- package/dist/esm/flows/wallet-connect.js +5 -4
- package/dist/esm/flows/wallet-connect.js.map +1 -1
- package/dist/esm/index.js +5 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/password-provider.js +319 -0
- package/dist/esm/password-provider.js.map +1 -0
- package/dist/esm/types.js +9 -1
- package/dist/esm/types.js.map +1 -1
- package/dist/types/auth-manager.d.ts +83 -2
- package/dist/types/auth-manager.d.ts.map +1 -1
- package/dist/types/flows/dwn-discovery.d.ts +40 -53
- package/dist/types/flows/dwn-discovery.d.ts.map +1 -1
- package/dist/types/flows/dwn-registration.d.ts +20 -1
- package/dist/types/flows/dwn-registration.d.ts.map +1 -1
- package/dist/types/flows/import-identity.d.ts.map +1 -1
- package/dist/types/flows/local-connect.d.ts +2 -0
- package/dist/types/flows/local-connect.d.ts.map +1 -1
- package/dist/types/flows/session-restore.d.ts +2 -0
- package/dist/types/flows/session-restore.d.ts.map +1 -1
- package/dist/types/flows/wallet-connect.d.ts +2 -2
- package/dist/types/flows/wallet-connect.d.ts.map +1 -1
- package/dist/types/index.d.ts +5 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/password-provider.d.ts +194 -0
- package/dist/types/password-provider.d.ts.map +1 -0
- package/dist/types/types.d.ts +106 -1
- package/dist/types/types.d.ts.map +1 -1
- package/package.json +8 -9
- package/src/auth-manager.ts +284 -9
- package/src/flows/dwn-discovery.ts +99 -79
- package/src/flows/dwn-registration.ts +60 -5
- package/src/flows/import-identity.ts +2 -0
- package/src/flows/local-connect.ts +24 -3
- package/src/flows/session-restore.ts +22 -2
- package/src/flows/wallet-connect.ts +5 -4
- package/src/index.ts +10 -1
- package/src/password-provider.ts +383 -0
- package/src/types.ts +114 -1
package/dist/types/types.d.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Public types for the authentication and identity management SDK.
|
|
4
4
|
*/
|
|
5
5
|
import type { ConnectPermissionRequest, EnboxUserAgent, HdIdentityVault, LocalDwnStrategy, PortableIdentity } from '@enbox/agent';
|
|
6
|
+
import type { PasswordProvider } from './password-provider.js';
|
|
6
7
|
export type { ConnectPermissionRequest, HdIdentityVault, IdentityVaultBackup, LocalDwnStrategy, PortableIdentity } from '@enbox/agent';
|
|
7
8
|
export type { EnboxUserAgent } from '@enbox/agent';
|
|
8
9
|
/**
|
|
@@ -132,13 +133,44 @@ export interface RegistrationOptions {
|
|
|
132
133
|
* Pre-existing registration tokens from a previous session, keyed by
|
|
133
134
|
* DWN endpoint URL. If a valid (non-expired) token exists for an
|
|
134
135
|
* endpoint, it is used directly without re-running the auth flow.
|
|
136
|
+
*
|
|
137
|
+
* When {@link persistTokens} is `true`, this field is ignored —
|
|
138
|
+
* tokens are loaded automatically from the `StorageAdapter`.
|
|
135
139
|
*/
|
|
136
140
|
registrationTokens?: Record<string, RegistrationTokenData>;
|
|
137
141
|
/**
|
|
138
142
|
* Called when new or refreshed registration tokens are obtained.
|
|
139
143
|
* The app should persist these for future sessions.
|
|
144
|
+
*
|
|
145
|
+
* When {@link persistTokens} is `true`, tokens are saved automatically
|
|
146
|
+
* to the `StorageAdapter`. This callback is still invoked (if provided)
|
|
147
|
+
* **after** the automatic save, so consumers can observe token changes
|
|
148
|
+
* without handling persistence themselves.
|
|
140
149
|
*/
|
|
141
150
|
onRegistrationTokens?: (tokens: Record<string, RegistrationTokenData>) => void;
|
|
151
|
+
/**
|
|
152
|
+
* Automatically persist and restore registration tokens using the
|
|
153
|
+
* auth manager's `StorageAdapter`.
|
|
154
|
+
*
|
|
155
|
+
* When `true`, tokens are loaded from storage before registration and
|
|
156
|
+
* saved back after new or refreshed tokens are obtained. This removes
|
|
157
|
+
* the need for consumers to implement their own token I/O via
|
|
158
|
+
* {@link registrationTokens} and {@link onRegistrationTokens}.
|
|
159
|
+
*
|
|
160
|
+
* Defaults to `false` for backward compatibility.
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```ts
|
|
164
|
+
* const auth = await AuthManager.create({
|
|
165
|
+
* registration: {
|
|
166
|
+
* onSuccess: () => {},
|
|
167
|
+
* onFailure: (err) => console.error(err),
|
|
168
|
+
* persistTokens: true,
|
|
169
|
+
* },
|
|
170
|
+
* });
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
persistTokens?: boolean;
|
|
142
174
|
}
|
|
143
175
|
/** Options for {@link AuthManager.create}. */
|
|
144
176
|
export interface AuthManagerOptions {
|
|
@@ -182,8 +214,32 @@ export interface AuthManagerOptions {
|
|
|
182
214
|
/**
|
|
183
215
|
* Default password for vault operations.
|
|
184
216
|
* If not provided, an insecure default is used (with a console warning).
|
|
217
|
+
*
|
|
218
|
+
* For more flexible password acquisition (env vars, TTY prompts,
|
|
219
|
+
* chained fallbacks), use {@link passwordProvider} instead.
|
|
185
220
|
*/
|
|
186
221
|
password?: string;
|
|
222
|
+
/**
|
|
223
|
+
* A composable password provider for obtaining the vault password.
|
|
224
|
+
*
|
|
225
|
+
* When set, this provider is consulted by `connect()`,
|
|
226
|
+
* `restoreSession()`, and `connectHeadless()` whenever a password
|
|
227
|
+
* is needed and none was given explicitly. It takes precedence over
|
|
228
|
+
* the static {@link password} option.
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* ```ts
|
|
232
|
+
* import { AuthManager, PasswordProvider } from '@enbox/auth';
|
|
233
|
+
*
|
|
234
|
+
* const auth = await AuthManager.create({
|
|
235
|
+
* passwordProvider: PasswordProvider.chain([
|
|
236
|
+
* PasswordProvider.fromEnv('ENBOX_PASSWORD'),
|
|
237
|
+
* PasswordProvider.fromTty(),
|
|
238
|
+
* ]),
|
|
239
|
+
* });
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
242
|
+
passwordProvider?: PasswordProvider;
|
|
187
243
|
/**
|
|
188
244
|
* Sync interval for DWN synchronization.
|
|
189
245
|
* - `'off'` — disable sync
|
|
@@ -256,6 +312,39 @@ export interface ImportFromPortableOptions {
|
|
|
256
312
|
export interface RestoreSessionOptions {
|
|
257
313
|
/** Password to unlock the vault (needed if vault is locked). */
|
|
258
314
|
password?: string;
|
|
315
|
+
/**
|
|
316
|
+
* Called when the vault is locked and a password is required to proceed.
|
|
317
|
+
*
|
|
318
|
+
* If provided, this callback is invoked instead of falling back to the
|
|
319
|
+
* default password or the insecure static phrase. This is the recommended
|
|
320
|
+
* way to implement interactive password prompts (e.g., a PIN entry dialog
|
|
321
|
+
* or CLI prompt).
|
|
322
|
+
*
|
|
323
|
+
* @returns The password entered by the user.
|
|
324
|
+
*
|
|
325
|
+
* @example Browser PIN dialog
|
|
326
|
+
* ```ts
|
|
327
|
+
* const session = await auth.restoreSession({
|
|
328
|
+
* onPasswordRequired: async () => {
|
|
329
|
+
* return await showPinDialog();
|
|
330
|
+
* },
|
|
331
|
+
* });
|
|
332
|
+
* ```
|
|
333
|
+
*/
|
|
334
|
+
onPasswordRequired?: () => Promise<string>;
|
|
335
|
+
}
|
|
336
|
+
/** Options for {@link AuthManager.connectHeadless}. */
|
|
337
|
+
export interface HeadlessConnectOptions {
|
|
338
|
+
/** Vault password (overrides manager default). */
|
|
339
|
+
password?: string;
|
|
340
|
+
}
|
|
341
|
+
/** Options for {@link AuthManager.shutdown}. */
|
|
342
|
+
export interface ShutdownOptions {
|
|
343
|
+
/**
|
|
344
|
+
* Milliseconds to wait for pending sync operations before shutting down.
|
|
345
|
+
* Default: `2000`.
|
|
346
|
+
*/
|
|
347
|
+
timeout?: number;
|
|
259
348
|
}
|
|
260
349
|
/** Options for {@link AuthManager.disconnect}. */
|
|
261
350
|
export interface DisconnectOptions {
|
|
@@ -284,6 +373,14 @@ export interface StorageAdapter {
|
|
|
284
373
|
remove(key: string): Promise<void>;
|
|
285
374
|
/** Clear all stored data. */
|
|
286
375
|
clear(): Promise<void>;
|
|
376
|
+
/**
|
|
377
|
+
* Close the underlying storage resources (e.g. LevelDB handles).
|
|
378
|
+
*
|
|
379
|
+
* Optional — not all adapters need cleanup. Called by
|
|
380
|
+
* {@link AuthManager.shutdown} to release resources so the process
|
|
381
|
+
* can exit cleanly.
|
|
382
|
+
*/
|
|
383
|
+
close?(): Promise<void>;
|
|
287
384
|
}
|
|
288
385
|
/** The insecure default password used when none is provided. */
|
|
289
386
|
export declare const INSECURE_DEFAULT_PASSWORD = "insecure-static-phrase";
|
|
@@ -301,12 +398,20 @@ export declare const STORAGE_KEYS: {
|
|
|
301
398
|
/** The connected DID (for wallet-connected sessions). */
|
|
302
399
|
readonly CONNECTED_DID: "enbox:auth:connectedDid";
|
|
303
400
|
/**
|
|
304
|
-
* The base URL of the local DWN server discovered via the `dwn://
|
|
401
|
+
* The base URL of the local DWN server discovered via the `dwn://connect`
|
|
305
402
|
* browser redirect flow. Persisted so subsequent page loads can skip the
|
|
306
403
|
* redirect and inject the endpoint directly.
|
|
307
404
|
*
|
|
308
405
|
* @see https://github.com/enboxorg/enbox/issues/589
|
|
309
406
|
*/
|
|
310
407
|
readonly LOCAL_DWN_ENDPOINT: "enbox:auth:localDwnEndpoint";
|
|
408
|
+
/**
|
|
409
|
+
* JSON-serialised `Record<string, RegistrationTokenData>` for DWN endpoint
|
|
410
|
+
* registration tokens. Automatically loaded before registration and saved
|
|
411
|
+
* after new/refreshed tokens are obtained when `persistTokens` is enabled.
|
|
412
|
+
*
|
|
413
|
+
* @see https://github.com/enboxorg/enbox/issues/690
|
|
414
|
+
*/
|
|
415
|
+
readonly REGISTRATION_TOKENS: "enbox:auth:registrationTokens";
|
|
311
416
|
};
|
|
312
417
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,wBAAwB,EAAE,cAAc,EAAE,eAAe,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enbox/auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Headless authentication and identity management SDK for Enbox",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/esm/index.js",
|
|
7
7
|
"module": "./dist/esm/index.js",
|
|
8
8
|
"types": "./dist/types/index.d.ts",
|
|
9
9
|
"scripts": {
|
|
10
|
-
"clean": "
|
|
11
|
-
"build:esm": "
|
|
10
|
+
"clean": "rm -rf dist",
|
|
11
|
+
"build:esm": "rm -rf dist/esm dist/types && bun tsc -p tsconfig.json",
|
|
12
12
|
"build": "bun run clean && bun run build:esm",
|
|
13
13
|
"lint": "eslint . --max-warnings 0",
|
|
14
14
|
"lint:fix": "eslint . --fix",
|
|
@@ -56,16 +56,15 @@
|
|
|
56
56
|
"bun": ">=1.0.0"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@enbox/agent": "0.
|
|
59
|
+
"@enbox/agent": "0.4.0",
|
|
60
60
|
"@enbox/common": "0.0.7",
|
|
61
61
|
"@enbox/dids": "0.0.9",
|
|
62
62
|
"@enbox/dwn-clients": "0.1.0",
|
|
63
|
-
"level": "8.0.
|
|
63
|
+
"level": "8.0.1"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@types/node": "
|
|
67
|
-
"bun-types": "
|
|
68
|
-
"
|
|
69
|
-
"typescript": "5.5.4"
|
|
66
|
+
"@types/node": "22.19.15",
|
|
67
|
+
"bun-types": "1.3.10",
|
|
68
|
+
"typescript": "5.9.3"
|
|
70
69
|
}
|
|
71
70
|
}
|
package/src/auth-manager.ts
CHANGED
|
@@ -12,23 +12,28 @@ import type { BearerIdentity, PortableIdentity } from '@enbox/agent';
|
|
|
12
12
|
import { AuthEventEmitter } from './events.js';
|
|
13
13
|
import { AuthSession } from './identity-session.js';
|
|
14
14
|
import { createDefaultStorage } from './storage/storage.js';
|
|
15
|
+
import { discoverLocalDwn } from './flows/dwn-discovery.js';
|
|
15
16
|
import { localConnect } from './flows/local-connect.js';
|
|
16
17
|
import { restoreSession } from './flows/session-restore.js';
|
|
17
18
|
import { STORAGE_KEYS } from './types.js';
|
|
18
19
|
import { VaultManager } from './vault/vault-manager.js';
|
|
19
20
|
import { walletConnect } from './flows/wallet-connect.js';
|
|
21
|
+
|
|
22
|
+
import type { PasswordProvider } from './password-provider.js';
|
|
20
23
|
import type {
|
|
21
24
|
AuthEvent,
|
|
22
25
|
AuthEventHandler,
|
|
23
26
|
AuthManagerOptions,
|
|
24
27
|
AuthState,
|
|
25
28
|
DisconnectOptions,
|
|
29
|
+
HeadlessConnectOptions,
|
|
26
30
|
IdentityInfo,
|
|
27
31
|
ImportFromPhraseOptions,
|
|
28
32
|
ImportFromPortableOptions,
|
|
29
33
|
LocalConnectOptions,
|
|
30
34
|
RegistrationOptions,
|
|
31
35
|
RestoreSessionOptions,
|
|
36
|
+
ShutdownOptions,
|
|
32
37
|
StorageAdapter,
|
|
33
38
|
SyncOption,
|
|
34
39
|
WalletConnectOptions,
|
|
@@ -76,31 +81,45 @@ export class AuthManager {
|
|
|
76
81
|
private _session: AuthSession | undefined;
|
|
77
82
|
private _state: AuthState = 'uninitialized';
|
|
78
83
|
private _isConnecting = false;
|
|
84
|
+
private _isShutDown = false;
|
|
79
85
|
|
|
80
86
|
// Default options from create()
|
|
81
87
|
private _defaultPassword?: string;
|
|
88
|
+
private _passwordProvider?: PasswordProvider;
|
|
82
89
|
private _defaultSync?: SyncOption;
|
|
83
90
|
private _defaultDwnEndpoints?: string[];
|
|
84
91
|
private _registration?: RegistrationOptions;
|
|
85
92
|
|
|
93
|
+
/**
|
|
94
|
+
* The local DWN server endpoint discovered during `create()`, if any.
|
|
95
|
+
* `undefined` means no local server was found. This is set before any
|
|
96
|
+
* event listeners are attached, so consumers should check this property
|
|
97
|
+
* after `create()` returns rather than relying solely on events.
|
|
98
|
+
*/
|
|
99
|
+
private _localDwnEndpoint?: string;
|
|
100
|
+
|
|
86
101
|
private constructor(params: {
|
|
87
102
|
userAgent: EnboxUserAgent;
|
|
88
103
|
emitter: AuthEventEmitter;
|
|
89
104
|
storage: StorageAdapter;
|
|
90
105
|
vault: VaultManager;
|
|
91
106
|
defaultPassword?: string;
|
|
107
|
+
passwordProvider?: PasswordProvider;
|
|
92
108
|
defaultSync?: SyncOption;
|
|
93
109
|
defaultDwnEndpoints?: string[];
|
|
94
110
|
registration?: RegistrationOptions;
|
|
111
|
+
localDwnEndpoint?: string;
|
|
95
112
|
}) {
|
|
96
113
|
this._userAgent = params.userAgent;
|
|
97
114
|
this._emitter = params.emitter;
|
|
98
115
|
this._storage = params.storage;
|
|
99
116
|
this._vault = params.vault;
|
|
100
117
|
this._defaultPassword = params.defaultPassword;
|
|
118
|
+
this._passwordProvider = params.passwordProvider;
|
|
101
119
|
this._defaultSync = params.defaultSync;
|
|
102
120
|
this._defaultDwnEndpoints = params.defaultDwnEndpoints;
|
|
103
121
|
this._registration = params.registration;
|
|
122
|
+
this._localDwnEndpoint = params.localDwnEndpoint;
|
|
104
123
|
}
|
|
105
124
|
|
|
106
125
|
/**
|
|
@@ -117,11 +136,27 @@ export class AuthManager {
|
|
|
117
136
|
const emitter = new AuthEventEmitter();
|
|
118
137
|
const storage = options.storage ?? createDefaultStorage();
|
|
119
138
|
|
|
139
|
+
// Run local DWN discovery BEFORE creating the agent. Discovery has
|
|
140
|
+
// zero vault/DWN dependencies — it only checks the URL fragment,
|
|
141
|
+
// reads localStorage, and validates via GET /info.
|
|
142
|
+
//
|
|
143
|
+
// When a local DWN server is available, the agent is created in
|
|
144
|
+
// "remote mode": it skips creating an in-process DWN and routes all
|
|
145
|
+
// DWN operations through RPC to the local server.
|
|
146
|
+
let localDwnEndpoint: string | undefined;
|
|
147
|
+
if (!options.agent && options.localDwnStrategy !== 'off') {
|
|
148
|
+
localDwnEndpoint = await discoverLocalDwn(storage);
|
|
149
|
+
// NOTE: We intentionally do NOT emit 'local-dwn-available' here
|
|
150
|
+
// because event listeners aren't attached yet. Consumers should
|
|
151
|
+
// check `authManager.localDwnEndpoint` after create() returns.
|
|
152
|
+
}
|
|
153
|
+
|
|
120
154
|
// Use a pre-built agent or create one with the given options.
|
|
121
155
|
const userAgent = options.agent ?? await EnboxUserAgent.create({
|
|
122
156
|
dataPath : options.dataPath,
|
|
123
157
|
agentVault : options.agentVault,
|
|
124
158
|
localDwnStrategy : options.localDwnStrategy,
|
|
159
|
+
localDwnEndpoint,
|
|
125
160
|
});
|
|
126
161
|
|
|
127
162
|
const vault = new VaultManager(userAgent.vault, emitter);
|
|
@@ -132,9 +167,11 @@ export class AuthManager {
|
|
|
132
167
|
storage,
|
|
133
168
|
vault,
|
|
134
169
|
defaultPassword : options.password,
|
|
170
|
+
passwordProvider : options.passwordProvider,
|
|
135
171
|
defaultSync : options.sync,
|
|
136
172
|
defaultDwnEndpoints : options.dwnEndpoints,
|
|
137
173
|
registration : options.registration,
|
|
174
|
+
localDwnEndpoint,
|
|
138
175
|
});
|
|
139
176
|
|
|
140
177
|
// Determine initial state.
|
|
@@ -170,6 +207,7 @@ export class AuthManager {
|
|
|
170
207
|
emitter : this._emitter,
|
|
171
208
|
storage : this._storage,
|
|
172
209
|
defaultPassword : this._defaultPassword,
|
|
210
|
+
passwordProvider : this._passwordProvider,
|
|
173
211
|
defaultSync : this._defaultSync,
|
|
174
212
|
defaultDwnEndpoints : this._defaultDwnEndpoints,
|
|
175
213
|
registration : this._registration,
|
|
@@ -186,7 +224,7 @@ export class AuthManager {
|
|
|
186
224
|
}
|
|
187
225
|
|
|
188
226
|
/**
|
|
189
|
-
* Connect to an external wallet via the
|
|
227
|
+
* Connect to an external wallet via the Enbox Connect relay protocol.
|
|
190
228
|
*
|
|
191
229
|
* This runs the full WalletConnect flow: generates a URI for QR display,
|
|
192
230
|
* validates the PIN, imports the delegated DID, and processes grants.
|
|
@@ -202,8 +240,22 @@ export class AuthManager {
|
|
|
202
240
|
|
|
203
241
|
try {
|
|
204
242
|
// Ensure the agent is initialized and started before wallet connect.
|
|
205
|
-
const
|
|
206
|
-
|
|
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) {
|
|
207
259
|
await this._userAgent.initialize({ password });
|
|
208
260
|
}
|
|
209
261
|
await this._userAgent.start({ password });
|
|
@@ -303,11 +355,12 @@ export class AuthManager {
|
|
|
303
355
|
try {
|
|
304
356
|
const session = await restoreSession(
|
|
305
357
|
{
|
|
306
|
-
userAgent
|
|
307
|
-
emitter
|
|
308
|
-
storage
|
|
309
|
-
defaultPassword
|
|
310
|
-
|
|
358
|
+
userAgent : this._userAgent,
|
|
359
|
+
emitter : this._emitter,
|
|
360
|
+
storage : this._storage,
|
|
361
|
+
defaultPassword : this._defaultPassword,
|
|
362
|
+
passwordProvider : this._passwordProvider,
|
|
363
|
+
defaultSync : this._defaultSync,
|
|
311
364
|
},
|
|
312
365
|
options,
|
|
313
366
|
);
|
|
@@ -322,6 +375,90 @@ export class AuthManager {
|
|
|
322
375
|
}
|
|
323
376
|
}
|
|
324
377
|
|
|
378
|
+
/**
|
|
379
|
+
* Lightweight vault unlock for one-shot utilities and subprocesses.
|
|
380
|
+
*
|
|
381
|
+
* Unlocks the vault and retrieves the active (or first available)
|
|
382
|
+
* identity **without** starting sync, DWN registration, or persisting
|
|
383
|
+
* session markers. This is the recommended replacement for calling
|
|
384
|
+
* `agent.start({ password })` directly.
|
|
385
|
+
*
|
|
386
|
+
* Typical use cases:
|
|
387
|
+
* - Git credential helpers that need to sign a token and exit
|
|
388
|
+
* - CLI utilities that perform a single operation
|
|
389
|
+
* - Any subprocess that shares a data directory with a long-running daemon
|
|
390
|
+
*
|
|
391
|
+
* @param options - Optional password override.
|
|
392
|
+
* @returns An active AuthSession (with sync disabled).
|
|
393
|
+
*
|
|
394
|
+
* @example
|
|
395
|
+
* ```ts
|
|
396
|
+
* const session = await auth.connectHeadless({ password });
|
|
397
|
+
* const did = session.did; // ready to use
|
|
398
|
+
* await auth.shutdown(); // clean exit
|
|
399
|
+
* ```
|
|
400
|
+
*/
|
|
401
|
+
async connectHeadless(options?: HeadlessConnectOptions): Promise<AuthSession> {
|
|
402
|
+
let password = options?.password ?? this._defaultPassword;
|
|
403
|
+
|
|
404
|
+
// Try the password provider if no explicit password.
|
|
405
|
+
if (!password && this._passwordProvider) {
|
|
406
|
+
const isFirstLaunch = await this._userAgent.firstLaunch();
|
|
407
|
+
password = await this._passwordProvider.getPassword({
|
|
408
|
+
reason: isFirstLaunch ? 'create' : 'unlock',
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (!password) {
|
|
413
|
+
throw new Error(
|
|
414
|
+
'[@enbox/auth] connectHeadless() requires a password. ' +
|
|
415
|
+
'Provide one via options.password, a passwordProvider, or the AuthManager default.'
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Unlock the vault (initialise on first launch).
|
|
420
|
+
if (await this._userAgent.firstLaunch()) {
|
|
421
|
+
await this._userAgent.initialize({ password });
|
|
422
|
+
} else {
|
|
423
|
+
await this._userAgent.start({ password });
|
|
424
|
+
}
|
|
425
|
+
this._emitter.emit('vault-unlocked', {});
|
|
426
|
+
|
|
427
|
+
// Find the active identity.
|
|
428
|
+
const identities = await this._userAgent.identity.list();
|
|
429
|
+
if (identities.length === 0) {
|
|
430
|
+
throw new Error('[@enbox/auth] No identities found in vault.');
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Prefer the previously-active identity, fall back to first.
|
|
434
|
+
const savedDid = await this._storage.get(STORAGE_KEYS.ACTIVE_IDENTITY);
|
|
435
|
+
const identity = (savedDid
|
|
436
|
+
? identities.find(id => id.did.uri === savedDid || id.metadata.connectedDid === savedDid)
|
|
437
|
+
: undefined
|
|
438
|
+
) ?? identities[0];
|
|
439
|
+
|
|
440
|
+
const connectedDid = identity.metadata.connectedDid ?? identity.did.uri;
|
|
441
|
+
const delegateDid = identity.metadata.connectedDid ? identity.did.uri : undefined;
|
|
442
|
+
|
|
443
|
+
const identityInfo: IdentityInfo = {
|
|
444
|
+
didUri : connectedDid,
|
|
445
|
+
name : identity.metadata.name,
|
|
446
|
+
connectedDid : identity.metadata.connectedDid,
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
// No sync, no registration, no session persistence markers.
|
|
450
|
+
this._session = new AuthSession({
|
|
451
|
+
agent : this._userAgent,
|
|
452
|
+
did : connectedDid,
|
|
453
|
+
delegateDid,
|
|
454
|
+
identity : identityInfo,
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
this._setState('connected');
|
|
458
|
+
|
|
459
|
+
return this._session;
|
|
460
|
+
}
|
|
461
|
+
|
|
325
462
|
// ─── Session management ────────────────────────────────────────
|
|
326
463
|
|
|
327
464
|
/** The current active session, or `undefined` if not connected. */
|
|
@@ -329,6 +466,43 @@ export class AuthManager {
|
|
|
329
466
|
return this._session;
|
|
330
467
|
}
|
|
331
468
|
|
|
469
|
+
/**
|
|
470
|
+
* Lock the auth manager.
|
|
471
|
+
*
|
|
472
|
+
* Stops sync, clears the active session, and locks the underlying vault
|
|
473
|
+
* so the password must be provided again to resume. Session storage
|
|
474
|
+
* markers are preserved so {@link restoreSession} can reconnect after
|
|
475
|
+
* the vault is unlocked again.
|
|
476
|
+
*
|
|
477
|
+
* After locking, the state transitions to `'locked'`.
|
|
478
|
+
*
|
|
479
|
+
* @param options - Optional lock configuration.
|
|
480
|
+
* @param options.timeout - Milliseconds to wait for sync to stop. Default: `2000`.
|
|
481
|
+
*/
|
|
482
|
+
async lock(options: { timeout?: number } = {}): Promise<void> {
|
|
483
|
+
const { timeout = 2000 } = options;
|
|
484
|
+
const did = this._session?.did;
|
|
485
|
+
|
|
486
|
+
// 1. Stop sync.
|
|
487
|
+
if ('sync' in this._userAgent && typeof (this._userAgent as any).sync?.stopSync === 'function') {
|
|
488
|
+
await (this._userAgent as any).sync.stopSync(timeout);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// 2. Clear the session (but keep storage markers for restore).
|
|
492
|
+
this._session = undefined;
|
|
493
|
+
|
|
494
|
+
// 3. Lock the vault (also emits 'vault-locked').
|
|
495
|
+
await this._vault.lock();
|
|
496
|
+
|
|
497
|
+
// 4. Transition state.
|
|
498
|
+
this._setState('locked');
|
|
499
|
+
|
|
500
|
+
// 5. Emit session-end if there was an active session.
|
|
501
|
+
if (did) {
|
|
502
|
+
this._emitter.emit('session-end', { did });
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
332
506
|
/**
|
|
333
507
|
* Disconnect the current session.
|
|
334
508
|
*
|
|
@@ -385,6 +559,86 @@ export class AuthManager {
|
|
|
385
559
|
}
|
|
386
560
|
}
|
|
387
561
|
|
|
562
|
+
/**
|
|
563
|
+
* Gracefully shut down the auth manager, releasing all resources.
|
|
564
|
+
*
|
|
565
|
+
* This goes beyond {@link disconnect} or {@link lock}: it stops sync,
|
|
566
|
+
* clears the active session, locks the vault, and **closes** the
|
|
567
|
+
* underlying storage handles (e.g. LevelDB) so the process can exit
|
|
568
|
+
* without dangling timers or open file descriptors.
|
|
569
|
+
*
|
|
570
|
+
* After calling `shutdown()`, the `AuthManager` instance should not be
|
|
571
|
+
* reused — create a new one via {@link AuthManager.create} if needed.
|
|
572
|
+
*
|
|
573
|
+
* Idempotent: calling `shutdown()` more than once is safe.
|
|
574
|
+
*
|
|
575
|
+
* @param options - Optional shutdown configuration.
|
|
576
|
+
* @param options.timeout - Milliseconds to wait for sync to stop. Default: `2000`.
|
|
577
|
+
*
|
|
578
|
+
* @example
|
|
579
|
+
* ```ts
|
|
580
|
+
* const session = await auth.connectHeadless({ password });
|
|
581
|
+
* // ... perform work ...
|
|
582
|
+
* await auth.shutdown(); // clean exit, no process.exit() needed
|
|
583
|
+
* ```
|
|
584
|
+
*/
|
|
585
|
+
async shutdown(options: ShutdownOptions = {}): Promise<void> {
|
|
586
|
+
if (this._isShutDown) {
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const { timeout = 2000 } = options;
|
|
591
|
+
const did = this._session?.did;
|
|
592
|
+
|
|
593
|
+
// 1. Stop sync.
|
|
594
|
+
if ('sync' in this._userAgent &&
|
|
595
|
+
typeof (this._userAgent as any).sync?.stopSync === 'function') {
|
|
596
|
+
try {
|
|
597
|
+
await (this._userAgent as any).sync.stopSync(timeout);
|
|
598
|
+
} catch {
|
|
599
|
+
// Best-effort — don't block shutdown on sync errors.
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// 2. Clear the active session.
|
|
604
|
+
this._session = undefined;
|
|
605
|
+
|
|
606
|
+
// 3. Lock the vault (emits 'vault-locked').
|
|
607
|
+
try {
|
|
608
|
+
await this._vault.lock();
|
|
609
|
+
} catch {
|
|
610
|
+
// Vault may already be locked or uninitialised — safe to ignore.
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// 4. Close the sync engine (releases LevelDB handles, timers).
|
|
614
|
+
if ('sync' in this._userAgent &&
|
|
615
|
+
typeof (this._userAgent as any).sync?.close === 'function') {
|
|
616
|
+
try {
|
|
617
|
+
await (this._userAgent as any).sync.close();
|
|
618
|
+
} catch {
|
|
619
|
+
// Best-effort.
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// 5. Close the storage adapter (e.g. LevelDB session store).
|
|
624
|
+
if (typeof this._storage.close === 'function') {
|
|
625
|
+
try {
|
|
626
|
+
await this._storage.close();
|
|
627
|
+
} catch {
|
|
628
|
+
// Best-effort.
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// 6. Mark as shut down and transition state.
|
|
633
|
+
this._isShutDown = true;
|
|
634
|
+
this._setState('locked');
|
|
635
|
+
|
|
636
|
+
// 7. Emit session-end if there was an active session.
|
|
637
|
+
if (did) {
|
|
638
|
+
this._emitter.emit('session-end', { did });
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
388
642
|
// ─── Multi-identity ────────────────────────────────────────────
|
|
389
643
|
|
|
390
644
|
/**
|
|
@@ -432,9 +686,19 @@ export class AuthManager {
|
|
|
432
686
|
connectedDid : identity.metadata.connectedDid,
|
|
433
687
|
};
|
|
434
688
|
|
|
435
|
-
//
|
|
689
|
+
// Register the identity for sync and restart sync.
|
|
436
690
|
const sync = this._defaultSync;
|
|
437
691
|
if (sync !== 'off') {
|
|
692
|
+
// Register the identity for sync (idempotent — ignores "already registered" errors).
|
|
693
|
+
try {
|
|
694
|
+
await this._userAgent.sync.registerIdentity({
|
|
695
|
+
did : connectedDid,
|
|
696
|
+
options : { delegateDid, protocols: [] },
|
|
697
|
+
});
|
|
698
|
+
} catch {
|
|
699
|
+
// Already registered — safe to ignore.
|
|
700
|
+
}
|
|
701
|
+
|
|
438
702
|
const syncMode = sync === undefined ? 'live' : 'poll';
|
|
439
703
|
const syncInterval = sync ?? (syncMode === 'live' ? '5m' : '2m');
|
|
440
704
|
this._userAgent.sync.startSync({ mode: syncMode, interval: syncInterval })
|
|
@@ -549,6 +813,17 @@ export class AuthManager {
|
|
|
549
813
|
return this._userAgent;
|
|
550
814
|
}
|
|
551
815
|
|
|
816
|
+
/**
|
|
817
|
+
* The local DWN server endpoint discovered during `create()`, if any.
|
|
818
|
+
*
|
|
819
|
+
* When set, the agent is operating in remote mode (no in-process DWN).
|
|
820
|
+
* This property is available immediately after `create()` returns,
|
|
821
|
+
* before any event listeners are attached.
|
|
822
|
+
*/
|
|
823
|
+
get localDwnEndpoint(): string | undefined {
|
|
824
|
+
return this._localDwnEndpoint;
|
|
825
|
+
}
|
|
826
|
+
|
|
552
827
|
// ─── Private helpers ───────────────────────────────────────────
|
|
553
828
|
|
|
554
829
|
private _setState(state: AuthState): void {
|