@enbox/agent 0.2.2 → 0.3.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/browser.mjs +9 -9
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/agent-did-resolver-cache.js.map +1 -1
- package/dist/esm/anonymous-dwn-api.js +1 -1
- package/dist/esm/bearer-identity.js +1 -1
- package/dist/esm/connect.js +3 -3
- package/dist/esm/connect.js.map +1 -1
- package/dist/esm/did-api.js +3 -3
- package/dist/esm/did-api.js.map +1 -1
- package/dist/esm/dwn-api.js +39 -8
- package/dist/esm/dwn-api.js.map +1 -1
- package/dist/esm/dwn-discovery-file.js +244 -0
- package/dist/esm/dwn-discovery-file.js.map +1 -0
- package/dist/esm/dwn-discovery-payload.js +253 -0
- package/dist/esm/dwn-discovery-payload.js.map +1 -0
- package/dist/esm/dwn-encryption.js.map +1 -1
- package/dist/esm/dwn-key-delivery.js.map +1 -1
- package/dist/esm/dwn-record-upgrade.js.map +1 -1
- package/dist/esm/{web5-user-agent.js → enbox-user-agent.js} +12 -7
- package/dist/esm/enbox-user-agent.js.map +1 -0
- package/dist/esm/identity-api.js +3 -3
- package/dist/esm/identity-api.js.map +1 -1
- package/dist/esm/index.js +3 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/local-dwn.js +150 -26
- package/dist/esm/local-dwn.js.map +1 -1
- package/dist/esm/local-key-manager.js +2 -2
- package/dist/esm/local-key-manager.js.map +1 -1
- package/dist/esm/oidc.js +11 -11
- package/dist/esm/oidc.js.map +1 -1
- package/dist/esm/permissions-api.js.map +1 -1
- package/dist/esm/store-data.js.map +1 -1
- package/dist/esm/sync-api.js +2 -2
- package/dist/esm/sync-api.js.map +1 -1
- package/dist/esm/sync-engine-level.js +2 -2
- package/dist/esm/sync-engine-level.js.map +1 -1
- package/dist/esm/test-harness.js +3 -3
- package/dist/esm/test-harness.js.map +1 -1
- package/dist/esm/utils-internal.js +2 -2
- package/dist/types/agent-did-resolver-cache.d.ts +7 -7
- package/dist/types/agent-did-resolver-cache.d.ts.map +1 -1
- package/dist/types/anonymous-dwn-api.d.ts +3 -3
- package/dist/types/anonymous-dwn-api.d.ts.map +1 -1
- package/dist/types/bearer-identity.d.ts +1 -1
- package/dist/types/connect.d.ts +8 -8
- package/dist/types/connect.d.ts.map +1 -1
- package/dist/types/did-api.d.ts +12 -11
- package/dist/types/did-api.d.ts.map +1 -1
- package/dist/types/dwn-api.d.ts +27 -11
- package/dist/types/dwn-api.d.ts.map +1 -1
- package/dist/types/dwn-discovery-file.d.ts +122 -0
- package/dist/types/dwn-discovery-file.d.ts.map +1 -0
- package/dist/types/dwn-discovery-payload.d.ts +105 -0
- package/dist/types/dwn-discovery-payload.d.ts.map +1 -0
- package/dist/types/dwn-encryption.d.ts +8 -8
- package/dist/types/dwn-encryption.d.ts.map +1 -1
- package/dist/types/dwn-key-delivery.d.ts +5 -5
- package/dist/types/dwn-key-delivery.d.ts.map +1 -1
- package/dist/types/dwn-record-upgrade.d.ts +2 -2
- package/dist/types/dwn-record-upgrade.d.ts.map +1 -1
- package/dist/types/{web5-user-agent.d.ts → enbox-user-agent.d.ts} +17 -13
- package/dist/types/enbox-user-agent.d.ts.map +1 -0
- package/dist/types/identity-api.d.ts +10 -10
- package/dist/types/identity-api.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/local-dwn.d.ts +93 -15
- package/dist/types/local-dwn.d.ts.map +1 -1
- package/dist/types/local-key-manager.d.ts +9 -9
- package/dist/types/local-key-manager.d.ts.map +1 -1
- package/dist/types/oidc.d.ts +23 -19
- package/dist/types/oidc.d.ts.map +1 -1
- package/dist/types/permissions-api.d.ts +4 -4
- package/dist/types/permissions-api.d.ts.map +1 -1
- package/dist/types/store-data.d.ts +3 -3
- package/dist/types/store-data.d.ts.map +1 -1
- package/dist/types/store-did.d.ts +2 -2
- package/dist/types/store-did.d.ts.map +1 -1
- package/dist/types/store-identity.d.ts +2 -2
- package/dist/types/store-identity.d.ts.map +1 -1
- package/dist/types/store-key.d.ts +2 -2
- package/dist/types/store-key.d.ts.map +1 -1
- package/dist/types/sync-api.d.ts +9 -9
- package/dist/types/sync-api.d.ts.map +1 -1
- package/dist/types/sync-engine-level.d.ts +9 -9
- package/dist/types/sync-engine-level.d.ts.map +1 -1
- package/dist/types/sync-messages.d.ts +5 -5
- package/dist/types/sync-messages.d.ts.map +1 -1
- package/dist/types/test-harness.d.ts +4 -4
- package/dist/types/test-harness.d.ts.map +1 -1
- package/dist/types/types/agent.d.ts +24 -19
- package/dist/types/types/agent.d.ts.map +1 -1
- package/dist/types/types/identity.d.ts +1 -1
- package/dist/types/types/key-manager.d.ts +2 -2
- package/dist/types/types/key-manager.d.ts.map +1 -1
- package/dist/types/types/sync.d.ts +2 -2
- package/dist/types/types/sync.d.ts.map +1 -1
- package/dist/types/utils-internal.d.ts +4 -4
- package/dist/types/utils-internal.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/agent-did-resolver-cache.ts +8 -8
- package/src/anonymous-dwn-api.ts +4 -4
- package/src/bearer-identity.ts +1 -1
- package/src/connect.ts +12 -12
- package/src/did-api.ts +13 -11
- package/src/dwn-api.ts +61 -16
- package/src/dwn-discovery-file.ts +305 -0
- package/src/dwn-discovery-payload.ts +308 -0
- package/src/dwn-encryption.ts +8 -8
- package/src/dwn-key-delivery.ts +5 -5
- package/src/dwn-record-upgrade.ts +2 -2
- package/src/{web5-user-agent.ts → enbox-user-agent.ts} +26 -16
- package/src/identity-api.ts +11 -11
- package/src/index.ts +3 -1
- package/src/local-dwn.ts +154 -28
- package/src/local-key-manager.ts +10 -10
- package/src/oidc.ts +40 -30
- package/src/permissions-api.ts +5 -5
- package/src/store-data.ts +7 -7
- package/src/store-did.ts +2 -2
- package/src/store-identity.ts +2 -2
- package/src/store-key.ts +2 -2
- package/src/sync-api.ts +10 -10
- package/src/sync-engine-level.ts +12 -12
- package/src/sync-messages.ts +5 -5
- package/src/test-harness.ts +9 -9
- package/src/types/agent.ts +31 -20
- package/src/types/identity.ts +1 -1
- package/src/types/key-manager.ts +2 -2
- package/src/types/sync.ts +2 -2
- package/src/utils-internal.ts +4 -4
- package/dist/esm/web5-user-agent.js.map +0 -1
- package/dist/types/web5-user-agent.d.ts.map +0 -1
package/src/local-dwn.ts
CHANGED
|
@@ -1,24 +1,49 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Local DWN discovery —
|
|
3
|
-
*
|
|
2
|
+
* Local DWN discovery — discovers a running `@enbox/dwn-server` instance
|
|
3
|
+
* so the agent can route traffic to it.
|
|
4
4
|
*
|
|
5
|
+
* Discovery channels (tried in order):
|
|
6
|
+
* 1. **In-memory cache** — serves a recent positive or negative result.
|
|
7
|
+
* 2. **Discovery file** (`~/.enbox/dwn.json`) — written by `electrobun-dwn`
|
|
8
|
+
* on startup. Fast filesystem read, no network. Available for CLI and
|
|
9
|
+
* native apps; skipped in browsers.
|
|
10
|
+
* 3. **Port probing** (fallback) — sequential HTTP `GET /info` on well-known
|
|
11
|
+
* localhost ports. Works everywhere but is slower.
|
|
12
|
+
*
|
|
13
|
+
* @see https://github.com/enboxorg/enbox/issues/585
|
|
5
14
|
* @module
|
|
6
15
|
*/
|
|
7
16
|
|
|
8
|
-
import type {
|
|
17
|
+
import type { EnboxRpc } from '@enbox/dwn-clients';
|
|
18
|
+
|
|
19
|
+
import type { DwnDiscoveryFile } from './dwn-discovery-file.js';
|
|
9
20
|
|
|
10
|
-
/**
|
|
11
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Well-known ports the local DWN desktop app may bind to.
|
|
23
|
+
*
|
|
24
|
+
* Per the DWN Transport Spec, clients probe ports `55500` through `55509`
|
|
25
|
+
* (inclusive). Port `3000` is included as a development convenience.
|
|
26
|
+
*
|
|
27
|
+
* @see https://identity.foundation/dwn-transport/#port-probing
|
|
28
|
+
*/
|
|
29
|
+
export const localDwnPortCandidates = [3000, 55500, 55501, 55502, 55503, 55504, 55505, 55506, 55507, 55508, 55509] as const;
|
|
12
30
|
|
|
13
|
-
/**
|
|
14
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Hosts probed when discovering a local DWN server.
|
|
33
|
+
*
|
|
34
|
+
* Per the DWN Transport Spec, clients MUST use `127.0.0.1` rather than
|
|
35
|
+
* `localhost` to avoid DNS resolution ambiguity.
|
|
36
|
+
*
|
|
37
|
+
* @see https://identity.foundation/dwn-transport/#port-probing
|
|
38
|
+
*/
|
|
39
|
+
export const localDwnHostCandidates = ['127.0.0.1'] as const;
|
|
15
40
|
|
|
16
41
|
/**
|
|
17
42
|
* Controls how the agent discovers and routes to a local DWN server.
|
|
18
43
|
*
|
|
44
|
+
* - `'off'` — (default) skip local discovery entirely.
|
|
19
45
|
* - `'prefer'` — probe localhost first; fall back to DID-document endpoints.
|
|
20
46
|
* - `'only'` — require a local server; throw if none is found.
|
|
21
|
-
* - `'off'` — skip local discovery entirely.
|
|
22
47
|
*/
|
|
23
48
|
export type LocalDwnStrategy = 'prefer' | 'only' | 'off';
|
|
24
49
|
|
|
@@ -26,31 +51,51 @@ export type LocalDwnStrategy = 'prefer' | 'only' | 'off';
|
|
|
26
51
|
export const localDwnServerName = '@enbox/dwn-server';
|
|
27
52
|
|
|
28
53
|
/** Strips a trailing slash from a URL so endpoint comparisons are consistent. */
|
|
29
|
-
function normalizeBaseUrl(url: string): string {
|
|
54
|
+
export function normalizeBaseUrl(url: string): string {
|
|
30
55
|
return url.endsWith('/') ? url.slice(0, -1) : url;
|
|
31
56
|
}
|
|
32
57
|
|
|
33
58
|
/**
|
|
34
|
-
*
|
|
59
|
+
* Discovers a running local DWN server.
|
|
35
60
|
*
|
|
36
61
|
* Results are cached for {@link _cacheTtlMs} milliseconds (default 10 s) to
|
|
37
|
-
* avoid repeated
|
|
62
|
+
* avoid repeated I/O on hot paths such as sync.
|
|
63
|
+
*
|
|
64
|
+
* @example Discovery with file-based channel
|
|
65
|
+
* ```ts
|
|
66
|
+
* import { DwnDiscoveryFile } from './dwn-discovery-file.js';
|
|
67
|
+
*
|
|
68
|
+
* const discoveryFile = new DwnDiscoveryFile();
|
|
69
|
+
* const discovery = new LocalDwnDiscovery(rpcClient, 10_000, discoveryFile);
|
|
70
|
+
* const endpoint = await discovery.getEndpoint();
|
|
71
|
+
* ```
|
|
38
72
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
73
|
+
* @example Browser: inject cached endpoint from `dwn://register` redirect
|
|
74
|
+
* ```ts
|
|
75
|
+
* const discovery = new LocalDwnDiscovery(rpcClient);
|
|
76
|
+
* discovery.setCachedEndpoint('http://127.0.0.1:55557');
|
|
77
|
+
* ```
|
|
41
78
|
*/
|
|
42
79
|
export class LocalDwnDiscovery {
|
|
43
80
|
private _cachedEndpoint?: string;
|
|
44
81
|
private _cacheExpiry = 0;
|
|
45
82
|
|
|
46
83
|
constructor(
|
|
47
|
-
private _rpcClient:
|
|
48
|
-
private _cacheTtlMs = 10_000
|
|
84
|
+
private _rpcClient: EnboxRpc,
|
|
85
|
+
private _cacheTtlMs = 10_000,
|
|
86
|
+
private _discoveryFile?: DwnDiscoveryFile,
|
|
49
87
|
) {}
|
|
50
88
|
|
|
51
89
|
/**
|
|
52
|
-
* Returns the base URL of a local DWN server, or `undefined` if none
|
|
53
|
-
*
|
|
90
|
+
* Returns the base URL of a local DWN server, or `undefined` if none
|
|
91
|
+
* is discoverable.
|
|
92
|
+
*
|
|
93
|
+
* The discovery order is:
|
|
94
|
+
* 1. In-memory cache (if not expired).
|
|
95
|
+
* 2. `~/.enbox/dwn.json` discovery file (if a {@link DwnDiscoveryFile}
|
|
96
|
+
* was provided). The endpoint from the file is validated via
|
|
97
|
+
* `GET /info` to ensure the server is still running.
|
|
98
|
+
* 3. Sequential port probing on well-known localhost ports (fallback).
|
|
54
99
|
*/
|
|
55
100
|
public async getEndpoint(): Promise<string | undefined> {
|
|
56
101
|
const now = Date.now();
|
|
@@ -58,24 +103,105 @@ export class LocalDwnDiscovery {
|
|
|
58
103
|
return this._cachedEndpoint;
|
|
59
104
|
}
|
|
60
105
|
|
|
106
|
+
// Channel 1: file-based discovery.
|
|
107
|
+
const fileEndpoint = await this._tryDiscoveryFile();
|
|
108
|
+
if (fileEndpoint !== undefined) {
|
|
109
|
+
this._setCacheEntry(fileEndpoint, now);
|
|
110
|
+
return fileEndpoint;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Channel 2: sequential port probing (fallback).
|
|
114
|
+
const probeEndpoint = await this._probePortCandidates();
|
|
115
|
+
// Cache both positive and negative results.
|
|
116
|
+
this._setCacheEntry(probeEndpoint, now);
|
|
117
|
+
return probeEndpoint;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Inject a cached endpoint (e.g. from a `dwn://register` browser redirect
|
|
122
|
+
* or from `localStorage`). The endpoint is validated via `GET /info` before
|
|
123
|
+
* caching.
|
|
124
|
+
*
|
|
125
|
+
* @returns `true` if the endpoint was validated and cached, `false` otherwise.
|
|
126
|
+
*/
|
|
127
|
+
public async setCachedEndpoint(endpoint: string): Promise<boolean> {
|
|
128
|
+
const normalized = normalizeBaseUrl(endpoint);
|
|
129
|
+
const valid = await this._validateEndpoint(normalized);
|
|
130
|
+
if (valid) {
|
|
131
|
+
this._setCacheEntry(normalized, Date.now());
|
|
132
|
+
}
|
|
133
|
+
return valid;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Clear the in-memory cache, forcing the next {@link getEndpoint} call
|
|
138
|
+
* to perform a fresh discovery.
|
|
139
|
+
*/
|
|
140
|
+
public clearCache(): void {
|
|
141
|
+
this._cachedEndpoint = undefined;
|
|
142
|
+
this._cacheExpiry = 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ─── Private discovery channels ────────────────────────────────
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Try the `~/.enbox/dwn.json` discovery file. Returns the endpoint if
|
|
149
|
+
* the file exists, is valid, and the endpoint passes `GET /info`
|
|
150
|
+
* validation. Returns `undefined` otherwise.
|
|
151
|
+
*/
|
|
152
|
+
private async _tryDiscoveryFile(): Promise<string | undefined> {
|
|
153
|
+
if (!this._discoveryFile) {
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
const record = await this._discoveryFile.read();
|
|
159
|
+
if (!record) {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Validate that the server is actually alive and is ours.
|
|
164
|
+
const valid = await this._validateEndpoint(record.endpoint);
|
|
165
|
+
return valid ? record.endpoint : undefined;
|
|
166
|
+
} catch {
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Sequential HTTP probe on well-known localhost port candidates.
|
|
173
|
+
* Returns the first endpoint whose `GET /info` response identifies
|
|
174
|
+
* as `@enbox/dwn-server`, or `undefined` if none is found.
|
|
175
|
+
*/
|
|
176
|
+
private async _probePortCandidates(): Promise<string | undefined> {
|
|
61
177
|
for (const port of localDwnPortCandidates) {
|
|
62
178
|
for (const host of localDwnHostCandidates) {
|
|
63
179
|
const endpoint = `http://${host}:${port}`;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
this._cachedEndpoint = normalizeBaseUrl(endpoint);
|
|
68
|
-
this._cacheExpiry = now + this._cacheTtlMs;
|
|
69
|
-
return this._cachedEndpoint;
|
|
70
|
-
}
|
|
71
|
-
} catch {
|
|
72
|
-
// keep probing candidate endpoints
|
|
180
|
+
const valid = await this._validateEndpoint(endpoint);
|
|
181
|
+
if (valid) {
|
|
182
|
+
return normalizeBaseUrl(endpoint);
|
|
73
183
|
}
|
|
74
184
|
}
|
|
75
185
|
}
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
76
188
|
|
|
77
|
-
|
|
189
|
+
/**
|
|
190
|
+
* Call `GET /info` on the endpoint and check that
|
|
191
|
+
* `serverInfo.server === '@enbox/dwn-server'`.
|
|
192
|
+
*/
|
|
193
|
+
private async _validateEndpoint(endpoint: string): Promise<boolean> {
|
|
194
|
+
try {
|
|
195
|
+
const serverInfo = await this._rpcClient.getServerInfo(endpoint);
|
|
196
|
+
return serverInfo.server === localDwnServerName;
|
|
197
|
+
} catch {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Update the in-memory cache entry. */
|
|
203
|
+
private _setCacheEntry(endpoint: string | undefined, now: number): void {
|
|
204
|
+
this._cachedEndpoint = endpoint;
|
|
78
205
|
this._cacheExpiry = now + this._cacheTtlMs;
|
|
79
|
-
return undefined;
|
|
80
206
|
}
|
|
81
207
|
}
|
package/src/local-key-manager.ts
CHANGED
|
@@ -48,7 +48,7 @@ import { Encryption, HdKey } from '@enbox/dwn-sdk-js';
|
|
|
48
48
|
|
|
49
49
|
import type { AgentDataStore } from './store-data.js';
|
|
50
50
|
import type { AgentKeyManager } from './types/key-manager.js';
|
|
51
|
-
import type {
|
|
51
|
+
import type { EnboxPlatformAgent } from './types/agent.js';
|
|
52
52
|
|
|
53
53
|
import { InMemoryKeyStore } from './store-key.js';
|
|
54
54
|
|
|
@@ -121,7 +121,7 @@ type SupportedKeyGeneratorAlgorithm =
|
|
|
121
121
|
* the application exits.
|
|
122
122
|
*/
|
|
123
123
|
export type LocalKmsParams = {
|
|
124
|
-
agent?:
|
|
124
|
+
agent?: EnboxPlatformAgent;
|
|
125
125
|
|
|
126
126
|
/**
|
|
127
127
|
* An optional property to specify a custom {@link AgentDataStore} instance for key management. If
|
|
@@ -164,12 +164,12 @@ export interface LocalKmsUnwrapKeyParams extends KmsUriUnwrapKeyParams {
|
|
|
164
164
|
|
|
165
165
|
export class LocalKeyManager implements AgentKeyManager {
|
|
166
166
|
/**
|
|
167
|
-
* Holds the instance of a `
|
|
168
|
-
* the `LocalKeyManager`. This agent is used to interact with other
|
|
167
|
+
* Holds the instance of a `EnboxPlatformAgent` that represents the current execution context for
|
|
168
|
+
* the `LocalKeyManager`. This agent is used to interact with other Enbox agent components. It's
|
|
169
169
|
* vital to ensure this instance is set to correctly contextualize operations within the broader
|
|
170
|
-
*
|
|
170
|
+
* Enbox Agent framework.
|
|
171
171
|
*/
|
|
172
|
-
private _agent?:
|
|
172
|
+
private _agent?: EnboxPlatformAgent;
|
|
173
173
|
|
|
174
174
|
/**
|
|
175
175
|
* A private map that stores instances of cryptographic algorithm implementations. Each key in
|
|
@@ -196,12 +196,12 @@ export class LocalKeyManager implements AgentKeyManager {
|
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
/**
|
|
199
|
-
* Retrieves the `
|
|
199
|
+
* Retrieves the `EnboxPlatformAgent` execution context.
|
|
200
200
|
*
|
|
201
|
-
* @returns The `
|
|
201
|
+
* @returns The `EnboxPlatformAgent` instance that represents the current execution context.
|
|
202
202
|
* @throws Will throw an error if the `agent` instance property is undefined.
|
|
203
203
|
*/
|
|
204
|
-
get agent():
|
|
204
|
+
get agent(): EnboxPlatformAgent {
|
|
205
205
|
if (this._agent === undefined) {
|
|
206
206
|
throw new Error('LocalKeyManager: Unable to determine agent execution context.');
|
|
207
207
|
}
|
|
@@ -209,7 +209,7 @@ export class LocalKeyManager implements AgentKeyManager {
|
|
|
209
209
|
return this._agent;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
set agent(agent:
|
|
212
|
+
set agent(agent: EnboxPlatformAgent) {
|
|
213
213
|
this._agent = agent;
|
|
214
214
|
}
|
|
215
215
|
|
package/src/oidc.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ConnectPermissionRequest } from './connect.js';
|
|
2
|
+
import type { EnboxAgent } from './types/agent.js';
|
|
2
3
|
import type { RequireOnly } from '@enbox/common';
|
|
3
|
-
import type { Web5Agent } from './types/agent.js';
|
|
4
4
|
import type { DidDocument, PortableDid } from '@enbox/dids';
|
|
5
5
|
import type { DwnDataEncodedRecordsWriteMessage, DwnPermissionScope, DwnProtocolDefinition } from './types/dwn.js';
|
|
6
6
|
import type {
|
|
@@ -34,7 +34,7 @@ import { isRecordPermissionScope } from './dwn-api.js';
|
|
|
34
34
|
* @see {@link https://www.rfc-editor.org/rfc/rfc9126.html | OAuth 2.0 Pushed Authorization Requests}
|
|
35
35
|
*/
|
|
36
36
|
export type PushedAuthRequest = {
|
|
37
|
-
/** The JWT which contains the {@link
|
|
37
|
+
/** The JWT which contains the {@link EnboxConnectAuthRequest} */
|
|
38
38
|
request: string;
|
|
39
39
|
};
|
|
40
40
|
|
|
@@ -134,7 +134,7 @@ export type SIOPv2AuthRequest = {
|
|
|
134
134
|
* An auth request that is compatible with both Web5 Connect and (hopefully, WIP) OIDC SIOPv2
|
|
135
135
|
* The contents of this are inserted into a JWT inside of the {@link PushedAuthRequest}.
|
|
136
136
|
*/
|
|
137
|
-
export type
|
|
137
|
+
export type EnboxConnectAuthRequest = {
|
|
138
138
|
/** The user friendly name of the client/app to be displayed when prompting end-user with permission requests. */
|
|
139
139
|
displayName: string;
|
|
140
140
|
|
|
@@ -168,16 +168,16 @@ export type SIOPv2AuthResponse = {
|
|
|
168
168
|
};
|
|
169
169
|
|
|
170
170
|
/** An auth response that is compatible with both Web5 Connect and (hopefully, WIP) OIDC SIOPv2 */
|
|
171
|
-
export type
|
|
171
|
+
export type EnboxConnectAuthResponse = {
|
|
172
172
|
delegateGrants: DwnDataEncodedRecordsWriteMessage[];
|
|
173
173
|
delegatePortableDid: PortableDid;
|
|
174
174
|
} & SIOPv2AuthResponse;
|
|
175
175
|
|
|
176
176
|
/** Represents the different OIDC endpoint types.
|
|
177
177
|
* 1. `pushedAuthorizationRequest`: client sends {@link PushedAuthRequest} receives {@link PushedAuthResponse}
|
|
178
|
-
* 2. `authorize`: provider gets the {@link
|
|
179
|
-
* 3. `callback`: provider sends {@link
|
|
180
|
-
* 4. `token`: client gets {@link
|
|
178
|
+
* 2. `authorize`: provider gets the {@link EnboxConnectAuthRequest} JWT that was stored by the PAR
|
|
179
|
+
* 3. `callback`: provider sends {@link EnboxConnectAuthResponse} to this endpoint
|
|
180
|
+
* 4. `token`: client gets {@link EnboxConnectAuthResponse} from this endpoint
|
|
181
181
|
*/
|
|
182
182
|
type OidcEndpoint =
|
|
183
183
|
| 'pushedAuthorizationRequest'
|
|
@@ -210,17 +210,17 @@ function buildOidcUrl({
|
|
|
210
210
|
/** 1. client sends {@link PushedAuthRequest} & client receives {@link PushedAuthResponse} */
|
|
211
211
|
case 'pushedAuthorizationRequest':
|
|
212
212
|
return concatenateUrl(baseURL, 'par');
|
|
213
|
-
/** 2. provider gets {@link
|
|
213
|
+
/** 2. provider gets {@link EnboxConnectAuthRequest} */
|
|
214
214
|
case 'authorize':
|
|
215
215
|
if (!authParam)
|
|
216
216
|
{throw new Error(
|
|
217
217
|
`authParam must be providied when building a token URL`
|
|
218
218
|
);}
|
|
219
219
|
return concatenateUrl(baseURL, `authorize/${authParam}.jwt`);
|
|
220
|
-
/** 3. provider sends {@link
|
|
220
|
+
/** 3. provider sends {@link EnboxConnectAuthResponse} */
|
|
221
221
|
case 'callback':
|
|
222
222
|
return concatenateUrl(baseURL, `callback`);
|
|
223
|
-
/** 4. client gets {@link
|
|
223
|
+
/** 4. client gets {@link EnboxConnectAuthResponse */
|
|
224
224
|
case 'token':
|
|
225
225
|
if (!tokenParam)
|
|
226
226
|
{throw new Error(
|
|
@@ -248,20 +248,20 @@ async function generateCodeChallenge(): Promise<{ codeChallengeBytes: Uint8Array
|
|
|
248
248
|
return { codeChallengeBytes, codeChallengeBase64Url };
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
-
/** Client creates the {@link
|
|
251
|
+
/** Client creates the {@link EnboxConnectAuthRequest} */
|
|
252
252
|
async function createAuthRequest(
|
|
253
253
|
options: RequireOnly<
|
|
254
|
-
|
|
254
|
+
EnboxConnectAuthRequest,
|
|
255
255
|
'client_id' | 'scope' | 'redirect_uri' | 'permissionRequests' | 'displayName'
|
|
256
256
|
>
|
|
257
|
-
): Promise<
|
|
257
|
+
): Promise<EnboxConnectAuthRequest> {
|
|
258
258
|
// Generate a random state value to associate the authorization request with the response.
|
|
259
259
|
const stateBytes = CryptoUtils.randomBytes(16);
|
|
260
260
|
|
|
261
261
|
// Generate a random nonce value to associate the ID Token with the authorization request.
|
|
262
262
|
const nonceBytes = CryptoUtils.randomBytes(16);
|
|
263
263
|
|
|
264
|
-
const requestObject:
|
|
264
|
+
const requestObject: EnboxConnectAuthRequest = {
|
|
265
265
|
...options,
|
|
266
266
|
nonce : Convert.uint8Array(nonceBytes).toBase64Url(),
|
|
267
267
|
response_type : 'id_token',
|
|
@@ -313,13 +313,13 @@ async function encryptAuthRequest({
|
|
|
313
313
|
/** Create a response object compatible with Web5 Connect and OIDC SIOPv2 */
|
|
314
314
|
async function createResponseObject(
|
|
315
315
|
options: RequireOnly<
|
|
316
|
-
|
|
316
|
+
EnboxConnectAuthResponse,
|
|
317
317
|
'iss' | 'sub' | 'aud' | 'delegateGrants' | 'delegatePortableDid'
|
|
318
318
|
>
|
|
319
|
-
): Promise<
|
|
319
|
+
): Promise<EnboxConnectAuthResponse> {
|
|
320
320
|
const currentTimeInSeconds = Math.floor(Date.now() / 1000);
|
|
321
321
|
|
|
322
|
-
const responseObject:
|
|
322
|
+
const responseObject: EnboxConnectAuthResponse = {
|
|
323
323
|
...options,
|
|
324
324
|
iat : currentTimeInSeconds,
|
|
325
325
|
exp : currentTimeInSeconds + 600, // Expires in 10 minutes.
|
|
@@ -406,10 +406,10 @@ async function verifyJwt({ jwt }: { jwt: string }): Promise<Record<string, unkno
|
|
|
406
406
|
}
|
|
407
407
|
|
|
408
408
|
/**
|
|
409
|
-
* Fetches the {@
|
|
409
|
+
* Fetches the {@EnboxConnectAuthRequest} from the authorize endpoint and decrypts it
|
|
410
410
|
* using the encryption key passed via QR code.
|
|
411
411
|
*/
|
|
412
|
-
const getAuthRequest = async (request_uri: string, encryption_key: string): Promise<
|
|
412
|
+
const getAuthRequest = async (request_uri: string, encryption_key: string): Promise<EnboxConnectAuthRequest> => {
|
|
413
413
|
const authRequest = await fetch(request_uri, { signal: AbortSignal.timeout(30_000) });
|
|
414
414
|
const jwe = await authRequest.text();
|
|
415
415
|
const jwt = await decryptAuthRequest({
|
|
@@ -418,7 +418,7 @@ const getAuthRequest = async (request_uri: string, encryption_key: string): Prom
|
|
|
418
418
|
});
|
|
419
419
|
const web5ConnectAuthRequest = (await verifyJwt({
|
|
420
420
|
jwt,
|
|
421
|
-
})) as
|
|
421
|
+
})) as EnboxConnectAuthRequest;
|
|
422
422
|
|
|
423
423
|
return web5ConnectAuthRequest;
|
|
424
424
|
};
|
|
@@ -461,7 +461,7 @@ async function decryptAuthRequest({
|
|
|
461
461
|
|
|
462
462
|
/**
|
|
463
463
|
* The client uses to decrypt the jwe obtained from the auth server which contains
|
|
464
|
-
* the {@link
|
|
464
|
+
* the {@link EnboxConnectAuthResponse} that was sent by the provider to the auth server.
|
|
465
465
|
*
|
|
466
466
|
* @async
|
|
467
467
|
* @param {BearerDid} clientDid - The did that was initially used by the client for ECDH at connect init.
|
|
@@ -517,7 +517,7 @@ async function decryptAuthResponse(
|
|
|
517
517
|
return jwt;
|
|
518
518
|
}
|
|
519
519
|
|
|
520
|
-
/** Derives a shared ECDH private key in order to encrypt the {@link
|
|
520
|
+
/** Derives a shared ECDH private key in order to encrypt the {@link EnboxConnectAuthResponse} */
|
|
521
521
|
async function deriveSharedKey(
|
|
522
522
|
privateKeyDid: BearerDid,
|
|
523
523
|
publicKeyDid: DidDocument
|
|
@@ -616,12 +616,12 @@ function shouldUseDelegatePermission(scope: DwnPermissionScope): boolean {
|
|
|
616
616
|
|
|
617
617
|
/**
|
|
618
618
|
* Creates the permission grants that assign to the selectedDid the level of
|
|
619
|
-
* permissions that the web app requested in the {@link
|
|
619
|
+
* permissions that the web app requested in the {@link EnboxConnectAuthRequest}
|
|
620
620
|
*/
|
|
621
621
|
async function createPermissionGrants(
|
|
622
622
|
selectedDid: string,
|
|
623
623
|
delegateBearerDid: BearerDid,
|
|
624
|
-
agent:
|
|
624
|
+
agent: EnboxAgent,
|
|
625
625
|
scopes: DwnPermissionScope[],
|
|
626
626
|
): Promise<DwnDataEncodedRecordsWriteMessage[]> {
|
|
627
627
|
const permissionsApi = new AgentPermissionsApi({ agent });
|
|
@@ -683,7 +683,7 @@ async function createPermissionGrants(
|
|
|
683
683
|
*/
|
|
684
684
|
async function prepareProtocol(
|
|
685
685
|
selectedDid: string,
|
|
686
|
-
agent:
|
|
686
|
+
agent: EnboxAgent,
|
|
687
687
|
protocolDefinition: DwnProtocolDefinition
|
|
688
688
|
): Promise<void> {
|
|
689
689
|
|
|
@@ -744,17 +744,17 @@ async function prepareProtocol(
|
|
|
744
744
|
/**
|
|
745
745
|
* Creates a delegate did which the web app will use as its future indentity.
|
|
746
746
|
* Assigns to that DID the level of permissions that the web app requested in
|
|
747
|
-
* the {@link
|
|
747
|
+
* the {@link EnboxConnectAuthRequest}. Encrypts via ECDH key that the web app
|
|
748
748
|
* will have access to because the web app has the public key which it provided
|
|
749
|
-
* in the {@link
|
|
750
|
-
* {@link
|
|
749
|
+
* in the {@link EnboxConnectAuthRequest}. Then sends the ciphertext of this
|
|
750
|
+
* {@link EnboxConnectAuthResponse} to the callback endpoint. Which the
|
|
751
751
|
* web app will need to retrieve from the token endpoint and decrypt with the pin to access.
|
|
752
752
|
*/
|
|
753
753
|
async function submitAuthResponse(
|
|
754
754
|
selectedDid: string,
|
|
755
|
-
authRequest:
|
|
755
|
+
authRequest: EnboxConnectAuthRequest,
|
|
756
756
|
randomPin: string,
|
|
757
|
-
agent:
|
|
757
|
+
agent: EnboxAgent
|
|
758
758
|
): Promise<void> {
|
|
759
759
|
const delegateBearerDid = await DidJwk.create();
|
|
760
760
|
const delegatePortableDid = await delegateBearerDid.export();
|
|
@@ -852,3 +852,13 @@ export const Oidc = {
|
|
|
852
852
|
generateCodeChallenge,
|
|
853
853
|
submitAuthResponse,
|
|
854
854
|
};
|
|
855
|
+
|
|
856
|
+
// ---------------------------------------------------------------------------
|
|
857
|
+
// Deprecated aliases — migration aid
|
|
858
|
+
// ---------------------------------------------------------------------------
|
|
859
|
+
|
|
860
|
+
/** @deprecated Use {@link EnboxConnectAuthRequest} instead. */
|
|
861
|
+
export type Web5ConnectAuthRequest = EnboxConnectAuthRequest;
|
|
862
|
+
|
|
863
|
+
/** @deprecated Use {@link EnboxConnectAuthResponse} instead. */
|
|
864
|
+
export type Web5ConnectAuthResponse = EnboxConnectAuthResponse;
|
package/src/permissions-api.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { EnboxAgent } from './types/agent.js';
|
|
2
2
|
import type { CreateGrantParams, CreateRequestParams, CreateRevocationParams, FetchPermissionRequestParams, FetchPermissionsParams, GetPermissionParams, IsGrantRevokedParams, PermissionGrantEntry, PermissionRequestEntry, PermissionRevocationEntry, PermissionsApi } from './types/permissions.js';
|
|
3
3
|
import type { DwnDataEncodedRecordsWriteMessage, DwnMessageParams, DwnMessagesPermissionScope, DwnPermissionScope, DwnProtocolPermissionScope, DwnRecordsPermissionScope, ProcessDwnRequest } from './types/dwn.js';
|
|
4
4
|
import type { PermissionGrant, PermissionGrantData, PermissionRequestData, PermissionRevocationData } from '@enbox/dwn-sdk-js';
|
|
@@ -13,20 +13,20 @@ export class AgentPermissionsApi implements PermissionsApi {
|
|
|
13
13
|
/** cache for fetching a permission {@link PermissionGrant}, keyed by a specific MessageType and protocol */
|
|
14
14
|
private _cachedPermissions: TtlCache<string, PermissionGrantEntry> = new TtlCache({ ttl: 60 * 1000 });
|
|
15
15
|
|
|
16
|
-
private _agent?:
|
|
16
|
+
private _agent?: EnboxAgent;
|
|
17
17
|
|
|
18
|
-
get agent():
|
|
18
|
+
get agent(): EnboxAgent {
|
|
19
19
|
if (!this._agent) {
|
|
20
20
|
throw new Error('AgentPermissionsApi: Agent is not set');
|
|
21
21
|
}
|
|
22
22
|
return this._agent;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
set agent(agent:
|
|
25
|
+
set agent(agent:EnboxAgent) {
|
|
26
26
|
this._agent = agent;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
constructor({ agent }: { agent?:
|
|
29
|
+
constructor({ agent }: { agent?: EnboxAgent } = {}) {
|
|
30
30
|
this._agent = agent;
|
|
31
31
|
}
|
|
32
32
|
|
package/src/store-data.ts
CHANGED
|
@@ -4,7 +4,7 @@ import ms from 'ms';
|
|
|
4
4
|
import { Convert, Stream, TtlCache } from '@enbox/common';
|
|
5
5
|
|
|
6
6
|
import type { DwnMessageParams } from './types/dwn.js';
|
|
7
|
-
import type {
|
|
7
|
+
import type { EnboxPlatformAgent } from './types/agent.js';
|
|
8
8
|
import type { ProtocolDefinition, RecordsReadReplyEntry } from '@enbox/dwn-sdk-js';
|
|
9
9
|
|
|
10
10
|
import { Protocols } from '@enbox/dwn-sdk-js';
|
|
@@ -13,7 +13,7 @@ import { DwnInterface } from './types/dwn.js';
|
|
|
13
13
|
import { getDataStoreTenant, TENANT_SEPARATOR } from './utils-internal.js';
|
|
14
14
|
|
|
15
15
|
export type DataStoreTenantParams = {
|
|
16
|
-
agent:
|
|
16
|
+
agent: EnboxPlatformAgent;
|
|
17
17
|
tenant?: string;
|
|
18
18
|
};
|
|
19
19
|
|
|
@@ -269,7 +269,7 @@ export class DwnDataStore<TStoreObject extends Record<string, any> = Jwk> implem
|
|
|
269
269
|
}
|
|
270
270
|
|
|
271
271
|
protected async getAllRecords(_params: {
|
|
272
|
-
agent:
|
|
272
|
+
agent: EnboxPlatformAgent;
|
|
273
273
|
tenantDid: string;
|
|
274
274
|
}): Promise<TStoreObject[]> {
|
|
275
275
|
throw new Error('Not implemented: Classes extending DwnDataStore must implement getAllRecords()');
|
|
@@ -278,7 +278,7 @@ export class DwnDataStore<TStoreObject extends Record<string, any> = Jwk> implem
|
|
|
278
278
|
private async getRecord({ recordId, tenantDid, agent, useCache }: {
|
|
279
279
|
recordId: string;
|
|
280
280
|
tenantDid: string;
|
|
281
|
-
agent:
|
|
281
|
+
agent: EnboxPlatformAgent;
|
|
282
282
|
useCache: boolean;
|
|
283
283
|
}): Promise<TStoreObject | undefined> {
|
|
284
284
|
// If caching is enabled, check the cache for the record ID.
|
|
@@ -322,7 +322,7 @@ export class DwnDataStore<TStoreObject extends Record<string, any> = Jwk> implem
|
|
|
322
322
|
* If the tenant DID lacks an X25519 keyAgreement key, the error propagates
|
|
323
323
|
* — plaintext fallback is not allowed.
|
|
324
324
|
*/
|
|
325
|
-
private async installProtocol(tenant: string, agent:
|
|
325
|
+
private async installProtocol(tenant: string, agent: EnboxPlatformAgent): Promise<void> {
|
|
326
326
|
let definition = this._recordProtocolDefinition;
|
|
327
327
|
let encryptionActive = false;
|
|
328
328
|
|
|
@@ -353,7 +353,7 @@ export class DwnDataStore<TStoreObject extends Record<string, any> = Jwk> implem
|
|
|
353
353
|
private async lookupRecordId({ id, tenantDid, agent }: {
|
|
354
354
|
id: string;
|
|
355
355
|
tenantDid: string;
|
|
356
|
-
agent:
|
|
356
|
+
agent: EnboxPlatformAgent;
|
|
357
357
|
}): Promise<string | undefined> {
|
|
358
358
|
// Check the index for a matching ID and extend the index TTL.
|
|
359
359
|
let recordId = this._index.get(`${tenantDid}${TENANT_SEPARATOR}${id}`, { updateAgeOnGet: true });
|
|
@@ -373,7 +373,7 @@ export class DwnDataStore<TStoreObject extends Record<string, any> = Jwk> implem
|
|
|
373
373
|
private async getExistingRecordEntry({ id, tenantDid, agent }: {
|
|
374
374
|
id: string;
|
|
375
375
|
tenantDid: string;
|
|
376
|
-
agent:
|
|
376
|
+
agent: EnboxPlatformAgent;
|
|
377
377
|
}): Promise<RecordsReadReplyEntry | undefined> {
|
|
378
378
|
// Look up the DWN record ID of the object in the store with the given `id`.
|
|
379
379
|
const recordId = await this.lookupRecordId({ id, tenantDid, agent });
|
package/src/store-did.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { PortableDid } from '@enbox/dids';
|
|
|
3
3
|
import { Convert } from '@enbox/common';
|
|
4
4
|
import { isPortableDid } from '@enbox/dids';
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type { EnboxPlatformAgent } from './types/agent.js';
|
|
7
7
|
import type { AgentDataStore, DataStoreDeleteParams, DataStoreGetParams, DataStoreListParams, DataStoreSetParams } from './store-data.js';
|
|
8
8
|
|
|
9
9
|
import { DwnInterface } from './types/dwn.js';
|
|
@@ -43,7 +43,7 @@ export class DwnDidStore extends DwnDataStore<PortableDid> implements AgentDataS
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
protected async getAllRecords({ agent, tenantDid }: {
|
|
46
|
-
agent:
|
|
46
|
+
agent: EnboxPlatformAgent;
|
|
47
47
|
tenantDid: string;
|
|
48
48
|
}): Promise<PortableDid[]> {
|
|
49
49
|
// Clear the index since it will be rebuilt from the query results.
|
package/src/store-identity.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Convert } from '@enbox/common';
|
|
2
2
|
|
|
3
|
+
import type { EnboxPlatformAgent } from './types/agent.js';
|
|
3
4
|
import type { IdentityMetadata } from './types/identity.js';
|
|
4
|
-
import type { Web5PlatformAgent } from './types/agent.js';
|
|
5
5
|
import type { AgentDataStore, DataStoreDeleteParams, DataStoreGetParams, DataStoreListParams, DataStoreSetParams } from './store-data.js';
|
|
6
6
|
|
|
7
7
|
import { DwnInterface } from './types/dwn.js';
|
|
@@ -47,7 +47,7 @@ export class DwnIdentityStore extends DwnDataStore<IdentityMetadata> implements
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
protected async getAllRecords({ agent, tenantDid }: {
|
|
50
|
-
agent:
|
|
50
|
+
agent: EnboxPlatformAgent;
|
|
51
51
|
tenantDid: string;
|
|
52
52
|
}): Promise<IdentityMetadata[]> {
|
|
53
53
|
// Clear the index since it will be rebuilt from the query results.
|
package/src/store-key.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { isPrivateJwk, KEY_URI_PREFIX_JWK } from '@enbox/crypto';
|
|
|
5
5
|
|
|
6
6
|
import type { RecordsReadReply } from '@enbox/dwn-sdk-js';
|
|
7
7
|
|
|
8
|
-
import type {
|
|
8
|
+
import type { EnboxPlatformAgent } from './types/agent.js';
|
|
9
9
|
|
|
10
10
|
import { DwnInterface } from './types/dwn.js';
|
|
11
11
|
import { JwkProtocolDefinition } from './store-data-protocols.js';
|
|
@@ -45,7 +45,7 @@ export class DwnKeyStore extends DwnDataStore<Jwk> implements AgentDataStore<Jwk
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
protected async getAllRecords({ agent, tenantDid }: {
|
|
48
|
-
agent:
|
|
48
|
+
agent: EnboxPlatformAgent;
|
|
49
49
|
tenantDid: string;
|
|
50
50
|
}): Promise<Jwk[]> {
|
|
51
51
|
// Clear the index since it will be rebuilt from the query results.
|