@abtnode/blocklet-services 1.16.18 → 1.16.19-beta-e6aac665

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 (31) hide show
  1. package/api/libs/connect/session.js +17 -19
  2. package/api/libs/connect/v1.js +5 -1
  3. package/api/libs/connect/v2.js +5 -4
  4. package/api/libs/image.js +2 -2
  5. package/api/routes/federated.js +108 -19
  6. package/api/routes/oauth.js +19 -26
  7. package/api/routes/user-session.js +5 -1
  8. package/api/routes/user.js +1 -0
  9. package/api/services/auth/connect/invite.js +8 -5
  10. package/api/util/federated.js +45 -0
  11. package/api/validators/login.js +1 -1
  12. package/build/asset-manifest.json +15 -15
  13. package/build/index.html +1 -1
  14. package/build/service-worker.js +1 -1
  15. package/build/static/js/4076.8055ce74.chunk.js +2 -0
  16. package/build/static/js/4461.f9a883d3.chunk.js +2 -0
  17. package/build/static/js/5468.21a861b9.chunk.js +2 -0
  18. package/build/static/js/5547.42b98889.chunk.js +3 -0
  19. package/build/static/js/8622.6aa0a4d4.chunk.js +2 -0
  20. package/build/static/js/8944.939de854.chunk.js +2 -0
  21. package/build/static/js/main.6f748ae5.js +3 -0
  22. package/package.json +24 -27
  23. package/build/static/js/4076.f5369a1d.chunk.js +0 -2
  24. package/build/static/js/4461.3cd698bf.chunk.js +0 -2
  25. package/build/static/js/5468.b54ce65b.chunk.js +0 -2
  26. package/build/static/js/5547.570ded36.chunk.js +0 -3
  27. package/build/static/js/8622.ebd44812.chunk.js +0 -2
  28. package/build/static/js/8944.559ade67.chunk.js +0 -2
  29. package/build/static/js/main.bf370e0c.js +0 -3
  30. /package/build/static/js/{5547.570ded36.chunk.js.LICENSE.txt → 5547.42b98889.chunk.js.LICENSE.txt} +0 -0
  31. /package/build/static/js/{main.bf370e0c.js.LICENSE.txt → main.6f748ae5.js.LICENSE.txt} +0 -0
