@agentunion/fastaun 0.2.17 → 0.2.19
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/auth.d.ts +6 -0
- package/dist/auth.js +375 -212
- package/dist/auth.js.map +1 -1
- package/dist/client.d.ts +14 -1
- package/dist/client.js +1255 -729
- package/dist/client.js.map +1 -1
- package/dist/discovery.d.ts +3 -0
- package/dist/discovery.js +29 -2
- package/dist/discovery.js.map +1 -1
- package/dist/e2ee-group.d.ts +2 -1
- package/dist/e2ee-group.js +207 -56
- package/dist/e2ee-group.js.map +1 -1
- package/dist/e2ee.js +45 -11
- package/dist/e2ee.js.map +1 -1
- package/dist/events.js +1 -1
- package/dist/events.js.map +1 -1
- package/dist/keystore/aid-db.d.ts +13 -1
- package/dist/keystore/aid-db.js +31 -3
- package/dist/keystore/aid-db.js.map +1 -1
- package/dist/keystore/file.d.ts +6 -0
- package/dist/keystore/file.js +20 -9
- package/dist/keystore/file.js.map +1 -1
- package/dist/keystore/index.d.ts +2 -0
- package/dist/keystore/sqlite-backup.js +5 -5
- package/dist/keystore/sqlite-backup.js.map +1 -1
- package/dist/logger.d.ts +2 -0
- package/dist/logger.js +69 -13
- package/dist/logger.js.map +1 -1
- package/dist/namespaces/auth.d.ts +13 -4
- package/dist/namespaces/auth.js +354 -150
- package/dist/namespaces/auth.js.map +1 -1
- package/dist/namespaces/custody.d.ts +1 -0
- package/dist/namespaces/custody.js +138 -56
- package/dist/namespaces/custody.js.map +1 -1
- package/dist/namespaces/meta.d.ts +1 -0
- package/dist/namespaces/meta.js +26 -0
- package/dist/namespaces/meta.js.map +1 -1
- package/dist/secret-store/file-store.js +3 -2
- package/dist/secret-store/file-store.js.map +1 -1
- package/dist/transport.js +83 -2
- package/dist/transport.js.map +1 -1
- package/package.json +42 -42
package/dist/namespaces/auth.js
CHANGED
|
@@ -132,8 +132,10 @@ async function fetchWithTimeout(input, init, timeoutMs = AGENT_MD_HTTP_TIMEOUT_M
|
|
|
132
132
|
}
|
|
133
133
|
export class AuthNamespace {
|
|
134
134
|
_client;
|
|
135
|
+
_log;
|
|
135
136
|
constructor(client) {
|
|
136
137
|
this._client = client;
|
|
138
|
+
this._log = client._logger.for('aun_core.namespace.auth');
|
|
137
139
|
}
|
|
138
140
|
get _internal() {
|
|
139
141
|
return this._client;
|
|
@@ -143,19 +145,33 @@ export class AuthNamespace {
|
|
|
143
145
|
* 优先使用已预置的 _gatewayUrl,否则基于 AID 自动发现。
|
|
144
146
|
*
|
|
145
147
|
* 发现流程:
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
148
|
+
* 1. 若 _gatewayUrl 已预置(内存),直接返回
|
|
149
|
+
* 2. 从 keystore metadata 读 cached gateway_url(跨进程复用)
|
|
150
|
+
* 3. 开发环境:先 gateway.{issuer},再 fallback {aid}(泛域名在开发环境可能不可用)
|
|
151
|
+
* 4. 生产环境:先 {aid}(泛域名 nameservice),再 fallback gateway.{issuer}
|
|
150
152
|
*/
|
|
151
153
|
async _resolveGateway(aid) {
|
|
152
154
|
// 访问内部属性
|
|
153
155
|
const client = this._internal;
|
|
154
156
|
const gatewayUrl = client._gatewayUrl;
|
|
155
|
-
if (gatewayUrl)
|
|
157
|
+
if (gatewayUrl) {
|
|
158
|
+
this._log.debug(`_resolveGateway: using preset gateway=${gatewayUrl}`);
|
|
156
159
|
return gatewayUrl;
|
|
160
|
+
}
|
|
157
161
|
const resolvedAid = aid ?? client._aid;
|
|
158
162
|
if (resolvedAid) {
|
|
163
|
+
// 从 keystore metadata 读持久化的 gateway_url(避免每次进程启动都做 well-known discovery)
|
|
164
|
+
try {
|
|
165
|
+
const cachedGateway = this._loadCachedGatewayUrl(resolvedAid);
|
|
166
|
+
if (cachedGateway) {
|
|
167
|
+
this._log.debug(`_resolveGateway from keystore cache aid=${resolvedAid} gateway=${cachedGateway}`);
|
|
168
|
+
client._gatewayUrl = cachedGateway;
|
|
169
|
+
return cachedGateway;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch (exc) {
|
|
173
|
+
this._log.debug(`load cached gateway_url failed: ${exc instanceof Error ? exc.message : String(exc)}`);
|
|
174
|
+
}
|
|
159
175
|
const parts = resolvedAid.split('.');
|
|
160
176
|
const issuerDomain = parts.length > 1 ? parts.slice(1).join('.') : resolvedAid;
|
|
161
177
|
const configModel = client._configModel;
|
|
@@ -169,139 +185,258 @@ export class AuthNamespace {
|
|
|
169
185
|
const [primaryUrl, fallbackUrl] = configModel.verifySsl
|
|
170
186
|
? [aidUrl, gatewayDomainUrl]
|
|
171
187
|
: [gatewayDomainUrl, aidUrl];
|
|
188
|
+
this._log.debug(`_resolveGateway start: aid=${resolvedAid} primary=${primaryUrl}`);
|
|
172
189
|
try {
|
|
173
|
-
|
|
190
|
+
const url = await discovery.discover(primaryUrl);
|
|
191
|
+
this._log.debug(`_resolveGateway primary ok: aid=${resolvedAid} gateway=${url}`);
|
|
192
|
+
this._persistGatewayUrl(resolvedAid, url);
|
|
193
|
+
return url;
|
|
174
194
|
}
|
|
175
|
-
catch {
|
|
195
|
+
catch (exc) {
|
|
176
196
|
// 主路径失败,尝试 fallback
|
|
197
|
+
this._log.warn(`_resolveGateway primary failed, trying fallback: aid=${resolvedAid} primary=${primaryUrl} err=${exc instanceof Error ? exc.message : String(exc)}`);
|
|
177
198
|
}
|
|
178
|
-
|
|
199
|
+
const url = await discovery.discover(fallbackUrl);
|
|
200
|
+
this._log.debug(`_resolveGateway fallback ok: aid=${resolvedAid} gateway=${url}`);
|
|
201
|
+
this._persistGatewayUrl(resolvedAid, url);
|
|
202
|
+
return url;
|
|
179
203
|
}
|
|
180
204
|
throw new ValidationError("unable to resolve gateway: set client._gatewayUrl or provide 'aid' for auto-discovery");
|
|
181
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* 从 keystore metadata 读取 cached gateway_url(用于跨进程复用,避免重做 discovery)
|
|
208
|
+
*/
|
|
209
|
+
_loadCachedGatewayUrl(aid) {
|
|
210
|
+
if (!aid)
|
|
211
|
+
return '';
|
|
212
|
+
const keystore = this._internal._keystore;
|
|
213
|
+
try {
|
|
214
|
+
const db = keystore._getDB?.(aid);
|
|
215
|
+
if (!db || typeof db.getMetadata !== 'function')
|
|
216
|
+
return '';
|
|
217
|
+
const raw = db.getMetadata('gateway_url');
|
|
218
|
+
if (!raw)
|
|
219
|
+
return '';
|
|
220
|
+
// saveIdentity 走 JSON.stringify;这里 JSON.parse 兼容;裸字符串也兼容
|
|
221
|
+
try {
|
|
222
|
+
const value = JSON.parse(raw);
|
|
223
|
+
if (typeof value === 'string')
|
|
224
|
+
return value.trim();
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
// 解析失败回退到裸字符串
|
|
228
|
+
}
|
|
229
|
+
return String(raw).trim();
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
return '';
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* 持久化 gateway_url 到 keystore metadata,供跨进程复用
|
|
237
|
+
*/
|
|
238
|
+
_persistGatewayUrl(aid, gatewayUrl) {
|
|
239
|
+
if (!gatewayUrl || !aid)
|
|
240
|
+
return;
|
|
241
|
+
try {
|
|
242
|
+
const keystore = this._internal._keystore;
|
|
243
|
+
const db = keystore._getDB?.(aid);
|
|
244
|
+
if (!db || typeof db.setMetadata !== 'function')
|
|
245
|
+
return;
|
|
246
|
+
// 与 saveIdentity 保持一致用 JSON 编码,避免 saveIdentity 之后被覆盖时格式冲突
|
|
247
|
+
db.setMetadata('gateway_url', JSON.stringify(gatewayUrl));
|
|
248
|
+
}
|
|
249
|
+
catch (exc) {
|
|
250
|
+
this._log.debug(`persist gateway_url failed aid=${aid} err=${exc instanceof Error ? exc.message : String(exc)}`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
182
253
|
/** 创建新 AID */
|
|
183
254
|
async createAid(params) {
|
|
255
|
+
const tStart = Date.now();
|
|
184
256
|
const aid = String(params?.aid ?? '');
|
|
185
257
|
if (!aid)
|
|
186
258
|
throw new Error("auth.create_aid requires 'aid'");
|
|
187
259
|
const client = this._internal;
|
|
260
|
+
this._log.debug(`createAid enter: aid=${aid}`);
|
|
188
261
|
const gatewayUrl = await this._resolveGateway(aid);
|
|
189
262
|
client._gatewayUrl = gatewayUrl;
|
|
190
263
|
const auth = client._auth;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
264
|
+
try {
|
|
265
|
+
const result = await auth.createAid(gatewayUrl, aid);
|
|
266
|
+
client._aid = aid;
|
|
267
|
+
client._identity = auth.loadIdentityOrNone(aid);
|
|
268
|
+
this._log.info(`createAid success: aid=${aid} gateway=${gatewayUrl}`);
|
|
269
|
+
this._log.debug(`createAid exit: elapsed=${Date.now() - tStart}ms aid=${aid}`);
|
|
270
|
+
return {
|
|
271
|
+
aid: result.aid,
|
|
272
|
+
cert_pem: result.cert,
|
|
273
|
+
gateway: gatewayUrl,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
catch (err) {
|
|
277
|
+
this._log.error(`createAid failed: aid=${aid} err=${err instanceof Error ? err.message : String(err)}`, err instanceof Error ? err : undefined);
|
|
278
|
+
this._log.debug(`createAid exit (error): elapsed=${Date.now() - tStart}ms aid=${aid} err=${err instanceof Error ? err.message : String(err)}`);
|
|
279
|
+
throw err;
|
|
280
|
+
}
|
|
199
281
|
}
|
|
200
282
|
/** 认证(登录) */
|
|
201
283
|
async authenticate(params) {
|
|
284
|
+
const tStart = Date.now();
|
|
202
285
|
const request = { ...(params ?? {}) };
|
|
203
286
|
const aid = request.aid;
|
|
204
287
|
const client = this._internal;
|
|
288
|
+
this._log.debug(`authenticate enter: aid=${aid ?? '-'}`);
|
|
205
289
|
const gatewayUrl = await this._resolveGateway(aid);
|
|
206
290
|
client._gatewayUrl = gatewayUrl;
|
|
207
291
|
const auth = client._auth;
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
292
|
+
try {
|
|
293
|
+
const result = await auth.authenticate(gatewayUrl, { aid });
|
|
294
|
+
client._aid = result.aid ?? null;
|
|
295
|
+
client._identity = auth.loadIdentityOrNone(String(result.aid));
|
|
296
|
+
this._log.info(`authenticate success: aid=${result.aid} gateway=${gatewayUrl}`);
|
|
297
|
+
this._log.debug(`authenticate exit: elapsed=${Date.now() - tStart}ms aid=${aid ?? '-'}`);
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
300
|
+
catch (err) {
|
|
301
|
+
this._log.error(`authenticate failed: aid=${aid ?? '-'} err=${err instanceof Error ? err.message : String(err)}`, err instanceof Error ? err : undefined);
|
|
302
|
+
this._log.debug(`authenticate exit (error): elapsed=${Date.now() - tStart}ms aid=${aid ?? '-'} err=${err instanceof Error ? err.message : String(err)}`);
|
|
303
|
+
throw err;
|
|
304
|
+
}
|
|
212
305
|
}
|
|
213
306
|
async signAgentMd(content, opts) {
|
|
307
|
+
const tStart = Date.now();
|
|
214
308
|
const client = this._internal;
|
|
215
309
|
const targetAid = String(opts?.aid ?? client._aid ?? '').trim();
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
310
|
+
this._log.debug(`signAgentMd enter: aid=${targetAid || '-'} content_len=${content?.length ?? 0}`);
|
|
311
|
+
try {
|
|
312
|
+
const identity = client._auth.loadIdentityOrNone(targetAid || undefined);
|
|
313
|
+
if (!identity) {
|
|
314
|
+
this._log.error(`signAgentMd failed: no local identity for aid=${targetAid || '-'}`);
|
|
315
|
+
throw new StateError('no local identity found, call auth.createAid() first');
|
|
316
|
+
}
|
|
317
|
+
const privateKeyPem = String(identity.private_key_pem ?? '').trim();
|
|
318
|
+
const certPem = normalizeIdentityCertPem(identity);
|
|
319
|
+
if (!privateKeyPem || !certPem) {
|
|
320
|
+
this._log.error(`signAgentMd failed: identity missing key/cert aid=${identity.aid ?? '-'}`);
|
|
321
|
+
throw new StateError('local identity missing private key or certificate');
|
|
322
|
+
}
|
|
323
|
+
let payload = parseAgentMdTailSignature(String(content ?? '')).payload;
|
|
324
|
+
if (payload && !payload.endsWith('\n') && !payload.endsWith('\r')) {
|
|
325
|
+
payload += '\n';
|
|
326
|
+
}
|
|
327
|
+
const signer = createSign('sha256');
|
|
328
|
+
signer.update(payload);
|
|
329
|
+
signer.end();
|
|
330
|
+
const signature = signer.sign(privateKeyPem).toString('base64');
|
|
331
|
+
const fingerprint = certificateSha256Fingerprint(certPem);
|
|
332
|
+
if (!fingerprint) {
|
|
333
|
+
this._log.error(`signAgentMd failed: cert fingerprint computation failed aid=${identity.aid ?? '-'}`);
|
|
334
|
+
throw new StateError('agent.md cert fingerprint failed');
|
|
335
|
+
}
|
|
336
|
+
const signedBlock = [
|
|
337
|
+
AGENT_MD_SIGNATURE_MARKER,
|
|
338
|
+
`cert_fingerprint: ${fingerprint}`,
|
|
339
|
+
`timestamp: ${Math.floor(Date.now() / 1000)}`,
|
|
340
|
+
`signature: ${signature}`,
|
|
341
|
+
'-->',
|
|
342
|
+
].join('\n');
|
|
343
|
+
this._log.debug(`signAgentMd exit: elapsed=${Date.now() - tStart}ms aid=${identity.aid ?? '-'} fingerprint=${fingerprint.slice(0, 23)}...`);
|
|
344
|
+
return payload + signedBlock;
|
|
345
|
+
}
|
|
346
|
+
catch (err) {
|
|
347
|
+
this._log.debug(`signAgentMd exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
|
|
348
|
+
throw err;
|
|
219
349
|
}
|
|
220
|
-
const privateKeyPem = String(identity.private_key_pem ?? '').trim();
|
|
221
|
-
const certPem = normalizeIdentityCertPem(identity);
|
|
222
|
-
if (!privateKeyPem || !certPem) {
|
|
223
|
-
throw new StateError('local identity missing private key or certificate');
|
|
224
|
-
}
|
|
225
|
-
let payload = parseAgentMdTailSignature(String(content ?? '')).payload;
|
|
226
|
-
if (payload && !payload.endsWith('\n') && !payload.endsWith('\r')) {
|
|
227
|
-
payload += '\n';
|
|
228
|
-
}
|
|
229
|
-
const signer = createSign('sha256');
|
|
230
|
-
signer.update(payload);
|
|
231
|
-
signer.end();
|
|
232
|
-
const signature = signer.sign(privateKeyPem).toString('base64');
|
|
233
|
-
const fingerprint = certificateSha256Fingerprint(certPem);
|
|
234
|
-
if (!fingerprint) {
|
|
235
|
-
throw new StateError('agent.md cert fingerprint failed');
|
|
236
|
-
}
|
|
237
|
-
const signedBlock = [
|
|
238
|
-
AGENT_MD_SIGNATURE_MARKER,
|
|
239
|
-
`cert_fingerprint: ${fingerprint}`,
|
|
240
|
-
`timestamp: ${Math.floor(Date.now() / 1000)}`,
|
|
241
|
-
`signature: ${signature}`,
|
|
242
|
-
'-->',
|
|
243
|
-
].join('\n');
|
|
244
|
-
return payload + signedBlock;
|
|
245
350
|
}
|
|
246
351
|
async verifyAgentMd(content, opts) {
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
352
|
+
const tStart = Date.now();
|
|
353
|
+
this._log.debug(`verifyAgentMd enter: aid_hint=${opts?.aid ?? '-'} content_len=${content?.length ?? 0}`);
|
|
354
|
+
try {
|
|
355
|
+
const { payload, fields, parseError } = parseAgentMdTailSignature(String(content ?? ''));
|
|
356
|
+
if (!fields) {
|
|
357
|
+
if (!parseError) {
|
|
358
|
+
this._log.debug(`verifyAgentMd exit: elapsed=${Date.now() - tStart}ms (unsigned)`);
|
|
359
|
+
return agentMdResult('unsigned', payload);
|
|
360
|
+
}
|
|
361
|
+
this._log.warn(`verifyAgentMd invalid: parse_error=${parseError}`);
|
|
362
|
+
this._log.debug(`verifyAgentMd exit: elapsed=${Date.now() - tStart}ms (parse_error)`);
|
|
363
|
+
return agentMdResult('invalid', payload, parseError);
|
|
364
|
+
}
|
|
365
|
+
let expectedAid = String(opts?.aid ?? '').trim();
|
|
366
|
+
const payloadAid = extractAgentMDAid(payload);
|
|
367
|
+
if (expectedAid && payloadAid && expectedAid !== payloadAid) {
|
|
368
|
+
this._log.warn(`verifyAgentMd invalid: aid_mismatch hint=${expectedAid} payload_aid=${payloadAid}`);
|
|
369
|
+
this._log.debug(`verifyAgentMd exit: elapsed=${Date.now() - tStart}ms (aid_mismatch)`);
|
|
370
|
+
return agentMdResult('invalid', payload, 'aid mismatch', payloadAid);
|
|
251
371
|
}
|
|
252
|
-
return agentMdResult('invalid', payload, parseError);
|
|
253
|
-
}
|
|
254
|
-
let expectedAid = String(opts?.aid ?? '').trim();
|
|
255
|
-
const payloadAid = extractAgentMDAid(payload);
|
|
256
|
-
if (expectedAid && payloadAid && expectedAid !== payloadAid) {
|
|
257
|
-
return agentMdResult('invalid', payload, 'aid mismatch', payloadAid);
|
|
258
|
-
}
|
|
259
|
-
if (!expectedAid) {
|
|
260
|
-
expectedAid = payloadAid;
|
|
261
|
-
}
|
|
262
|
-
let certPem = String(opts?.certPem ?? opts?.cert_pem ?? '').trim();
|
|
263
|
-
if (!certPem) {
|
|
264
372
|
if (!expectedAid) {
|
|
265
|
-
|
|
373
|
+
expectedAid = payloadAid;
|
|
266
374
|
}
|
|
267
|
-
|
|
268
|
-
if (
|
|
269
|
-
|
|
375
|
+
let certPem = String(opts?.certPem ?? opts?.cert_pem ?? '').trim();
|
|
376
|
+
if (!certPem) {
|
|
377
|
+
if (!expectedAid) {
|
|
378
|
+
this._log.warn('verifyAgentMd invalid: aid required to verify agent.md');
|
|
379
|
+
this._log.debug(`verifyAgentMd exit: elapsed=${Date.now() - tStart}ms (aid_required)`);
|
|
380
|
+
return agentMdResult('invalid', payload, 'aid required to verify agent.md');
|
|
381
|
+
}
|
|
382
|
+
const fetchPeerCert = this._internal._fetchPeerCert;
|
|
383
|
+
if (typeof fetchPeerCert !== 'function') {
|
|
384
|
+
this._log.warn(`verifyAgentMd invalid: client cannot fetch peer cert aid=${expectedAid}`);
|
|
385
|
+
this._log.debug(`verifyAgentMd exit: elapsed=${Date.now() - tStart}ms (no_fetch_fn)`);
|
|
386
|
+
return agentMdResult('invalid', payload, 'aid required to verify agent.md', expectedAid);
|
|
387
|
+
}
|
|
388
|
+
try {
|
|
389
|
+
certPem = String(await fetchPeerCert(expectedAid, fields.cert_fingerprint)).trim();
|
|
390
|
+
}
|
|
391
|
+
catch (error) {
|
|
392
|
+
this._log.warn(`verifyAgentMd invalid: peer cert fetch failed aid=${expectedAid} err=${String(error)}`);
|
|
393
|
+
this._log.debug(`verifyAgentMd exit: elapsed=${Date.now() - tStart}ms (cert_fetch_failed)`);
|
|
394
|
+
return agentMdResult('invalid', payload, String(error), expectedAid, fields.cert_fingerprint);
|
|
395
|
+
}
|
|
270
396
|
}
|
|
397
|
+
let cert;
|
|
271
398
|
try {
|
|
272
|
-
|
|
399
|
+
cert = new X509Certificate(certPem);
|
|
273
400
|
}
|
|
274
401
|
catch (error) {
|
|
275
|
-
|
|
402
|
+
this._log.warn(`verifyAgentMd invalid: cert parse failed aid=${expectedAid || '-'} err=${String(error)}`);
|
|
403
|
+
this._log.debug(`verifyAgentMd exit: elapsed=${Date.now() - tStart}ms (cert_parse_failed)`);
|
|
404
|
+
return agentMdResult('invalid', payload, `invalid certificate: ${String(error)}`, expectedAid, fields.cert_fingerprint);
|
|
276
405
|
}
|
|
406
|
+
const actualFingerprint = certificateSha256Fingerprint(certPem);
|
|
407
|
+
if (!actualFingerprint || actualFingerprint.toLowerCase() !== fields.cert_fingerprint.toLowerCase()) {
|
|
408
|
+
this._log.warn(`verifyAgentMd invalid: fingerprint_mismatch expected=${fields.cert_fingerprint} actual=${actualFingerprint}`);
|
|
409
|
+
this._log.debug(`verifyAgentMd exit: elapsed=${Date.now() - tStart}ms (fingerprint_mismatch)`);
|
|
410
|
+
return agentMdResult('invalid', payload, 'certificate fingerprint mismatch', expectedAid, fields.cert_fingerprint);
|
|
411
|
+
}
|
|
412
|
+
const cn = extractCommonName(cert.subject);
|
|
413
|
+
if (expectedAid && cn && cn !== expectedAid) {
|
|
414
|
+
this._log.warn(`verifyAgentMd invalid: cert cn=${cn} aid=${expectedAid}`);
|
|
415
|
+
this._log.debug(`verifyAgentMd exit: elapsed=${Date.now() - tStart}ms (cn_mismatch)`);
|
|
416
|
+
return agentMdResult('invalid', payload, 'certificate aid mismatch', expectedAid, fields.cert_fingerprint);
|
|
417
|
+
}
|
|
418
|
+
const signature = Buffer.from(fields.signature, 'base64');
|
|
419
|
+
if (!signature.length) {
|
|
420
|
+
this._log.warn(`verifyAgentMd invalid: empty signature aid=${expectedAid || '-'}`);
|
|
421
|
+
this._log.debug(`verifyAgentMd exit: elapsed=${Date.now() - tStart}ms (empty_signature)`);
|
|
422
|
+
return agentMdResult('invalid', payload, 'invalid signature', expectedAid, fields.cert_fingerprint);
|
|
423
|
+
}
|
|
424
|
+
const verifier = createVerify('sha256');
|
|
425
|
+
verifier.update(payload);
|
|
426
|
+
verifier.end();
|
|
427
|
+
const ok = verifier.verify(cert.publicKey, signature);
|
|
428
|
+
if (!ok) {
|
|
429
|
+
this._log.warn(`verifyAgentMd invalid: signature verification failed aid=${expectedAid || '-'}`);
|
|
430
|
+
this._log.debug(`verifyAgentMd exit: elapsed=${Date.now() - tStart}ms (signature_failed)`);
|
|
431
|
+
return agentMdResult('invalid', payload, 'signature verification failed', expectedAid, fields.cert_fingerprint, parseAgentMdTimestamp(fields.timestamp));
|
|
432
|
+
}
|
|
433
|
+
this._log.debug(`verifyAgentMd exit: elapsed=${Date.now() - tStart}ms (verified) aid=${expectedAid || payloadAid}`);
|
|
434
|
+
return agentMdResult('verified', payload, '', expectedAid || payloadAid, fields.cert_fingerprint, parseAgentMdTimestamp(fields.timestamp));
|
|
277
435
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
282
|
-
catch (error) {
|
|
283
|
-
return agentMdResult('invalid', payload, `invalid certificate: ${String(error)}`, expectedAid, fields.cert_fingerprint);
|
|
284
|
-
}
|
|
285
|
-
const actualFingerprint = certificateSha256Fingerprint(certPem);
|
|
286
|
-
if (!actualFingerprint || actualFingerprint.toLowerCase() !== fields.cert_fingerprint.toLowerCase()) {
|
|
287
|
-
return agentMdResult('invalid', payload, 'certificate fingerprint mismatch', expectedAid, fields.cert_fingerprint);
|
|
288
|
-
}
|
|
289
|
-
const cn = extractCommonName(cert.subject);
|
|
290
|
-
if (expectedAid && cn && cn !== expectedAid) {
|
|
291
|
-
return agentMdResult('invalid', payload, 'certificate aid mismatch', expectedAid, fields.cert_fingerprint);
|
|
292
|
-
}
|
|
293
|
-
const signature = Buffer.from(fields.signature, 'base64');
|
|
294
|
-
if (!signature.length) {
|
|
295
|
-
return agentMdResult('invalid', payload, 'invalid signature', expectedAid, fields.cert_fingerprint);
|
|
296
|
-
}
|
|
297
|
-
const verifier = createVerify('sha256');
|
|
298
|
-
verifier.update(payload);
|
|
299
|
-
verifier.end();
|
|
300
|
-
const ok = verifier.verify(cert.publicKey, signature);
|
|
301
|
-
if (!ok) {
|
|
302
|
-
return agentMdResult('invalid', payload, 'signature verification failed', expectedAid, fields.cert_fingerprint, parseAgentMdTimestamp(fields.timestamp));
|
|
436
|
+
catch (err) {
|
|
437
|
+
this._log.debug(`verifyAgentMd exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
|
|
438
|
+
throw err;
|
|
303
439
|
}
|
|
304
|
-
return agentMdResult('verified', payload, '', expectedAid || payloadAid, fields.cert_fingerprint, parseAgentMdTimestamp(fields.timestamp));
|
|
305
440
|
}
|
|
306
441
|
async _resolveAgentMdUrl(aid) {
|
|
307
442
|
const resolvedAid = String(aid ?? '').trim();
|
|
@@ -355,79 +490,148 @@ export class AuthNamespace {
|
|
|
355
490
|
return token;
|
|
356
491
|
}
|
|
357
492
|
async uploadAgentMd(content) {
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
493
|
+
const tStart = Date.now();
|
|
494
|
+
this._log.debug(`uploadAgentMd enter: content_len=${content?.length ?? 0}`);
|
|
495
|
+
try {
|
|
496
|
+
const client = this._internal;
|
|
497
|
+
const auth = client._auth;
|
|
498
|
+
const identity = auth.loadIdentityOrNone(client._aid ?? undefined);
|
|
499
|
+
if (!identity) {
|
|
500
|
+
throw new StateError('no local identity found, call auth.createAid() first');
|
|
501
|
+
}
|
|
502
|
+
const aid = String(identity.aid ?? client._aid ?? '').trim();
|
|
503
|
+
if (!aid) {
|
|
504
|
+
throw new StateError('no local identity found, call auth.createAid() first');
|
|
505
|
+
}
|
|
506
|
+
const gatewayUrl = await this._resolveGateway(aid);
|
|
507
|
+
client._gatewayUrl = gatewayUrl;
|
|
508
|
+
const token = await this._ensureAgentMdUploadToken(aid, gatewayUrl);
|
|
509
|
+
const response = await fetchWithTimeout(await this._resolveAgentMdUrl(aid), {
|
|
510
|
+
method: 'PUT',
|
|
511
|
+
headers: {
|
|
512
|
+
Authorization: `Bearer ${token}`,
|
|
513
|
+
'Content-Type': 'text/markdown; charset=utf-8',
|
|
514
|
+
},
|
|
515
|
+
body: content,
|
|
516
|
+
});
|
|
517
|
+
if (response.status === 404) {
|
|
518
|
+
throw new NotFoundError(`agent.md endpoint not found for aid: ${aid}`);
|
|
519
|
+
}
|
|
520
|
+
if (!response.ok) {
|
|
521
|
+
const message = (await response.text()).trim();
|
|
522
|
+
throw new AUNError(`upload agent.md failed: HTTP ${response.status}${message ? ` - ${message}` : ''}`);
|
|
523
|
+
}
|
|
524
|
+
const payload = await response.json();
|
|
525
|
+
if (!isJsonObject(payload)) {
|
|
526
|
+
throw new AUNError('upload agent.md returned invalid JSON payload');
|
|
527
|
+
}
|
|
528
|
+
this._log.debug(`uploadAgentMd exit: elapsed=${Date.now() - tStart}ms aid=${aid}`);
|
|
529
|
+
return payload;
|
|
363
530
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
throw
|
|
531
|
+
catch (err) {
|
|
532
|
+
this._log.debug(`uploadAgentMd exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
|
|
533
|
+
throw err;
|
|
367
534
|
}
|
|
368
|
-
const gatewayUrl = await this._resolveGateway(aid);
|
|
369
|
-
client._gatewayUrl = gatewayUrl;
|
|
370
|
-
const token = await this._ensureAgentMdUploadToken(aid, gatewayUrl);
|
|
371
|
-
const response = await fetchWithTimeout(await this._resolveAgentMdUrl(aid), {
|
|
372
|
-
method: 'PUT',
|
|
373
|
-
headers: {
|
|
374
|
-
Authorization: `Bearer ${token}`,
|
|
375
|
-
'Content-Type': 'text/markdown; charset=utf-8',
|
|
376
|
-
},
|
|
377
|
-
body: content,
|
|
378
|
-
});
|
|
379
|
-
if (response.status === 404) {
|
|
380
|
-
throw new NotFoundError(`agent.md endpoint not found for aid: ${aid}`);
|
|
381
|
-
}
|
|
382
|
-
if (!response.ok) {
|
|
383
|
-
const message = (await response.text()).trim();
|
|
384
|
-
throw new AUNError(`upload agent.md failed: HTTP ${response.status}${message ? ` - ${message}` : ''}`);
|
|
385
|
-
}
|
|
386
|
-
const payload = await response.json();
|
|
387
|
-
if (!isJsonObject(payload)) {
|
|
388
|
-
throw new AUNError('upload agent.md returned invalid JSON payload');
|
|
389
|
-
}
|
|
390
|
-
return payload;
|
|
391
535
|
}
|
|
392
536
|
async downloadAgentMd(aid) {
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
537
|
+
const tStart = Date.now();
|
|
538
|
+
this._log.debug(`downloadAgentMd enter: aid=${aid}`);
|
|
539
|
+
try {
|
|
540
|
+
const targetAid = String(aid ?? '').trim();
|
|
541
|
+
if (!targetAid) {
|
|
542
|
+
throw new ValidationError('downloadAgentMd requires non-empty aid');
|
|
543
|
+
}
|
|
544
|
+
const response = await fetchWithTimeout(await this._resolveAgentMdUrl(targetAid), {
|
|
545
|
+
method: 'GET',
|
|
546
|
+
headers: {
|
|
547
|
+
Accept: 'text/markdown',
|
|
548
|
+
},
|
|
549
|
+
});
|
|
550
|
+
if (response.status === 404) {
|
|
551
|
+
throw new NotFoundError(`agent.md not found for aid: ${targetAid}`);
|
|
552
|
+
}
|
|
553
|
+
if (!response.ok) {
|
|
554
|
+
const message = (await response.text()).trim();
|
|
555
|
+
throw new AUNError(`download agent.md failed: HTTP ${response.status}${message ? ` - ${message}` : ''}`);
|
|
556
|
+
}
|
|
557
|
+
const text = await response.text();
|
|
558
|
+
this._log.debug(`downloadAgentMd exit: elapsed=${Date.now() - tStart}ms aid=${targetAid} bytes=${text.length}`);
|
|
559
|
+
return text;
|
|
560
|
+
}
|
|
561
|
+
catch (err) {
|
|
562
|
+
this._log.debug(`downloadAgentMd exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
|
|
563
|
+
throw err;
|
|
564
|
+
}
|
|
411
565
|
}
|
|
412
566
|
/** 下载证书 */
|
|
413
567
|
async downloadCert(params) {
|
|
414
|
-
|
|
568
|
+
const tStart = Date.now();
|
|
569
|
+
this._log.debug(`downloadCert enter`);
|
|
570
|
+
try {
|
|
571
|
+
const result = await this._client.call('auth.download_cert', params ?? {});
|
|
572
|
+
this._log.debug(`downloadCert exit: elapsed=${Date.now() - tStart}ms`);
|
|
573
|
+
return result;
|
|
574
|
+
}
|
|
575
|
+
catch (err) {
|
|
576
|
+
this._log.debug(`downloadCert exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
|
|
577
|
+
throw err;
|
|
578
|
+
}
|
|
415
579
|
}
|
|
416
580
|
/** 请求签发证书 */
|
|
417
581
|
async requestCert(params) {
|
|
418
|
-
|
|
582
|
+
const tStart = Date.now();
|
|
583
|
+
this._log.debug(`requestCert enter`);
|
|
584
|
+
try {
|
|
585
|
+
const result = await this._client.call('auth.request_cert', params);
|
|
586
|
+
this._log.debug(`requestCert exit: elapsed=${Date.now() - tStart}ms`);
|
|
587
|
+
return result;
|
|
588
|
+
}
|
|
589
|
+
catch (err) {
|
|
590
|
+
this._log.debug(`requestCert exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
|
|
591
|
+
throw err;
|
|
592
|
+
}
|
|
419
593
|
}
|
|
420
594
|
/** 续期证书 */
|
|
421
595
|
async renewCert(params) {
|
|
422
|
-
|
|
596
|
+
const tStart = Date.now();
|
|
597
|
+
this._log.debug(`renewCert enter`);
|
|
598
|
+
try {
|
|
599
|
+
const result = await this._client.call('auth.renew_cert', params ?? {});
|
|
600
|
+
this._log.debug(`renewCert exit: elapsed=${Date.now() - tStart}ms`);
|
|
601
|
+
return result;
|
|
602
|
+
}
|
|
603
|
+
catch (err) {
|
|
604
|
+
this._log.debug(`renewCert exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
|
|
605
|
+
throw err;
|
|
606
|
+
}
|
|
423
607
|
}
|
|
424
608
|
/** 密钥轮换 */
|
|
425
609
|
async rekey(params) {
|
|
426
|
-
|
|
610
|
+
const tStart = Date.now();
|
|
611
|
+
this._log.debug(`rekey enter`);
|
|
612
|
+
try {
|
|
613
|
+
const result = await this._client.call('auth.rekey', params ?? {});
|
|
614
|
+
this._log.debug(`rekey exit: elapsed=${Date.now() - tStart}ms`);
|
|
615
|
+
return result;
|
|
616
|
+
}
|
|
617
|
+
catch (err) {
|
|
618
|
+
this._log.debug(`rekey exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
|
|
619
|
+
throw err;
|
|
620
|
+
}
|
|
427
621
|
}
|
|
428
622
|
/** 获取信任根证书列表 */
|
|
429
623
|
async trustRoots(params) {
|
|
430
|
-
|
|
624
|
+
const tStart = Date.now();
|
|
625
|
+
this._log.debug(`trustRoots enter`);
|
|
626
|
+
try {
|
|
627
|
+
const result = await this._client.call('meta.trust_roots', params ?? {});
|
|
628
|
+
this._log.debug(`trustRoots exit: elapsed=${Date.now() - tStart}ms`);
|
|
629
|
+
return result;
|
|
630
|
+
}
|
|
631
|
+
catch (err) {
|
|
632
|
+
this._log.debug(`trustRoots exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
|
|
633
|
+
throw err;
|
|
634
|
+
}
|
|
431
635
|
}
|
|
432
636
|
}
|
|
433
637
|
//# sourceMappingURL=auth.js.map
|