@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.
Files changed (42) hide show
  1. package/dist/auth.d.ts +6 -0
  2. package/dist/auth.js +375 -212
  3. package/dist/auth.js.map +1 -1
  4. package/dist/client.d.ts +14 -1
  5. package/dist/client.js +1255 -729
  6. package/dist/client.js.map +1 -1
  7. package/dist/discovery.d.ts +3 -0
  8. package/dist/discovery.js +29 -2
  9. package/dist/discovery.js.map +1 -1
  10. package/dist/e2ee-group.d.ts +2 -1
  11. package/dist/e2ee-group.js +207 -56
  12. package/dist/e2ee-group.js.map +1 -1
  13. package/dist/e2ee.js +45 -11
  14. package/dist/e2ee.js.map +1 -1
  15. package/dist/events.js +1 -1
  16. package/dist/events.js.map +1 -1
  17. package/dist/keystore/aid-db.d.ts +13 -1
  18. package/dist/keystore/aid-db.js +31 -3
  19. package/dist/keystore/aid-db.js.map +1 -1
  20. package/dist/keystore/file.d.ts +6 -0
  21. package/dist/keystore/file.js +20 -9
  22. package/dist/keystore/file.js.map +1 -1
  23. package/dist/keystore/index.d.ts +2 -0
  24. package/dist/keystore/sqlite-backup.js +5 -5
  25. package/dist/keystore/sqlite-backup.js.map +1 -1
  26. package/dist/logger.d.ts +2 -0
  27. package/dist/logger.js +69 -13
  28. package/dist/logger.js.map +1 -1
  29. package/dist/namespaces/auth.d.ts +13 -4
  30. package/dist/namespaces/auth.js +354 -150
  31. package/dist/namespaces/auth.js.map +1 -1
  32. package/dist/namespaces/custody.d.ts +1 -0
  33. package/dist/namespaces/custody.js +138 -56
  34. package/dist/namespaces/custody.js.map +1 -1
  35. package/dist/namespaces/meta.d.ts +1 -0
  36. package/dist/namespaces/meta.js +26 -0
  37. package/dist/namespaces/meta.js.map +1 -1
  38. package/dist/secret-store/file-store.js +3 -2
  39. package/dist/secret-store/file-store.js.map +1 -1
  40. package/dist/transport.js +83 -2
  41. package/dist/transport.js.map +1 -1
  42. package/package.json +42 -42