@@ -48,7 +48,13 @@ const { isInvitedUserOnly, createTokenFn, getDidConnectVersion } = require('../.
48
48
  const { transferPassport } = require('../auth/utils');
49
49
  const { migrateAccount, declareAccount } = require('../../services/oauth');
50
50
  const { getTrustedIssuers, getFederatedTrustedIssuers } = require('../../util/blocklet-utils');
51
- const { getUserAvatarUrl, migrateAuth0, getFederatedMaster, shouldSyncFederated } = require('../../util/federated');
51
+ const {
52
+ getUserAvatarUrl,
53
+ migrateAuth0,
54
+ getFederatedMaster,
55
+ shouldSyncFederated,
56
+ getUserWithinFederated,
57
+ } = require('../../util/federated');
52
58
 
53
59
  const vcTypes = [VC_TYPE_GENERAL_PASSPORT, VC_TYPE_NODE_PASSPORT];
54
60
 
@@ -215,23 +221,16 @@ module.exports = {
215
221
  const blocklet = await request.getBlocklet();
216
222
  const blockletInfo = await request.getBlockletInfo();
217
223
  const { wallet, secret, name, passportColor, did: teamDid } = blockletInfo;
224
+ const sourceAppPid = getSourceAppPid(request);
218
225
 
219
226
  // Check user approved
220
- const user = await node.getUser({
221
- teamDid,
222
- user: {
223
- did: userDid,
224
- },
225
- options: {
226
- enableConnectedAccount: true,
227
- },
228
- });
229
- if (user && !user.approved) {
227
+ const currentUser = await getUserWithinFederated({ sourceAppPid, teamDid, userDid, userPk }, { node, blocklet });
228
+ if (currentUser && !currentUser.approved) {
230
229
  throw new Error(messages.notAllowed[locale]);
231
230
  }
232
231
 
233
- const realDid = user?.did || userDid;
234
- const realPk = user?.pk || userPk;
232
+ const realDid = currentUser?.did || userDid;
233
+ const realPk = currentUser?.pk || userPk;
235
234
 
236
235
  // Get auth config
237
236
  const authConfig = (await request.getServiceConfig(NODE_SERVICES.AUTH, { componentId })) || {};
@@ -244,7 +243,6 @@ module.exports = {
244
243
  let defaultTtlPolicy = 'never';
245
244
  let issuePassport = false;
246
245
 
247
- const sourceAppPid = getSourceAppPid(request);
248
246
  const provider = getLoginProvider(request);
249
247
  const masterSite = getFederatedMaster(blocklet);
250
248
 
@@ -318,7 +316,7 @@ module.exports = {
318
316
 
319
317
  // Get user passport from vc
320
318
  let passport = vc ? createUserPassport(vc) : null;
321
- if (user && passport && isUserPassportRevoked(user, passport)) {
319
+ if (currentUser && passport && isUserPassportRevoked(currentUser, passport)) {
322
320
  throw new Error(messages.passportRevoked[locale](passport.title, name));
323
321
  }
324
322
 
@@ -342,19 +340,19 @@ module.exports = {
342
340
  }
343
341
  : null;
344
342
 
345
- let fullName = user?.fullName;
343
+ let fullName = currentUser?.fullName;
346
344
  // Update profile
347
345
  const passportForLog = passport || { name: 'Guest', role: 'guest' };
348
346
 
349
347
  const connectAccount = { provider, did: userDid, pk: userPk };
350
348
 
351
349
  let updatedUser;
352
- if (user) {
350
+ if (currentUser) {
353
351
  updatedUser = await node.loginUser({
354
352
  teamDid,
355
353
  user: {
356
- did: user.did,
357
- pk: user.pk,
354
+ did: currentUser.did,
355
+ pk: currentUser.pk,
358
356
  locale,
359
357
  passport,
360
358
  sourceAppPid,
@@ -66,7 +66,11 @@ module.exports = (node, opts) => {
66
66
 
67
67
  const handlerOpts = {
68
68
  authenticator,
69
- tokenStorage: new DynamicStorage({ dbPath: path.join(opts.dataDir, 'connections.db') }),
69
+ tokenStorage: new DynamicStorage({
70
+ dbPath: path.join(opts.dataDir, 'connections.db'),
71
+ model: 'Connection',
72
+ primaryKey: 'token',
73
+ }),
70
74
  sendNotificationFn: async (connectedDid, message, { req }) => {
71
75
  const { wallet } = await req.getBlockletInfo();
72
76
  return sendToUser(
@@ -1,5 +1,5 @@
1
1
  const path = require('path');
2
- const { NedbStorage } = require('@did-connect/storage-nedb');
2
+ const DynamicStorage = require('@abtnode/connect-storage');
3
3
  const { Authenticator } = require('@did-connect/authenticator');
4
4
  const createHandlers = require('@blocklet/sdk/lib/connect/handler');
5
5
  const { sendToUser } = require('@blocklet/sdk/lib/util/send-notification');
@@ -22,9 +22,10 @@ module.exports = (node, opts) => {
22
22
  const handlers = createHandlers({
23
23
  logger,
24
24
  authenticator,
25
- storage: new NedbStorage({
26
- // FIXME: @wangshijun this does not work anymore
27
- dbPath: path.join(opts.dataDir, 'sessions.db'),
25
+ storage: new DynamicStorage({
26
+ dbPath: path.join(opts.dataDir, 'connections.db'),
27
+ model: 'ConnectionV2',
28
+ primaryKey: 'sessionId',
28
29
  }),
29
30
  socketPathname: `${WELLKNOWN_SERVICE_PATH_PREFIX}/api/connect/relay/websocket`,
30
31
  sendNotificationFn: async (connectedDid, message, { request }) => {
package/api/libs/image.js CHANGED
@@ -235,7 +235,7 @@ const processImage = (src, extension, dest, params) => {
235
235
  dimensions.height = height;
236
236
  }
237
237
 
238
- const pipeline = sharp({ animated: true }).timeout({ seconds: 30 });
238
+ const pipeline = sharp({ animated: true, limitInputPixels: 0 }).timeout({ seconds: 60 });
239
239
  if (rotate) {
240
240
  pipeline.rotate(rotate);
241
241
  }
@@ -268,7 +268,7 @@ const processImage = (src, extension, dest, params) => {
268
268
  pipeline.negate();
269
269
  }
270
270
 
271
- pipeline[format || EXTENSIONS[extension]]({ quality, progressive: !!progressive, force: true });
271
+ pipeline[format || EXTENSIONS[extension]]({ quality, progressive: !!progressive, dither: 0, force: true });
272
272
 
273
273
  pipeline.on('error', (err) => {
274
274
  reject(err);
@@ -1,4 +1,4 @@
1
- const { WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
1
+ const { WELLKNOWN_SERVICE_PATH_PREFIX, FEDERATED } = require('@abtnode/constant');
2
2
  const { signV2 } = require('@arcblock/jwt');
3
3
  const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
4
4
  const cloneDeep = require('lodash/cloneDeep');
@@ -25,13 +25,12 @@ const { createTokenFn, getDidConnectVersion } = require('../util');
25
25
  const ensureBlocklet = require('../middlewares/ensure-blocklet');
26
26
  const verifyFederatedCall = require('../middlewares/verify-federated-call');
27
27
  const checkFederatedCorsCall = require('../middlewares/check-federated-cors-call');
28
- const { getUserAvatarUrl, getFederatedMaster } = require('../util/federated');
28
+ const { getUserAvatarUrl, getFederatedMaster, getUserWithinFederated } = require('../util/federated');
29
29
  const { declareAccount, migrateAccount } = require('../services/oauth');
30
30
 
31
31
  const PREFIX = WELLKNOWN_SERVICE_PATH_PREFIX;
32
32
 
33
33
  const prefix = `${PREFIX}/api/federated`;
34
- const limitSync = pLimit(1);
35
34
 
36
35
  function getAuditLogActorByFederatedSite(blocklet) {
37
36
  return {
@@ -60,8 +59,13 @@ async function syncSwitchProfile(user, { node, teamDid, dataDir }) {
60
59
  });
61
60
  }
62
61
 
63
- async function syncConnectAccount(user, { node, teamDid, dataDir }) {
62
+ /**
63
+ * 处理站点群中某一站点推送的同步 connectAccount 的请求
64
+ */
65
+
66
+ async function syncConnectAccount(user, { node, teamDid, dataDir, blocklet }) {
64
67
  const tempUser = pick(user, ['did', 'pk', 'avatar', 'fullName', 'email', 'connectedAccount', 'sourceAppPid']);
68
+ const masterSite = getFederatedMaster(blocklet);
65
69
 
66
70
  // 处理 avatar
67
71
  if (tempUser.avatar) {
@@ -73,15 +77,71 @@ async function syncConnectAccount(user, { node, teamDid, dataDir }) {
73
77
  }
74
78
  }
75
79
 
80
+ // NOTICE: 如果当前站点不是 master,就需要考虑该 member 需要向 master 请求同步一次指定账户,因为指定账户可能存在于站点群,而不存在于当前站点中的情况
81
+ if (masterSite.appPid !== teamDid) {
82
+ await getUserWithinFederated(
83
+ {
84
+ sourceAppPid: tempUser.sourceAppPid,
85
+ teamDid,
86
+ userDid: tempUser.did,
87
+ userPk: tempUser.pk,
88
+ },
89
+ { blocklet, node }
90
+ );
91
+ }
92
+
76
93
  await node.loginUser({
77
94
  teamDid,
78
95
  user: tempUser,
79
96
  });
80
97
  }
81
98
 
99
+ /**
100
+ * member 站点向 master 站点请求拉取一个用户信息
101
+ */
102
+ async function pullUserAccount(user, { node, teamDid, blocklet }) {
103
+ const { did } = user;
104
+ const currentUser = await node.getUser({
105
+ teamDid,
106
+ user: {
107
+ did,
108
+ },
109
+ options: {
110
+ enableConnectedAccount: true,
111
+ },
112
+ });
113
+
114
+ if (!currentUser) return null;
115
+
116
+ const syncUser = pick(currentUser, [
117
+ 'did',
118
+ 'pk',
119
+ 'fullName',
120
+ 'email',
121
+ 'remark',
122
+ 'sourceProvider',
123
+ 'locale',
124
+ 'approved',
125
+ 'extra',
126
+ 'sourceAppPid',
127
+ ]);
128
+ syncUser.avatar = getUserAvatarUrl(currentUser.avatar, blocklet);
129
+ syncUser.email = syncUser.email || '';
130
+ syncUser.connectedAccounts = currentUser.connectedAccounts.map((x) => {
131
+ const connectAccount = pick(x, ['did', 'pk', 'provider', 'id']);
132
+ if (!connectAccount.id) {
133
+ delete connectAccount.id;
134
+ }
135
+ return connectAccount;
136
+ });
137
+
138
+ return syncUser;
139
+ }
140
+
82
141
  const syncFnMaps = {
83
142
  switchProfile: syncSwitchProfile,
84
143
  connectAccount: syncConnectAccount,
144
+ pullAccount: pullUserAccount,
85
145
  };
86
146
 
87
147
  module.exports = {
@@ -186,6 +246,7 @@ module.exports = {
186
246
  signer: permanentWallet.address,
187
247
  data: signV2(permanentWallet.address, permanentWallet.secretKey, { sites: federated.sites }),
188
248
  };
249
+ const limitSync = pLimit(FEDERATED.SYNC_LIMIT);
189
250
 
190
251
  const waitingList = federated.sites
191
252
  .filter((item) => item.appId !== federated.config.appId)
@@ -313,9 +374,15 @@ module.exports = {
313
374
  const { verifySite } = req.body;
314
375
  const teamDid = blocklet.appPid;
315
376
  const { users = null, sites = null, userSessions = null } = req.body.verifyData;
377
+ const resultData = {
378
+ users: [],
379
+ sites: [],
380
+ userSessions: [],
381
+ };
316
382
 
317
383
  // FIXME: @zhanghan 校验 users 和 sites 数据合法性
318
384
  if (!isNil(sites)) {
385
+ const limitSync = pLimit(FEDERATED.SYNC_LIMIT);
319
386
  const pendingSiteList = [];
320
387
  const federated = cloneDeep(blocklet.settings.federated || {});
321
388
  federated.sites = sites;
@@ -327,34 +394,39 @@ module.exports = {
327
394
  });
328
395
  })
329
396
  );
330
- await Promise.all(pendingSiteList);
397
+ const resList = await Promise.all(pendingSiteList);
398
+ resultData.sites = resList;
331
399
  }
332
400
 
333
401
  if (!isNil(users)) {
334
402
  if (Array.isArray(users)) {
403
+ const limitSync = pLimit(FEDERATED.SYNC_LIMIT);
335
404
  const pendingUserList = [];
336
405
  const nodeInfo = await req.getNodeInfo();
337
406
  const { dataDir } = await getApplicationInfo({ node, nodeInfo, teamDid });
338
407
  for (const user of users) {
339
408
  pendingUserList.push(
340
409
  limitSync(async () => {
341
- await syncFnMaps[user.action]?.(
410
+ const result = await syncFnMaps[user.action]?.(
342
411
  {
343
412
  ...user,
344
413
  sourceAppPid: user.sourceAppPid === teamDid ? null : user.sourceAppPid,
345
414
  },
346
- { node, teamDid, dataDir }
415
+ { node, teamDid, dataDir, blocklet }
347
416
  );
417
+ return result;
348
418
  })
349
419
  );
350
420
  }
351
- await Promise.all(pendingUserList);
421
+ const resList = await Promise.all(pendingUserList);
422
+ resultData.users = resList;
352
423
  }
353
424
  }
354
425
 
355
426
  if (!isNil(userSessions)) {
356
427
  if (Array.isArray(userSessions)) {
357
428
  const pendingUserSessionList = [];
429
+ const limitSync = pLimit(FEDERATED.SYNC_LIMIT);
358
430
  for (const userSession of userSessions) {
359
431
  const { action, ...userSessionItem } = userSession;
360
432
  pendingUserSessionList.push(
@@ -367,7 +439,8 @@ module.exports = {
367
439
  })
368
440
  );
369
441
  }
370
- await Promise.all(pendingUserSessionList);
442
+ const resList = await Promise.all(pendingUserSessionList);
443
+ resultData.userSessions = resList;
371
444
  }
372
445
  }
373
446
 
@@ -381,10 +454,13 @@ module.exports = {
381
454
  },
382
455
  node
383
456
  );
384
- res.json({});
457
+ res.json(resultData);
385
458
  });
386
459
 
387
- // step 4 检测自动登录条件(member 向 master 发起请求)
460
+ /**
461
+ * @deprecated
462
+ * step 4 检测自动登录条件(member 向 master 发起请求)
463
+ */
388
464
  server.post(
389
465
  `${prefix}/prelogin`,
390
466
  cors({ credentials: true, origin: true }),
@@ -425,7 +501,10 @@ module.exports = {
425
501
  }
426
502
  );
427
503
 
428
- // step 5 完成 member 的自动登录(member 向 master 请求)
504
+ /**
505
+ * @deprecated
506
+ * step 5 完成 member 的自动登录(member 向 master 请求)
507
+ */
429
508
  server.post(
430
509
  `${prefix}/login`,
431
510
  cors({ credentials: true, origin: true }),
@@ -591,7 +670,10 @@ module.exports = {
591
670
  res.json({ sessionToken, refreshToken });
592
671
  });
593
672
 
594
- // member 要求 master 退出登录
673
+ /**
674
+ * @deprecated
675
+ * member 要求 master 退出登录
676
+ */
595
677
  server.post(
596
678
  `${prefix}/logout`,
597
679
  cors({ credentials: true, origin: true }),
@@ -698,11 +780,18 @@ module.exports = {
698
780
  const teamDid = blocklet.appPid;
699
781
 
700
782
  const sessionConfig = blocklet.settings?.session || {};
701
- const prevUser = await node.getUser({
702
- teamDid,
703
- user: { did: user.did },
704
- options: { enableConnectedAccount: true },
705
- });
783
+ const prevUser = await getUserWithinFederated(
784
+ {
785
+ teamDid,
786
+ sourceAppPid: verifySite.appPid,
787
+ userDid: user.did,
788
+ userPk: user.pk,
789
+ },
790
+ {
791
+ node,
792
+ blocklet,
793
+ }
794
+ );
706
795
  // HACK: member 调用 master 时,将 passport 的 role 还原为 master 中原有的 role
707
796
  const targetPassport = passport?.id ? (prevUser?.passports || []).find((item) => item.id === passport.id) : null;
708
797
 
@@ -718,7 +807,6 @@ module.exports = {
718
807
  }
719
808
  const realDid = prevUser?.did || user.did;
720
809
  const realPk = prevUser?.pk || user.pk;
721
- // NOTICE: 这里是 Master 登录,不需要 sourceAppPid 字段
722
810
  const newUser = await node.loginUser({
723
811
  teamDid,
724
812
  user: {
@@ -726,6 +814,7 @@ module.exports = {
726
814
  did: realDid,
727
815
  pk: realPk,
728
816
  passport: targetPassport,
817
+ sourceAppPid: verifySite.appPid,
729
818
  connectedAccount: {
730
819
  provider: provider || LOGIN_PROVIDER.WALLET,
731
820
  did: user.did,
@@ -23,7 +23,12 @@ const initJwt = require('../libs/jwt');
23
23
  const { sendToUser } = require('../libs/notification');
24
24
  const { isInvitedUserOnly, createTokenFn, getDidConnectVersion } = require('../util');
25
25
  const { ApiError } = require('../util/error');
26
- const { loginAuth0, getFederatedMaster, getOAuthUserInfo, shouldSyncFederated } = require('../util/federated');
26
+ const {
27
+ getFederatedMaster,
28
+ getOAuthUserInfo,
29
+ shouldSyncFederated,
30
+ getUserWithinFederated,
31
+ } = require('../util/federated');
27
32
 
28
33
  const PREFIX = WELLKNOWN_SERVICE_PATH_PREFIX;
29
34
 
@@ -99,15 +104,7 @@ async function login(req, node, options) {
99
104
 
100
105
  const lastLoginIp = get(req, 'headers[x-real-ip]') || '';
101
106
  let passport = { name: 'Guest', role: 'guest' };
102
- let currentUser = await node.getUser({
103
- teamDid,
104
- user: {
105
- did: userDid,
106
- },
107
- options: {
108
- enableConnectedAccount: true,
109
- },
110
- });
107
+ let currentUser = await getUserWithinFederated({ sourceAppPid, teamDid, userDid, userPk }, { node, blocklet });
111
108
  let doc;
112
109
  let passportForLog;
113
110
  const fullName = currentUser?.fullName || oauthInfo?.nickname;
@@ -117,6 +114,7 @@ async function login(req, node, options) {
117
114
  pk: userPk,
118
115
  id: oauthInfo.sub,
119
116
  };
117
+ const masterSite = getFederatedMaster(blocklet);
120
118
  let profile;
121
119
  // 当前账户已存在,更新账户信息
122
120
  if (currentUser) {
@@ -156,6 +154,7 @@ async function login(req, node, options) {
156
154
  if (invitedUserOnly) {
157
155
  throw new ApiError(403, t('needInviteToLogin', locale));
158
156
  }
157
+
159
158
  passportForLog = { name: 'Guest', role: 'guest' };
160
159
  profile = {
161
160
  fullName: oauthInfo.nickname,
@@ -186,7 +185,6 @@ async function login(req, node, options) {
186
185
  node
187
186
  );
188
187
 
189
- const masterSite = getFederatedMaster(blocklet);
190
188
  const ua = req.headers['user-agent'];
191
189
  const userSessionDoc = await node.upsertUserSession({
192
190
  teamDid,
@@ -279,15 +277,16 @@ async function invite(req, node, options) {
279
277
 
280
278
  // NOTICE: 如果是统一登录,则向 master 站点发起 oauth 登录请求,auth0 的账户信息必须由 master 来生成
281
279
  if (sourceAppPid) {
282
- const data = await loginAuth0({
280
+ const data = await getOAuthUserInfo({
283
281
  request: req,
284
282
  blocklet,
285
- locale,
286
283
  provider,
287
284
  token,
288
285
  sourceAppPid,
286
+ locale,
289
287
  });
290
- ({ oauthInfo, userWallet } = data);
288
+ userWallet = data.wallet;
289
+ oauthInfo = data.info;
291
290
  } else {
292
291
  const authClient = getAuthClient(blocklet, provider);
293
292
  oauthInfo = await authClient.getProfile(token);
@@ -299,15 +298,9 @@ async function invite(req, node, options) {
299
298
  let userPk = userWallet.publicKey;
300
299
 
301
300
  let profile;
302
- const currentUser = await node.getUser({
303
- teamDid,
304
- user: {
305
- did: userDid,
306
- },
307
- options: {
308
- enableConnectedAccount: true,
309
- },
310
- });
301
+
302
+ const currentUser = await getUserWithinFederated({ sourceAppPid, teamDid, userDid, userPk }, { node, blocklet });
303
+
311
304
  const { dataDir, name: applicationName } = await getApplicationInfo({ node, nodeInfo, teamDid });
312
305
  if (currentUser) {
313
306
  profile = {
@@ -340,8 +333,8 @@ async function invite(req, node, options) {
340
333
  profile,
341
334
  statusEndpointBaseUrl,
342
335
  teamDid,
343
- userDid,
344
- userPk,
336
+ userDid: userWallet.address,
337
+ userPk: userWallet.publicKey,
345
338
  locale,
346
339
  provider,
347
340
  });
@@ -506,7 +499,7 @@ async function bind(req, node, options) {
506
499
  if (!avatar) {
507
500
  const nodeInfo = await req.getNodeInfo();
508
501
  const { dataDir } = await getApplicationInfo({ node, nodeInfo, teamDid });
509
- avatar = await getAvatarByEmail(userInfo.email);
502
+ avatar = userInfo.picture ? await getAvatarByUrl(userInfo.picture) : await getAvatarByEmail(userInfo.email);
510
503
  avatar = await extractUserAvatar(avatar, { dataDir });
511
504
  }
512
505
 
@@ -7,6 +7,7 @@ const defaults = require('lodash/defaults');
7
7
  const cloneDeep = require('lodash/cloneDeep');
8
8
  const pLimit = require('p-limit');
9
9
  const logger = require('@abtnode/logger')('blocklet-services:user-session');
10
+ const { getSourceProvider } = require('@blocklet/meta/lib/did-utils');
10
11
 
11
12
  const ensureBlocklet = require('../middlewares/ensure-blocklet');
12
13
  const { getUserAvatarUrl } = require('../util/federated');
@@ -103,6 +104,9 @@ module.exports = {
103
104
  },
104
105
  sites: [],
105
106
  });
107
+ const sourceProvider = getSourceProvider(user);
108
+
109
+ const provider = sourceProvider || LOGIN_PROVIDER.WALLET;
106
110
 
107
111
  const memberSite = federated.sites.find((item) => item.appPid === appPid);
108
112
  const postUser = pick(user, ['did', 'pk', 'fullName', 'locale']);
@@ -121,7 +125,7 @@ module.exports = {
121
125
  user: postUser,
122
126
  passport: passportId ? { id: passportId } : undefined,
123
127
  walletOS: 'web',
124
- provider: LOGIN_PROVIDER.WALLET,
128
+ provider,
125
129
  },
126
130
  site: memberSite,
127
131
  });
@@ -175,6 +175,7 @@ async function loginOAuth(
175
175
  throw new ApiError(400, error.message);
176
176
  }
177
177
 
178
+ // FIXME: @zhanghan 如果有 sourceAppPid,则这里拿到的 userWallet 是不对的?
178
179
  const userWallet = fromAppDid(id, blockletWallet.secretKey);
179
180
  const userDid = userWallet.address;
180
181
  const userPk = userWallet.publicKey;
@@ -13,6 +13,7 @@ const { LOGIN_PROVIDER } = require('@blocklet/constant');
13
13
  const logger = require('@abtnode/logger')(require('../../../../package.json').name);
14
14
 
15
15
  const { createTokenFn, getDidConnectVersion } = require('../../../util');
16
+ const { getUserWithinFederated } = require('../../../util/federated');
16
17
 
17
18
  module.exports = function createRoutes(node, authenticator, createSessionToken) {
18
19
  return {
@@ -65,7 +66,9 @@ module.exports = function createRoutes(node, authenticator, createSessionToken)
65
66
  const statusEndpointBaseUrl = joinUrl(baseUrl, WELLKNOWN_SERVICE_PATH_PREFIX);
66
67
  const endpoint = baseUrl;
67
68
 
68
- const { passport, response, role, profile } = await handleInvitationResponse({
69
+ await getUserWithinFederated({ sourceAppPid, teamDid, userDid, userPk }, { node, blocklet });
70
+
71
+ const { passport, response, role, profile, user } = await handleInvitationResponse({
69
72
  req,
70
73
  node,
71
74
  nodeInfo,
@@ -85,7 +88,7 @@ module.exports = function createRoutes(node, authenticator, createSessionToken)
85
88
  const createToken = createTokenFn(createSessionToken);
86
89
  const sessionConfig = blocklet.settings?.session || {};
87
90
  const { sessionToken, refreshToken } = createToken(
88
- userDid,
91
+ user.did,
89
92
  {
90
93
  secret,
91
94
  passport,
@@ -102,7 +105,7 @@ module.exports = function createRoutes(node, authenticator, createSessionToken)
102
105
  const userSessionDoc = await node.upsertUserSession({
103
106
  teamDid,
104
107
  visitorId,
105
- userDid,
108
+ userDid: user.did,
106
109
  appPid: teamDid,
107
110
  passportId: response.data.id,
108
111
  status: 'online',
@@ -111,7 +114,7 @@ module.exports = function createRoutes(node, authenticator, createSessionToken)
111
114
  });
112
115
  node.syncUserSession({
113
116
  teamDid,
114
- userDid,
117
+ userDid: user.did,
115
118
  visitorId: userSessionDoc.visitorId,
116
119
  passportId: passport?.id,
117
120
  targetAppPid: sourceAppPid,
@@ -127,7 +130,7 @@ module.exports = function createRoutes(node, authenticator, createSessionToken)
127
130
  true
128
131
  );
129
132
  await updateSession({ passportId: response.data.id });
130
- logger.info('invite.success', { userDid });
133
+ logger.info('invite.success', { userDid: user.did });
131
134
 
132
135
  return response;
133
136
  },
@@ -115,6 +115,50 @@ function shouldSyncFederated(sourceAppPid, blocklet) {
115
115
  return false;
116
116
  }
117
117
 
118
+ async function getUserWithinFederated({ userDid, userPk, teamDid, sourceAppPid }, { node, blocklet }) {
119
+ let currentUser = await node.getUser({
120
+ teamDid,
121
+ user: {
122
+ did: userDid,
123
+ },
124
+ options: {
125
+ enableConnectedAccount: true,
126
+ },
127
+ });
128
+ if (!currentUser && shouldSyncFederated(sourceAppPid, blocklet)) {
129
+ const masterSite = getFederatedMaster(blocklet);
130
+ const syncResults = await node.syncFederated({
131
+ did: teamDid,
132
+ data: {
133
+ users: [
134
+ {
135
+ did: userDid,
136
+ pk: userPk,
137
+ action: 'pullAccount',
138
+ },
139
+ ],
140
+ },
141
+ syncSites: [masterSite],
142
+ });
143
+ const resultItem = syncResults.find((x) => x.site.appPid === masterSite.appPid);
144
+ if (resultItem) {
145
+ const { result } = resultItem;
146
+ const [pullUser] = result?.users || [];
147
+ if (pullUser) {
148
+ pullUser.sourceAppPid = masterSite.appPid;
149
+ pullUser.connectedAccount = pullUser.connectedAccounts;
150
+ delete pullUser.connectedAccounts;
151
+ currentUser = await node.loginUser({
152
+ teamDid,
153
+ user: pullUser,
154
+ });
155
+ }
156
+ }
157
+ }
158
+
159
+ return currentUser;
160
+ }
161
+
118
162
  module.exports = {
119
163
  isMaster,
120
164
  shouldSyncFederated,
@@ -124,4 +168,5 @@ module.exports = {
124
168
  migrateAuth0,
125
169
  getOAuthUserInfo,
126
170
  loginByMember,
171
+ getUserWithinFederated,
127
172
  };
@@ -25,7 +25,7 @@ const loginUserWalletSchema = Joi.object({
25
25
  nonce: Joi.string().required(),
26
26
  visitorId: Joi.string().optional(),
27
27
  passportId: Joi.string().optional(),
28
- sourceAppPid: Joi.DID().trim().optional(),
28
+ sourceAppPid: Joi.DID().trim().empty(null),
29
29
  locale: Joi.string().optional(),
30
30
  }).empty(null);
31
31