@abtnode/blocklet-services 1.16.19-beta-340de95d → 1.16.19-beta-7b2db880

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 (32) hide show
  1. package/api/libs/connect/session.js +17 -19
  2. package/api/libs/connect/v2.js +2 -5
  3. package/api/libs/image.js +64 -64
  4. package/api/libs/open-graph/emoji.js +2 -2
  5. package/api/libs/open-graph/index.js +1 -0
  6. package/api/routes/federated.js +108 -19
  7. package/api/routes/oauth.js +24 -27
  8. package/api/routes/user-session.js +5 -1
  9. package/api/routes/user.js +1 -0
  10. package/api/services/auth/connect/invite.js +8 -5
  11. package/api/util/federated.js +45 -0
  12. package/api/validators/login.js +1 -1
  13. package/build/asset-manifest.json +17 -17
  14. package/build/index.html +1 -1
  15. package/build/service-worker.js +1 -1
  16. package/build/static/js/4076.8055ce74.chunk.js +2 -0
  17. package/build/static/js/4461.f9a883d3.chunk.js +2 -0
  18. package/build/static/js/5468.21a861b9.chunk.js +2 -0
  19. package/build/static/js/5547.42b98889.chunk.js +3 -0
  20. package/build/static/js/{8181.6c2a7dcb.chunk.js → 8181.bc510ea3.chunk.js} +2 -2
  21. package/build/static/js/8622.6aa0a4d4.chunk.js +2 -0
  22. package/build/static/js/{8944.939de854.chunk.js → 8944.5b8c231c.chunk.js} +2 -2
  23. package/build/static/js/main.09227d84.js +3 -0
  24. package/package.json +24 -27
  25. package/build/static/js/4076.f5369a1d.chunk.js +0 -2
  26. package/build/static/js/4461.3cd698bf.chunk.js +0 -2
  27. package/build/static/js/5468.b54ce65b.chunk.js +0 -2
  28. package/build/static/js/5547.570ded36.chunk.js +0 -3
  29. package/build/static/js/8622.ebd44812.chunk.js +0 -2
  30. package/build/static/js/main.581a3a26.js +0 -3
  31. /package/build/static/js/{5547.570ded36.chunk.js.LICENSE.txt → 5547.42b98889.chunk.js.LICENSE.txt} +0 -0
  32. /package/build/static/js/{main.581a3a26.js.LICENSE.txt → main.09227d84.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,
@@ -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,10 +22,7 @@ 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'),
28
- }),
25
+ storage: new DynamicStorage({ dbPath: path.join(opts.dataDir, 'connections.db'), v2: true }),
29
26
  socketPathname: `${WELLKNOWN_SERVICE_PATH_PREFIX}/api/connect/relay/websocket`,
