@abtnode/blocklet-services 1.16.5 → 1.16.6-beta-8be2fe37
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/libs/connect/session.js +85 -31
- package/api/routes/blocklet.js +24 -0
- package/api/routes/oauth.js +2 -2
- package/api/services/auth/connect/exchange-passport.js +58 -0
- package/api/services/auth/connect/login.js +2 -1
- package/api/services/auth/connect/pre-setup.js +2 -2
- package/api/services/auth/connect/receive-transfer-app-owner.js +385 -0
- package/api/services/auth/connect/transfer-app-owner.js +30 -0
- package/api/services/auth/index.js +6 -0
- package/api/services/oauth/index.js +30 -23
- package/build/asset-manifest.json +27 -21
- package/build/index.html +1 -1
- package/build/static/css/{204.1d1e88ad.chunk.css → 61.03a48b17.chunk.css} +1 -1
- package/build/static/js/162.58348adf.chunk.js +3 -0
- package/build/static/js/343.54712c04.chunk.js +2 -0
- package/build/static/js/359.c47779c2.chunk.js +2 -0
- package/build/static/js/371.ccef728f.chunk.js +2 -0
- package/build/static/js/42.a0526d16.chunk.js +2 -0
- package/build/static/js/547.f8830a9e.chunk.js +2 -0
- package/build/static/js/573.2052cb80.chunk.js +2 -0
- package/build/static/js/61.20fc6264.chunk.js +3 -0
- package/build/static/js/648.b5b229e5.chunk.js +3 -0
- package/build/static/js/716.874040d8.chunk.js +2 -0
- package/build/static/js/868.9c03fa86.chunk.js +2 -0
- package/build/static/js/main.88eb4897.js +3 -0
- package/package.json +32 -30
- package/build/static/js/204.df50af69.chunk.js +0 -3
- package/build/static/js/343.b31c2008.chunk.js +0 -2
- package/build/static/js/371.88127b62.chunk.js +0 -2
- package/build/static/js/573.2687bb44.chunk.js +0 -2
- package/build/static/js/648.7a2e44c8.chunk.js +0 -3
- package/build/static/js/712.9667cdcd.chunk.js +0 -3
- package/build/static/js/716.e1534c42.chunk.js +0 -2
- package/build/static/js/868.4d364267.chunk.js +0 -2
- package/build/static/js/main.3b61bb8b.js +0 -3
- /package/build/static/js/{712.9667cdcd.chunk.js.LICENSE.txt → 162.58348adf.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{204.df50af69.chunk.js.LICENSE.txt → 61.20fc6264.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{648.7a2e44c8.chunk.js.LICENSE.txt → 648.b5b229e5.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{main.3b61bb8b.js.LICENSE.txt → main.88eb4897.js.LICENSE.txt} +0 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
const { get } = require('lodash');
|
|
2
|
+
const { toHex } = require('@ocap/util');
|
|
3
|
+
const pRetry = require('p-retry');
|
|
4
|
+
const { messages, getApplicationInfo, getPassportStatusEndpoint, getUser } = require('@abtnode/auth/lib/auth');
|
|
5
|
+
const { getKeyPairClaim, getAuthPrincipalForTransferAppOwnerShip } = require('@abtnode/auth/lib/server');
|
|
6
|
+
const sleep = require('@abtnode/util/lib/sleep');
|
|
7
|
+
const { getChainClient } = require('@abtnode/util/lib/get-chain-client');
|
|
8
|
+
const formatContext = require('@abtnode/util/lib/format-context');
|
|
9
|
+
const { extractUserAvatar } = require('@abtnode/util/lib/user-avatar');
|
|
10
|
+
const { ensureAccountOnMainChain } = require('@abtnode/util/lib/ensure-account-on-main-chain');
|
|
11
|
+
const {
|
|
12
|
+
createPassport,
|
|
13
|
+
createPassportVC,
|
|
14
|
+
createUserPassport,
|
|
15
|
+
getRoleFromLocalPassport,
|
|
16
|
+
} = require('@abtnode/auth/lib/passport');
|
|
17
|
+
const getBlockletWallet = require('@blocklet/meta/lib/wallet');
|
|
18
|
+
const { getBlockletChainInfo, isInProgress, isRunning } = require('@blocklet/meta/lib/util');
|
|
19
|
+
const { ROLES } = require('@abtnode/constant');
|
|
20
|
+
const logger = require('@abtnode/logger')('blocklet-service:transfer-blocklet-owner');
|
|
21
|
+
|
|
22
|
+
const { mergeUserData } = require('../../oauth');
|
|
23
|
+
|
|
24
|
+
const migrateAppOnChain = async (blocklet, oldSk, newSk) => {
|
|
25
|
+
if (process.env.NODE_ENV === 'test') {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
logger.info('Preparing for on-chain migration', { did: blocklet.meta.did });
|
|
30
|
+
if (!oldSk) {
|
|
31
|
+
// should not be here
|
|
32
|
+
logger.error('on-chain migration aborted because oldSk is empty', { did: blocklet.meta.did });
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!newSk) {
|
|
37
|
+
// should not be here
|
|
38
|
+
logger.error('on-chain migration aborted because newSk is empty', { did: blocklet.meta.did });
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ensure account changed
|
|
43
|
+
const type = blocklet.configObj?.BLOCKLET_WALLET_TYPE;
|
|
44
|
+
const oldWallet = getBlockletWallet(oldSk, undefined, type);
|
|
45
|
+
const newWallet = getBlockletWallet(newSk, undefined, type);
|
|
46
|
+
if (oldWallet.address === newWallet.address) {
|
|
47
|
+
// should not be here
|
|
48
|
+
logger.info('on-chain migration aborted because newSk same with oldSk', { did: blocklet.meta.did });
|
|
49
|
+
await ensureAccountOnMainChain(newWallet, blocklet.meta.title);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ensure chain host
|
|
54
|
+
const chainHost = getBlockletChainInfo(blocklet).host;
|
|
55
|
+
if (!chainHost || chainHost === 'none') {
|
|
56
|
+
logger.info('on-chain migration aborted because CHAIN_HOST is empty', { did: blocklet.meta.did });
|
|
57
|
+
await ensureAccountOnMainChain(newWallet, blocklet.meta.title);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// migrate on chain
|
|
62
|
+
logger.info('on-chain migration for chain ', { did: blocklet.meta.did, host: chainHost });
|
|
63
|
+
const client = getChainClient(chainHost);
|
|
64
|
+
const oldResult = await client.getAccountState({ address: oldWallet.address });
|
|
65
|
+
const newResult = await client.getAccountState({ address: newWallet.address });
|
|
66
|
+
|
|
67
|
+
if (oldResult.state && !newResult.state) {
|
|
68
|
+
logger.info('on-chain migration for wallets', { oldAddress: oldWallet.address, newAddress: newWallet.address });
|
|
69
|
+
|
|
70
|
+
// migrate old account to new account
|
|
71
|
+
const tx = await client.signAccountMigrateTx({
|
|
72
|
+
tx: {
|
|
73
|
+
itx: {
|
|
74
|
+
address: newWallet.address,
|
|
75
|
+
pk: newWallet.publicKey,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
wallet: oldWallet,
|
|
79
|
+
});
|
|
80
|
+
const hash = await client.sendAccountMigrateTx({ tx, wallet: oldWallet });
|
|
81
|
+
logger.info('on-chain migration done', { did: blocklet.meta.did, hash });
|
|
82
|
+
} else {
|
|
83
|
+
if (!oldResult.state) {
|
|
84
|
+
logger.info('on-chain migration aborted because oldSk not declared on chain', { did: blocklet.meta.did });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (newResult.state) {
|
|
88
|
+
logger.info('on-chain migration aborted because newSk declared on chain', { did: blocklet.meta.did });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// should not throw error because signAccountMigrateTx is already happened
|
|
93
|
+
try {
|
|
94
|
+
await ensureAccountOnMainChain(newWallet, blocklet.meta.title);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
logger.error('ensureAccountOnMainChain failed', { error });
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
module.exports = function createRoutes(node, _, createSessionToken) {
|
|
101
|
+
const getBlocklet = async (did) => {
|
|
102
|
+
const blocklet = await node.getBlocklet({ did, attachConfig: false });
|
|
103
|
+
if (!blocklet) {
|
|
104
|
+
throw new Error(`application not found: ${did}`);
|
|
105
|
+
}
|
|
106
|
+
return blocklet;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
action: 'receive-transfer-app-owner',
|
|
111
|
+
authPrincipal: false,
|
|
112
|
+
claims: [
|
|
113
|
+
{
|
|
114
|
+
authPrincipal: getAuthPrincipalForTransferAppOwnerShip(node),
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
keyPair: async (opts) => {
|
|
118
|
+
const {
|
|
119
|
+
extraParams: { appDid, transferId, locale },
|
|
120
|
+
} = opts;
|
|
121
|
+
|
|
122
|
+
const transfer = await node.checkTransferAppOwnerSession({ appDid, transferId });
|
|
123
|
+
|
|
124
|
+
if (transfer.appDid !== appDid) {
|
|
125
|
+
throw new Error(`Invalid appDid. Expect: ${transfer.appDid}, Actual: ${appDid}`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const blocklet = await getBlocklet(appDid);
|
|
129
|
+
|
|
130
|
+
if (isInProgress(blocklet.status) || isRunning(blocklet.status)) {
|
|
131
|
+
throw new Error(messages.appIsRunning[locale]);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return getKeyPairClaim(node, { title: blocklet.meta.title, declare: false })(opts);
|
|
135
|
+
},
|
|
136
|
+
profile: async ({ extraParams: { locale = 'en' } }) => {
|
|
137
|
+
return {
|
|
138
|
+
fields: ['fullName', 'email', 'avatar'],
|
|
139
|
+
description: messages.requestProfile[locale],
|
|
140
|
+
};
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
onAuth: async ({ userDid, userPk, claims, req, updateSession, extraParams, baseUrl }) => {
|
|
145
|
+
logger.info('transferAppOwnerHandler', extraParams);
|
|
146
|
+
|
|
147
|
+
// prepare data
|
|
148
|
+
|
|
149
|
+
const { locale = 'en', appDid, transferId } = extraParams;
|
|
150
|
+
|
|
151
|
+
if (!transferId) {
|
|
152
|
+
throw new Error('missing transferId in extraParams');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (!appDid) {
|
|
156
|
+
throw new Error(messages.missingBlockletDid[locale]);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const profile = claims.find((x) => x.type === 'profile');
|
|
160
|
+
if (!profile) {
|
|
161
|
+
throw new Error('app keyPair must be provided');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const keyPair = claims.find((x) => x.type === 'keyPair');
|
|
165
|
+
if (!keyPair) {
|
|
166
|
+
throw new Error(messages.missingKeyPair[locale]);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const newSk = toHex(keyPair.secret);
|
|
170
|
+
if (!newSk) {
|
|
171
|
+
throw new Error(`keyPair.secret is empty: ${appDid}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const blocklet = await node.getBlocklet({ did: appDid, attachRuntimeInfo: false });
|
|
175
|
+
if (!blocklet) {
|
|
176
|
+
throw new Error(messages.invalidBlocklet[locale]);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (blocklet.appDid !== appDid) {
|
|
180
|
+
throw new Error(`Invalid appDid. Expect: ${blocklet.appDid}, Actual: ${appDid}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const oldSk = (blocklet.environments || []).find((x) => x.key === 'BLOCKLET_APP_SK').value;
|
|
184
|
+
|
|
185
|
+
const initialized = !!blocklet.settings?.initialized;
|
|
186
|
+
if (!initialized) {
|
|
187
|
+
throw new Error(messages.appNotInitialized[locale]);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const oldOwnerDid = blocklet.settings?.owner?.did;
|
|
191
|
+
if (userDid === oldOwnerDid) {
|
|
192
|
+
throw new Error(messages.notAllowedTransferToSelf[locale]);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const { appPid } = blocklet;
|
|
196
|
+
|
|
197
|
+
const transfer = await node.checkTransferAppOwnerSession({ appDid, transferId });
|
|
198
|
+
|
|
199
|
+
if (transfer.appDid !== appDid) {
|
|
200
|
+
throw new Error(`Invalid appDid. Expect: ${transfer.appDid}, Actual: ${appDid}`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const {
|
|
204
|
+
name: issuerName,
|
|
205
|
+
wallet: issuerWallet,
|
|
206
|
+
passportColor,
|
|
207
|
+
dataDir,
|
|
208
|
+
} = await getApplicationInfo({ node, teamDid: appPid });
|
|
209
|
+
|
|
210
|
+
const statusEndpointBaseUrl = baseUrl;
|
|
211
|
+
const endpoint = baseUrl;
|
|
212
|
+
|
|
213
|
+
const vcParams = {
|
|
214
|
+
issuerName,
|
|
215
|
+
issuerWallet,
|
|
216
|
+
ownerDid: userDid,
|
|
217
|
+
passport: await createPassport({
|
|
218
|
+
name: ROLES.OWNER,
|
|
219
|
+
node,
|
|
220
|
+
teamDid: appPid,
|
|
221
|
+
locale,
|
|
222
|
+
endpoint,
|
|
223
|
+
}),
|
|
224
|
+
endpoint: getPassportStatusEndpoint({
|
|
225
|
+
baseUrl: statusEndpointBaseUrl,
|
|
226
|
+
userDid,
|
|
227
|
+
teamDid: appPid,
|
|
228
|
+
}),
|
|
229
|
+
types: [],
|
|
230
|
+
ownerProfile: profile,
|
|
231
|
+
preferredColor: passportColor,
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const vc = createPassportVC(vcParams);
|
|
235
|
+
|
|
236
|
+
const role = getRoleFromLocalPassport(get(vc, 'credentialSubject.passport'));
|
|
237
|
+
const passport = createUserPassport(vc, { role });
|
|
238
|
+
|
|
239
|
+
const user = await getUser(node, appPid, userDid);
|
|
240
|
+
|
|
241
|
+
// update state
|
|
242
|
+
|
|
243
|
+
await node.updateBlockletOwner({ did: appPid, owner: { did: userDid, pk: userPk } });
|
|
244
|
+
|
|
245
|
+
const avatar = await extractUserAvatar(get(profile, 'avatar'), {
|
|
246
|
+
dataDir,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const now = new Date().toISOString();
|
|
250
|
+
|
|
251
|
+
if (user) {
|
|
252
|
+
const doc = await node.updateUser({
|
|
253
|
+
teamDid: appPid,
|
|
254
|
+
user: {
|
|
255
|
+
...mergeUserData(user, {
|
|
256
|
+
avatar,
|
|
257
|
+
locale,
|
|
258
|
+
lastLoginIp: get(req, 'headers[x-real-ip]') || '',
|
|
259
|
+
lastUsedPassport: passport,
|
|
260
|
+
connectedAccount: { provider: 'wallet' },
|
|
261
|
+
}),
|
|
262
|
+
...profile,
|
|
263
|
+
avatar,
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
await node.createAuditLog(
|
|
267
|
+
{
|
|
268
|
+
action: 'updateUser',
|
|
269
|
+
args: { teamDid: appPid, userDid, passport, reason: 'transfer ownership' },
|
|
270
|
+
context: formatContext(Object.assign(req, { user })),
|
|
271
|
+
result: doc,
|
|
272
|
+
},
|
|
273
|
+
node
|
|
274
|
+
);
|
|
275
|
+
} else {
|
|
276
|
+
const doc = await node.addUser({
|
|
277
|
+
teamDid: appPid,
|
|
278
|
+
user: {
|
|
279
|
+
...profile,
|
|
280
|
+
avatar,
|
|
281
|
+
did: userDid,
|
|
282
|
+
pk: userPk,
|
|
283
|
+
approved: true,
|
|
284
|
+
locale,
|
|
285
|
+
passports: [passport],
|
|
286
|
+
firstLoginAt: now,
|
|
287
|
+
lastLoginAt: now,
|
|
288
|
+
lastLoginIp: get(req, 'headers[x-real-ip]') || '',
|
|
289
|
+
extraConfigs: {
|
|
290
|
+
sourceProvider: 'wallet',
|
|
291
|
+
connectedAccounts: [
|
|
292
|
+
{
|
|
293
|
+
provider: 'wallet',
|
|
294
|
+
did: userDid,
|
|
295
|
+
pk: userPk,
|
|
296
|
+
lastLoginAt: now,
|
|
297
|
+
firstLoginAt: now,
|
|
298
|
+
},
|
|
299
|
+
],
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
await node.createAuditLog(
|
|
305
|
+
{
|
|
306
|
+
action: 'addUser',
|
|
307
|
+
args: { teamDid: appPid, userDid, passport, reason: 'transfer ownership' },
|
|
308
|
+
context: formatContext(Object.assign(req, { user: doc })),
|
|
309
|
+
result: doc,
|
|
310
|
+
},
|
|
311
|
+
node
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// NOTICE: must NOT use appDid here if update BLOCKLET_APP_SK
|
|
316
|
+
await node.configBlocklet(
|
|
317
|
+
{
|
|
318
|
+
did: blocklet.appPid,
|
|
319
|
+
configs: [{ key: 'BLOCKLET_APP_SK', value: newSk, secure: true }],
|
|
320
|
+
skipHook: true,
|
|
321
|
+
skipDidDocument: true,
|
|
322
|
+
},
|
|
323
|
+
formatContext(Object.assign(req, { user: { did: userDid, fullName: 'Owner', role: 'owner' } }))
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
try {
|
|
327
|
+
if (oldOwnerDid) {
|
|
328
|
+
const oldOwner = await getUser(node, appPid, oldOwnerDid);
|
|
329
|
+
if (oldOwner) {
|
|
330
|
+
const filteredPassports = (oldOwner.passports || []).filter((p) => p.role !== ROLES.OWNER);
|
|
331
|
+
if (filteredPassports.length !== (oldOwner.passports || []).length) {
|
|
332
|
+
logger.info('update old owner passport', {
|
|
333
|
+
oldOwnerDid,
|
|
334
|
+
filteredPassports: filteredPassports.map((x) => x.id),
|
|
335
|
+
});
|
|
336
|
+
await node.updateUser({
|
|
337
|
+
teamDid: appPid,
|
|
338
|
+
user: {
|
|
339
|
+
did: oldOwnerDid,
|
|
340
|
+
pk: oldOwner.pk,
|
|
341
|
+
passports: filteredPassports,
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
} catch (error) {
|
|
348
|
+
logger.error('remove old owner passport failed', { error });
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
try {
|
|
352
|
+
await node.closeTransferAppOwnerSession({
|
|
353
|
+
appPid: blocklet.appPid,
|
|
354
|
+
transferId,
|
|
355
|
+
status: 'success',
|
|
356
|
+
});
|
|
357
|
+
} catch (error) {
|
|
358
|
+
logger.error('closeTransferAppOwnerSession failed', { error });
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// migrate on-chain account
|
|
362
|
+
pRetry(() => migrateAppOnChain(blocklet, oldSk, newSk), {
|
|
363
|
+
retries: 10,
|
|
364
|
+
onFailedAttempt: console.error,
|
|
365
|
+
}).catch((error) => {
|
|
366
|
+
logger.error('migrateAppOnChain failed', { error });
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
await sleep(3000);
|
|
370
|
+
logger.info('transfer ownership success', { userDid });
|
|
371
|
+
|
|
372
|
+
// Generate new session token that client can save to localStorage
|
|
373
|
+
const sessionToken = await createSessionToken(userDid, { passport: vc, role });
|
|
374
|
+
await updateSession({ sessionToken }, true);
|
|
375
|
+
await updateSession({ passportId: vc?.id });
|
|
376
|
+
logger.info('invite.success', { userDid });
|
|
377
|
+
|
|
378
|
+
return {
|
|
379
|
+
disposition: 'attachment',
|
|
380
|
+
type: 'VerifiableCredential',
|
|
381
|
+
data: vc,
|
|
382
|
+
};
|
|
383
|
+
},
|
|
384
|
+
};
|
|
385
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const { getAppDidOwnerClaims } = require('@abtnode/auth/lib/server');
|
|
2
|
+
const logger = require('@abtnode/logger')('blocklet-service:transfer-blocklet-owner');
|
|
3
|
+
|
|
4
|
+
module.exports = function createRoutes(node) {
|
|
5
|
+
const getBlocklet = async (did) => {
|
|
6
|
+
const blocklet = await node.getBlocklet({ did, attachConfig: false });
|
|
7
|
+
if (!blocklet) {
|
|
8
|
+
throw new Error(`application not found: ${did}`);
|
|
9
|
+
}
|
|
10
|
+
return blocklet;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
action: 'transfer-app-owner',
|
|
15
|
+
authPrincipal: false,
|
|
16
|
+
claims: getAppDidOwnerClaims(),
|
|
17
|
+
onAuth: async ({ userDid: appDid, updateSession }) => {
|
|
18
|
+
const blocklet = await getBlocklet(appDid);
|
|
19
|
+
if (blocklet.appDid !== appDid) {
|
|
20
|
+
throw new Error(`Invalid appDid. Expect: ${blocklet.appDid}, Actual: ${appDid}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const result = await node.createTransferAppOwnerSession({ appDid });
|
|
24
|
+
|
|
25
|
+
logger.info('create transfer app owner', result);
|
|
26
|
+
|
|
27
|
+
await updateSession({ result });
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
};
|
|
@@ -26,6 +26,7 @@ const createLoginRoutes = require('./connect/login');
|
|
|
26
26
|
const createBindWallerRoutes = require('./connect/bind-wallet');
|
|
27
27
|
const createInviteRoutes = require('./connect/invite');
|
|
28
28
|
const createIssuePassportAuth = require('./connect/issue-passport');
|
|
29
|
+
const createExchangePassportAuth = require('./connect/exchange-passport');
|
|
29
30
|
const createLostPassportListAuth = require('./connect/lost-passport-list');
|
|
30
31
|
const createLostPassportIssueAuth = require('./connect/lost-passport-issue');
|
|
31
32
|
const createPreSetupAuth = require('./connect/pre-setup');
|
|
@@ -35,6 +36,8 @@ const createSwitchPassportAuth = require('./connect/switch-passport');
|
|
|
35
36
|
const createRotateKeyPairAuth = require('./connect/rotate-key-pair');
|
|
36
37
|
const createFuelAuth = require('./connect/fuel');
|
|
37
38
|
const createMigrateToStructV2Routes = require('./connect/migrate-app-to-struct-v2');
|
|
39
|
+
const createTransferAppOwnerRoutes = require('./connect/transfer-app-owner');
|
|
40
|
+
const createReceiveTransferAppOwnerRoutes = require('./connect/receive-transfer-app-owner');
|
|
38
41
|
const createSessionRoutes = require('./session');
|
|
39
42
|
const createPassportRoutes = require('./passport');
|
|
40
43
|
const { getRedirectUrl } = require('../../util');
|
|
@@ -169,6 +172,7 @@ const init = ({ node, options }) => {
|
|
|
169
172
|
handler.attach(Object.assign({ app }, createBindWallerRoutes(node, authenticator, createSessionToken)));
|
|
170
173
|
handler.attach(Object.assign({ app }, createInviteRoutes(node, authenticator, createSessionToken)));
|
|
171
174
|
handler.attach(Object.assign({ app }, createIssuePassportAuth(node, authenticator, createSessionToken)));
|
|
175
|
+
handler.attach(Object.assign({ app }, createExchangePassportAuth(node, authenticator, createSessionToken)));
|
|
172
176
|
handler.attach(Object.assign({ app }, createLostPassportListAuth(node, authenticator, createSessionToken)));
|
|
173
177
|
handler.attach(Object.assign({ app }, createLostPassportIssueAuth(node, authenticator, createSessionToken)));
|
|
174
178
|
handler.attach(Object.assign({ app }, createPreSetupAuth(node, authenticator, createSessionToken)));
|
|
@@ -178,6 +182,8 @@ const init = ({ node, options }) => {
|
|
|
178
182
|
handler.attach(Object.assign({ app }, createRotateKeyPairAuth(node, authenticator, createSessionToken)));
|
|
179
183
|
handler.attach(Object.assign({ app }, createFuelAuth(authenticator, createSessionToken)));
|
|
180
184
|
handler.attach(Object.assign({ app }, createMigrateToStructV2Routes(node, options)));
|
|
185
|
+
handler.attach(Object.assign({ app }, createTransferAppOwnerRoutes(node, options)));
|
|
186
|
+
handler.attach(Object.assign({ app }, createReceiveTransferAppOwnerRoutes(node, options, createSessionToken)));
|
|
181
187
|
});
|
|
182
188
|
};
|
|
183
189
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { upsertToPassports } = require('@abtnode/auth/lib/passport');
|
|
2
|
+
const { getChainClient } = require('@abtnode/util/lib/get-chain-client');
|
|
2
3
|
const cloneDeep = require('lodash/cloneDeep');
|
|
3
|
-
const Client = require('@ocap/client');
|
|
4
4
|
const { default: urlFriendly } = require('@blocklet/meta/lib/url-friendly');
|
|
5
5
|
const { slugify } = require('transliteration');
|
|
6
6
|
const { MAIN_CHAIN_ENDPOINT } = require('@abtnode/constant');
|
|
@@ -10,7 +10,7 @@ const { types } = require('@arcblock/did');
|
|
|
10
10
|
const logger = require('@abtnode/logger')('blocklet-services:oauth');
|
|
11
11
|
|
|
12
12
|
function mergeUserData(oldUser, updateData) {
|
|
13
|
-
const
|
|
13
|
+
const now = new Date().toISOString();
|
|
14
14
|
const { extraConfigs = {} } = oldUser || {};
|
|
15
15
|
const newUser = {
|
|
16
16
|
did: oldUser.did,
|
|
@@ -19,14 +19,14 @@ function mergeUserData(oldUser, updateData) {
|
|
|
19
19
|
oldUser.passports || [],
|
|
20
20
|
updateData.lastUsedPassport && {
|
|
21
21
|
...updateData.lastUsedPassport,
|
|
22
|
-
lastLoginAt:
|
|
22
|
+
lastLoginAt: now,
|
|
23
23
|
}
|
|
24
24
|
),
|
|
25
25
|
extraConfigs: {
|
|
26
26
|
...extraConfigs,
|
|
27
27
|
connectedAccounts: updateConnectedAccount(extraConfigs.connectedAccounts || [], updateData.connectedAccount),
|
|
28
28
|
},
|
|
29
|
-
lastLoginAt:
|
|
29
|
+
lastLoginAt: now,
|
|
30
30
|
};
|
|
31
31
|
if (updateData.lastLoginIp) {
|
|
32
32
|
newUser.lastLoginIp = updateData.lastLoginIp;
|
|
@@ -39,20 +39,23 @@ function mergeUserData(oldUser, updateData) {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
function updateConnectedAccount(connectedAccounts = [], connectedAccount = {}) {
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
42
|
+
const now = new Date().toISOString();
|
|
43
|
+
const updated = cloneDeep(connectedAccounts);
|
|
44
|
+
const updates = Array.isArray(connectedAccount) ? connectedAccount : [connectedAccount];
|
|
45
|
+
updates.forEach((x) => {
|
|
46
|
+
const findAccountIndex = updated.findIndex((item) => item.provider === x.provider && item.did === x.did);
|
|
47
|
+
if (findAccountIndex > -1) {
|
|
48
|
+
updated[findAccountIndex] = {
|
|
49
|
+
...connectedAccounts[findAccountIndex],
|
|
50
|
+
...x,
|
|
51
|
+
lastLoginAt: now,
|
|
52
|
+
};
|
|
53
|
+
} else {
|
|
54
|
+
updated.push(x);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
54
57
|
|
|
55
|
-
return
|
|
58
|
+
return updated;
|
|
56
59
|
}
|
|
57
60
|
|
|
58
61
|
async function getRawUser(teamDid, userDid, { getUser, getUsers }) {
|
|
@@ -75,7 +78,7 @@ async function getRawUser(teamDid, userDid, { getUser, getUsers }) {
|
|
|
75
78
|
}
|
|
76
79
|
|
|
77
80
|
async function declareAccountByChain({ chainHost, wallet, moniker }) {
|
|
78
|
-
const chainClient =
|
|
81
|
+
const chainClient = getChainClient(chainHost);
|
|
79
82
|
const { address } = wallet;
|
|
80
83
|
const { state } = await chainClient.getAccountState({ address }, { ignoreFields: ['context'] });
|
|
81
84
|
if (state) {
|
|
@@ -103,13 +106,15 @@ async function declareAccount({ wallet, moniker, blocklet }) {
|
|
|
103
106
|
if (chainHost && chainHost !== 'none') {
|
|
104
107
|
chainHostList.push(chainHost);
|
|
105
108
|
}
|
|
106
|
-
const waitingList = chainHostList.map((item) =>
|
|
109
|
+
const waitingList = [...new Set(chainHostList)].map((item) =>
|
|
110
|
+
declareAccountByChain({ chainHost: item, wallet, moniker })
|
|
111
|
+
);
|
|
107
112
|
await Promise.all(waitingList);
|
|
108
113
|
}
|
|
109
114
|
|
|
110
115
|
async function migrateAccountByChain({ chainHost, user, wallet }) {
|
|
111
116
|
try {
|
|
112
|
-
const client =
|
|
117
|
+
const client = getChainClient(chainHost);
|
|
113
118
|
const tx = await client.signAccountMigrateTx({
|
|
114
119
|
tx: {
|
|
115
120
|
itx: {
|
|
@@ -120,10 +125,10 @@ async function migrateAccountByChain({ chainHost, user, wallet }) {
|
|
|
120
125
|
wallet,
|
|
121
126
|
});
|
|
122
127
|
const hash = await client.sendAccountMigrateTx({ tx, wallet });
|
|
123
|
-
logger.info('migration account done', { did: user.did, hash });
|
|
128
|
+
logger.info('migration account done', { chain: chainHost, did: user.did, hash });
|
|
124
129
|
return hash;
|
|
125
130
|
} catch (error) {
|
|
126
|
-
logger.error('migration account failed', { did: user.did, error });
|
|
131
|
+
logger.error('migration account failed', { chain: chainHost, did: user.did, error });
|
|
127
132
|
return undefined;
|
|
128
133
|
}
|
|
129
134
|
}
|
|
@@ -134,7 +139,9 @@ async function migrateAccount({ wallet, blocklet, user }) {
|
|
|
134
139
|
if (chainHost && chainHost !== 'none') {
|
|
135
140
|
chainHostList.push(chainHost);
|
|
136
141
|
}
|
|
137
|
-
const waitingList = chainHostList.map((item) =>
|
|
142
|
+
const waitingList = [...new Set(chainHostList)].map((item) =>
|
|
143
|
+
migrateAccountByChain({ chainHost: item, wallet, user })
|
|
144
|
+
);
|
|
138
145
|
await Promise.all(waitingList);
|
|
139
146
|
}
|
|
140
147
|
|
|
@@ -1,25 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"files": {
|
|
3
3
|
"main.css": "/.blocklet/proxy/blocklet-service/static/css/main.632501d5.css",
|
|
4
|
-
"main.js": "/.blocklet/proxy/blocklet-service/static/js/main.
|
|
5
|
-
"static/js/716.
|
|
4
|
+
"main.js": "/.blocklet/proxy/blocklet-service/static/js/main.88eb4897.js",
|
|
5
|
+
"static/js/716.874040d8.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/716.874040d8.chunk.js",
|
|
6
|
+
"static/js/359.c47779c2.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/359.c47779c2.chunk.js",
|
|
6
7
|
"static/js/255.279b1bca.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/255.279b1bca.chunk.js",
|
|
7
|
-
"static/js/371.
|
|
8
|
+
"static/js/371.ccef728f.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/371.ccef728f.chunk.js",
|
|
8
9
|
"static/js/737.ed53dec5.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/737.ed53dec5.chunk.js",
|
|
9
10
|
"static/js/460.97b02ba6.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/460.97b02ba6.chunk.js",
|
|
10
|
-
"static/js/868.
|
|
11
|
-
"static/js/
|
|
11
|
+
"static/js/868.9c03fa86.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/868.9c03fa86.chunk.js",
|
|
12
|
+
"static/js/547.f8830a9e.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/547.f8830a9e.chunk.js",
|
|
13
|
+
"static/js/343.54712c04.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/343.54712c04.chunk.js",
|
|
12
14
|
"static/js/682.c64ae291.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/682.c64ae291.chunk.js",
|
|
13
15
|
"static/js/711.6c22b7c7.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/711.6c22b7c7.chunk.js",
|
|
14
16
|
"static/js/437.d815f0c0.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/437.d815f0c0.chunk.js",
|
|
15
17
|
"static/js/690.e2a01dd2.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/690.e2a01dd2.chunk.js",
|
|
16
|
-
"static/js/
|
|
17
|
-
"static/js/
|
|
18
|
+
"static/js/42.a0526d16.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/42.a0526d16.chunk.js",
|
|
19
|
+
"static/js/648.b5b229e5.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/648.b5b229e5.chunk.js",
|
|
20
|
+
"static/js/162.58348adf.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/162.58348adf.chunk.js",
|
|
18
21
|
"static/js/610.40349d57.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/610.40349d57.chunk.js",
|
|
19
|
-
"static/css/
|
|
20
|
-
"static/js/
|
|
22
|
+
"static/css/61.03a48b17.chunk.css": "/.blocklet/proxy/blocklet-service/static/css/61.03a48b17.chunk.css",
|
|
23
|
+
"static/js/61.20fc6264.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/61.20fc6264.chunk.js",
|
|
21
24
|
"static/js/199.3d76c24b.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/199.3d76c24b.chunk.js",
|
|
22
|
-
"static/js/573.
|
|
25
|
+
"static/js/573.2052cb80.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/573.2052cb80.chunk.js",
|
|
23
26
|
"static/media/ubuntu-mono-all-400-normal.woff": "/.blocklet/proxy/blocklet-service/static/media/ubuntu-mono-all-400-normal.c879328bc62e9c68268f.woff",
|
|
24
27
|
"static/media/lato-all-400-normal.woff": "/.blocklet/proxy/blocklet-service/static/media/lato-all-400-normal.3dc1eff492ab1f598560.woff",
|
|
25
28
|
"static/media/iconify.cjs": "/.blocklet/proxy/blocklet-service/static/media/iconify.32b54549e843e448ee9b.cjs",
|
|
@@ -42,28 +45,31 @@
|
|
|
42
45
|
"router-template-styles/styles.css": "/.blocklet/proxy/blocklet-service/router-template-styles/styles.css",
|
|
43
46
|
"index.html": "/.blocklet/proxy/blocklet-service/index.html",
|
|
44
47
|
"main.632501d5.css.map": "/.blocklet/proxy/blocklet-service/static/css/main.632501d5.css.map",
|
|
45
|
-
"main.
|
|
46
|
-
"716.
|
|
48
|
+
"main.88eb4897.js.map": "/.blocklet/proxy/blocklet-service/static/js/main.88eb4897.js.map",
|
|
49
|
+
"716.874040d8.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/716.874040d8.chunk.js.map",
|
|
50
|
+
"359.c47779c2.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/359.c47779c2.chunk.js.map",
|
|
47
51
|
"255.279b1bca.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/255.279b1bca.chunk.js.map",
|
|
48
|
-
"371.
|
|
52
|
+
"371.ccef728f.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/371.ccef728f.chunk.js.map",
|
|
49
53
|
"737.ed53dec5.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/737.ed53dec5.chunk.js.map",
|
|
50
54
|
"460.97b02ba6.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/460.97b02ba6.chunk.js.map",
|
|
51
|
-
"868.
|
|
52
|
-
"
|
|
55
|
+
"868.9c03fa86.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/868.9c03fa86.chunk.js.map",
|
|
56
|
+
"547.f8830a9e.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/547.f8830a9e.chunk.js.map",
|
|
57
|
+
"343.54712c04.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/343.54712c04.chunk.js.map",
|
|
53
58
|
"682.c64ae291.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/682.c64ae291.chunk.js.map",
|
|
54
59
|
"711.6c22b7c7.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/711.6c22b7c7.chunk.js.map",
|
|
55
60
|
"437.d815f0c0.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/437.d815f0c0.chunk.js.map",
|
|
56
61
|
"690.e2a01dd2.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/690.e2a01dd2.chunk.js.map",
|
|
57
|
-
"
|
|
58
|
-
"
|
|
62
|
+
"42.a0526d16.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/42.a0526d16.chunk.js.map",
|
|
63
|
+
"648.b5b229e5.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/648.b5b229e5.chunk.js.map",
|
|
64
|
+
"162.58348adf.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/162.58348adf.chunk.js.map",
|
|
59
65
|
"610.40349d57.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/610.40349d57.chunk.js.map",
|
|
60
|
-
"
|
|
61
|
-
"
|
|
66
|
+
"61.03a48b17.chunk.css.map": "/.blocklet/proxy/blocklet-service/static/css/61.03a48b17.chunk.css.map",
|
|
67
|
+
"61.20fc6264.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/61.20fc6264.chunk.js.map",
|
|
62
68
|
"199.3d76c24b.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/199.3d76c24b.chunk.js.map",
|
|
63
|
-
"573.
|
|
69
|
+
"573.2052cb80.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/573.2052cb80.chunk.js.map"
|
|
64
70
|
},
|
|
65
71
|
"entrypoints": [
|
|
66
72
|
"static/css/main.632501d5.css",
|
|
67
|
-
"static/js/main.
|
|
73
|
+
"static/js/main.88eb4897.js"
|
|
68
74
|
]
|
|
69
75
|
}
|
package/build/index.html
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0"/><meta name="theme-color" content="#000000"/><title>Blocklet Service</title><script src=".well-known/service/api/env"></script><script src="/__blocklet__.js"></script><script defer="defer" src="/.blocklet/proxy/blocklet-service/static/js/main.
|
|
1
|
+
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0"/><meta name="theme-color" content="#000000"/><title>Blocklet Service</title><script src=".well-known/service/api/env"></script><script src="/__blocklet__.js"></script><script defer="defer" src="/.blocklet/proxy/blocklet-service/static/js/main.88eb4897.js"></script><link href="/.blocklet/proxy/blocklet-service/static/css/main.632501d5.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|