@abtnode/auth 1.16.42 → 1.16.43-beta-20250419-231352-c78ac93d

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.
@@ -9,6 +9,7 @@ const formatContext = require('@abtnode/util/lib/format-context');
9
9
  const getRandomMessage = require('@abtnode/util/lib/get-random-message');
10
10
  const getNodeWallet = require('@abtnode/util/lib/get-app-wallet');
11
11
  const CustomError = require('@abtnode/util/lib/custom-error');
12
+ const getOrigin = require('@abtnode/util/lib/get-origin');
12
13
  const { getActivePassports } = require('@abtnode/util/lib/passport');
13
14
  const { getDisplayName, getBlockletAppIdList } = require('@blocklet/meta/lib/util');
14
15
  const {
@@ -433,6 +434,8 @@ const createLostPassportIssueRoute = ({ node, type, authServicePrefix, createTok
433
434
  walletOS,
434
435
  device: deviceData,
435
436
  },
437
+ locale,
438
+ origin: await getOrigin({ req }),
436
439
  });
437
440
 
438
441
  if (shouldSyncFederated(sourceAppPid, blocklet)) {
package/lib/oauth.js CHANGED
@@ -6,6 +6,7 @@ const { ROLES } = require('@abtnode/constant');
6
6
  const formatContext = require('@abtnode/util/lib/format-context');
7
7
  const { getBlockletAppIdList } = require('@blocklet/meta/lib/util');
8
8
  const { LOGIN_PROVIDER } = require('@blocklet/constant');
9
+ const getOrigin = require('@abtnode/util/lib/get-origin');
9
10
 
10
11
  const { getApplicationInfo } = require('./auth');
11
12
 
@@ -79,8 +80,9 @@ function createPassportSwitcher(node, createToken, mode = 'server') {
79
80
 
80
81
  const { passportId } = req.body;
81
82
  const userDid = req.user.did;
82
- const teamDid = mode === 'server' ? info.did : req.getBlockletDid();
83
- const secret = mode === 'server' ? await node.getSessionSecret() : (await req.getBlockletInfo()).secret;
83
+ const isServer = mode === 'server';
84
+ const teamDid = isServer ? info.did : req.getBlockletDid();
85
+ const secret = isServer ? await node.getSessionSecret() : (await req.getBlockletInfo()).secret;
84
86
 
85
87
  // NOTICE: 这里获取的 did 是当前登录用户的永久 did,无需查询 connectedAccount
86
88
  const user = await node.getUser({ teamDid, user: { did: userDid } });
@@ -91,15 +93,19 @@ function createPassportSwitcher(node, createToken, mode = 'server') {
91
93
  ? passports.find((item) => item.id === passportId)
92
94
  : { name: 'Guest', role: 'guest', scope: 'passport' };
93
95
 
94
- // 需要更新用户会话中记录的 passportId
95
- await node.upsertUserSession({
96
- teamDid,
97
- userDid: user.did,
98
- visitorId,
99
- appPid: teamDid,
100
- passportId: passport?.id ?? null,
101
- status: 'online',
102
- });
96
+ if (!isServer) {
97
+ // 需要更新用户会话中记录的 passportId
98
+ await node.upsertUserSession({
99
+ teamDid,
100
+ userDid: user.did,
101
+ visitorId,
102
+ appPid: teamDid,
103
+ passportId: passport?.id ?? null,
104
+ status: 'online',
105
+ locale: req.blockletLocale,
106
+ origin: await getOrigin({ req }),
107
+ });
108
+ }
103
109
 
104
110
  await node.createAuditLog(
105
111
  {
package/lib/passkey.js CHANGED
@@ -17,11 +17,13 @@ const {
17
17
  verifyRegistrationResponse,
18
18
  } = require('@simplewebauthn/server');
19
19
  const { updateConnectedAccount, getAvatarByEmail, extractUserAvatar } = require('@abtnode/util/lib/user');
20
+ const getOrigin = require('@abtnode/util/lib/get-origin');
20
21
 
21
22
  const { getApplicationInfo, handleInvitationReceive, canSessionBeElevated } = require('./auth');
22
23
  const { validateVerifyDestroyRequest } = require('./server');
23
24
  const { getLastUsedPassport } = require('./passport');
24
25
  const { getSessionConfig, checkInvitedUserOnly } = require('./oauth');
26
+ const { findFederatedSite, getUserAvatarUrl } = require('./util/federated');
25
27
 
26
28
  const PASSKEY_TIMEOUT = 60000;
27
29
  const PASSKEY_ACTIONS = {
@@ -43,7 +45,7 @@ const shouldVerifyUser = (action) =>
43
45
 
44
46
  const isRegisterAction = (action) => ['register', 'connect-owner'].includes(action);
45
47
 
46
- const getOrigin = (req) => req.get('host').split(':')[0];
48
+ const getDomain = (req) => req.get('host').split(':')[0];
47
49
  const emailSchema = Joi.string().email().required();
48
50
  const tryDecodeEmail = (encoded) => {
49
51
  try {
@@ -78,6 +80,16 @@ const formatBuffersToBase64 = (obj) => {
78
80
  return obj;
79
81
  };
80
82
 
83
+ const getTrustOrigins = ({ blocklet, req }) => {
84
+ const result = [`https://${getDomain(req)}`];
85
+ if (blocklet) {
86
+ const trustOrigins = blocklet.configObj?.TRUST_ORIGINS;
87
+ if (trustOrigins) result.push(...trustOrigins.split(','));
88
+ }
89
+
90
+ return result;
91
+ };
92
+
81
93
  function createPasskeyHandlers(node, mode, createToken) {
82
94
  const ensurePasskeySession = async (req, res, next) => {
83
95
  const { challenge } = req.query;
@@ -204,7 +216,7 @@ function createPasskeyHandlers(node, mode, createToken) {
204
216
  data: {
205
217
  ...req.query,
206
218
  user: user?.did,
207
- origin: getOrigin(req),
219
+ origin: getDomain(req),
208
220
  email,
209
221
  userName,
210
222
  },
@@ -225,7 +237,7 @@ function createPasskeyHandlers(node, mode, createToken) {
225
237
  });
226
238
 
227
239
  const options = {
228
- rpID: getOrigin(req),
240
+ rpID: getDomain(req),
229
241
  rpName: info.name,
230
242
  userID: toBuffer(userID),
231
243
  userName,
@@ -275,8 +287,8 @@ function createPasskeyHandlers(node, mode, createToken) {
275
287
  const { verified, registrationInfo } = await verifyRegistrationResponse({
276
288
  response: body,
277
289
  expectedChallenge: toBase64(req.passkeySession.challenge),
278
- expectedOrigin: `https://${getOrigin(req)}`,
279
- expectedRPID: getOrigin(req),
290
+ expectedOrigin: getTrustOrigins({ blocklet: req.blocklet, req }),
291
+ expectedRPID: getDomain(req),
280
292
  requireUserVerification: shouldVerifyUser(action),
281
293
  });
282
294
  logger.info('passkey.register.verified', { teamDid, verified, registrationInfo });
@@ -445,7 +457,7 @@ function createPasskeyHandlers(node, mode, createToken) {
445
457
  data: {
446
458
  ...req.query,
447
459
  user: req.userExpanded?.did,
448
- origin: getOrigin(req),
460
+ origin: getDomain(req),
449
461
  },
450
462
  },
451
463
  });
@@ -464,7 +476,7 @@ function createPasskeyHandlers(node, mode, createToken) {
464
476
  req.userExpanded = await node.getUser({ teamDid, user: { did: exist.userDid } });
465
477
  }
466
478
 
467
- const expectedRPID = getOrigin(req);
479
+ const expectedRPID = getDomain(req);
468
480
  let allowedCredentials = [];
469
481
  if (req.query.credentialId) {
470
482
  allowedCredentials = (req.userExpanded?.connectedAccounts || [])
@@ -522,8 +534,8 @@ function createPasskeyHandlers(node, mode, createToken) {
522
534
  const verification = await verifyAuthenticationResponse({
523
535
  response: body,
524
536
  expectedChallenge: toBase64(passkeySession.challenge),
525
- expectedOrigin: `https://${getOrigin(req)}`,
526
- expectedRPID: getOrigin(req),
537
+ expectedOrigin: getTrustOrigins({ blocklet: req.blocklet, req }),
538
+ expectedRPID: getDomain(req),
527
539
  credential: {
528
540
  id: passkey.id,
529
541
  publicKey: fromBase64(passkey.pk),
@@ -537,20 +549,40 @@ function createPasskeyHandlers(node, mode, createToken) {
537
549
  }
538
550
  logger.info('passkey.auth.verified', { verified, authenticationInfo });
539
551
 
540
- const createUserSession = () =>
541
- node.upsertUserSession({
552
+ const createUserSession = async ({ targetAppPid } = {}) => {
553
+ const ua = req.get('user-agent');
554
+ const lastLoginIp = getRequestIP(req);
555
+ const userSessionDoc = await node.upsertUserSession({
542
556
  teamDid,
543
557
  userDid: user.did,
544
558
  visitorId: req.get('x-blocklet-visitor-id'),
545
559
  appPid: teamDid,
546
560
  passportId: null,
547
561
  status: 'online',
548
- ua: req.get('user-agent'),
562
+ ua,
549
563
  lastLoginIp: getRequestIP(req),
550
564
  extra: {
551
565
  walletOS: 'passkey',
552
566
  },
567
+ locale: getLocale(req),
568
+ origin: await getOrigin({ req }),
553
569
  });
570
+ if (targetAppPid) {
571
+ node.syncUserSession({
572
+ teamDid,
573
+ userDid: userSessionDoc.userDid,
574
+ visitorId: userSessionDoc.visitorId,
575
+ passportId: null,
576
+ targetAppPid,
577
+ ua,
578
+ lastLoginIp,
579
+ extra: {
580
+ walletOS: 'passkey',
581
+ },
582
+ });
583
+ }
584
+ return userSessionDoc;
585
+ };
554
586
 
555
587
  const createTokens = async (updated, passport, role, profile, result) => {
556
588
  const { secret } = await getApplicationInfo({ node, nodeInfo: info, teamDid });
@@ -581,8 +613,8 @@ function createPasskeyHandlers(node, mode, createToken) {
581
613
  return result;
582
614
  };
583
615
 
584
- const loginUser = () =>
585
- node.loginUser({
616
+ const loginUser = async () => {
617
+ const _result = await node.loginUser({
586
618
  teamDid,
587
619
  user: {
588
620
  did: user.did,
@@ -593,6 +625,8 @@ function createPasskeyHandlers(node, mode, createToken) {
593
625
  },
594
626
  },
595
627
  });
628
+ return _result;
629
+ };
596
630
 
597
631
  const result = { verified, userDid: user.did };
598
632
 
@@ -604,10 +638,45 @@ function createPasskeyHandlers(node, mode, createToken) {
604
638
  PASSKEY_ACTIONS['connect-to-did-domain'],
605
639
  ].includes(passkeySession.data.action)
606
640
  ) {
607
- const updated = await loginUser();
608
- const passport = getLastUsedPassport(updated.passports);
609
- await createTokens(updated, passport, passport.role, user, result);
610
- logger.info('passkey.auth.loggedIn', { teamDid, userDid: user.did, passport });
641
+ const { targetAppPid } = req.query;
642
+ // FIXME: @zhanghan 这里目前只是一个 hack 的方式,passkey 和 federated 结合的流程需要重新梳理优化
643
+ const isFederatedHack = targetAppPid && targetAppPid !== teamDid;
644
+ if (mode === 'service' && isFederatedHack) {
645
+ const findMemberSite = findFederatedSite(req.blocklet, targetAppPid);
646
+ if (findMemberSite) {
647
+ const postUser = pick(user, ['did', 'pk', 'fullName', 'locale', 'inviter', 'generation']);
648
+ postUser.lastLoginAt = getRequestIP(req);
649
+
650
+ if (user.email) {
651
+ postUser.email = user.email;
652
+ }
653
+ if (user.avatar) {
654
+ postUser.avatar = getUserAvatarUrl(user.avatar, req.blocklet);
655
+ }
656
+ // 这里是为了在 member 上创建用户
657
+ const _result = await node.loginFederated({
658
+ did: teamDid,
659
+ data: {
660
+ user: postUser,
661
+ walletOS: 'passkey',
662
+ provider: LOGIN_PROVIDER.PASSKEY,
663
+ },
664
+ site: findMemberSite,
665
+ });
666
+ result.sessionToken = _result.sessionToken;
667
+ result.refreshToken = _result.refreshToken;
668
+
669
+ const session = await createUserSession({ targetAppPid });
670
+ result.visitorId = session.visitorId;
671
+ } else {
672
+ throw new Error('Target site not found');
673
+ }
674
+ } else {
675
+ const updated = await loginUser();
676
+ const passport = getLastUsedPassport(updated.passports);
677
+ await createTokens(updated, passport, passport.role, user, result);
678
+ logger.info('passkey.auth.loggedIn', { teamDid, userDid: user.did, passport });
679
+ }
611
680
  }
612
681
 
613
682
  if (passkeySession.data.action === 'disconnect') {
@@ -764,6 +833,6 @@ function createPasskeyHandlers(node, mode, createToken) {
764
833
  module.exports = {
765
834
  createPasskeyHandlers,
766
835
  getSessionConfig,
767
- getOrigin,
836
+ getDomain,
768
837
  formatBuffersToBase64,
769
838
  };
package/lib/server.js CHANGED
@@ -41,6 +41,7 @@ const {
41
41
  } = require('@abtnode/constant');
42
42
  const parseBooleanString = require('@abtnode/util/lib/parse-boolean-string');
43
43
  const { getDeviceData } = require('@abtnode/util/lib/device');
44
+ const getOrigin = require('@abtnode/util/lib/get-origin');
44
45
 
45
46
  const {
46
47
  messages,
@@ -862,6 +863,7 @@ const createLaunchBlockletHandler =
862
863
  walletOS: didwallet?.os,
863
864
  userDid,
864
865
  device: context ? deviceData : null,
866
+ origin: await getOrigin({ req }),
865
867
  },
866
868
  });
867
869
  await updateSession({ setupToken: result.setupToken, visitorId: result.visitorId });
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.16.42",
6
+ "version": "1.16.43-beta-20250419-231352-c78ac93d",
7
7
  "description": "Simple lib to manage auth in ABT Node",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -20,22 +20,22 @@
20
20
  "author": "linchen <linchen1987@foxmail.com> (http://github.com/linchen1987)",
21
21
  "license": "Apache-2.0",
22
22
  "dependencies": {
23
- "@abtnode/constant": "1.16.42",
24
- "@abtnode/logger": "1.16.42",
25
- "@abtnode/util": "1.16.42",
26
- "@arcblock/did": "1.20.0",
27
- "@arcblock/did-auth": "1.20.0",
28
- "@arcblock/jwt": "^1.20.0",
29
- "@arcblock/nft-display": "^2.13.0",
30
- "@arcblock/validator": "^1.20.0",
31
- "@arcblock/vc": "1.20.0",
32
- "@blocklet/constant": "1.16.42",
33
- "@blocklet/meta": "1.16.42",
34
- "@blocklet/sdk": "1.16.42",
35
- "@ocap/client": "^1.20.0",
36
- "@ocap/mcrypto": "1.20.0",
37
- "@ocap/util": "1.20.0",
38
- "@ocap/wallet": "1.20.0",
23
+ "@abtnode/constant": "1.16.43-beta-20250419-231352-c78ac93d",
24
+ "@abtnode/logger": "1.16.43-beta-20250419-231352-c78ac93d",
25
+ "@abtnode/util": "1.16.43-beta-20250419-231352-c78ac93d",
26
+ "@arcblock/did": "1.20.1",
27
+ "@arcblock/did-auth": "1.20.1",
28
+ "@arcblock/jwt": "^1.20.1",
29
+ "@arcblock/nft-display": "^2.13.7",
30
+ "@arcblock/validator": "^1.20.1",
31
+ "@arcblock/vc": "1.20.1",
32
+ "@blocklet/constant": "1.16.43-beta-20250419-231352-c78ac93d",
33
+ "@blocklet/meta": "1.16.43-beta-20250419-231352-c78ac93d",
34
+ "@blocklet/sdk": "1.16.43-beta-20250419-231352-c78ac93d",
35
+ "@ocap/client": "^1.20.1",
36
+ "@ocap/mcrypto": "1.20.1",
37
+ "@ocap/util": "1.20.1",
38
+ "@ocap/wallet": "1.20.1",
39
39
  "@simplewebauthn/server": "^13.0.0",
40
40
  "axios": "^1.7.9",
41
41
  "flat": "^5.0.2",
@@ -52,5 +52,5 @@
52
52
  "devDependencies": {
53
53
  "jest": "^29.7.0"
54
54
  },
55
- "gitHead": "04e4adf8f2c1d6289850c66159c0a68ea946d5e6"
55
+ "gitHead": "207acad34e8ccf318cd7539c1ac717cee7951b53"
56
56
  }