30
27
  sendNotificationFn: async (connectedDid, message, { request }) => {
31
28
  const { wallet } = await request.getBlockletInfo();
package/api/libs/image.js CHANGED
@@ -130,68 +130,6 @@ const isImageRequest = (req) => {
130
130
 
131
131
  const getImageContentType = (extension) => (extension === 'svg' ? 'image/svg+xml' : `image/${extension}`);
132
132
 
133
- const tasks = {};
134
- const processAndRespond = (req, res, cacheDir, getSrc, ext, sendOptions = { maxAge: '356d', immutable: true }) => {
135
- if (fs.existsSync(cacheDir) === false) {
136
- fs.mkdirSync(cacheDir, { recursive: true });
137
- }
138
-
139
- const params = req.imageFilter;
140
- const extension = toLower(ext || path.extname(req.path).slice(1));
141
-
142
- // NOTE: 不要使用 `req.accepts('image/webp')`,这里需要排除掉 `Accept: */*` 和 `Accept: image/*` 的情况
143
- const acceptWebp = req.accepts().includes('image/webp');
144
- if (!acceptWebp) {
145
- if (params.f === 'webp') {
146
- params.f = undefined;
147
- }
148
- if ((!extension || extension === 'webp') && !params.f) {
149
- params.f = 'png';
150
- }
151
- }
152
- if (!extension && !params.f) {
153
- params.f = 'png';
154
- }
155
-
156
- if (!extension && !params.f) {
157
- res.status(400).send('Image filter failed: either extension or format must be specified');
158
- return;
159
- }
160
-
161
- const cacheKey = md5(stringify({ target: req.target, path: req.originalUrl, params }));
162
- const destPath = getCacheFilePath(cacheDir, `${cacheKey}.${params.f || extension}`);
163
- if (fs.existsSync(destPath)) {
164
- res.header('Content-Type', getImageContentType(params.f || extension));
165
- res.sendFile(destPath, sendOptions);
166
- return;
167
- }
168
-
169
- // do the convert
170
- tasks[cacheKey] ??= getSrc(req)
171
- .then(([src, _extension]) => processImage(src, toLower(_extension), destPath, params))
172
- .finally(() => {
173
- setTimeout(() => {
174
- delete tasks[cacheKey];
175
- }, 1000);
176
- });
177
-
178
- tasks[cacheKey]
179
- .then(() => {
180
- logger.info('image filter succeed', { params, url: req.originalUrl, destPath });
181
- res.header('Content-Type', getImageContentType(params.f || extension));
182
- res.sendFile(destPath, sendOptions);
183
- })
184
- .catch((err) => {
185
- logger.error('image filter failed', { error: err, params, url: req.url });
186
- if (params.e) {
187
- res.status(500).send(`Image service error: ${err.message}`);
188
- } else {
189
- res.status(500);
190
- res.sendFile(errorImage, { maxAge: 0 });
191
- }
192
- });
193
- };
194
-
195
133
  const processImage = (src, extension, dest, params) => {
196
134
  return new Promise((resolve, reject) => {
197
135
  // output stream
@@ -235,7 +173,7 @@ const processImage = (src, extension, dest, params) => {
235
173
  dimensions.height = height;
236
174
  }
237
175
 
238
- const pipeline = sharp({ animated: true }).timeout({ seconds: 30 });
176
+ const pipeline = sharp({ animated: true, limitInputPixels: 0 }).timeout({ seconds: 60 });
239
177
  if (rotate) {
240
178
  pipeline.rotate(rotate);
241
179
  }
@@ -268,7 +206,7 @@ const processImage = (src, extension, dest, params) => {
268
206
  pipeline.negate();
269
207
  }
270
208
 
271
- pipeline[format || EXTENSIONS[extension]]({ quality, progressive: !!progressive, force: true });
209
+ pipeline[format || EXTENSIONS[extension]]({ quality, progressive: !!progressive, dither: 0, force: true });
272
210
 
273
211
  pipeline.on('error', (err) => {
274
212
  reject(err);
@@ -282,6 +220,68 @@ const processImage = (src, extension, dest, params) => {
282
220
  });
283
221
  };
284
222
 
223
+ const tasks = {};
224
+ const processAndRespond = (req, res, cacheDir, getSrc, ext, sendOptions = { maxAge: '356d', immutable: true }) => {
225
+ if (fs.existsSync(cacheDir) === false) {
226
+ fs.mkdirSync(cacheDir, { recursive: true });
227
+ }
228
+
229
+ const params = req.imageFilter;
230
+ const extension = toLower(ext || path.extname(req.path).slice(1));
231
+
232
+ // NOTE: 不要使用 `req.accepts('image/webp')`,这里需要排除掉 `Accept: */*` 和 `Accept: image/*` 的情况
233
+ const acceptWebp = req.accepts().includes('image/webp');
234
+ if (!acceptWebp) {
235
+ if (params.f === 'webp') {
236
+ params.f = undefined;
237
+ }
238
+ if ((!extension || extension === 'webp') && !params.f) {
239
+ params.f = 'png';
240
+ }
241
+ }
242
+ if (!extension && !params.f) {
243
+ params.f = 'png';
244
+ }
245
+
246
+ if (!extension && !params.f) {
247
+ res.status(400).send('Image filter failed: either extension or format must be specified');
248
+ return;
249
+ }
250
+
251
+ const cacheKey = md5(stringify({ target: req.target, path: req.originalUrl, params }));
252
+ const destPath = getCacheFilePath(cacheDir, `${cacheKey}.${params.f || extension}`);
253
+ if (fs.existsSync(destPath)) {
254
+ res.header('Content-Type', getImageContentType(params.f || extension));
255
+ res.sendFile(destPath, sendOptions);
256
+ return;
257
+ }
258
+
259
+ // do the convert
260
+ tasks[cacheKey] ??= getSrc(req)
261
+ .then(([src, _extension]) => processImage(src, toLower(_extension), destPath, params))
262
+ .finally(() => {
263
+ setTimeout(() => {
264
+ delete tasks[cacheKey];
265
+ }, 1000);
266
+ });
267
+
268
+ tasks[cacheKey]
269
+ .then(() => {
270
+ logger.info('image filter succeed', { params, url: req.originalUrl, destPath });
271
+ res.header('Content-Type', getImageContentType(params.f || extension));
272
+ res.sendFile(destPath, sendOptions);
273
+ })
274
+ .catch((err) => {
275
+ logger.error('image filter failed', { error: err, params, url: req.url });
276
+ if (params.e) {
277
+ res.status(500).send(`Image service error: ${err.message}`);
278
+ } else {
279
+ res.status(500);
280
+ res.sendFile(errorImage, { maxAge: 0 });
281
+ }
282
+ });
283
+ };
284
+
285
285
  module.exports = {
286
286
  isImageAccepted,
287
287
  isImageRequest,
@@ -5,8 +5,6 @@ const fetch = require('node-fetch').default;
5
5
  const U200D = String.fromCharCode(8205); // zero-width joiner
6
6
  const UFE0Fg = /\uFE0F/g; // variation selector regex
7
7
 
8
- const getIconCode = (char) => toCodePoint(char.indexOf(U200D) < 0 ? char.replace(UFE0Fg, '') : char);
9
-
10
8
  const toCodePoint = (unicodeSurrogates) => {
11
9
  const r = [];
12
10
  let c = 0;
@@ -26,6 +24,8 @@ const toCodePoint = (unicodeSurrogates) => {
26
24
  return r.join('-');
27
25
  };
28
26
 
27
+ const getIconCode = (char) => toCodePoint(char.indexOf(U200D) < 0 ? char.replace(UFE0Fg, '') : char);
28
+
29
29
  const apis = {
30
30
  twemoji: (code) => `https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/svg/${code.toLowerCase()}.svg`,
31
31
  openmoji: 'https://cdn.jsdelivr.net/npm/@svgmoji/openmoji@2.0.0/svg/',
@@ -107,6 +107,7 @@ const getOgImage = ({ input, info, cacheDir, format, tmpDir }) => {
107
107
  return destPath;
108
108
  }
109
109
 
110
+ // eslint-disable-next-line no-use-before-define
110
111
  generateTasks[cacheKey] ??= generateOgImage(params, tmpDir).finally(() => {
111
112
  setTimeout(() => {
112
113
  delete generateTasks[cacheKey];
@@ -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
 
@@ -70,6 +75,7 @@ async function login(req, node, options) {
70
75
  throw new ApiError(400, t('oauthCantBeOwner', locale));
71
76
  }
72
77
  const { did: teamDid, wallet: blockletWallet, secret, appUrl } = await req.getBlockletInfo();
78
+
73
79
  let userWallet;
74
80
  let oauthInfo;
75
81
 
@@ -89,6 +95,7 @@ async function login(req, node, options) {
89
95
  oauthInfo = await authClient.getProfile(token);
90
96
  userWallet = fromAppDid(oauthInfo.sub, blockletWallet.secretKey);
91
97
  }
98
+
92
99
  const userDid = userWallet.address;
93
100
  const userPk = userWallet.publicKey;
94
101
 
@@ -99,15 +106,7 @@ async function login(req, node, options) {
99
106
 
100
107
  const lastLoginIp = get(req, 'headers[x-real-ip]') || '';
101
108
  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
- });
109
+ let currentUser = await getUserWithinFederated({ sourceAppPid, teamDid, userDid, userPk }, { node, blocklet });
111
110
  let doc;
112
111
  let passportForLog;
113
112
  const fullName = currentUser?.fullName || oauthInfo?.nickname;
@@ -117,6 +116,7 @@ async function login(req, node, options) {
117
116
  pk: userPk,
118
117
  id: oauthInfo.sub,
119
118
  };
119
+ const masterSite = getFederatedMaster(blocklet);
120
120
  let profile;
121
121
  // 当前账户已存在,更新账户信息
122
122
  if (currentUser) {
@@ -156,6 +156,7 @@ async function login(req, node, options) {
156
156
  if (invitedUserOnly) {
157
157
  throw new ApiError(403, t('needInviteToLogin', locale));
158
158
  }
159
+
159
160
  passportForLog = { name: 'Guest', role: 'guest' };
160
161
  profile = {
161
162
  fullName: oauthInfo.nickname,
@@ -186,7 +187,6 @@ async function login(req, node, options) {
186
187
  node
187
188
  );
188
189
 
189
- const masterSite = getFederatedMaster(blocklet);
190
190
  const ua = req.headers['user-agent'];
191
191
  const userSessionDoc = await node.upsertUserSession({
192
192
  teamDid,
@@ -277,37 +277,34 @@ async function invite(req, node, options) {
277
277
  let userWallet;
278
278
  let oauthInfo;
279
279
 
280
+ const { did: teamDid, wallet: blockletWallet, secret } = await req.getBlockletInfo();
281
+
280
282
  // NOTICE: 如果是统一登录,则向 master 站点发起 oauth 登录请求,auth0 的账户信息必须由 master 来生成
281
283
  if (sourceAppPid) {
282
- const data = await loginAuth0({
284
+ const data = await getOAuthUserInfo({
283
285
  request: req,
284
286
  blocklet,
285
- locale,
286
287
  provider,
287
288
  token,
288
289
  sourceAppPid,
290
+ locale,
289
291
  });
290
- ({ oauthInfo, userWallet } = data);
292
+ userWallet = data.wallet;
293
+ oauthInfo = data.info;
291
294
  } else {
292
295
  const authClient = getAuthClient(blocklet, provider);
293
296
  oauthInfo = await authClient.getProfile(token);
294
297
  userWallet = fromAppDid(oauthInfo.sub, blockletWallet.secretKey);
295
298
  }
296
- const { did: teamDid, wallet: blockletWallet, secret } = await req.getBlockletInfo();
299
+
297
300
  const nodeInfo = await req.getNodeInfo();
298
301
  let userDid = userWallet.address;
299
302
  let userPk = userWallet.publicKey;
300
303
 
301
304
  let profile;
302
- const currentUser = await node.getUser({
303
- teamDid,
304
- user: {
305
- did: userDid,
306
- },
307
- options: {
308
- enableConnectedAccount: true,
309
- },
310
- });
305
+
306
+ const currentUser = await getUserWithinFederated({ sourceAppPid, teamDid, userDid, userPk }, { node, blocklet });
307
+
311
308
  const { dataDir, name: applicationName } = await getApplicationInfo({ node, nodeInfo, teamDid });
312
309
  if (currentUser) {
313
310
  profile = {
@@ -340,8 +337,8 @@ async function invite(req, node, options) {
340
337
  profile,
341
338
  statusEndpointBaseUrl,
342
339
  teamDid,
343
- userDid,
344
- userPk,
340
+ userDid: userWallet.address,
341
+ userPk: userWallet.publicKey,
345
342
  locale,
346
343
  provider,
347
344
  });
@@ -506,7 +503,7 @@ async function bind(req, node, options) {
506
503
  if (!avatar) {
507
504
  const nodeInfo = await req.getNodeInfo();
508
505
  const { dataDir } = await getApplicationInfo({ node, nodeInfo, teamDid });
509
- avatar = await getAvatarByEmail(userInfo.email);
506
+ avatar = userInfo.picture ? await getAvatarByUrl(userInfo.picture) : await getAvatarByEmail(userInfo.email);
510
507
  avatar = await extractUserAvatar(avatar, { dataDir });
511
508
  }
512
509