package/dist/auth.js CHANGED
@@ -309,263 +309,375 @@ export class AuthFlow {
309
309
  * 如果 AID 已在服务端注册但本地证书丢失,尝试从 PKI 端点下载恢复。
310
310
  */
311
311
  async createAid(gatewayUrl, aid) {
312
+ const tStart = Date.now();
312
313
  AuthFlow._validateAidName(aid);
313
- let identity = this._ensureLocalIdentity(aid);
314
- if (identity.cert) {
315
- return { aid: identity.aid, cert: identity.cert };
316
- }
317
- // 本地有密钥但无证书——尝试注册
314
+ this._logger.debug(`createAid enter: aid=${aid}, gateway=${gatewayUrl}`);
318
315
  try {
319
- const created = await this._createAid(gatewayUrl, identity);
320
- Object.assign(identity, created);
321
- }
322
- catch (e) {
323
- if (!(e instanceof AUNError) || !String(e.message).includes('already exists')) {
324
- throw e;
316
+ let identity = this._ensureLocalIdentity(aid);
317
+ if (identity.cert) {
318
+ this._logger.debug(`createAid exit: elapsed=${Date.now() - tStart}ms (already has cert) aid=${aid}`);
319
+ return { aid: identity.aid, cert: identity.cert };
325
320
  }
326
- // AID 已在服务端注册,尝试从 PKI 下载恢复证书
321
+ // 本地有密钥但无证书——尝试注册
327
322
  try {
328
- identity = await this._recoverCertViaDownload(gatewayUrl, identity);
323
+ const created = await this._createAid(gatewayUrl, identity);
324
+ Object.assign(identity, created);
325
+ this._logger.debug(`AID register ok: aid=${aid}`);
329
326
  }
330
- catch {
331
- throw new StateError(`AID ${aid} already registered on server but local certificate is missing. ` +
332
- `Certificate download recovery failed. Options: ` +
333
- `(1) use a different AID name, or ` +
334
- `(2) restart Kite server to clear registration.`);
327
+ catch (e) {
328
+ if (!(e instanceof AUNError) || !String(e.message).includes('already exists')) {
329
+ this._logger.error(`AID register failed: aid=${aid}, error=${e instanceof Error ? e.message : String(e)}`);
330
+ throw e;
331
+ }
332
+ // AID 已在服务端注册,尝试从 PKI 下载恢复证书
333
+ try {
334
+ identity = await this._recoverCertViaDownload(gatewayUrl, identity);
335
+ }
336
+ catch {
337
+ throw new StateError(`AID ${aid} already registered on server but local certificate is missing. ` +
338
+ `Certificate download recovery failed. Options: ` +
339
+ `(1) use a different AID name, or ` +
340
+ `(2) restart Kite server to clear registration.`);
341
+ }
335
342
  }
343
+ this._persistIdentity(identity);
344
+ this._aid = String(identity.aid);
345
+ this._logger.debug(`createAid exit: elapsed=${Date.now() - tStart}ms aid=${aid}`);
346
+ return { aid: identity.aid, cert: identity.cert };
347
+ }
348
+ catch (err) {
349
+ this._logger.debug(`createAid exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
350
+ throw err;
336
351
  }
337
- this._persistIdentity(identity);
338
- this._aid = String(identity.aid);
339
- return { aid: identity.aid, cert: identity.cert };
340
352
  }
341
353
  /**
342
354
  * 认证(登录)到 Gateway。
343
355
  * 执行两阶段挑战-应答认证,返回 token 信息。
344
356
  */
345
357
  async authenticate(gatewayUrl, opts) {
358
+ const tStart = Date.now();
346
359
  let identity = this._loadIdentityOrRaise(opts?.aid);
347
- if (!identity.cert) {
348
- // 本地有密钥但无证书——尝试从 PKI 下载恢复
349
- try {
350
- identity = await this._recoverCertViaDownload(gatewayUrl, identity);
351
- this._persistIdentity(identity);
360
+ this._logger.debug(`authenticate enter: aid=${identity.aid}, gateway=${gatewayUrl}`);
361
+ try {
362
+ // 优先复用 keystore 里的 cached access_token(未过期且有 refresh_token)
363
+ // 避免每次调 authenticate 都走两阶段重登的网络往返
364
+ // 注意:_loadIdentityOrRaise 不带 instance_state(access_token 等),
365
+ // 这里需要主动走 _loadInstanceState 拿到 token
366
+ const instanceState = this._loadInstanceState(String(identity.aid)) || {};
367
+ const identityWithState = { ...identity, ...instanceState };
368
+ const cachedToken = AuthFlow._getCachedAccessToken(identityWithState);
369
+ const cachedRefresh = String(identityWithState.refresh_token ?? '');
370
+ if (cachedToken && cachedRefresh) {
371
+ this._logger.debug(`authenticate reusing cached token: aid=${identity.aid} expires_at=${identityWithState.access_token_expires_at}`);
372
+ this._aid = String(identity.aid);
373
+ return {
374
+ aid: identity.aid,
375
+ access_token: cachedToken,
376
+ refresh_token: cachedRefresh,
377
+ expires_at: identityWithState.access_token_expires_at,
378
+ gateway: gatewayUrl,
379
+ };
352
380
  }
353
- catch (e) {
354
- throw new StateError(`local certificate missing and recovery failed: ${e instanceof Error ? e.message : String(e)}. ` +
355
- `Run auth.createAid() to register a new identity.`);
381
+ if (!identity.cert) {
382
+ // 本地有密钥但无证书——尝试从 PKI 下载恢复
383
+ try {
384
+ identity = await this._recoverCertViaDownload(gatewayUrl, identity);
385
+ this._persistIdentity(identity);
386
+ }
387
+ catch (e) {
388
+ throw new StateError(`local certificate missing and recovery failed: ${e instanceof Error ? e.message : String(e)}. ` +
389
+ `Run auth.createAid() to register a new identity.`);
390
+ }
356
391
  }
357
- }
358
- let login;
359
- try {
360
- login = await this._login(gatewayUrl, identity);
361
- }
362
- catch (e) {
363
- // 证书未在服务端注册或公钥不匹配 — 自动重新注册
364
- if (e instanceof AuthError && (String(e.message).includes('not registered') || String(e.message).includes('public key mismatch'))) {
365
- this._logger.warn(`证书未在服务端注册,自动重新注册: aid=${identity.aid}`);
366
- const created = await this._createAid(gatewayUrl, identity);
367
- identity.cert = created.cert;
368
- this._persistIdentity(identity);
392
+ let login;
393
+ try {
369
394
  login = await this._login(gatewayUrl, identity);
395
+ this._logger.debug(`auth login ok: aid=${identity.aid}`);
370
396
  }
371
- else {
372
- throw e;
397
+ catch (e) {
398
+ // 证书未在服务端注册或公钥不匹配 — 自动重新注册
399
+ if (e instanceof AuthError && (String(e.message).includes('not registered') || String(e.message).includes('public key mismatch'))) {
400
+ this._logger.warn(`cert not registered on server, auto re-register: aid=${identity.aid}`);
401
+ const created = await this._createAid(gatewayUrl, identity);
402
+ identity.cert = created.cert;
403
+ this._persistIdentity(identity);
404
+ login = await this._login(gatewayUrl, identity);
405
+ }
406
+ else {
407
+ throw e;
408
+ }
373
409
  }
410
+ AuthFlow._rememberTokens(identity, login);
411
+ await this._validateNewCert(identity, gatewayUrl);
412
+ this._persistIdentity(identity);
413
+ this._aid = String(identity.aid);
414
+ this._logger.debug(`authenticate exit: elapsed=${Date.now() - tStart}ms aid=${identity.aid}`);
415
+ return {
416
+ aid: identity.aid,
417
+ access_token: identity.access_token,
418
+ refresh_token: identity.refresh_token,
419
+ expires_at: identity.access_token_expires_at,
420
+ gateway: gatewayUrl,
421
+ };
422
+ }
423
+ catch (err) {
424
+ this._logger.debug(`authenticate exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
425
+ throw err;
374
426
  }
375
- AuthFlow._rememberTokens(identity, login);
376
- await this._validateNewCert(identity, gatewayUrl);
377
- this._persistIdentity(identity);
378
- this._aid = String(identity.aid);
379
- return {
380
- aid: identity.aid,
381
- access_token: identity.access_token,
382
- refresh_token: identity.refresh_token,
383
- expires_at: identity.access_token_expires_at,
384
- gateway: gatewayUrl,
385
- };
386
427
  }
387
428
  /**
388
429
  * 确保已认证(自动创建 + 登录)。
389
430
  * 如果没有本地身份则创建,然后执行登录流程。
390
431
  */
391
432
  async ensureAuthenticated(gatewayUrl) {
392
- const identity = this._ensureIdentity();
393
- if (!identity.cert) {
394
- const created = await this._createAid(gatewayUrl, identity);
395
- Object.assign(identity, created);
433
+ const tStart = Date.now();
434
+ this._logger.debug(`ensureAuthenticated enter: gateway=${gatewayUrl}`);
435
+ try {
436
+ const identity = this._ensureIdentity();
437
+ if (!identity.cert) {
438
+ const created = await this._createAid(gatewayUrl, identity);
439
+ Object.assign(identity, created);
440
+ this._persistIdentity(identity);
441
+ }
442
+ const login = await this._login(gatewayUrl, identity);
443
+ AuthFlow._rememberTokens(identity, login);
444
+ await this._validateNewCert(identity, gatewayUrl);
396
445
  this._persistIdentity(identity);
446
+ const token = identity.access_token || identity.token || identity.kite_token;
447
+ if (!token) {
448
+ throw new AuthError('login2 did not return access token');
449
+ }
450
+ this._logger.debug(`ensureAuthenticated exit: elapsed=${Date.now() - tStart}ms aid=${identity.aid}`);
451
+ return { token, identity };
397
452
  }
398
- const login = await this._login(gatewayUrl, identity);
399
- AuthFlow._rememberTokens(identity, login);
400
- await this._validateNewCert(identity, gatewayUrl);
401
- this._persistIdentity(identity);
402
- const token = identity.access_token || identity.token || identity.kite_token;
403
- if (!token) {
404
- throw new AuthError('login2 did not return access token');
453
+ catch (err) {
454
+ this._logger.debug(`ensureAuthenticated exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
455
+ throw err;
405
456
  }
406
- return { token, identity };
407
457
  }
408
458
  /**
409
459
  * 刷新缓存的 token。
410
460
  * 使用 refresh_token 获取新的 access_token。
411
461
  */
412
462
  async refreshCachedTokens(gatewayUrl, identity) {
463
+ const tStart = Date.now();
413
464
  const refreshToken = String(identity.refresh_token || '');
414
465
  if (!refreshToken) {
466
+ this._logger.error(`token refresh failed: missing refresh_token, aid=${identity.aid}`);
415
467
  throw new AuthError('missing refresh_token');
416
468
  }
417
- const refreshed = await this._refreshAccessToken(gatewayUrl, refreshToken);
418
- AuthFlow._rememberTokens(identity, refreshed);
419
- await this._validateNewCert(identity, gatewayUrl);
420
- this._persistIdentity(identity);
421
- return identity;
469
+ this._logger.debug(`refreshCachedTokens enter: aid=${identity.aid}`);
470
+ try {
471
+ const refreshed = await this._refreshAccessToken(gatewayUrl, refreshToken);
472
+ AuthFlow._rememberTokens(identity, refreshed);
473
+ await this._validateNewCert(identity, gatewayUrl);
474
+ this._persistIdentity(identity);
475
+ this._logger.debug(`refreshCachedTokens exit: elapsed=${Date.now() - tStart}ms aid=${identity.aid}`);
476
+ return identity;
477
+ }
478
+ catch (err) {
479
+ this._logger.debug(`refreshCachedTokens exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
480
+ throw err;
481
+ }
422
482
  }
423
483
  /**
424
484
  * 使用 token 初始化 WebSocket 会话。
425
485
  * 发送 auth.connect RPC 完成会话握手。
426
486
  */
427
487
  async initializeWithToken(transport, challenge, accessToken, opts) {
428
- const nonce = this._extractChallengeNonce(challenge);
429
- this.setInstanceContext({
430
- deviceId: String(opts?.deviceId ?? ''),
431
- slotId: String(opts?.slotId ?? ''),
432
- });
433
- await this._initializeSession(transport, nonce, accessToken, {
434
- deviceId: String(opts?.deviceId ?? ''),
435
- slotId: String(opts?.slotId ?? ''),
436
- deliveryMode: opts?.deliveryMode ?? null,
437
- });
488
+ const tStart = Date.now();
489
+ this._logger.debug(`initializeWithToken enter: deviceId=${opts?.deviceId ?? ''}, slotId=${opts?.slotId ?? ''}`);
490
+ try {
491
+ const nonce = this._extractChallengeNonce(challenge);
492
+ this.setInstanceContext({
493
+ deviceId: String(opts?.deviceId ?? ''),
494
+ slotId: String(opts?.slotId ?? ''),
495
+ });
496
+ await this._initializeSession(transport, nonce, accessToken, {
497
+ deviceId: String(opts?.deviceId ?? ''),
498
+ slotId: String(opts?.slotId ?? ''),
499
+ deliveryMode: opts?.deliveryMode ?? null,
500
+ connectionKind: opts?.connectionKind,
501
+ shortTtlMs: opts?.shortTtlMs,
502
+ extraInfo: opts?.extraInfo,
503
+ });
504
+ this._logger.debug(`initializeWithToken exit: elapsed=${Date.now() - tStart}ms`);
505
+ }
506
+ catch (err) {
507
+ this._logger.debug(`initializeWithToken exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
508
+ throw err;
509
+ }
438
510
  }
439
511
  /**
440
512
  * 连接会话(自动选择认证策略)。
441
513
  * 依次尝试:显式 token → 缓存 token → 刷新 token → 完整重认证。
442
514
  */
443
515
  async connectSession(transport, challenge, gatewayUrl, opts) {
516
+ const tStart = Date.now();
444
517
  const nonce = this._extractChallengeNonce(challenge);
445
518
  const deviceId = String(opts?.deviceId ?? '');
446
519
  const slotId = String(opts?.slotId ?? '');
447
520
  const deliveryMode = opts?.deliveryMode ?? null;
521
+ const connectionKind = opts?.connectionKind;
522
+ const shortTtlMs = opts?.shortTtlMs;
523
+ const extraInfo = opts?.extraInfo;
448
524
  this.setInstanceContext({ deviceId, slotId });
449
- let identity;
525
+ this._logger.debug(`connectSession enter: gateway=${gatewayUrl}, device_id=${deviceId}, slot_id=${slotId}, has_explicit_token=${!!opts?.accessToken}, has_challenge_nonce=${!!nonce}`);
450
526
  try {
451
- identity = this.loadIdentity();
452
- }
453
- catch {
454
- identity = null;
455
- }
456
- // 策略 1:显式 token
457
- const explicitToken = String(opts?.accessToken || '');
458
- if (explicitToken && identity !== null) {
527
+ let identity;
459
528
  try {
460
- await this._initializeSession(transport, nonce, explicitToken, {
461
- deviceId,
462
- slotId,
463
- deliveryMode,
464
- });
465
- identity.access_token = explicitToken;
466
- this._persistIdentity(identity);
467
- return { token: explicitToken, identity };
529
+ identity = this.loadIdentity();
468
530
  }
469
- catch (exc) {
470
- if (!(exc instanceof AuthError))
471
- throw exc;
472
- this._logger.debug(`explicit_token 认证失败,尝试下一方式: ${exc.message}`);
531
+ catch {
532
+ identity = null;
473
533
  }
474
- }
475
- // 无本地身份时,执行完整认证
476
- if (identity === null) {
477
- const authContext = await this.ensureAuthenticated(gatewayUrl);
478
- const token = String(authContext.token);
479
- await this._initializeSession(transport, nonce, token, {
480
- deviceId,
481
- slotId,
482
- deliveryMode,
483
- });
484
- return authContext;
485
- }
486
- // 策略 2:缓存的 access_token
487
- const cachedToken = AuthFlow._getCachedAccessToken(identity);
488
- if (cachedToken) {
489
- try {
490
- await this._initializeSession(transport, nonce, cachedToken, {
534
+ // 策略 1:显式 token
535
+ const explicitToken = String(opts?.accessToken || '');
536
+ if (explicitToken && identity !== null) {
537
+ try {
538
+ await this._initializeSession(transport, nonce, explicitToken, {
539
+ deviceId,
540
+ slotId,
541
+ deliveryMode,
542
+ connectionKind,
543
+ shortTtlMs,
544
+ extraInfo,
545
+ });
546
+ identity.access_token = explicitToken;
547
+ this._persistIdentity(identity);
548
+ this._logger.debug(`connectSession exit: elapsed=${Date.now() - tStart}ms strategy=explicit_token aid=${String(identity.aid ?? '')}`);
549
+ return { token: explicitToken, identity };
550
+ }
551
+ catch (exc) {
552
+ if (!(exc instanceof AuthError))
553
+ throw exc;
554
+ this._logger.debug(`explicit_token auth failed, try next method: ${exc.message}`);
555
+ }
556
+ }
557
+ // 无本地身份时,执行完整认证
558
+ if (identity === null) {
559
+ const authContext = await this.ensureAuthenticated(gatewayUrl);
560
+ const token = String(authContext.token);
561
+ await this._initializeSession(transport, nonce, token, {
491
562
  deviceId,
492
563
  slotId,
493
564
  deliveryMode,
565
+ connectionKind,
566
+ shortTtlMs,
567
+ extraInfo,
494
568
  });
495
- return { token: cachedToken, identity };
569
+ this._logger.debug(`connectSession exit: elapsed=${Date.now() - tStart}ms strategy=ensure_authenticated aid=${String(authContext.identity?.aid ?? '')}`);
570
+ return authContext;
496
571
  }
497
- catch (exc) {
498
- if (!(exc instanceof AuthError))
499
- throw exc;
500
- this._logger.debug(`cached_token 认证失败,尝试刷新: ${exc.message}`);
501
- }
502
- }
503
- // 策略 3:refresh_token
504
- const refreshToken = String(identity.refresh_token || '');
505
- if (refreshToken) {
506
- try {
507
- identity = await this.refreshCachedTokens(gatewayUrl, identity);
508
- const newCachedToken = AuthFlow._getCachedAccessToken(identity);
509
- if (newCachedToken) {
510
- await this._initializeSession(transport, nonce, newCachedToken, {
572
+ // 策略 2:缓存的 access_token
573
+ const cachedToken = AuthFlow._getCachedAccessToken(identity);
574
+ if (cachedToken) {
575
+ try {
576
+ await this._initializeSession(transport, nonce, cachedToken, {
511
577
  deviceId,
512
578
  slotId,
513
579
  deliveryMode,
580
+ connectionKind,
581
+ shortTtlMs,
582
+ extraInfo,
514
583
  });
515
- return { token: newCachedToken, identity };
584
+ this._logger.debug(`connectSession exit: elapsed=${Date.now() - tStart}ms strategy=cached_token aid=${String(identity.aid ?? '')}`);
585
+ return { token: cachedToken, identity };
586
+ }
587
+ catch (exc) {
588
+ if (!(exc instanceof AuthError))
589
+ throw exc;
590
+ this._logger.debug(`cached_token auth failed, try refresh: ${exc.message}`);
516
591
  }
517
592
  }
518
- catch (exc) {
519
- if (!(exc instanceof AuthError))
520
- throw exc;
521
- this._logger.debug(`refresh_token 认证失败,将重新登录: ${exc.message}`);
593
+ // 策略 3:refresh_token
594
+ const refreshToken = String(identity.refresh_token || '');
595
+ if (refreshToken) {
596
+ try {
597
+ identity = await this.refreshCachedTokens(gatewayUrl, identity);
598
+ const newCachedToken = AuthFlow._getCachedAccessToken(identity);
599
+ if (newCachedToken) {
600
+ await this._initializeSession(transport, nonce, newCachedToken, {
601
+ deviceId,
602
+ slotId,
603
+ deliveryMode,
604
+ connectionKind,
605
+ shortTtlMs,
606
+ extraInfo,
607
+ });
608
+ this._logger.debug(`connectSession exit: elapsed=${Date.now() - tStart}ms strategy=refresh_token aid=${String(identity.aid ?? '')}`);
609
+ return { token: newCachedToken, identity };
610
+ }
611
+ }
612
+ catch (exc) {
613
+ if (!(exc instanceof AuthError))
614
+ throw exc;
615
+ this._logger.debug(`refresh_token auth failed, will re-login: ${exc.message}`);
616
+ }
522
617
  }
618
+ // 策略 4:完整重认证
619
+ const login = await this.authenticate(gatewayUrl, { aid: identity.aid });
620
+ const token = String(login.access_token || '');
621
+ if (!token) {
622
+ throw new AuthError('authenticate did not return access_token');
623
+ }
624
+ await this._initializeSession(transport, nonce, token, {
625
+ deviceId,
626
+ slotId,
627
+ deliveryMode,
628
+ connectionKind,
629
+ shortTtlMs,
630
+ extraInfo,
631
+ });
632
+ identity = this.loadIdentity(identity.aid);
633
+ this._logger.debug(`connectSession exit: elapsed=${Date.now() - tStart}ms strategy=full_reauth aid=${String(identity?.aid ?? '')}`);
634
+ return { token, identity };
523
635
  }
524
- // 策略 4:完整重认证
525
- const login = await this.authenticate(gatewayUrl, { aid: identity.aid });
526
- const token = String(login.access_token || '');
527
- if (!token) {
528
- throw new AuthError('authenticate did not return access_token');
636
+ catch (err) {
637
+ this._logger.debug(`connectSession exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
638
+ throw err;
529
639
  }
530
- await this._initializeSession(transport, nonce, token, {
531
- deviceId,
532
- slotId,
533
- deliveryMode,
534
- });
535
- identity = this.loadIdentity(identity.aid);
536
- return { token, identity };
537
640
  }
538
641
  /**
539
642
  * 验证对端证书:时间有效性 + 链验证 + CRL + OCSP + AID 绑定。
540
643
  * 用于 E2EE 握手中验证通信对端的身份证书。
541
644
  */
542
645
  async verifyPeerCertificate(gatewayUrl, certPem, expectedAid) {
543
- const cert = _loadX509(certPem);
544
- const nowMs = Date.now();
545
- _ensureCertTimeValid(cert, 'peer certificate', nowMs);
546
- await this._verifyAuthCertChain(gatewayUrl, cert, expectedAid);
547
- try {
548
- await this._verifyAuthCertRevocation(gatewayUrl, cert, expectedAid);
549
- }
550
- catch (e) {
551
- const errMsg = e instanceof Error ? e.message : String(e);
552
- if (/revoked/i.test(errMsg))
553
- throw e instanceof AuthError ? e : new AuthError(errMsg);
554
- this._logger.warn(`CRL 检查不可用,降级继续: ${errMsg}`);
555
- }
646
+ const tStart = Date.now();
647
+ this._logger.debug(`verifyPeerCertificate enter: aid=${expectedAid}, gateway=${gatewayUrl}`);
556
648
  try {
557
- await this._verifyAuthCertOcsp(gatewayUrl, cert, expectedAid);
558
- }
559
- catch (exc) {
560
- const errMsg = exc instanceof Error ? exc.message : String(exc);
561
- if (/revoked/i.test(errMsg))
562
- throw exc instanceof AuthError ? exc : new AuthError(errMsg);
563
- this._logger.warn(`OCSP 校验不可用,降级继续: ${errMsg}`);
649
+ const cert = _loadX509(certPem);
650
+ const nowMs = Date.now();
651
+ _ensureCertTimeValid(cert, 'peer certificate', nowMs);
652
+ await this._verifyAuthCertChain(gatewayUrl, cert, expectedAid);
653
+ try {
654
+ await this._verifyAuthCertRevocation(gatewayUrl, cert, expectedAid);
655
+ }
656
+ catch (e) {
657
+ const errMsg = e instanceof Error ? e.message : String(e);
658
+ if (/revoked/i.test(errMsg))
659
+ throw e instanceof AuthError ? e : new AuthError(errMsg);
660
+ this._logger.warn(`CRL verification unavailable, skipping: ${errMsg}`);
661
+ }
662
+ try {
663
+ await this._verifyAuthCertOcsp(gatewayUrl, cert, expectedAid);
664
+ }
665
+ catch (exc) {
666
+ const errMsg = exc instanceof Error ? exc.message : String(exc);
667
+ if (/revoked/i.test(errMsg))
668
+ throw exc instanceof AuthError ? exc : new AuthError(errMsg);
669
+ this._logger.warn(`OCSP verification unavailable, skipping: ${errMsg}`);
670
+ }
671
+ // 检查 CN 匹配
672
+ const cn = _certSubjectCN(cert);
673
+ if (cn !== expectedAid) {
674
+ throw new AuthError(`peer cert CN mismatch: expected ${expectedAid}, got ${cn || 'none'}`);
675
+ }
676
+ this._logger.debug(`verifyPeerCertificate exit: elapsed=${Date.now() - tStart}ms aid=${expectedAid}`);
564
677
  }
565
- // 检查 CN 匹配
566
- const cn = _certSubjectCN(cert);
567
- if (cn !== expectedAid) {
568
- throw new AuthError(`peer cert CN mismatch: expected ${expectedAid}, got ${cn || 'none'}`);
678
+ catch (err) {
679
+ this._logger.debug(`verifyPeerCertificate exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
680
+ throw err;
569
681
  }
570
682
  }
571
683
  // ── 内部方法:短连接 RPC ────────────────────────────────────
@@ -574,6 +686,7 @@ export class AuthFlow {
574
686
  * 流程:连接 → 接收 challenge → 发送请求 → 接收响应 → 关闭。
575
687
  */
576
688
  async _shortRpc(gatewayUrl, method, params) {
689
+ this._logger.debug(`_shortRpc enter: method=${method}, gateway=${gatewayUrl}`);
577
690
  const ws = await this._connectionFactory(gatewayUrl);
578
691
  try {
579
692
  // 接收 challenge(第一条消息)
@@ -651,39 +764,62 @@ export class AuthFlow {
651
764
  }
652
765
  /** 两阶段登录 */
653
766
  async _login(gatewayUrl, identity) {
767
+ const tStart = Date.now();
654
768
  const clientNonce = this._crypto.newClientNonce();
655
- // Phase 1: 发送 AID + 证书 + 客户端 nonce
656
- const phase1 = await this._shortRpc(gatewayUrl, 'auth.aid_login1', {
657
- aid: identity.aid,
658
- cert: identity.cert,
659
- client_nonce: clientNonce,
660
- });
661
- // 验证 Phase 1 响应(证书链 + 签名)
662
- await this._verifyPhase1Response(gatewayUrl, phase1, clientNonce);
663
- // Phase 2: 用私钥签名 nonce
664
- const [signature, clientTime] = this._crypto.signLoginNonce(String(identity.private_key_pem), String(phase1.nonce));
665
- const phase2 = await this._shortRpc(gatewayUrl, 'auth.aid_login2', {
666
- aid: identity.aid,
667
- request_id: phase1.request_id,
668
- nonce: phase1.nonce,
669
- client_time: clientTime,
670
- signature,
671
- });
672
- return phase2;
769
+ this._logger.debug(`_login enter: aid=${identity.aid}`);
770
+ try {
771
+ // Phase 1: 发送 AID + 证书 + 客户端 nonce
772
+ const phase1 = await this._shortRpc(gatewayUrl, 'auth.aid_login1', {
773
+ aid: identity.aid,
774
+ cert: identity.cert,
775
+ client_nonce: clientNonce,
776
+ });
777
+ this._logger.debug(`login1 response recv: aid=${identity.aid}, request_id=${phase1.request_id}`);
778
+ // 验证 Phase 1 响应(证书链 + 签名)
779
+ await this._verifyPhase1Response(gatewayUrl, phase1, clientNonce);
780
+ this._logger.debug(`login1 verify ok: aid=${identity.aid}`);
781
+ // Phase 2: 用私钥签名 nonce
782
+ const [signature, clientTime] = this._crypto.signLoginNonce(String(identity.private_key_pem), String(phase1.nonce));
783
+ this._logger.debug(`login2 start: aid=${identity.aid}`);
784
+ const phase2 = await this._shortRpc(gatewayUrl, 'auth.aid_login2', {
785
+ aid: identity.aid,
786
+ request_id: phase1.request_id,
787
+ nonce: phase1.nonce,
788
+ client_time: clientTime,
789
+ signature,
790
+ });
791
+ this._logger.debug(`_login exit: elapsed=${Date.now() - tStart}ms aid=${identity.aid}, has_token=${!!phase2.access_token || !!phase2.token}`);
792
+ return phase2;
793
+ }
794
+ catch (err) {
795
+ this._logger.debug(`_login exit (error): elapsed=${Date.now() - tStart}ms aid=${identity.aid} err=${err instanceof Error ? err.message : String(err)}`);
796
+ throw err;
797
+ }
673
798
  }
674
799
  /** 刷新 access_token */
675
800
  async _refreshAccessToken(gatewayUrl, refreshToken) {
676
- const result = await this._shortRpc(gatewayUrl, 'auth.refresh_token', {
677
- refresh_token: refreshToken,
678
- });
679
- if (!result.success) {
680
- throw new AuthError(String(result.error || 'refresh failed'));
801
+ const tStart = Date.now();
802
+ this._logger.debug(`_refreshAccessToken enter`);
803
+ try {
804
+ const result = await this._shortRpc(gatewayUrl, 'auth.refresh_token', {
805
+ refresh_token: refreshToken,
806
+ });
807
+ if (!result.success) {
808
+ throw new AuthError(String(result.error || 'refresh failed'));
809
+ }
810
+ this._logger.debug(`_refreshAccessToken exit: elapsed=${Date.now() - tStart}ms`);
811
+ return result;
812
+ }
813
+ catch (err) {
814
+ this._logger.debug(`_refreshAccessToken exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
815
+ throw err;
681
816
  }
682
- return result;
683
817
  }
684
818
  /** 会话初始化:发送 auth.connect RPC */
685
819
  async _initializeSession(transport, nonce, token, opts) {
686
- const result = await transport.call('auth.connect', {
820
+ const connectionKind = String(opts?.connectionKind ?? 'long');
821
+ this._logger.debug(`session init start: deviceId=${opts?.deviceId ?? ''}, slotId=${opts?.slotId ?? ''}, kind=${connectionKind}`);
822
+ const request = {
687
823
  nonce,
688
824
  auth: { method: 'kite_token', token },
689
825
  protocol: { min: '1.0', max: '1.0' },
@@ -694,11 +830,28 @@ export class AuthFlow {
694
830
  e2ee: true,
695
831
  group_e2ee: true,
696
832
  },
697
- });
833
+ };
834
+ // extra_info:应用层自定义信息(PID/HOME/备注等),踢人时透传给被踢方
835
+ const extraInfo = opts?.extraInfo;
836
+ if (extraInfo && Object.keys(extraInfo).length > 0) {
837
+ request.extra_info = extraInfo;
838
+ }
839
+ // 长短连接选项:默认 long 时不写入 options(保持 wire 兼容)
840
+ if (connectionKind === 'short') {
841
+ const options = { kind: 'short' };
842
+ const ttl = Number(opts?.shortTtlMs ?? 0);
843
+ if (ttl > 0) {
844
+ options.short_ttl_ms = ttl;
845
+ }
846
+ request.options = options;
847
+ }
848
+ const result = await transport.call('auth.connect', request);
698
849
  const status = isJsonObject(result) ? result.status : undefined;
699
850
  if (status !== 'ok') {
851
+ this._logger.error(`sessioninitfailed: status=${status}, result=${JSON.stringify(result)}`);
700
852
  throw new AuthError(`initialize failed: ${JSON.stringify(result)}`);
701
853
  }
854
+ this._logger.debug('sessioninitok');
702
855
  }
703
856
  // ── 内部方法:证书验证 ──────────────────────────────────────
704
857
  /**
@@ -732,7 +885,7 @@ export class AuthFlow {
732
885
  const errMsg = e instanceof Error ? e.message : String(e);
733
886
  if (/revoked/i.test(errMsg))
734
887
  throw e instanceof AuthError ? e : new AuthError(errMsg);
735
- this._logger.warn(`CRL 检查不可用,降级继续: ${errMsg}`);
888
+ this._logger.warn(`CRL verification unavailable, skipping: ${errMsg}`);
736
889
  }
737
890
  try {
738
891
  await this._verifyAuthCertOcsp(gatewayUrl, authCert);
@@ -741,7 +894,7 @@ export class AuthFlow {
741
894
  const errMsg = exc instanceof Error ? exc.message : String(exc);
742
895
  if (/revoked/i.test(errMsg))
743
896
  throw exc instanceof AuthError ? exc : new AuthError(errMsg);
744
- this._logger.warn(`OCSP 校验不可用,降级继续: ${errMsg}`);
897
+ this._logger.warn(`OCSP verification unavailable, skipping: ${errMsg}`);
745
898
  }
746
899
  // 验证 client_nonce 签名
747
900
  try {
@@ -915,7 +1068,7 @@ export class AuthFlow {
915
1068
  revokedSerials = new Set(serialsArr.map((s) => String(s).toLowerCase()));
916
1069
  }
917
1070
  // 如果两者都没有,返回空集合(无吊销记录)
918
- this._logger.debug('CRL PEM 解析失败,使用 JSON revoked_serials 降级');
1071
+ this._logger.debug('CRL PEM parse failed, using JSON revoked_serials fallback');
919
1072
  }
920
1073
  // 缓存 TTL:默认 5 分钟,最大 24 小时
921
1074
  const now = Date.now();
@@ -1088,7 +1241,7 @@ export class AuthFlow {
1088
1241
  // OCSP DER 解析失败时,降级信赖 JSON status 字段
1089
1242
  if (e instanceof AuthError)
1090
1243
  throw e;
1091
- this._logger.debug(`OCSP DER 解析失败,使用 JSON status 降级: ${e instanceof Error ? e.message : String(e)}`);
1244
+ this._logger.debug(`OCSP DER parse failed, using JSON status fallback: ${e instanceof Error ? e.message : String(e)}`);
1092
1245
  if (!status) {
1093
1246
  throw new AuthError('gateway OCSP endpoint returned invalid response and no status field');
1094
1247
  }
@@ -1359,7 +1512,7 @@ export class AuthFlow {
1359
1512
  const errMsg = e instanceof Error ? e.message : String(e);
1360
1513
  if (/revoked/i.test(errMsg))
1361
1514
  throw e instanceof AuthError ? e : new AuthError(errMsg);
1362
- this._logger.warn(`CRL 检查不可用,降级继续: ${errMsg}`);
1515
+ this._logger.warn(`CRL verification unavailable, skipping: ${errMsg}`);
1363
1516
  }
1364
1517
  try {
1365
1518
  await this._verifyAuthCertOcsp(gatewayUrl, cert);
@@ -1368,7 +1521,7 @@ export class AuthFlow {
1368
1521
  const errMsg = exc instanceof Error ? exc.message : String(exc);
1369
1522
  if (/revoked/i.test(errMsg))
1370
1523
  throw exc instanceof AuthError ? exc : new AuthError(errMsg);
1371
- this._logger.warn(`OCSP 校验不可用,降级继续: ${errMsg}`);
1524
+ this._logger.warn(`OCSP verification unavailable, skipping: ${errMsg}`);
1372
1525
  }
1373
1526
  }
1374
1527
  // 验证通过,正式接受
@@ -1376,10 +1529,10 @@ export class AuthFlow {
1376
1529
  }
1377
1530
  catch (e) {
1378
1531
  if (e instanceof AuthError) {
1379
- this._logger.warn(`拒绝服务端返回的 new_cert (${identity.aid}): ${e.message}`);
1532
+ this._logger.warn(`rejected server new_cert (${identity.aid}): ${e.message}`);
1380
1533
  }
1381
1534
  else {
1382
- this._logger.warn(`new_cert 验证异常 (${identity.aid}): ${e instanceof Error ? e.message : String(e)}`);
1535
+ this._logger.warn(`new_cert verification error (${identity.aid}): ${e instanceof Error ? e.message : String(e)}`);
1383
1536
  }
1384
1537
  }
1385
1538
  // active_cert 同步:验证公钥匹配后更新本地 cert
@@ -1396,12 +1549,12 @@ export class AuthFlow {
1396
1549
  identity.cert = activeCertPem;
1397
1550
  }
1398
1551
  else {
1399
- this._logger.warn(`服务端 active_cert 公钥与本地私钥不匹配,拒绝同步 (aid=${identity.aid})`);
1552
+ this._logger.warn(`active_cert public key mismatch with local private key, rejecting sync (aid=${identity.aid})`);
1400
1553
  }
1401
1554
  }
1402
1555
  }
1403
1556
  catch (e) {
1404
- this._logger.warn(`active_cert 同步异常 (${identity.aid}): ${e instanceof Error ? e.message : String(e)}`);
1557
+ this._logger.warn(`active_cert sync error (${identity.aid}): ${e instanceof Error ? e.message : String(e)}`);
1405
1558
  }
1406
1559
  }
1407
1560
  }
@@ -1467,12 +1620,22 @@ export class AuthFlow {
1467
1620
  /** 确保本地有指定 AID 的身份(不存在则创建密钥对) */
1468
1621
  _ensureLocalIdentity(aid) {
1469
1622
  const existing = this._keystore.loadIdentity(aid);
1470
- if (existing) {
1623
+ // 必须确认有 keypair(private_key_pem + public_key_der_b64)才算"已存在"
1624
+ // 否则 keystore 可能只有 metadata(如 gateway_url)但没有真正的密钥材料
1625
+ if (existing && existing.private_key_pem && existing.public_key_der_b64) {
1471
1626
  this._aid = aid;
1472
1627
  return existing;
1473
1628
  }
1474
1629
  const identity = this._crypto.generateIdentity();
1475
1630
  identity.aid = aid;
1631
+ // 保留 keystore 已有的 metadata(如 gateway_url),避免覆盖
1632
+ if (existing) {
1633
+ for (const [k, v] of Object.entries(existing)) {
1634
+ if (k !== 'aid' && !(k in identity)) {
1635
+ identity[k] = v;
1636
+ }
1637
+ }
1638
+ }
1476
1639
  this._persistIdentity(identity); // 立即持久化密钥对
1477
1640
  this._aid = aid;
1478
1641
  return identity;