@abtnode/blocklet-services 1.16.14-beta-a898bfcb → 1.16.14-beta-d802cd3c
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 +20 -7
- 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/dashboard/index.js +1 -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 +70 -69
- 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/4603.dce369d5.chunk.css +2 -0
- package/build/static/js/{1162.40dc4181.chunk.js → 1162.ff3136ae.chunk.js} +2 -2
- 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/{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.080a986b.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/5176.68fd3fde.chunk.js +2 -0
- package/build/static/js/5233.faabaae9.chunk.js +2 -0
- package/build/static/js/{5430.e0f10ab3.chunk.js → 5430.8114614c.chunk.js} +2 -2
- package/build/static/js/5645.a17cd258.chunk.js +2 -0
- package/build/static/js/6315.59e078f6.chunk.js +2 -0
- package/build/static/js/652.d534b309.chunk.js +2 -0
- package/build/static/js/{6737.c05aedb4.chunk.js → 6737.4f37dd4a.chunk.js} +2 -2
- 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.c7bfb323.chunk.js → 766.5dd16d47.chunk.js} +2 -2
- package/build/static/js/780.32bf1f5b.chunk.js +2 -0
- 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.0357601e.js +3 -0
- package/build/static/media/index.20414a3fa1e6c5498a67.cjs +1 -0
- package/package.json +36 -35
- package/build/static/js/1237.7fd9e6ea.chunk.js +0 -3
- package/build/static/js/1610.13323d09.chunk.js +0 -2
- package/build/static/js/2130.5655904d.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/3464.54f63e09.chunk.js +0 -2
- package/build/static/js/3963.f4a75432.chunk.js +0 -3
- 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/5176.52b53df7.chunk.js +0 -2
- package/build/static/js/5233.e44190bf.chunk.js +0 -2
- package/build/static/js/5645.48cf12d4.chunk.js +0 -2
- package/build/static/js/6315.dec40b9b.chunk.js +0 -2
- package/build/static/js/652.7a2367a9.chunk.js +0 -2
- package/build/static/js/6891.eed3eb07.chunk.js +0 -2
- package/build/static/js/7345.fb7fa7cf.chunk.js +0 -3
- package/build/static/js/7371.0b48d1c0.chunk.js +0 -2
- package/build/static/js/780.860d0370.chunk.js +0 -2
- package/build/static/js/7902.9316a419.chunk.js +0 -2
- package/build/static/js/8395.656effd4.chunk.js +0 -2
- package/build/static/js/868.cd5abe55.chunk.js +0 -2
- package/build/static/js/9476.302a7b02.chunk.js +0 -2
- package/build/static/js/9645.8ef1036e.chunk.js +0 -2
- package/build/static/js/982.3e297296.chunk.js +0 -3
- package/build/static/js/main.d78266c4.js +0 -3
- /package/build/static/js/{1237.7fd9e6ea.chunk.js.LICENSE.txt → 1237.1d0b8f1e.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{2130.5655904d.chunk.js.LICENSE.txt → 2130.59fef4ad.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{3963.f4a75432.chunk.js.LICENSE.txt → 3963.2e06f9bc.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{445.080a986b.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/{982.3e297296.chunk.js.LICENSE.txt → 982.11dc355f.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{main.d78266c4.js.LICENSE.txt → main.0357601e.js.LICENSE.txt} +0 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
const { WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
2
|
+
const { signV2 } = require('@arcblock/jwt');
|
|
3
|
+
const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
|
|
4
|
+
const cloneDeep = require('lodash/cloneDeep');
|
|
5
|
+
const get = require('lodash/get');
|
|
6
|
+
|
|
7
|
+
const cors = require('cors');
|
|
8
|
+
const { LOGIN_PROVIDER } = require('@blocklet/constant');
|
|
9
|
+
const pick = require('lodash/pick');
|
|
10
|
+
|
|
11
|
+
const { api } = require('../libs/api');
|
|
12
|
+
const initJwt = require('../libs/jwt');
|
|
13
|
+
const { createTokenFn, getDidConnectVersion } = require('../util');
|
|
14
|
+
const verifyFederatedCall = require('../middlewares/verify-federated-call');
|
|
15
|
+
const ensureBlocklet = require('../middlewares/ensure-blocklet');
|
|
16
|
+
const checkFederatedCorsCall = require('../middlewares/check-federated-cors-call');
|
|
17
|
+
const { getUserAvatarUrl } = require('../util/federated');
|
|
18
|
+
|
|
19
|
+
const PREFIX = WELLKNOWN_SERVICE_PATH_PREFIX;
|
|
20
|
+
|
|
21
|
+
const prefix = `${PREFIX}/api/federated`;
|
|
22
|
+
|
|
23
|
+
module.exports = {
|
|
24
|
+
preInit(server) {
|
|
25
|
+
server.options(`${prefix}/prelogin`, cors({ credentials: true, origin: true }));
|
|
26
|
+
server.options(`${prefix}/login`, cors({ credentials: true, origin: true }));
|
|
27
|
+
server.options(`${prefix}/logout`, cors({ credentials: true, origin: true }));
|
|
28
|
+
},
|
|
29
|
+
init(server, node, options) {
|
|
30
|
+
// step 1 申请加入(member 向 master 申请)
|
|
31
|
+
server.post(`${prefix}/join`, ensureBlocklet(), async (req, res) => {
|
|
32
|
+
const { blocklet } = req;
|
|
33
|
+
const { site } = req.body;
|
|
34
|
+
|
|
35
|
+
const federated = cloneDeep(blocklet.settings.federated || {});
|
|
36
|
+
let sites = federated?.sites || [];
|
|
37
|
+
if (sites.length === 0) {
|
|
38
|
+
const nodeInfo = await req.getNodeInfo();
|
|
39
|
+
const blockletInfo = await req.getBlockletInfo();
|
|
40
|
+
const masterSite = {
|
|
41
|
+
appId: blockletInfo.wallet.address,
|
|
42
|
+
appPid: blockletInfo.permanentWallet.address,
|
|
43
|
+
migratedFrom: blocklet.migratedFrom || [],
|
|
44
|
+
appName: blockletInfo.name,
|
|
45
|
+
appDescription: blockletInfo.description,
|
|
46
|
+
appUrl: blockletInfo.appUrl,
|
|
47
|
+
appLogo:
|
|
48
|
+
blocklet.environmentObj.BLOCKLET_APP_LOGO ||
|
|
49
|
+
normalizePathPrefix(`${WELLKNOWN_SERVICE_PATH_PREFIX}/blocklet/logo`) ||
|
|
50
|
+
'/',
|
|
51
|
+
appLogoRect: blocklet.environmentObj.BLOCKLET_APP_LOGO_RECT,
|
|
52
|
+
did: blockletInfo.did,
|
|
53
|
+
pk: blockletInfo.permanentWallet.publicKey,
|
|
54
|
+
serverId: nodeInfo.did,
|
|
55
|
+
serverVersion: nodeInfo.version,
|
|
56
|
+
version: blocklet.meta.version,
|
|
57
|
+
};
|
|
58
|
+
sites = [masterSite];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let masterConfig = federated.config;
|
|
62
|
+
if (!masterConfig) {
|
|
63
|
+
masterConfig = {
|
|
64
|
+
appId: blocklet.appDid,
|
|
65
|
+
appPid: blocklet.appPid || blocklet.appDid,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (site) {
|
|
69
|
+
sites.push({
|
|
70
|
+
...site,
|
|
71
|
+
appliedAt: new Date(),
|
|
72
|
+
status: 'pending',
|
|
73
|
+
isMaster: false,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
// member 申请后,将 member 展示在列表中
|
|
77
|
+
// 更新的是自己
|
|
78
|
+
await node.setFederated({
|
|
79
|
+
did: blocklet.appDid,
|
|
80
|
+
config: {
|
|
81
|
+
config: masterConfig,
|
|
82
|
+
sites,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// 将新增的数据返回给 member
|
|
87
|
+
res.json({
|
|
88
|
+
sites,
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// step 2 审批(master 申批 member)
|
|
93
|
+
// core/state/lib/blocklet/manager/disk.js -> auditFederatedLogin
|
|
94
|
+
server.post(`${prefix}/audit-res`, ensureBlocklet(), verifyFederatedCall(), async (req, res) => {
|
|
95
|
+
const { blocklet } = req;
|
|
96
|
+
const { delegation, roles, appId, status } = req.body.verifyData;
|
|
97
|
+
const federated = cloneDeep(blocklet.settings.federated || {});
|
|
98
|
+
federated.config.delegation = delegation;
|
|
99
|
+
if (status === 'approved') {
|
|
100
|
+
const trustedPassports = blocklet.trustedPassports || [];
|
|
101
|
+
const hasTrustedPassport = trustedPassports.find((item) => item.issuerDid === appId);
|
|
102
|
+
if (!hasTrustedPassport) {
|
|
103
|
+
await node.configTrustedPassports({
|
|
104
|
+
teamDid: blocklet.meta.did,
|
|
105
|
+
trustedPassports: [
|
|
106
|
+
...trustedPassports,
|
|
107
|
+
{
|
|
108
|
+
issuerDid: appId,
|
|
109
|
+
remark: 'Generated on join federated login',
|
|
110
|
+
mappings: roles.map((item) => {
|
|
111
|
+
return {
|
|
112
|
+
from: { passport: item.name },
|
|
113
|
+
to: { role: 'guest' },
|
|
114
|
+
};
|
|
115
|
+
}),
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
await node.setFederated({
|
|
123
|
+
did: blocklet.meta.did,
|
|
124
|
+
config: federated,
|
|
125
|
+
});
|
|
126
|
+
res.json(federated);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// step 3 同步站点群信息(master 向 member 广播站点群信息,广播请求在 manager/disk.js 文件中 class FederatedBlockletManager 发起 )
|
|
130
|
+
// 该路由为 member 接受响应的 api(只允许 master 调用)
|
|
131
|
+
server.post(`${prefix}/sync`, ensureBlocklet(), verifyFederatedCall({ isMaster: true }), async (req, res) => {
|
|
132
|
+
const { sites } = req.body.verifyData;
|
|
133
|
+
const { blocklet } = req;
|
|
134
|
+
const federated = cloneDeep(blocklet.settings.federated || {});
|
|
135
|
+
federated.sites = sites;
|
|
136
|
+
await node.setFederated({
|
|
137
|
+
did: blocklet.meta.did,
|
|
138
|
+
config: federated,
|
|
139
|
+
});
|
|
140
|
+
res.json(federated);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// step 4 检测自动登录条件(member 向 master 发起请求)
|
|
144
|
+
server.post(
|
|
145
|
+
`${prefix}/prelogin`,
|
|
146
|
+
cors({ credentials: true, origin: true }),
|
|
147
|
+
ensureBlocklet(),
|
|
148
|
+
checkFederatedCorsCall(),
|
|
149
|
+
async (req, res) => {
|
|
150
|
+
const { blocklet } = req;
|
|
151
|
+
|
|
152
|
+
const teamDid = blocklet.meta.did;
|
|
153
|
+
let user = null;
|
|
154
|
+
if (req.user) {
|
|
155
|
+
user = { ...req.user };
|
|
156
|
+
const currentUser = await node.getUser({
|
|
157
|
+
teamDid,
|
|
158
|
+
user: {
|
|
159
|
+
did: req.user.did,
|
|
160
|
+
},
|
|
161
|
+
options: {
|
|
162
|
+
enableConnectedAccount: true,
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
user.avatar = getUserAvatarUrl(currentUser.avatar, blocklet);
|
|
166
|
+
}
|
|
167
|
+
const blockletInfo = await req.getBlockletInfo();
|
|
168
|
+
const site = {
|
|
169
|
+
appId: blockletInfo.wallet.address,
|
|
170
|
+
appPid: blockletInfo.permanentWallet.address,
|
|
171
|
+
appName: blockletInfo.name,
|
|
172
|
+
appDescription: blockletInfo.description,
|
|
173
|
+
appUrl: blockletInfo.appUrl,
|
|
174
|
+
appLogo:
|
|
175
|
+
blocklet.environmentObj.BLOCKLET_APP_LOGO ||
|
|
176
|
+
normalizePathPrefix(`${WELLKNOWN_SERVICE_PATH_PREFIX}/blocklet/logo`) ||
|
|
177
|
+
'/',
|
|
178
|
+
appLogoRect: blocklet.environmentObj.BLOCKLET_APP_LOGO_RECT,
|
|
179
|
+
did: blockletInfo.did,
|
|
180
|
+
};
|
|
181
|
+
res.json({ user, site });
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
// step 5 完成自动登录(member 向 master 请求)
|
|
186
|
+
server.post(
|
|
187
|
+
`${prefix}/login`,
|
|
188
|
+
cors({ credentials: true, origin: true }),
|
|
189
|
+
ensureBlocklet(),
|
|
190
|
+
checkFederatedCorsCall(),
|
|
191
|
+
async (req, res) => {
|
|
192
|
+
if (!req.user) {
|
|
193
|
+
res.status(401).send('Unauthorized');
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
let data;
|
|
197
|
+
const { onlyWriteCookie = false } = req.body;
|
|
198
|
+
|
|
199
|
+
if (!onlyWriteCookie) {
|
|
200
|
+
const { blocklet } = req;
|
|
201
|
+
const { permanentWallet } = await req.getBlockletInfo();
|
|
202
|
+
const teamDid = blocklet.meta.did;
|
|
203
|
+
const federatedSites = blocklet.settings?.federated?.sites || [];
|
|
204
|
+
const loginSite = federatedSites.find((item) => item.appUrl === req.headers.origin);
|
|
205
|
+
|
|
206
|
+
const currentUser = await node.getUser({
|
|
207
|
+
teamDid,
|
|
208
|
+
user: {
|
|
209
|
+
did: req.user.did,
|
|
210
|
+
},
|
|
211
|
+
options: {
|
|
212
|
+
enableConnectedAccount: true,
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const user = pick(currentUser, ['did', 'pk', 'fullName', 'locale']);
|
|
217
|
+
user.lastLoginIp = get(req, 'headers[x-real-ip]') || '';
|
|
218
|
+
|
|
219
|
+
if (currentUser.email) {
|
|
220
|
+
user.email = currentUser.email;
|
|
221
|
+
}
|
|
222
|
+
if (currentUser.avatar) {
|
|
223
|
+
user.avatar = getUserAvatarUrl(currentUser.avatar, blocklet);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
({ data } = await api.post(`${loginSite.appUrl}${prefix}/token`, {
|
|
228
|
+
signer: permanentWallet.address,
|
|
229
|
+
data: signV2(permanentWallet.address, permanentWallet.secretKey, {
|
|
230
|
+
appId: teamDid,
|
|
231
|
+
role: req.user.role,
|
|
232
|
+
user,
|
|
233
|
+
passport: req.user.passport,
|
|
234
|
+
}),
|
|
235
|
+
}));
|
|
236
|
+
} catch {
|
|
237
|
+
res.status(500).send('Generate token error');
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
res.cookie('login_token', req.token, {
|
|
242
|
+
// HACK: 目前 sessionToken 的有效期就是 1h
|
|
243
|
+
maxAge: 60 * 60 * 1000,
|
|
244
|
+
expires: new Date(Date.now() + 60 * 60 * 1000),
|
|
245
|
+
sameSite: 'none',
|
|
246
|
+
secure: true,
|
|
247
|
+
});
|
|
248
|
+
res.json(data);
|
|
249
|
+
}
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
// step 6 发放自动登录 token(master 向 member 发起生成 token 请求)
|
|
253
|
+
server.post(`${prefix}/token`, ensureBlocklet(), verifyFederatedCall(), async (req, res) => {
|
|
254
|
+
const { user, appId, role, passport } = req.body.verifyData;
|
|
255
|
+
const { createSessionToken } = initJwt(node, options);
|
|
256
|
+
const createToken = createTokenFn(createSessionToken);
|
|
257
|
+
const { secret } = await req.getBlockletInfo();
|
|
258
|
+
const { blocklet } = req;
|
|
259
|
+
const teamDid = blocklet.meta.did;
|
|
260
|
+
|
|
261
|
+
const sessionConfig = blocklet.settings?.session || {};
|
|
262
|
+
const trustedPassports = blocklet.trustedPassports || [];
|
|
263
|
+
const masterPassport = trustedPassports.find((item) => item.issuerDid === appId);
|
|
264
|
+
const findMapping = masterPassport ? masterPassport.mappings.find((item) => item.from.passport === role) : null;
|
|
265
|
+
const targetPassport = findMapping
|
|
266
|
+
? {
|
|
267
|
+
role: findMapping.to?.role || 'guest',
|
|
268
|
+
name: findMapping.to?.role || 'Guest',
|
|
269
|
+
id: passport?.id || '',
|
|
270
|
+
}
|
|
271
|
+
: { role: 'guest', name: 'Guest' };
|
|
272
|
+
const doc = await node.loginUser({
|
|
273
|
+
teamDid,
|
|
274
|
+
user: {
|
|
275
|
+
...user,
|
|
276
|
+
// HACK: @zhanghan 这里会将 passport 插入到当前用户的 passport 列表中,federated 登录不应该插入 passport
|
|
277
|
+
// passport: findMapping ? targetPassport : null,
|
|
278
|
+
connectedAccount: {
|
|
279
|
+
provider: LOGIN_PROVIDER.FEDERATED,
|
|
280
|
+
id: appId,
|
|
281
|
+
did: user.did,
|
|
282
|
+
pk: user.pk,
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
const { sessionToken, refreshToken } = createToken(
|
|
287
|
+
doc.did,
|
|
288
|
+
{
|
|
289
|
+
secret,
|
|
290
|
+
role: targetPassport.role,
|
|
291
|
+
passport: targetPassport,
|
|
292
|
+
fullName: doc.fullName,
|
|
293
|
+
provider: LOGIN_PROVIDER.FEDERATED,
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
...sessionConfig,
|
|
297
|
+
didConnectVersion: getDidConnectVersion(req),
|
|
298
|
+
}
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
res.json({ sessionToken, refreshToken });
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// member 要求 master 退出登录
|
|
305
|
+
server.post(
|
|
306
|
+
`${prefix}/logout`,
|
|
307
|
+
cors({ credentials: true, origin: true }),
|
|
308
|
+
ensureBlocklet(),
|
|
309
|
+
checkFederatedCorsCall(),
|
|
310
|
+
(req, res) => {
|
|
311
|
+
res.clearCookie('login_token');
|
|
312
|
+
res.send('');
|
|
313
|
+
}
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
// member 主动调起 master 登录(实现登录 member 时,自动登录 master)
|
|
317
|
+
|
|
318
|
+
server.post(`${prefix}/loginByMember`, ensureBlocklet(), verifyFederatedCall(), async (req, res) => {
|
|
319
|
+
const { user, appId, passport } = req.body.verifyData;
|
|
320
|
+
const { createSessionToken } = initJwt(node, options);
|
|
321
|
+
const createToken = createTokenFn(createSessionToken);
|
|
322
|
+
const { secret } = await req.getBlockletInfo();
|
|
323
|
+
const { blocklet } = req;
|
|
324
|
+
const teamDid = blocklet.meta.did;
|
|
325
|
+
|
|
326
|
+
const sessionConfig = blocklet.settings?.session || {};
|
|
327
|
+
const prevUser = await node.getUser({
|
|
328
|
+
teamDid,
|
|
329
|
+
user: { did: user.did },
|
|
330
|
+
options: { enableConnectedAccount: true },
|
|
331
|
+
});
|
|
332
|
+
// HACK: member 调用 master 时,将 passport 的 role 还原为 master 中原有的 role
|
|
333
|
+
const targetPassport = passport?.id ? (prevUser?.passports || []).find((item) => item.id === passport.id) : null;
|
|
334
|
+
const doc = await node.loginUser({
|
|
335
|
+
teamDid,
|
|
336
|
+
user: {
|
|
337
|
+
...user,
|
|
338
|
+
passport: targetPassport,
|
|
339
|
+
connectedAccount: {
|
|
340
|
+
provider: LOGIN_PROVIDER.WALLET,
|
|
341
|
+
id: appId,
|
|
342
|
+
did: user.did,
|
|
343
|
+
pk: user.pk,
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
const { sessionToken, refreshToken } = createToken(
|
|
349
|
+
doc.did,
|
|
350
|
+
{
|
|
351
|
+
secret,
|
|
352
|
+
passport: targetPassport,
|
|
353
|
+
role: targetPassport?.role || 'guest',
|
|
354
|
+
fullName: doc.fullName,
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
...sessionConfig,
|
|
358
|
+
didConnectVersion: getDidConnectVersion(req),
|
|
359
|
+
}
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
res.json({ sessionToken, refreshToken });
|
|
363
|
+
});
|
|
364
|
+
},
|
|
365
|
+
};
|
package/api/routes/oauth.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { handleInvitationReceive, getApplicationInfo } = require('@abtnode/auth/lib/auth');
|
|
2
2
|
const { upsertToPassports, createPassportSvg } = require('@abtnode/auth/lib/passport');
|
|
3
3
|
const { WELLKNOWN_SERVICE_PATH_PREFIX, NODE_SERVICES, PASSPORT_STATUS } = require('@abtnode/constant');
|
|
4
|
-
const {
|
|
4
|
+
const { extractUserAvatar, getUserAvatarUrl, getAppAvatarUrl } = require('@abtnode/util/lib/user');
|
|
5
5
|
const { fromAppDid } = require('@arcblock/did-ext');
|
|
6
6
|
const { getBlockletAppIdList } = require('@blocklet/meta/lib/util');
|
|
7
7
|
const get = require('lodash/get');
|
|
@@ -198,11 +198,10 @@ async function invite(req, node, options) {
|
|
|
198
198
|
});
|
|
199
199
|
const { dataDir, name: applicationName } = await getApplicationInfo({ node, nodeInfo, teamDid });
|
|
200
200
|
if (currentUser) {
|
|
201
|
-
const avatar = await parseUserAvatar(currentUser.avatar, { did: teamDid, dataDir });
|
|
202
201
|
profile = {
|
|
203
202
|
email: currentUser.email,
|
|
204
203
|
fullName: currentUser.fullName,
|
|
205
|
-
avatar,
|
|
204
|
+
avatar: getUserAvatarUrl(baseUrl, currentUser.avatar),
|
|
206
205
|
};
|
|
207
206
|
userDid = currentUser.did;
|
|
208
207
|
userPk = currentUser.pk;
|
|
@@ -213,7 +212,7 @@ async function invite(req, node, options) {
|
|
|
213
212
|
profile = {
|
|
214
213
|
email,
|
|
215
214
|
fullName,
|
|
216
|
-
avatar,
|
|
215
|
+
avatar: getUserAvatarUrl(baseUrl, avatar),
|
|
217
216
|
};
|
|
218
217
|
}
|
|
219
218
|
|
|
@@ -390,12 +389,12 @@ module.exports = {
|
|
|
390
389
|
const userDid = req.user.did;
|
|
391
390
|
const blocklet = await req.getBlockletInfo();
|
|
392
391
|
const nodeInfo = await req.getNodeInfo();
|
|
393
|
-
const { did: teamDid, wallet: blockletWallet } = blocklet;
|
|
394
|
-
const {
|
|
392
|
+
const { did: teamDid, wallet: blockletWallet, appUrl } = blocklet;
|
|
393
|
+
const { passportColor } = await getApplicationInfo({ node, nodeInfo, teamDid });
|
|
395
394
|
const issuerDidList = uniq([blockletWallet.address, ...getBlockletAppIdList(blocklet)]);
|
|
396
395
|
// NOTICE: 这里获取的 did 是当前登录用户的永久 did,无需查询 connectedAccount
|
|
397
396
|
const user = await node.getUser({ teamDid, user: { did: userDid } });
|
|
398
|
-
const ownerAvatarUrl =
|
|
397
|
+
const ownerAvatarUrl = getUserAvatarUrl(appUrl, user.avatar);
|
|
399
398
|
const { passports = [] } = user || {};
|
|
400
399
|
// NOTICE: 保持每一种 role 的 passport 只有一个即可
|
|
401
400
|
const passportTypes = uniqBy(
|
|
@@ -417,7 +416,9 @@ module.exports = {
|
|
|
417
416
|
title: x.title,
|
|
418
417
|
issuer: x.issuer.name,
|
|
419
418
|
issuerDid: x.issuer.id,
|
|
419
|
+
issuerAvatarUrl: getAppAvatarUrl(appUrl),
|
|
420
420
|
ownerName: user?.fullName,
|
|
421
|
+
ownerDid: userDid,
|
|
421
422
|
ownerAvatarUrl,
|
|
422
423
|
isDataUrl: true,
|
|
423
424
|
preferredColor: passportColor || 'auto',
|
|
@@ -36,7 +36,7 @@ module.exports = function createRoutes(node, authenticator, createSessionToken)
|
|
|
36
36
|
};
|
|
37
37
|
},
|
|
38
38
|
|
|
39
|
-
signature: async ({ extraParams, context: { request, didwallet } }) => {
|
|
39
|
+
signature: async ({ extraParams, context: { request, baseUrl, didwallet } }) => {
|
|
40
40
|
const { locale, inviteId } = extraParams;
|
|
41
41
|
checkWalletVersion({ didwallet, locale });
|
|
42
42
|
const nodeInfo = await request.getNodeInfo();
|
|
@@ -48,6 +48,7 @@ module.exports = function createRoutes(node, authenticator, createSessionToken)
|
|
|
48
48
|
teamDid,
|
|
49
49
|
inviteId,
|
|
50
50
|
locale,
|
|
51
|
+
baseUrl,
|
|
51
52
|
});
|
|
52
53
|
},
|
|
53
54
|
},
|
|
@@ -20,19 +20,13 @@ module.exports = function createRoutes(node) {
|
|
|
20
20
|
},
|
|
21
21
|
|
|
22
22
|
claims: {
|
|
23
|
-
signature: async ({ extraParams, context: { request, didwallet } }) => {
|
|
23
|
+
signature: async ({ extraParams, context: { request, baseUrl, didwallet } }) => {
|
|
24
24
|
const { id, locale } = extraParams;
|
|
25
25
|
checkWalletVersion({ didwallet, locale });
|
|
26
26
|
const nodeInfo = await node.getNodeInfo();
|
|
27
27
|
const teamDid = request.headers['x-blocklet-did'];
|
|
28
28
|
|
|
29
|
-
return createIssuePassportRequest({
|
|
30
|
-
node,
|
|
31
|
-
nodeInfo,
|
|
32
|
-
teamDid,
|
|
33
|
-
id,
|
|
34
|
-
locale,
|
|
35
|
-
});
|
|
29
|
+
return createIssuePassportRequest({ node, nodeInfo, teamDid, id, locale, baseUrl });
|
|
36
30
|
},
|
|
37
31
|
},
|
|
38
32
|
|
|
@@ -36,7 +36,15 @@ module.exports = function createRoutes(node, authenticator, createSessionToken)
|
|
|
36
36
|
action: 'login',
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
await updateSession(
|
|
39
|
+
await updateSession(
|
|
40
|
+
{
|
|
41
|
+
sessionToken: result.sessionToken,
|
|
42
|
+
refreshToken: result.refreshToken,
|
|
43
|
+
federatedSessionToken: result.federatedSessionToken || '',
|
|
44
|
+
federatedRefreshToken: result.federatedRefreshToken || '',
|
|
45
|
+
},
|
|
46
|
+
true
|
|
47
|
+
);
|
|
40
48
|
|
|
41
49
|
return result;
|
|
42
50
|
} catch (err) {
|
|
@@ -203,7 +203,8 @@ module.exports = function createRoutes(node, _, createSessionToken) {
|
|
|
203
203
|
passportColor,
|
|
204
204
|
dataDir,
|
|
205
205
|
secret,
|
|
206
|
-
|
|
206
|
+
logo,
|
|
207
|
+
} = await getApplicationInfo({ node, teamDid: appPid, baseUrl });
|
|
207
208
|
|
|
208
209
|
const statusEndpointBaseUrl = baseUrl;
|
|
209
210
|
const endpoint = baseUrl;
|
|
@@ -211,6 +212,7 @@ module.exports = function createRoutes(node, _, createSessionToken) {
|
|
|
211
212
|
const vcParams = {
|
|
212
213
|
issuerName,
|
|
213
214
|
issuerWallet,
|
|
215
|
+
issuerAvatarUrl: logo,
|
|
214
216
|
ownerDid: userDid,
|
|
215
217
|
passport: await createPassport({
|
|
216
218
|
name: ROLES.OWNER,
|
|
@@ -3,6 +3,7 @@ const joinUrl = require('url-join');
|
|
|
3
3
|
const SealedBox = require('tweetnacl-sealedbox-js');
|
|
4
4
|
const { decodeEncryptionKey } = require('@abtnode/util/lib/security');
|
|
5
5
|
const { WELLKNOWN_SERVICE_PATH_PREFIX, USER_AVATAR_URL_PREFIX, USER_AVATAR_PATH_PREFIX } = require('@abtnode/constant');
|
|
6
|
+
const { LOGIN_PROVIDER } = require('@blocklet/constant');
|
|
6
7
|
|
|
7
8
|
const { PREFIXES } = require('../../util/constants');
|
|
8
9
|
const { createTokenFn, getDidConnectVersion } = require('../../util');
|
|
@@ -62,6 +63,7 @@ module.exports = {
|
|
|
62
63
|
passport: req.user.passport,
|
|
63
64
|
role: req.user.role,
|
|
64
65
|
fullName: user.fullName,
|
|
66
|
+
provider: req.user.provider || LOGIN_PROVIDER.WALLET,
|
|
65
67
|
},
|
|
66
68
|
{ ...sessionConfig, didConnectVersion: getDidConnectVersion(req) }
|
|
67
69
|
);
|
|
@@ -77,7 +79,7 @@ module.exports = {
|
|
|
77
79
|
}
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
res.json({ user, nextToken, nextRefreshToken });
|
|
82
|
+
res.json({ user, nextToken, nextRefreshToken, provider: req.user.provider || LOGIN_PROVIDER.WALLET });
|
|
81
83
|
};
|
|
82
84
|
|
|
83
85
|
const sessionApi = `${prefix}/api/did/session`;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const get = require('lodash/get');
|
|
2
2
|
const { WsServer } = require('@arcblock/ws');
|
|
3
3
|
const { WELLKNOWN_SERVICE_PATH_PREFIX, ROLES, EVENTS } = require('@abtnode/constant');
|
|
4
|
-
const { createStreamLogManager, getLogFiles } = require('@abtnode/
|
|
4
|
+
const { createStreamLogManager, getLogFiles } = require('@abtnode/core/lib/util/log');
|
|
5
5
|
const { BlockletEvents } = require('@blocklet/constant');
|
|
6
6
|
const eventHub =
|
|
7
7
|
process.env.NODE_ENV === 'test' ? require('@arcblock/event-hub/single') : require('@arcblock/event-hub');
|