@abtnode/blocklet-services 1.16.13 → 1.16.14-beta-0c29907f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api/index.js +5 -0
- package/api/libs/auth/utils.js +6 -8
- package/api/libs/connect/session.js +75 -23
- package/api/libs/connect/shared.js +12 -1
- package/api/libs/connect/v1.js +32 -3
- package/api/libs/jwt.js +8 -3
- package/api/middlewares/check-federated-cors-call.js +15 -0
- package/api/middlewares/ensure-blocklet.js +13 -0
- package/api/middlewares/verify-federated-call.js +35 -0
- package/api/routes/blocklet.js +19 -6
- package/api/routes/federated.js +365 -0
- package/api/routes/oauth.js +8 -7
- package/api/services/auth/connect/invite.js +2 -1
- package/api/services/auth/connect/issue-passport.js +2 -8
- package/api/services/auth/connect/login.js +9 -1
- package/api/services/auth/connect/receive-transfer-app-owner.js +3 -1
- package/api/services/auth/session.js +3 -1
- package/api/services/notification/blocklet-events-notifier.js +79 -28
- package/api/util/blocklet-utils.js +27 -0
- package/api/util/federated.js +41 -0
- package/build/asset-manifest.json +76 -73
- package/build/index.html +1 -1
- package/build/service-worker.js +1 -1
- package/build/service-worker.js.map +1 -1
- package/build/static/css/{8730.ecc708b7.chunk.css → 2130.b9437a64.chunk.css} +1 -1
- package/build/static/css/4603.dce369d5.chunk.css +2 -0
- package/build/static/js/1162.ff3136ae.chunk.js +2 -0
- package/build/static/js/1237.1d0b8f1e.chunk.js +3 -0
- package/build/static/js/2130.59fef4ad.chunk.js +3 -0
- package/build/static/js/3242.5dea77f3.chunk.js +2 -0
- package/build/static/js/3464.12806bfe.chunk.js +2 -0
- package/build/static/js/{3800.a4ffc6c2.chunk.js → 3800.faac00ca.chunk.js} +2 -2
- package/build/static/js/{3953.b81b810f.chunk.js → 3953.e3e51520.chunk.js} +2 -2
- package/build/static/js/3963.2e06f9bc.chunk.js +3 -0
- package/build/static/js/4319.e99b5af9.chunk.js +2 -0
- package/build/static/js/{445.4ee5d0c9.chunk.js → 445.6da52f3e.chunk.js} +3 -3
- package/build/static/js/4492.97b0d4f6.chunk.js +2 -0
- package/build/static/js/4547.b26ceabb.chunk.js +2 -0
- package/build/static/js/4603.f6b8d272.chunk.js +2 -0
- package/build/static/js/4651.0a58cbc3.chunk.js +3 -0
- package/build/static/js/5050.538edf89.chunk.js +3 -0
- package/build/static/js/5052.91c69c75.chunk.js +2 -0
- package/build/static/js/5176.68fd3fde.chunk.js +2 -0
- package/build/static/js/5233.faabaae9.chunk.js +2 -0
- package/build/static/js/5430.8114614c.chunk.js +2 -0
- package/build/static/js/5645.a17cd258.chunk.js +2 -0
- package/build/static/js/5711.aa17ef28.chunk.js +2 -0
- package/build/static/js/6315.59e078f6.chunk.js +2 -0
- package/build/static/js/652.5fc00cd3.chunk.js +2 -0
- package/build/static/js/6737.4f37dd4a.chunk.js +2 -0
- package/build/static/js/6891.6c7018ab.chunk.js +2 -0
- package/build/static/js/7345.4b4f8941.chunk.js +3 -0
- package/build/static/js/7371.da3c5cc3.chunk.js +2 -0
- package/build/static/js/{766.724b9aea.chunk.js → 766.5dd16d47.chunk.js} +2 -2
- package/build/static/js/8031.d9af3d84.chunk.js +2 -0
- package/build/static/js/8395.e5aa519c.chunk.js +2 -0
- package/build/static/js/868.aa2a1c4d.chunk.js +2 -0
- package/build/static/js/9476.bc480cfc.chunk.js +2 -0
- package/build/static/js/982.11dc355f.chunk.js +3 -0
- package/build/static/js/main.efa52345.js +3 -0
- package/build/static/media/index.20414a3fa1e6c5498a67.cjs +1 -0
- package/package.json +36 -35
- package/build/static/js/1162.c3a07ee0.chunk.js +0 -2
- package/build/static/js/1237.03f3a68a.chunk.js +0 -3
- package/build/static/js/1610.13323d09.chunk.js +0 -2
- package/build/static/js/2793.00991f08.chunk.js +0 -2
- package/build/static/js/2961.8237c11f.chunk.js +0 -3
- package/build/static/js/3242.86fc9bc6.chunk.js +0 -2
- package/build/static/js/338.2f0211d7.chunk.js +0 -2
- package/build/static/js/4319.5363b467.chunk.js +0 -2
- package/build/static/js/4547.b013b965.chunk.js +0 -2
- package/build/static/js/4651.ea828d09.chunk.js +0 -3
- package/build/static/js/5050.af9b1e9c.chunk.js +0 -3
- package/build/static/js/5052.da35270f.chunk.js +0 -2
- package/build/static/js/5176.36610ff7.chunk.js +0 -2
- package/build/static/js/5233.1f42910b.chunk.js +0 -2
- package/build/static/js/5430.f101b648.chunk.js +0 -2
- package/build/static/js/5645.48cf12d4.chunk.js +0 -2
- package/build/static/js/5711.13090883.chunk.js +0 -2
- package/build/static/js/5782.cf7e6574.chunk.js +0 -2
- package/build/static/js/6315.dec40b9b.chunk.js +0 -2
- package/build/static/js/652.0bde51bd.chunk.js +0 -2
- package/build/static/js/6737.719ec631.chunk.js +0 -2
- package/build/static/js/7240.6bbf13e8.chunk.js +0 -3
- package/build/static/js/7345.fb7fa7cf.chunk.js +0 -3
- package/build/static/js/7371.04b43294.chunk.js +0 -2
- package/build/static/js/8395.c43afdf4.chunk.js +0 -2
- package/build/static/js/868.cd5abe55.chunk.js +0 -2
- package/build/static/js/8730.0ac0b4df.chunk.js +0 -3
- package/build/static/js/9476.302a7b02.chunk.js +0 -2
- package/build/static/js/9645.822c1d69.chunk.js +0 -2
- package/build/static/js/main.c3544e79.js +0 -3
- /package/build/static/js/{1237.03f3a68a.chunk.js.LICENSE.txt → 1237.1d0b8f1e.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{8730.0ac0b4df.chunk.js.LICENSE.txt → 2130.59fef4ad.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{7240.6bbf13e8.chunk.js.LICENSE.txt → 3963.2e06f9bc.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{445.4ee5d0c9.chunk.js.LICENSE.txt → 445.6da52f3e.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{4651.ea828d09.chunk.js.LICENSE.txt → 4651.0a58cbc3.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{5050.af9b1e9c.chunk.js.LICENSE.txt → 5050.538edf89.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{7345.fb7fa7cf.chunk.js.LICENSE.txt → 7345.4b4f8941.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{2961.8237c11f.chunk.js.LICENSE.txt → 982.11dc355f.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{main.c3544e79.js.LICENSE.txt → main.efa52345.js.LICENSE.txt} +0 -0
package/api/index.js
CHANGED
|
@@ -35,6 +35,7 @@ const StudioService = require('./services/studio');
|
|
|
35
35
|
const AnalyticService = require('./services/analytics');
|
|
36
36
|
const createEnvRoutes = require('./routes/env');
|
|
37
37
|
const createOAuthRoutes = require('./routes/oauth');
|
|
38
|
+
const createFederatedRoutes = require('./routes/federated');
|
|
38
39
|
const createUserRoutes = require('./routes/user');
|
|
39
40
|
const createBlockletRoutes = require('./routes/blocklet');
|
|
40
41
|
const createConnectRelayRoutes = require('./routes/connect/relay');
|
|
@@ -188,6 +189,9 @@ module.exports = function createServer(node, serverOptions = {}) {
|
|
|
188
189
|
})
|
|
189
190
|
);
|
|
190
191
|
|
|
192
|
+
// HACK: 必须放在 `server.use(cors());` 前面,否则会影响其原来的作用
|
|
193
|
+
createFederatedRoutes.preInit(server, node, options);
|
|
194
|
+
|
|
191
195
|
// NOTE: will be overwrite by Blocklet Server in production
|
|
192
196
|
server.use(cors());
|
|
193
197
|
|
|
@@ -244,6 +248,7 @@ module.exports = function createServer(node, serverOptions = {}) {
|
|
|
244
248
|
|
|
245
249
|
// API: auth
|
|
246
250
|
createOAuthRoutes.init(server, node, options);
|
|
251
|
+
createFederatedRoutes.init(server, node, options);
|
|
247
252
|
createUserRoutes.init(server, node, options);
|
|
248
253
|
createEnvRoutes.init(server, node, options);
|
|
249
254
|
createBlockletRoutes.init(server, node);
|
package/api/libs/auth/utils.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { getPassportStatusEndpoint, getApplicationInfo } = require('@abtnode/auth/lib/auth');
|
|
2
2
|
const { createPassportVC } = require('@abtnode/auth/lib/passport');
|
|
3
3
|
const { VC_TYPE_NODE_PASSPORT, PASSPORT_STATUS } = require('@abtnode/constant');
|
|
4
|
-
const {
|
|
4
|
+
const { getAvatarByEmail, getAvatarByUrl, getUserAvatarUrl } = require('@abtnode/util/lib/user');
|
|
5
5
|
const { getBlockletAppIdList } = require('@blocklet/meta/lib/util');
|
|
6
6
|
const pick = require('lodash/pick');
|
|
7
7
|
const uniq = require('lodash/uniq');
|
|
@@ -18,7 +18,6 @@ async function transferPassport(fromUser, toUser, { req, teamDid, node, nodeInfo
|
|
|
18
18
|
name: issuerName,
|
|
19
19
|
wallet: issuerWallet,
|
|
20
20
|
passportColor,
|
|
21
|
-
dataDir,
|
|
22
21
|
} = await getApplicationInfo({ node, nodeInfo, teamDid });
|
|
23
22
|
|
|
24
23
|
const blocklet = await req.getBlockletInfo();
|
|
@@ -43,15 +42,14 @@ async function transferPassport(fromUser, toUser, { req, teamDid, node, nodeInfo
|
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
const attachments = await Promise.all(
|
|
46
|
-
waitPassportList.map(
|
|
47
|
-
const avatar = await parseUserAvatar(toUser.avatar, { dataDir });
|
|
45
|
+
waitPassportList.map((x) => {
|
|
48
46
|
const vcParams = {
|
|
49
47
|
issuerName,
|
|
50
48
|
issuerWallet,
|
|
51
49
|
ownerDid: toUser.did,
|
|
52
|
-
passport: pick(
|
|
50
|
+
passport: pick(x, ['name', 'title', 'endpoint', 'specVersion']),
|
|
53
51
|
endpoint: getPassportStatusEndpoint({
|
|
54
|
-
baseUrl:
|
|
52
|
+
baseUrl: x.endpoint,
|
|
55
53
|
userDid: toUser.did,
|
|
56
54
|
teamDid,
|
|
57
55
|
}),
|
|
@@ -59,7 +57,7 @@ async function transferPassport(fromUser, toUser, { req, teamDid, node, nodeInfo
|
|
|
59
57
|
ownerProfile: {
|
|
60
58
|
email: toUser.email,
|
|
61
59
|
fullName: toUser.fullName,
|
|
62
|
-
avatar,
|
|
60
|
+
avatar: getUserAvatarUrl(x.endpoint, toUser.avatar),
|
|
63
61
|
},
|
|
64
62
|
preferredColor: passportColor,
|
|
65
63
|
};
|
|
@@ -69,7 +67,7 @@ async function transferPassport(fromUser, toUser, { req, teamDid, node, nodeInfo
|
|
|
69
67
|
type: 'vc',
|
|
70
68
|
data: {
|
|
71
69
|
credential: vc,
|
|
72
|
-
tag:
|
|
70
|
+
tag: x.name,
|
|
73
71
|
},
|
|
74
72
|
};
|
|
75
73
|
})
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const get = require('lodash/get');
|
|
3
3
|
const joinUrl = require('url-join');
|
|
4
4
|
const formatContext = require('@abtnode/util/lib/format-context');
|
|
5
|
-
const { extractUserAvatar } = require('@abtnode/util/lib/user');
|
|
5
|
+
const { extractUserAvatar, getAppAvatarUrl } = require('@abtnode/util/lib/user');
|
|
6
6
|
const {
|
|
7
7
|
messages,
|
|
8
8
|
getVCFromClaims,
|
|
@@ -35,6 +35,9 @@ const {
|
|
|
35
35
|
const { getKeyPairClaim, getAuthPrincipalForMigrateAppToV2 } = require('@abtnode/auth/lib/server');
|
|
36
36
|
const merge = require('lodash/merge');
|
|
37
37
|
const { fromAppDid } = require('@arcblock/did-ext');
|
|
38
|
+
const { LOGIN_PROVIDER } = require('@blocklet/constant');
|
|
39
|
+
const { signV2 } = require('@arcblock/jwt');
|
|
40
|
+
const pick = require('lodash/pick');
|
|
38
41
|
|
|
39
42
|
const { getRolesFromAuthConfig, getBlockletAppIdList } = require('@blocklet/meta/lib/util');
|
|
40
43
|
|
|
@@ -44,6 +47,9 @@ const { isInvitedUserOnly, createTokenFn, getDidConnectVersion } = require('../.
|
|
|
44
47
|
const { transferPassport } = require('../auth/utils');
|
|
45
48
|
const { generateTranslate } = require('../translate');
|
|
46
49
|
const { migrateAccount, declareAccount } = require('../../services/oauth');
|
|
50
|
+
const { getTrustedIssuers, getLoginProvider } = require('../../util/blocklet-utils');
|
|
51
|
+
const { getFederatedMaster, getUserAvatarUrl } = require('../../util/federated');
|
|
52
|
+
const { api } = require('../api');
|
|
47
53
|
|
|
48
54
|
const vcTypes = [VC_TYPE_GENERAL_PASSPORT, VC_TYPE_NODE_PASSPORT];
|
|
49
55
|
|
|
@@ -136,6 +142,7 @@ module.exports = {
|
|
|
136
142
|
const blocklet = await request.getBlocklet();
|
|
137
143
|
const config = await request.getServiceConfig(NODE_SERVICES.AUTH, { componentId });
|
|
138
144
|
const { did: teamDid, wallet: blockletWallet } = await request.getBlockletInfo();
|
|
145
|
+
const provider = getLoginProvider(request);
|
|
139
146
|
|
|
140
147
|
const profileFields = get(config, 'profileFields');
|
|
141
148
|
|
|
@@ -148,8 +155,8 @@ module.exports = {
|
|
|
148
155
|
};
|
|
149
156
|
if (action === 'login') {
|
|
150
157
|
const [invitedUserOnly] = config ? await isInvitedUserOnly(config, node, teamDid) : [false];
|
|
151
|
-
|
|
152
|
-
const trustedIssuers =
|
|
158
|
+
|
|
159
|
+
const trustedIssuers = getTrustedIssuers(blocklet, { provider });
|
|
153
160
|
claims.verifiableCredential = {
|
|
154
161
|
type: 'verifiableCredential',
|
|
155
162
|
description: messages.requestPassport[locale],
|
|
@@ -283,6 +290,7 @@ module.exports = {
|
|
|
283
290
|
vc = createPassportVC({
|
|
284
291
|
issuerName: name,
|
|
285
292
|
issuerWallet: wallet,
|
|
293
|
+
issuerAvatarUrl: getAppAvatarUrl(baseUrl),
|
|
286
294
|
ownerDid: realDid,
|
|
287
295
|
passport: await createPassport({
|
|
288
296
|
name: defaultRole,
|
|
@@ -325,9 +333,11 @@ module.exports = {
|
|
|
325
333
|
let fullName = user?.fullName;
|
|
326
334
|
// Update profile
|
|
327
335
|
const passportForLog = passport || { name: 'Guest', role: 'guest' };
|
|
336
|
+
const provider = getLoginProvider(request);
|
|
337
|
+
let updatedUser;
|
|
328
338
|
if (user) {
|
|
329
339
|
// Update user
|
|
330
|
-
|
|
340
|
+
updatedUser = await node.loginUser({
|
|
331
341
|
teamDid,
|
|
332
342
|
user: {
|
|
333
343
|
did: user.did,
|
|
@@ -335,15 +345,15 @@ module.exports = {
|
|
|
335
345
|
locale,
|
|
336
346
|
passport,
|
|
337
347
|
lastLoginIp: get(request, 'headers[x-real-ip]') || '',
|
|
338
|
-
connectedAccount: [{ provider
|
|
348
|
+
connectedAccount: [{ provider, did: realDid }, connectedNft],
|
|
339
349
|
},
|
|
340
350
|
});
|
|
341
351
|
await node.createAuditLog(
|
|
342
352
|
{
|
|
343
353
|
action,
|
|
344
|
-
args: { teamDid, userDid: realDid, passport: passportForLog, provider
|
|
345
|
-
context: formatContext(Object.assign(request, { user:
|
|
346
|
-
result:
|
|
354
|
+
args: { teamDid, userDid: realDid, passport: passportForLog, provider },
|
|
355
|
+
context: formatContext(Object.assign(request, { user: updatedUser })),
|
|
356
|
+
result: updatedUser,
|
|
347
357
|
},
|
|
348
358
|
node
|
|
349
359
|
);
|
|
@@ -352,7 +362,7 @@ module.exports = {
|
|
|
352
362
|
const profile = claims.find((x) => x.type === 'profile');
|
|
353
363
|
fullName = profile.fullName;
|
|
354
364
|
|
|
355
|
-
|
|
365
|
+
updatedUser = await node.loginUser({
|
|
356
366
|
teamDid,
|
|
357
367
|
user: {
|
|
358
368
|
...profile,
|
|
@@ -366,7 +376,7 @@ module.exports = {
|
|
|
366
376
|
lastLoginIp: get(request, 'headers[x-real-ip]') || '',
|
|
367
377
|
connectedAccount: [
|
|
368
378
|
{
|
|
369
|
-
provider
|
|
379
|
+
provider,
|
|
370
380
|
did: realDid,
|
|
371
381
|
pk: realPk,
|
|
372
382
|
},
|
|
@@ -378,8 +388,8 @@ module.exports = {
|
|
|
378
388
|
{
|
|
379
389
|
action: 'addUser',
|
|
380
390
|
args: { teamDid, userDid: realDid, reason: `first login as ${passportForLog.role}` },
|
|
381
|
-
context: formatContext(Object.assign(request, { user:
|
|
382
|
-
result:
|
|
391
|
+
context: formatContext(Object.assign(request, { user: updatedUser })),
|
|
392
|
+
result: updatedUser,
|
|
383
393
|
},
|
|
384
394
|
node
|
|
385
395
|
);
|
|
@@ -390,7 +400,14 @@ module.exports = {
|
|
|
390
400
|
const sessionConfig = blocklet.settings?.session || {};
|
|
391
401
|
const { sessionToken, refreshToken } = createToken(
|
|
392
402
|
realDid,
|
|
393
|
-
{
|
|
403
|
+
{
|
|
404
|
+
secret,
|
|
405
|
+
passport,
|
|
406
|
+
role,
|
|
407
|
+
fullName,
|
|
408
|
+
// NOTE: token 中存储当前的 login provider
|
|
409
|
+
provider,
|
|
410
|
+
},
|
|
394
411
|
{ ...sessionConfig, didConnectVersion: getDidConnectVersion(request) }
|
|
395
412
|
);
|
|
396
413
|
logger.info(`${action}.success`, { userDid: realDid, role });
|
|
@@ -405,6 +422,37 @@ module.exports = {
|
|
|
405
422
|
await node.setBlockletInitialized({ did: teamDid, owner: { did: realDid, pk: realPk } });
|
|
406
423
|
}
|
|
407
424
|
|
|
425
|
+
const { permanentWallet } = await request.getBlockletInfo();
|
|
426
|
+
|
|
427
|
+
const masterSite = getFederatedMaster(blocklet);
|
|
428
|
+
let federatedSessionToken;
|
|
429
|
+
let federatedRefreshToken;
|
|
430
|
+
if (masterSite) {
|
|
431
|
+
const postUser = pick(updatedUser, ['did', 'pk', 'fullName', 'locale']);
|
|
432
|
+
postUser.lastLoginAt = get(request, 'headers[x-real-ip]') || '';
|
|
433
|
+
|
|
434
|
+
if (updatedUser.email) {
|
|
435
|
+
postUser.email = updatedUser.email;
|
|
436
|
+
}
|
|
437
|
+
if (updatedUser.avatar) {
|
|
438
|
+
postUser.avatar = getUserAvatarUrl(updatedUser.avatar, blocklet);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const { data } = await api.post(
|
|
442
|
+
joinUrl(masterSite.appUrl, WELLKNOWN_SERVICE_PATH_PREFIX, '/api/federated/loginByMember'),
|
|
443
|
+
{
|
|
444
|
+
signer: permanentWallet.address,
|
|
445
|
+
data: signV2(permanentWallet.address, permanentWallet.secretKey, {
|
|
446
|
+
user: postUser,
|
|
447
|
+
appId: blocklet.did,
|
|
448
|
+
passport,
|
|
449
|
+
}),
|
|
450
|
+
}
|
|
451
|
+
);
|
|
452
|
+
federatedSessionToken = data.sessionToken;
|
|
453
|
+
federatedRefreshToken = data.refreshToken;
|
|
454
|
+
}
|
|
455
|
+
|
|
408
456
|
// issue passport for the first login user in a invite-only team
|
|
409
457
|
if (issuePassport) {
|
|
410
458
|
return {
|
|
@@ -413,6 +461,8 @@ module.exports = {
|
|
|
413
461
|
data: vc,
|
|
414
462
|
sessionToken,
|
|
415
463
|
refreshToken,
|
|
464
|
+
federatedSessionToken,
|
|
465
|
+
federatedRefreshToken,
|
|
416
466
|
nextWorkflowData: {
|
|
417
467
|
userDid: realDid,
|
|
418
468
|
},
|
|
@@ -422,6 +472,8 @@ module.exports = {
|
|
|
422
472
|
return {
|
|
423
473
|
sessionToken,
|
|
424
474
|
refreshToken,
|
|
475
|
+
federatedSessionToken,
|
|
476
|
+
federatedRefreshToken,
|
|
425
477
|
nextWorkflowData: {
|
|
426
478
|
userDid: realDid,
|
|
427
479
|
},
|
|
@@ -462,7 +514,7 @@ module.exports = {
|
|
|
462
514
|
profile: {
|
|
463
515
|
type: 'profile',
|
|
464
516
|
description: messages.description[locale],
|
|
465
|
-
items: get(config, 'profileFields') || ['fullName', 'avatar'],
|
|
517
|
+
items: get(config, 'profileFields') || ['fullName', 'avatar', 'email'],
|
|
466
518
|
},
|
|
467
519
|
};
|
|
468
520
|
},
|
|
@@ -540,8 +592,8 @@ module.exports = {
|
|
|
540
592
|
const [invitedUserOnly] = await isInvitedUserOnly(config, node, teamDid);
|
|
541
593
|
|
|
542
594
|
const blocklet = await request.getBlocklet();
|
|
543
|
-
const
|
|
544
|
-
const trustedIssuers =
|
|
595
|
+
const provider = getLoginProvider(request);
|
|
596
|
+
const trustedIssuers = getTrustedIssuers(blocklet, { provider });
|
|
545
597
|
|
|
546
598
|
return {
|
|
547
599
|
verifiableCredential: {
|
|
@@ -633,7 +685,7 @@ module.exports = {
|
|
|
633
685
|
await node.createAuditLog(
|
|
634
686
|
{
|
|
635
687
|
action: 'switchPassport',
|
|
636
|
-
args: { teamDid, userDid, passport: passportForLog, provider:
|
|
688
|
+
args: { teamDid, userDid, passport: passportForLog, provider: LOGIN_PROVIDER.WALLET },
|
|
637
689
|
context: formatContext(Object.assign(request, { user })),
|
|
638
690
|
result: {},
|
|
639
691
|
},
|
|
@@ -702,8 +754,8 @@ module.exports = {
|
|
|
702
754
|
throw new Error(t('notFound', locale));
|
|
703
755
|
}
|
|
704
756
|
const oauthConnectedAccounts = oauthUser.connectedAccounts || [];
|
|
705
|
-
const sourceProvider = oauthUser.sourceProvider ||
|
|
706
|
-
if (oauthConnectedAccounts.find((item) => item.provider ===
|
|
757
|
+
const sourceProvider = oauthUser.sourceProvider || LOGIN_PROVIDER.WALLET;
|
|
758
|
+
if (oauthConnectedAccounts.find((item) => item.provider === LOGIN_PROVIDER.WALLET)) {
|
|
707
759
|
throw new Error(t('alreadyBindWallet', locale));
|
|
708
760
|
}
|
|
709
761
|
|
|
@@ -743,7 +795,7 @@ module.exports = {
|
|
|
743
795
|
|
|
744
796
|
return [];
|
|
745
797
|
},
|
|
746
|
-
onApprove: async ({ node, request, locale, userDid, userPk, claims, previousUserDid }) => {
|
|
798
|
+
onApprove: async ({ node, request, locale, userDid, userPk, claims, previousUserDid, baseUrl }) => {
|
|
747
799
|
const blocklet = await request.getBlocklet();
|
|
748
800
|
const { did: teamDid, wallet: blockletWallet } = await request.getBlockletInfo();
|
|
749
801
|
|
|
@@ -786,7 +838,7 @@ module.exports = {
|
|
|
786
838
|
const currentTime = new Date().toISOString();
|
|
787
839
|
|
|
788
840
|
const connectedAccount = {
|
|
789
|
-
provider:
|
|
841
|
+
provider: LOGIN_PROVIDER.WALLET,
|
|
790
842
|
did: userDid,
|
|
791
843
|
pk: userPk,
|
|
792
844
|
lastLoginAt: currentTime,
|
|
@@ -815,7 +867,7 @@ module.exports = {
|
|
|
815
867
|
};
|
|
816
868
|
}
|
|
817
869
|
|
|
818
|
-
await transferPassport(oauthUser, bindUser, { req: request, node, nodeInfo, teamDid });
|
|
870
|
+
await transferPassport(oauthUser, bindUser, { req: request, node, nodeInfo, teamDid, baseUrl });
|
|
819
871
|
|
|
820
872
|
const connectedAccounts = oauthUser?.connectedAccounts || [];
|
|
821
873
|
const sourceProvider = oauthUser?.sourceProvider;
|
|
@@ -826,7 +878,7 @@ module.exports = {
|
|
|
826
878
|
await node.createAuditLog(
|
|
827
879
|
{
|
|
828
880
|
action: 'connectAccount',
|
|
829
|
-
args: { teamDid, connectedAccount, provider:
|
|
881
|
+
args: { teamDid, connectedAccount, provider: LOGIN_PROVIDER.WALLET, userDid: oauthUser.did },
|
|
830
882
|
context: formatContext(Object.assign(request, { user: oauthUser })),
|
|
831
883
|
result: bindUser,
|
|
832
884
|
},
|
|
@@ -2,9 +2,11 @@ const get = require('lodash/get');
|
|
|
2
2
|
const joinUrl = require('url-join');
|
|
3
3
|
const { getConnectAppUrl, getChainInfo } = require('@blocklet/meta/lib/util');
|
|
4
4
|
const { WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
5
|
+
const { LOGIN_PROVIDER } = require('@blocklet/constant');
|
|
6
|
+
const { getProvider } = require('../../util/federated');
|
|
5
7
|
|
|
6
8
|
module.exports = {
|
|
7
|
-
appInfo: async ({ request, baseUrl }) => {
|
|
9
|
+
appInfo: async ({ request, baseUrl, extraParams }) => {
|
|
8
10
|
const groupPathPrefix = request.headers['x-group-path-prefix'] || '/';
|
|
9
11
|
|
|
10
12
|
const [blocklet, meta, info] = await Promise.all([
|
|
@@ -12,6 +14,13 @@ module.exports = {
|
|
|
12
14
|
request.getBlockletInfo(),
|
|
13
15
|
request.getNodeInfo(),
|
|
14
16
|
]);
|
|
17
|
+
// NOTE: 这里应该使用当前 blocklet 的 did,而不是 pid
|
|
18
|
+
let agentDid;
|
|
19
|
+
const provider = getProvider(request, extraParams);
|
|
20
|
+
// federated 登录模式下,需要告知原有的 blocklet-did
|
|
21
|
+
if (provider === LOGIN_PROVIDER.FEDERATED) {
|
|
22
|
+
agentDid = blocklet.appDid;
|
|
23
|
+
}
|
|
15
24
|
|
|
16
25
|
const version = get(blocklet, 'meta.version', '');
|
|
17
26
|
|
|
@@ -23,6 +32,8 @@ module.exports = {
|
|
|
23
32
|
updateSubEndpoint: true,
|
|
24
33
|
subscriptionEndpoint: joinUrl(groupPathPrefix, WELLKNOWN_SERVICE_PATH_PREFIX, 'websocket'),
|
|
25
34
|
nodeDid: info.did,
|
|
35
|
+
// NOTE: publisher 是 WalletAuthenticator 中自动添加的
|
|
36
|
+
agentDid,
|
|
26
37
|
};
|
|
27
38
|
},
|
|
28
39
|
|
package/api/libs/connect/v1.js
CHANGED
|
@@ -2,15 +2,19 @@ const path = require('path');
|
|
|
2
2
|
const get = require('lodash/get');
|
|
3
3
|
const { toAddress } = require('@ocap/util');
|
|
4
4
|
const { WalletAuthenticator } = require('@arcblock/did-auth');
|
|
5
|
+
const { types } = require('@ocap/mcrypto');
|
|
5
6
|
const getBlockletInfo = require('@blocklet/meta/lib/info');
|
|
6
7
|
const WalletHandlers = require('@blocklet/sdk/lib/wallet-handler');
|
|
7
8
|
const { getDelegation } = require('@blocklet/sdk/lib/connect/shared');
|
|
8
9
|
const { sendToUser, sendToRelay } = require('@blocklet/sdk/lib/util/send-notification');
|
|
9
10
|
const { WELLKNOWN_SERVICE_PATH_PREFIX, NODE_SERVICES_PREFIX } = require('@abtnode/constant');
|
|
10
11
|
const DynamicStorage = require('@abtnode/connect-storage');
|
|
12
|
+
const { fromPublicKey } = require('@ocap/wallet');
|
|
13
|
+
const { LOGIN_PROVIDER } = require('@blocklet/constant');
|
|
11
14
|
|
|
12
15
|
const cache = require('../../cache');
|
|
13
16
|
const { appInfo, chainInfo } = require('./shared');
|
|
17
|
+
const { getProvider } = require('../../util/federated');
|
|
14
18
|
|
|
15
19
|
module.exports = (node, opts) => {
|
|
16
20
|
const authenticator = new WalletAuthenticator({
|
|
@@ -20,15 +24,40 @@ module.exports = (node, opts) => {
|
|
|
20
24
|
const { wallet } = await request.getBlockletInfo();
|
|
21
25
|
return wallet.toJSON();
|
|
22
26
|
},
|
|
23
|
-
delegator: async ({ request }) => {
|
|
27
|
+
delegator: async ({ request, extraParams }) => {
|
|
28
|
+
const provider = getProvider(request, extraParams);
|
|
29
|
+
|
|
30
|
+
const blocklet = await request.getBlocklet();
|
|
31
|
+
if (provider === LOGIN_PROVIDER.FEDERATED) {
|
|
32
|
+
const pk = get(blocklet, 'settings.federated.sites[0].pk');
|
|
33
|
+
|
|
34
|
+
// NOTE: 这里的 type 参数必须保持跟生成 blocklet 时使用的是一致的
|
|
35
|
+
const type = {
|
|
36
|
+
role: types.RoleType.ROLE_APPLICATION,
|
|
37
|
+
pk: types.KeyType.ED25519,
|
|
38
|
+
hash: types.HashType.SHA3,
|
|
39
|
+
address: types.EncodingType.BASE58,
|
|
40
|
+
};
|
|
41
|
+
const delegator = fromPublicKey(pk, type);
|
|
42
|
+
delegator.provider = LOGIN_PROVIDER.FEDERATED;
|
|
43
|
+
return delegator;
|
|
44
|
+
}
|
|
45
|
+
|
|
24
46
|
const { wallet: delegatee, permanentWallet: delegator } = await request.getBlockletInfo();
|
|
25
47
|
if (delegatee.address !== delegator.address) {
|
|
26
48
|
return delegator;
|
|
27
49
|
}
|
|
28
50
|
return null;
|
|
29
51
|
},
|
|
30
|
-
delegation: async ({ request }) => {
|
|
52
|
+
delegation: async ({ request, extraParams }) => {
|
|
31
53
|
const { wallet: delegatee, permanentWallet: delegator } = await request.getBlockletInfo();
|
|
54
|
+
const provider = getProvider(request, extraParams);
|
|
55
|
+
if (provider === LOGIN_PROVIDER.FEDERATED) {
|
|
56
|
+
const blocklet = await request.getBlocklet();
|
|
57
|
+
const delegation = get(blocklet, 'settings.federated.config.delegation');
|
|
58
|
+
return delegation;
|
|
59
|
+
}
|
|
60
|
+
|
|
32
61
|
if (delegatee.address !== delegator.address) {
|
|
33
62
|
return getDelegation(delegator, delegatee);
|
|
34
63
|
}
|
|
@@ -55,7 +84,7 @@ module.exports = (node, opts) => {
|
|
|
55
84
|
|
|
56
85
|
// Only handles did-connect updates, because we need to determine the actual app
|
|
57
86
|
sendToRelayFn: async (topic, event, data) => {
|
|
58
|
-
const publisher = get(data, 'appInfo.publisher', '');
|
|
87
|
+
const publisher = get(data, 'appInfo.agentDid', '') || get(data, 'appInfo.publisher', '');
|
|
59
88
|
if (!publisher) {
|
|
60
89
|
return null;
|
|
61
90
|
}
|
package/api/libs/jwt.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable no-underscore-dangle */
|
|
2
2
|
const jwt = require('jsonwebtoken');
|
|
3
3
|
const { createAuthToken } = require('@abtnode/auth/lib/auth');
|
|
4
|
+
const { LOGIN_PROVIDER } = require('@blocklet/constant');
|
|
4
5
|
|
|
5
6
|
const getUser = async (node, teamDid, userDid) => {
|
|
6
7
|
const user = await node.getUserByDid({ teamDid, userDid });
|
|
@@ -15,7 +16,10 @@ const initJwt = (node, options) => {
|
|
|
15
16
|
// 保持默认有效期为 1 天
|
|
16
17
|
const ttl = options.sessionTtl || '1d';
|
|
17
18
|
|
|
18
|
-
const createSessionToken = (
|
|
19
|
+
const createSessionToken = (
|
|
20
|
+
did,
|
|
21
|
+
{ role, secret, passport, expiresIn, tokenType, fullName, provider = LOGIN_PROVIDER.WALLET }
|
|
22
|
+
) =>
|
|
19
23
|
createAuthToken({
|
|
20
24
|
did,
|
|
21
25
|
passport,
|
|
@@ -24,6 +28,7 @@ const initJwt = (node, options) => {
|
|
|
24
28
|
expiresIn: expiresIn || ttl,
|
|
25
29
|
tokenType,
|
|
26
30
|
fullName,
|
|
31
|
+
provider,
|
|
27
32
|
});
|
|
28
33
|
|
|
29
34
|
const verifySessionToken = (token, secret, { checkFromDb, teamDid, checkToken } = {}) =>
|
|
@@ -42,7 +47,7 @@ const initJwt = (node, options) => {
|
|
|
42
47
|
}
|
|
43
48
|
}
|
|
44
49
|
|
|
45
|
-
const { did, role, passport, fullName } = decoded;
|
|
50
|
+
const { did, role, passport, fullName, provider = LOGIN_PROVIDER.WALLET } = decoded;
|
|
46
51
|
let user;
|
|
47
52
|
if (!did) {
|
|
48
53
|
return reject(new Error('Invalid jwt token: invalid did'));
|
|
@@ -70,7 +75,7 @@ const initJwt = (node, options) => {
|
|
|
70
75
|
user.role = role;
|
|
71
76
|
user.passport = passport;
|
|
72
77
|
} else {
|
|
73
|
-
user = { did, role, passport, fullName };
|
|
78
|
+
user = { did, role, passport, fullName, provider };
|
|
74
79
|
}
|
|
75
80
|
|
|
76
81
|
return resolve(user);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
function checkFederatedCorsCall() {
|
|
2
|
+
return (req, res, next) => {
|
|
3
|
+
const caller = req.headers.origin;
|
|
4
|
+
const { blocklet } = req;
|
|
5
|
+
const federated = blocklet.settings.federated || {};
|
|
6
|
+
const sites = federated?.sites || [];
|
|
7
|
+
if (sites.find((item) => item.appUrl === caller && item.status === 'approved')) {
|
|
8
|
+
next();
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
res.status(403).send('Unauthorized');
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = checkFederatedCorsCall;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
function ensureBlocklet() {
|
|
2
|
+
return async (req, res, next) => {
|
|
3
|
+
const blocklet = await req.getBlocklet();
|
|
4
|
+
if (!blocklet) {
|
|
5
|
+
res.status(404).send('blocklet not found');
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
req.blocklet = blocklet;
|
|
9
|
+
next();
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module.exports = ensureBlocklet;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const { verify, decode } = require('@arcblock/jwt');
|
|
2
|
+
|
|
3
|
+
function verifyFederatedCall({ isMaster = false } = { isMaster: false }) {
|
|
4
|
+
return (req, res, next) => {
|
|
5
|
+
const { signer, data } = req.body;
|
|
6
|
+
const { blocklet } = req;
|
|
7
|
+
const federated = blocklet.settings.federated || {};
|
|
8
|
+
const sites = federated?.sites || [];
|
|
9
|
+
|
|
10
|
+
// TODO: @zhanghan 是否需要检查 status === approved?
|
|
11
|
+
// 暂时先不检查,member 只能更新自己的信息,无法更新自身的 status,目前没有任何有权限的信息的修改能力
|
|
12
|
+
const validSite = sites.find((siteItem) => {
|
|
13
|
+
let result = siteItem.appPid === signer;
|
|
14
|
+
if (isMaster) {
|
|
15
|
+
result = result && siteItem.isMaster !== false;
|
|
16
|
+
}
|
|
17
|
+
return result;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
if (!validSite) {
|
|
21
|
+
res.status(403).send('Unauthorized');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!verify(data, validSite.pk)) {
|
|
26
|
+
res.status(403).send('Invalid signature');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
req.body.verifyData = decode(data);
|
|
31
|
+
next();
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = verifyFederatedCall;
|
package/api/routes/blocklet.js
CHANGED
|
@@ -3,13 +3,14 @@ const fs = require('fs');
|
|
|
3
3
|
const cloneDeep = require('lodash/cloneDeep');
|
|
4
4
|
const dayjs = require('@abtnode/util/lib/dayjs');
|
|
5
5
|
const JWT = require('@arcblock/jwt');
|
|
6
|
+
const { isValid } = require('@arcblock/did');
|
|
6
7
|
const joinUrl = require('url-join');
|
|
7
8
|
const handleInstanceInStore = require('@abtnode/core/lib/util/public-to-store');
|
|
8
9
|
const { getPassportStatusEndpoint } = require('@abtnode/auth/lib/auth');
|
|
9
10
|
const { createPassportVC, createPassport, createUserPassport } = require('@abtnode/auth/lib/passport');
|
|
10
11
|
|
|
11
12
|
const formatContext = require('@abtnode/util/lib/format-context');
|
|
12
|
-
const {
|
|
13
|
+
const { getUserAvatarUrl, getAvatarFile, getAppAvatarUrl } = require('@abtnode/util/lib/user');
|
|
13
14
|
const {
|
|
14
15
|
attachSendLogoContext,
|
|
15
16
|
ensureBlockletExist,
|
|
@@ -27,7 +28,12 @@ const {
|
|
|
27
28
|
findComponentByIdV2,
|
|
28
29
|
forEachComponentV2Sync,
|
|
29
30
|
} = require('@blocklet/meta/lib/util');
|
|
30
|
-
const {
|
|
31
|
+
const {
|
|
32
|
+
WELLKNOWN_SERVICE_PATH_PREFIX,
|
|
33
|
+
USER_AVATAR_PATH_PREFIX,
|
|
34
|
+
ROLES,
|
|
35
|
+
USER_AVATAR_URL_PREFIX,
|
|
36
|
+
} = require('@abtnode/constant');
|
|
31
37
|
const logger = require('@abtnode/logger')(require('../../package.json').name);
|
|
32
38
|
|
|
33
39
|
const { createDownloadLogStream } = require('@abtnode/util/lib/log');
|
|
@@ -109,7 +115,14 @@ module.exports = {
|
|
|
109
115
|
|
|
110
116
|
try {
|
|
111
117
|
const blocklet = await req.getBlocklet();
|
|
112
|
-
|
|
118
|
+
let { fileName } = req.params;
|
|
119
|
+
|
|
120
|
+
if (isValid(fileName)) {
|
|
121
|
+
const user = await node.getUser({ teamDid: blocklet.appPid, user: { did: fileName } });
|
|
122
|
+
if (user && user.avatar.startsWith(USER_AVATAR_URL_PREFIX)) {
|
|
123
|
+
fileName = user.avatar.split('/').pop();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
113
126
|
|
|
114
127
|
const avatarFile = getAvatarFile(blocklet.env.dataDir, fileName);
|
|
115
128
|
|
|
@@ -174,7 +187,8 @@ module.exports = {
|
|
|
174
187
|
const { wallet, name, passportColor } = await req.getBlockletInfo();
|
|
175
188
|
// NOTICE: 这里的 did 是从 session 中取到的永久 did,不需要查询 connectedAccount
|
|
176
189
|
const user = await node.getUser({ teamDid, user: { did: userDid } });
|
|
177
|
-
|
|
190
|
+
const appUrl = blocklet.environments.find((x) => x.key === 'BLOCKLET_APP_URL').value;
|
|
191
|
+
user.avatar = getUserAvatarUrl(appUrl, user.avatar);
|
|
178
192
|
|
|
179
193
|
const { pk, locale = 'en' } = user;
|
|
180
194
|
|
|
@@ -200,12 +214,11 @@ module.exports = {
|
|
|
200
214
|
const role = ROLES.OWNER;
|
|
201
215
|
const hasOwnerPassport = (user.passports || []).some((x) => x.name === role);
|
|
202
216
|
if (hasOwnerPassport === false) {
|
|
203
|
-
const appUrl = blocklet.environments.find((item) => item.key === 'BLOCKLET_APP_URL').value;
|
|
204
|
-
|
|
205
217
|
// create vc
|
|
206
218
|
const vc = createPassportVC({
|
|
207
219
|
issuerName: name,
|
|
208
220
|
issuerWallet: wallet,
|
|
221
|
+
issuerAvatarUrl: getAppAvatarUrl(appUrl),
|
|
209
222
|
ownerDid: userDid,
|
|
210
223
|
passport: await createPassport({
|
|
211
224
|
name: role,
|