@agentunion/fastaun 0.4.4 → 0.4.5
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/CHANGELOG.md +15 -0
- package/_packed_docs/CHANGELOG.md +15 -0
- package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +1 -1
- package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +1 -1
- package/dist/aid-store.d.ts +1 -0
- package/dist/aid-store.js +26 -3
- package/dist/aid-store.js.map +1 -1
- package/dist/auth.d.ts +17 -32
- package/dist/auth.js +42 -295
- package/dist/auth.js.map +1 -1
- package/dist/client.d.ts +4 -3
- package/dist/client.js +77 -92
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/keystore/aid-db.d.ts +0 -4
- package/dist/keystore/aid-db.js +4 -95
- package/dist/keystore/aid-db.js.map +1 -1
- package/dist/keystore/file.d.ts +8 -3
- package/dist/keystore/file.js +103 -24
- package/dist/keystore/file.js.map +1 -1
- package/dist/keystore/index.d.ts +39 -36
- package/dist/keystore/index.js +3 -2
- package/dist/keystore/index.js.map +1 -1
- package/dist/register-flow.d.ts +49 -0
- package/dist/register-flow.js +366 -0
- package/dist/register-flow.js.map +1 -0
- package/dist/secret-store/file-store.js +6 -1
- package/dist/secret-store/file-store.js.map +1 -1
- package/dist/v2/session/keystore.d.ts +5 -0
- package/dist/v2/session/keystore.js +21 -3
- package/dist/v2/session/keystore.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/_packed_docs/0.4.0_/345/267/256/345/274/202/346/240/270/345/256/236/345/206/263/347/255/226/350/256/260/345/275/225.md +0 -302
- package/_packed_docs/AUN_SDK_0.4.0_/350/256/276/350/256/241/345/257/271/346/257/224/345/210/206/346/236/220.md +0 -194
- package/_packed_docs/AUN_SDK_/351/207/215/346/236/204/345/256/236/346/226/275/350/256/241/345/210/222.md +0 -596
- package/_packed_docs/AUN_SDK_/351/207/215/346/236/204/350/256/276/350/256/241/346/226/271/346/241/210_v3.md +0 -1698
- package/_packed_docs/python-sdk-v2-only-changelog.md +0 -189
package/dist/auth.js
CHANGED
|
@@ -17,7 +17,7 @@ import * as https from 'node:https';
|
|
|
17
17
|
import * as path from 'node:path';
|
|
18
18
|
import { URL, fileURLToPath } from 'node:url';
|
|
19
19
|
import WebSocket from 'ws';
|
|
20
|
-
import { AuthError, StateError, ValidationError,
|
|
20
|
+
import { AuthError, StateError, ValidationError, mapRemoteError } from './errors.js';
|
|
21
21
|
import { isJsonObject, } from './types.js';
|
|
22
22
|
import { VERSION as AUN_SDK_VERSION } from './version.js';
|
|
23
23
|
const _noopLogger = {
|
|
@@ -236,7 +236,7 @@ export class AuthFlow {
|
|
|
236
236
|
'access_token_expires_at',
|
|
237
237
|
];
|
|
238
238
|
_logger;
|
|
239
|
-
|
|
239
|
+
_tokenStore;
|
|
240
240
|
_crypto;
|
|
241
241
|
_aid;
|
|
242
242
|
_deviceId;
|
|
@@ -258,9 +258,11 @@ export class AuthFlow {
|
|
|
258
258
|
_chainVerifiedCache = new Map();
|
|
259
259
|
// Gateway CA 链预验证标记:cache_key -> verified
|
|
260
260
|
_gatewayCaVerified = new Map();
|
|
261
|
+
// 调用方注入的内存私钥(由 AUNClient.loadIdentity 传入,不走 tokenStore 解密)
|
|
262
|
+
_memIdentity = null;
|
|
261
263
|
constructor(opts) {
|
|
262
264
|
this._logger = opts.logger ?? _noopLogger;
|
|
263
|
-
this.
|
|
265
|
+
this._tokenStore = opts.tokenStore;
|
|
264
266
|
this._crypto = opts.crypto;
|
|
265
267
|
this._aid = opts.aid ?? null;
|
|
266
268
|
this._deviceId = String(opts.deviceId ?? '').trim();
|
|
@@ -273,10 +275,19 @@ export class AuthFlow {
|
|
|
273
275
|
this._rootCerts = this._loadRootCerts(this._rootCaPath);
|
|
274
276
|
}
|
|
275
277
|
// ── 公开方法 ────────────────────────────────────────────────
|
|
278
|
+
/**
|
|
279
|
+
* 注入内存私钥(由 AUNClient.loadIdentity 调用)。
|
|
280
|
+
* AuthFlow 内部不再从 tokenStore 解密私钥,只使用此处注入的明文。
|
|
281
|
+
*/
|
|
282
|
+
setIdentity(identity) {
|
|
283
|
+
this._memIdentity = identity;
|
|
284
|
+
if (identity?.aid)
|
|
285
|
+
this._aid = String(identity.aid);
|
|
286
|
+
}
|
|
276
287
|
/** 加载身份信息(密钥对 + 证书 + 元数据) */
|
|
277
288
|
loadIdentity(aid) {
|
|
278
289
|
const identity = this._loadIdentityOrRaise(aid);
|
|
279
|
-
const cert = this.
|
|
290
|
+
const cert = this._tokenStore.loadCert(String(identity.aid));
|
|
280
291
|
if (cert)
|
|
281
292
|
identity.cert = cert;
|
|
282
293
|
const instanceState = this._loadInstanceState(String(identity.aid));
|
|
@@ -317,235 +328,6 @@ export class AuthFlow {
|
|
|
317
328
|
this._deviceId = String(opts.deviceId ?? '').trim();
|
|
318
329
|
this._slotId = String(opts.slotId ?? '').trim();
|
|
319
330
|
}
|
|
320
|
-
/**
|
|
321
|
-
* 注册新 AID(原子流程,必须由应用层显式调用)。
|
|
322
|
-
*
|
|
323
|
-
* 安全约束(与 Python register_aid 对齐):
|
|
324
|
-
* - **绝不**被 SDK 内部任何路径自动调用(authenticate / connect / 等)
|
|
325
|
-
* - 本地已有任何痕迹 → 抛 IdentityConflictError,应用层应改用 loadIdentity
|
|
326
|
-
* - 服务端已注册同名 AID → 抛 IdentityConflictError
|
|
327
|
-
*
|
|
328
|
-
* 异常分支与恢复:
|
|
329
|
-
* A. 全新流程:临时目录生成 keypair → RPC → cert → 原子 rename
|
|
330
|
-
* B. 恢复流程:上次 RPC 没收到响应就崩溃;扫描 _pending,公钥匹配
|
|
331
|
-
* 则下载 cert 完成 promote(防止"已注册但本地没保存")
|
|
332
|
-
*/
|
|
333
|
-
async registerAid(gatewayUrl, aid) {
|
|
334
|
-
const tStart = Date.now();
|
|
335
|
-
AuthFlow._validateAidName(aid);
|
|
336
|
-
this._logger.debug(`registerAid enter: aid=${aid}, gateway=${gatewayUrl}`);
|
|
337
|
-
try {
|
|
338
|
-
// Step 1: 本地完整身份的幂等处理
|
|
339
|
-
// - 服务端 cert 公钥匹配 → 视为已注册成功,幂等返回
|
|
340
|
-
// - 服务端 cert 公钥不匹配 → IdentityConflictError(被别人占)
|
|
341
|
-
// - 服务端无记录 → IdentityConflictError(要求清理本地)
|
|
342
|
-
const existing = this._keystore.loadIdentity(aid);
|
|
343
|
-
if (existing !== null && existing.public_key_der_b64) {
|
|
344
|
-
this._logger.debug(`registerAid: local keypair exists, checking server for idempotency: aid=${aid}`);
|
|
345
|
-
const localPubB64 = String(existing.public_key_der_b64);
|
|
346
|
-
const serverCertPem = await this._downloadRegisteredCert(gatewayUrl, aid);
|
|
347
|
-
if (!serverCertPem) {
|
|
348
|
-
// 服务端无记录 → 用现有 keypair 发起注册
|
|
349
|
-
this._logger.debug(`registerAid: server has no record, registering with existing keypair: aid=${aid}`);
|
|
350
|
-
const pendingDir = this._keystore.pendingIdentityDir?.(aid);
|
|
351
|
-
let created;
|
|
352
|
-
try {
|
|
353
|
-
created = await this._createAid(gatewayUrl, existing);
|
|
354
|
-
}
|
|
355
|
-
catch (e) {
|
|
356
|
-
this._logger.warn(`registerAid RPC failed (existing keypair): aid=${aid}, error=${e instanceof Error ? e.message : String(e)}`);
|
|
357
|
-
throw e;
|
|
358
|
-
}
|
|
359
|
-
const certPem = String(created.cert ?? '');
|
|
360
|
-
if (!certPem) {
|
|
361
|
-
throw new AuthError(`registerAid: server response missing cert for ${aid}`);
|
|
362
|
-
}
|
|
363
|
-
existing.cert = certPem;
|
|
364
|
-
this._assertCertMatchesLocalKeypair(existing);
|
|
365
|
-
this._persistIdentity(existing);
|
|
366
|
-
this._aid = aid;
|
|
367
|
-
this._logger.debug(`registerAid exit (recovered): elapsed=${Date.now() - tStart}ms aid=${aid}`);
|
|
368
|
-
return { aid, cert: certPem };
|
|
369
|
-
}
|
|
370
|
-
// 比对公钥
|
|
371
|
-
const cert = _loadX509(serverCertPem);
|
|
372
|
-
const serverPubDer = _extractPublicKey(cert).export({ type: 'spki', format: 'der' });
|
|
373
|
-
const localPubDer = Buffer.from(localPubB64, 'base64');
|
|
374
|
-
if (!serverPubDer.equals(localPubDer)) {
|
|
375
|
-
throw new IdentityConflictError(`AID '${aid}' is registered by another party on server (public key mismatch). ` +
|
|
376
|
-
`Choose a different name.`);
|
|
377
|
-
}
|
|
378
|
-
// 公钥匹配 → 幂等返回;如本地缺 cert,把服务端 cert 写入
|
|
379
|
-
this._logger.info(`registerAid: idempotent return for already-registered AID: aid=${aid}`);
|
|
380
|
-
if (!existing.cert) {
|
|
381
|
-
existing.cert = serverCertPem;
|
|
382
|
-
this._persistIdentity(existing);
|
|
383
|
-
}
|
|
384
|
-
this._aid = aid;
|
|
385
|
-
return { aid, cert: serverCertPem };
|
|
386
|
-
}
|
|
387
|
-
// Step 2: 检查 _pending/ 残留临时目录(崩溃恢复)
|
|
388
|
-
const recovered = await this._tryRecoverPendingRegistration(gatewayUrl, aid);
|
|
389
|
-
if (recovered !== null) {
|
|
390
|
-
this._logger.info(`registerAid recovered from pending: aid=${aid}`);
|
|
391
|
-
return recovered;
|
|
392
|
-
}
|
|
393
|
-
// Step 3: 服务端查重
|
|
394
|
-
const existingCert = await this._downloadRegisteredCert(gatewayUrl, aid);
|
|
395
|
-
if (existingCert) {
|
|
396
|
-
this._logger.warn(`registerAid aborted: AID already registered on server: aid=${aid}`);
|
|
397
|
-
throw new IdentityConflictError(`AID '${aid}' is already registered on server. Choose a different name, or if you own the keypair use a recovery flow.`);
|
|
398
|
-
}
|
|
399
|
-
// Step 4: 创建临时目录 + 生成 keypair + 写 priv
|
|
400
|
-
const identity = this._crypto.generateIdentity();
|
|
401
|
-
identity.aid = aid;
|
|
402
|
-
const pendingDir = this._keystore.pendingIdentityDir(aid);
|
|
403
|
-
this._writePendingKeypair(pendingDir, identity);
|
|
404
|
-
// Step 5: 服务端注册拿 cert
|
|
405
|
-
let created;
|
|
406
|
-
try {
|
|
407
|
-
created = await this._createAid(gatewayUrl, identity);
|
|
408
|
-
}
|
|
409
|
-
catch (e) {
|
|
410
|
-
this._logger.warn(`registerAid RPC failed (pending kept for recovery): aid=${aid}, pending=${pendingDir}, error=${e instanceof Error ? e.message : String(e)}`);
|
|
411
|
-
throw e;
|
|
412
|
-
}
|
|
413
|
-
const certPem = String(created.cert ?? '');
|
|
414
|
-
if (!certPem) {
|
|
415
|
-
throw new AuthError(`registerAid: server response missing cert for ${aid}`);
|
|
416
|
-
}
|
|
417
|
-
identity.cert = certPem;
|
|
418
|
-
// Step 6: 校验 cert 公钥 == 本地公钥
|
|
419
|
-
this._assertCertMatchesLocalKeypair(identity);
|
|
420
|
-
// Step 7: 写 cert.pem 到临时目录
|
|
421
|
-
this._writePendingCert(pendingDir, certPem);
|
|
422
|
-
// Step 8: 原子 rename
|
|
423
|
-
try {
|
|
424
|
-
this._keystore.promotePendingIdentity(pendingDir, aid);
|
|
425
|
-
}
|
|
426
|
-
catch (e) {
|
|
427
|
-
this._logger.warn(`registerAid promote failed: aid=${aid}, error=${e instanceof Error ? e.message : String(e)}`);
|
|
428
|
-
throw new IdentityConflictError(`AID '${aid}' was created by another process during registration; pending dir kept for cleanup.`);
|
|
429
|
-
}
|
|
430
|
-
// Step 9: 标准持久化
|
|
431
|
-
this._persistIdentity(identity);
|
|
432
|
-
this._aid = aid;
|
|
433
|
-
this._logger.debug(`registerAid exit: elapsed=${Date.now() - tStart}ms aid=${aid}`);
|
|
434
|
-
return { aid: identity.aid, cert: identity.cert };
|
|
435
|
-
}
|
|
436
|
-
catch (err) {
|
|
437
|
-
this._logger.debug(`registerAid exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
|
|
438
|
-
throw err;
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
/**
|
|
442
|
-
* 检查 AIDs/_pending/ 下是否有该 aid 的残留,尝试恢复(崩溃恢复)。
|
|
443
|
-
* 返回 dict 表示恢复成功;null 表示无残留 / 残留已清理;抛错表示明确失败。
|
|
444
|
-
*/
|
|
445
|
-
async _tryRecoverPendingRegistration(gatewayUrl, aid) {
|
|
446
|
-
const aidsRoot = this._keystore._aidsRoot;
|
|
447
|
-
const pendingRoot = path.join(aidsRoot, '_pending');
|
|
448
|
-
if (!fs.existsSync(pendingRoot))
|
|
449
|
-
return null;
|
|
450
|
-
const safe = this._keystore.constructor === undefined ? aid : aid;
|
|
451
|
-
const safeAid = aid.replace(/\//g, '_').replace(/\\/g, '_').replace(/:/g, '_');
|
|
452
|
-
const prefix = safeAid + '-';
|
|
453
|
-
const candidates = fs.readdirSync(pendingRoot, { withFileTypes: true })
|
|
454
|
-
.filter((e) => e.isDirectory() && e.name.startsWith(prefix))
|
|
455
|
-
.map((e) => path.join(pendingRoot, e.name))
|
|
456
|
-
.sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
|
|
457
|
-
if (candidates.length === 0)
|
|
458
|
-
return null;
|
|
459
|
-
for (const pendingDir of candidates) {
|
|
460
|
-
const privPath = path.join(pendingDir, 'private', 'key.json');
|
|
461
|
-
if (!fs.existsSync(privPath)) {
|
|
462
|
-
try {
|
|
463
|
-
fs.rmSync(pendingDir, { recursive: true, force: true });
|
|
464
|
-
}
|
|
465
|
-
catch { /* ignore */ }
|
|
466
|
-
continue;
|
|
467
|
-
}
|
|
468
|
-
let privData;
|
|
469
|
-
try {
|
|
470
|
-
privData = JSON.parse(fs.readFileSync(privPath, 'utf-8'));
|
|
471
|
-
}
|
|
472
|
-
catch {
|
|
473
|
-
try {
|
|
474
|
-
fs.rmSync(pendingDir, { recursive: true, force: true });
|
|
475
|
-
}
|
|
476
|
-
catch { /* ignore */ }
|
|
477
|
-
continue;
|
|
478
|
-
}
|
|
479
|
-
const localPriv = privData.private_key_pem ?? '';
|
|
480
|
-
const localPubB64 = privData.public_key_der_b64 ?? '';
|
|
481
|
-
if (!localPriv || !localPubB64) {
|
|
482
|
-
try {
|
|
483
|
-
fs.rmSync(pendingDir, { recursive: true, force: true });
|
|
484
|
-
}
|
|
485
|
-
catch { /* ignore */ }
|
|
486
|
-
continue;
|
|
487
|
-
}
|
|
488
|
-
const serverCert = await this._downloadRegisteredCert(gatewayUrl, aid);
|
|
489
|
-
if (!serverCert) {
|
|
490
|
-
// 上次 RPC 没生效 → 清理临时目录,由调用方走全新注册流程
|
|
491
|
-
this._logger.info(`pending dir found but server has no registration; cleaning up: ${pendingDir}`);
|
|
492
|
-
try {
|
|
493
|
-
fs.rmSync(pendingDir, { recursive: true, force: true });
|
|
494
|
-
}
|
|
495
|
-
catch { /* ignore */ }
|
|
496
|
-
return null;
|
|
497
|
-
}
|
|
498
|
-
// 比对公钥
|
|
499
|
-
const cert = _loadX509(serverCert);
|
|
500
|
-
const certPubKey = _extractPublicKey(cert);
|
|
501
|
-
const certPubDer = certPubKey.export({ type: 'spki', format: 'der' });
|
|
502
|
-
const localPubDer = Buffer.from(localPubB64, 'base64');
|
|
503
|
-
if (!certPubDer.equals(localPubDer)) {
|
|
504
|
-
this._logger.warn(`pending dir public key does not match server cert; AID '${aid}' was taken`);
|
|
505
|
-
try {
|
|
506
|
-
fs.rmSync(pendingDir, { recursive: true, force: true });
|
|
507
|
-
}
|
|
508
|
-
catch { /* ignore */ }
|
|
509
|
-
throw new IdentityConflictError(`AID '${aid}' has been registered by another party while local pending registration was incomplete; local pending key discarded.`);
|
|
510
|
-
}
|
|
511
|
-
// 公钥匹配 → 上次 RPC 成功,补 cert + promote
|
|
512
|
-
this._logger.info(`pending recovery: cert public key matches; finalizing registration: aid=${aid}`);
|
|
513
|
-
this._writePendingCert(pendingDir, serverCert);
|
|
514
|
-
const identity = {
|
|
515
|
-
aid,
|
|
516
|
-
private_key_pem: localPriv,
|
|
517
|
-
public_key_der_b64: localPubB64,
|
|
518
|
-
curve: privData.curve ?? 'P-256',
|
|
519
|
-
cert: serverCert,
|
|
520
|
-
};
|
|
521
|
-
try {
|
|
522
|
-
this._keystore.promotePendingIdentity(pendingDir, aid);
|
|
523
|
-
}
|
|
524
|
-
catch (e) {
|
|
525
|
-
throw new IdentityConflictError(`AID '${aid}' was created by another process during recovery; pending dir kept for cleanup.`);
|
|
526
|
-
}
|
|
527
|
-
this._persistIdentity(identity);
|
|
528
|
-
this._aid = aid;
|
|
529
|
-
return { aid, cert: serverCert };
|
|
530
|
-
}
|
|
531
|
-
return null;
|
|
532
|
-
}
|
|
533
|
-
_writePendingKeypair(pendingDir, identity) {
|
|
534
|
-
const priv = String(identity.private_key_pem ?? '');
|
|
535
|
-
const pub = String(identity.public_key_der_b64 ?? '');
|
|
536
|
-
const curve = String(identity.curve ?? 'P-256');
|
|
537
|
-
if (!priv || !pub) {
|
|
538
|
-
throw new AuthError('registerAid: generated identity missing keypair fields');
|
|
539
|
-
}
|
|
540
|
-
const dir = path.join(pendingDir, 'private');
|
|
541
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
542
|
-
fs.writeFileSync(path.join(dir, 'key.json'), JSON.stringify({ private_key_pem: priv, public_key_der_b64: pub, curve }, null, 2), { encoding: 'utf-8', mode: 0o600 });
|
|
543
|
-
}
|
|
544
|
-
_writePendingCert(pendingDir, certPem) {
|
|
545
|
-
const dir = path.join(pendingDir, 'public');
|
|
546
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
547
|
-
fs.writeFileSync(path.join(dir, 'cert.pem'), certPem, { encoding: 'utf-8', mode: 0o600 });
|
|
548
|
-
}
|
|
549
331
|
/**
|
|
550
332
|
* 认证(登录)到 Gateway。
|
|
551
333
|
* 执行两阶段挑战-应答认证,返回 token 信息。
|
|
@@ -555,7 +337,7 @@ export class AuthFlow {
|
|
|
555
337
|
let identity = this._loadIdentityOrRaise(opts?.aid);
|
|
556
338
|
this._logger.debug(`authenticate enter: aid=${identity.aid}, gateway=${gatewayUrl}`);
|
|
557
339
|
try {
|
|
558
|
-
// 优先复用
|
|
340
|
+
// 优先复用 tokenStore 里的 cached access_token(未过期且有 refresh_token)
|
|
559
341
|
// 避免每次调 authenticate 都走两阶段重登的网络往返
|
|
560
342
|
// 注意:_loadIdentityOrRaise 不带 instance_state(access_token 等),
|
|
561
343
|
// 这里需要主动走 _loadInstanceState 拿到 token
|
|
@@ -976,15 +758,6 @@ export class AuthFlow {
|
|
|
976
758
|
});
|
|
977
759
|
}
|
|
978
760
|
// ── 内部方法:认证流程 ──────────────────────────────────────
|
|
979
|
-
/** 注册 AID 到 Gateway */
|
|
980
|
-
async _createAid(gatewayUrl, identity) {
|
|
981
|
-
const response = await this._shortRpc(gatewayUrl, 'auth.create_aid', {
|
|
982
|
-
aid: identity.aid,
|
|
983
|
-
public_key: identity.public_key_der_b64,
|
|
984
|
-
curve: identity.curve || 'P-256',
|
|
985
|
-
});
|
|
986
|
-
return { cert: response.cert };
|
|
987
|
-
}
|
|
988
761
|
/** 两阶段登录 */
|
|
989
762
|
async _login(gatewayUrl, identity) {
|
|
990
763
|
const tStart = Date.now();
|
|
@@ -1969,51 +1742,36 @@ export class AuthFlow {
|
|
|
1969
1742
|
}
|
|
1970
1743
|
// (_ensureLocalIdentity 已硬移除:注册和登录彻底分离,登录路径绝不再
|
|
1971
1744
|
// 隐式生成密钥;新身份必须由应用层显式调 registerAid)
|
|
1972
|
-
/** 加载身份信息,不存在或半成品时抛出 StateError
|
|
1745
|
+
/** 加载身份信息,不存在或半成品时抛出 StateError。
|
|
1746
|
+
* 优先使用调用方注入的内存私钥(_memIdentity),不从 tokenStore 解密私钥。
|
|
1747
|
+
*/
|
|
1973
1748
|
_loadIdentityOrRaise(aid) {
|
|
1974
1749
|
const requestedAid = aid ?? this._aid;
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
// 防线 A:拒绝半成品 identity(缺 keypair 任一字段)。
|
|
1981
|
-
// 这是上次 registerAid 半完成 / keystore 损坏的常见症状;如果让它流到
|
|
1982
|
-
// _login,签名一定失败,且服务端可能记录一次无效登录尝试。
|
|
1983
|
-
if (!existing.private_key_pem || !existing.public_key_der_b64) {
|
|
1984
|
-
throw new StateError(`local identity for aid ${requestedAid} is incomplete (missing keypair); ` +
|
|
1985
|
-
`call auth.registerAid() first`);
|
|
1750
|
+
// 优先路径:使用注入的内存 identity(私钥已由 AIDStore 解密并传入)
|
|
1751
|
+
if (this._memIdentity) {
|
|
1752
|
+
const mem = this._memIdentity;
|
|
1753
|
+
if (requestedAid && String(mem.aid ?? '') !== requestedAid) {
|
|
1754
|
+
throw new StateError(`identity mismatch: requested ${requestedAid}, loaded ${mem.aid}`);
|
|
1986
1755
|
}
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
existing.aid = requestedAid;
|
|
1990
|
-
return existing;
|
|
1991
|
-
}
|
|
1992
|
-
// 尝试加载任意身份
|
|
1993
|
-
const ks = this._keystore;
|
|
1994
|
-
if (typeof ks.loadAnyIdentity === 'function') {
|
|
1995
|
-
const existing = ks.loadAnyIdentity();
|
|
1996
|
-
if (existing !== null && existing !== undefined) {
|
|
1997
|
-
if (!existing.private_key_pem || !existing.public_key_der_b64) {
|
|
1998
|
-
throw new StateError(`local identity is incomplete (missing keypair); call auth.registerAid() first`);
|
|
1999
|
-
}
|
|
2000
|
-
const loadedAid = existing.aid;
|
|
2001
|
-
if (typeof loadedAid === 'string' && loadedAid) {
|
|
2002
|
-
this._aid = loadedAid;
|
|
2003
|
-
}
|
|
2004
|
-
return existing;
|
|
1756
|
+
if (!mem.private_key_pem || !mem.public_key_der_b64) {
|
|
1757
|
+
throw new StateError(`injected identity for aid ${mem.aid} is incomplete (missing keypair)`);
|
|
2005
1758
|
}
|
|
1759
|
+
if (requestedAid)
|
|
1760
|
+
this._aid = requestedAid;
|
|
1761
|
+
return { ...mem };
|
|
1762
|
+
}
|
|
1763
|
+
if (requestedAid) {
|
|
1764
|
+
throw new StateError(`no injected identity for aid ${requestedAid}; call AUNClient.loadIdentity(aid) first`);
|
|
2006
1765
|
}
|
|
2007
|
-
throw new StateError('no local identity found, call
|
|
1766
|
+
throw new StateError('no local identity found, call AUNClient.loadIdentity(aid) first');
|
|
2008
1767
|
}
|
|
2009
1768
|
// (_ensureIdentity 已硬移除:注册和登录彻底分离,登录路径绝不再隐式
|
|
2010
1769
|
// 生成密钥;调用方应改用 _loadIdentityOrRaise 获取已注册身份)
|
|
2011
1770
|
_loadInstanceState(aid) {
|
|
2012
|
-
|
|
2013
|
-
if (typeof loader !== 'function') {
|
|
1771
|
+
if (typeof this._tokenStore.loadInstanceState !== 'function') {
|
|
2014
1772
|
return null;
|
|
2015
1773
|
}
|
|
2016
|
-
return
|
|
1774
|
+
return this._tokenStore.loadInstanceState(aid, this._deviceId, this._slotId);
|
|
2017
1775
|
}
|
|
2018
1776
|
_persistIdentity(identity) {
|
|
2019
1777
|
const aid = String(identity.aid ?? '');
|
|
@@ -2028,27 +1786,16 @@ export class AuthFlow {
|
|
|
2028
1786
|
delete persisted[key];
|
|
2029
1787
|
}
|
|
2030
1788
|
}
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
// 从共享 metadata_kv 中移除实例级字段(它们已保存到 instance_state)
|
|
2037
|
-
const db = this._keystore._getDB?.(aid);
|
|
2038
|
-
if (db && typeof db.deleteMetadata === 'function') {
|
|
2039
|
-
for (const key of AuthFlow._INSTANCE_STATE_FIELDS) {
|
|
2040
|
-
db.deleteMetadata(key);
|
|
2041
|
-
db.deleteMetadata(`${key}_protection`);
|
|
2042
|
-
}
|
|
2043
|
-
}
|
|
2044
|
-
if (Object.keys(instanceState).length === 0) {
|
|
2045
|
-
return;
|
|
1789
|
+
delete persisted.private_key_pem;
|
|
1790
|
+
delete persisted.public_key_der_b64;
|
|
1791
|
+
delete persisted.curve;
|
|
1792
|
+
if (typeof persisted.cert === 'string' && persisted.cert) {
|
|
1793
|
+
this._tokenStore.saveCert(aid, persisted.cert);
|
|
2046
1794
|
}
|
|
2047
|
-
|
|
2048
|
-
if (typeof updater !== 'function') {
|
|
1795
|
+
if (Object.keys(instanceState).length === 0 || typeof this._tokenStore.updateInstanceState !== 'function') {
|
|
2049
1796
|
return;
|
|
2050
1797
|
}
|
|
2051
|
-
|
|
1798
|
+
this._tokenStore.updateInstanceState(aid, this._deviceId, this._slotId, (current) => {
|
|
2052
1799
|
Object.assign(current, instanceState);
|
|
2053
1800
|
return current;
|
|
2054
1801
|
});
|