@abtnode/auth 1.16.11-beta-0ae58a71 → 1.16.11-beta-f9719c31
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/lib/auth.js +46 -2
- package/lib/launcher.js +90 -0
- package/lib/lost-passport.js +1 -1
- package/lib/passport.js +2 -2
- package/lib/server.js +151 -47
- package/lib/util/get-auth-method.js +9 -1
- package/package.json +13 -12
package/lib/auth.js
CHANGED
|
@@ -191,6 +191,10 @@ const messages = {
|
|
|
191
191
|
en: 'Missing chain host',
|
|
192
192
|
zh: '缺少链的端点地址',
|
|
193
193
|
},
|
|
194
|
+
missingLauncherSession: {
|
|
195
|
+
en: 'Missing launcherUrl and launcherSessionId',
|
|
196
|
+
zh: '缺少启动器会话参数',
|
|
197
|
+
},
|
|
194
198
|
invalidBlocklet: {
|
|
195
199
|
en: 'Invalid Blocklet',
|
|
196
200
|
zh: '无效的 Blocklet',
|
|
@@ -249,10 +253,14 @@ const messages = {
|
|
|
249
253
|
en: 'Blocklet Space NFT ID is required',
|
|
250
254
|
zh: '应用空间 NFT ID 是必须的',
|
|
251
255
|
},
|
|
252
|
-
|
|
256
|
+
nftAlreadyConsumed: {
|
|
253
257
|
en: 'This NFT has already been used',
|
|
254
258
|
zh: '该 NFT 已经被使用过了',
|
|
255
259
|
},
|
|
260
|
+
sessionAlreadyConsumed: {
|
|
261
|
+
en: 'This session has already been used',
|
|
262
|
+
zh: '该会话已经被使用过了',
|
|
263
|
+
},
|
|
256
264
|
nftAlreadyExpired: {
|
|
257
265
|
en: 'This NFT has expired',
|
|
258
266
|
zh: '该 NFT 已经过期',
|
|
@@ -896,7 +904,7 @@ const handleIssuePassportResponse = async ({
|
|
|
896
904
|
};
|
|
897
905
|
};
|
|
898
906
|
|
|
899
|
-
const getVCFromClaims =
|
|
907
|
+
const getVCFromClaims = ({ claims, challenge, trustedIssuers, vcTypes, locale = 'en', vcId }) => {
|
|
900
908
|
const credential = claims
|
|
901
909
|
.filter(Boolean) // FIXES: https://github.com/ArcBlock/did-connect/issues/74
|
|
902
910
|
.find((x) => x.type === 'verifiableCredential' && vcTypes.some((item) => x.item.includes(item)));
|
|
@@ -1114,8 +1122,44 @@ const setUserInfoHeaders = (req) => {
|
|
|
1114
1122
|
}
|
|
1115
1123
|
};
|
|
1116
1124
|
|
|
1125
|
+
/**
|
|
1126
|
+
* Defines properties for an asset.
|
|
1127
|
+
* @typedef {Object} AssetClaim
|
|
1128
|
+
* @property {string} acquireUrl URL to acquire the asset
|
|
1129
|
+
* @property {string} asset The asset identifier
|
|
1130
|
+
* @property {string} description A description of the asset
|
|
1131
|
+
* @property {Filter[]} [filters] Filters that can be used to acquire the asset
|
|
1132
|
+
* @property {Object} [meta] Metadata associated with the asset
|
|
1133
|
+
* @property {string[]} [mfaCode] Multi-factor authentication code for the asset
|
|
1134
|
+
* @property {boolean} optional Whether the asset is optional or required
|
|
1135
|
+
* @property {string} ownerDid DID of the asset owner
|
|
1136
|
+
* @property {string} ownerPk Public key of the asset owner
|
|
1137
|
+
* @property {string} ownerProof Ownership proof for the asset
|
|
1138
|
+
* @property {string} [tag] Tag or category of the asset
|
|
1139
|
+
* @property {'asset'} type The type of entity, always "asset"
|
|
1140
|
+
*/
|
|
1141
|
+
|
|
1142
|
+
/**
|
|
1143
|
+
* @typedef {Object} Filter
|
|
1144
|
+
* @property {string} acquireUrl URL to acquire the asset through this filter
|
|
1145
|
+
* @property {string} tag Tag associated with this filter
|
|
1146
|
+
* @property {string[]} trustedIssuers Issuers trusted for this filter
|
|
1147
|
+
*/
|
|
1148
|
+
|
|
1149
|
+
/**
|
|
1150
|
+
*
|
|
1151
|
+
* @description 校验 NFT
|
|
1152
|
+
* @param {{
|
|
1153
|
+
* claims: any[],
|
|
1154
|
+
* challenge: any,
|
|
1155
|
+
* chainHost: string,
|
|
1156
|
+
* locale: 'zh' | 'en',
|
|
1157
|
+
* }}
|
|
1158
|
+
* @returns {Promise<import('@ocap/client').AssetState>}
|
|
1159
|
+
*/
|
|
1117
1160
|
const verifyNFT = async ({ claims, challenge, chainHost, locale }) => {
|
|
1118
1161
|
const client = getChainClient(chainHost);
|
|
1162
|
+
/** @type {AssetClaim} */
|
|
1119
1163
|
const claim = claims.find((x) => x.type === 'asset');
|
|
1120
1164
|
if (!claim) {
|
|
1121
1165
|
throw new Error(messages.missingNftClaim[locale]);
|
package/lib/launcher.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const Joi = require('joi');
|
|
2
|
+
const axios = require('@abtnode/util/lib/axios');
|
|
3
|
+
const joinURL = require('url-join');
|
|
4
|
+
const pRetry = require('p-retry');
|
|
5
|
+
const { stableStringify } = require('@arcblock/vc');
|
|
6
|
+
const { toBase58 } = require('@ocap/util');
|
|
7
|
+
const getNodeWallet = require('@abtnode/util/lib/get-app-wallet');
|
|
8
|
+
const formatError = require('@abtnode/util/lib/format-error');
|
|
9
|
+
|
|
10
|
+
const logger = require('@abtnode/logger')('@abtnode/auth:launcher');
|
|
11
|
+
|
|
12
|
+
const schema = Joi.object({
|
|
13
|
+
launcherSessionId: Joi.string().required(),
|
|
14
|
+
launcherUrl: Joi.string().uri().required(),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// eslint-disable-next-line require-await
|
|
18
|
+
const doRequest = async (serverSk, { launcherUrl, pathname, payload, method = 'post' }) => {
|
|
19
|
+
if (!serverSk) {
|
|
20
|
+
throw new Error('serverSk is required to request launcher');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const wallet = getNodeWallet(serverSk);
|
|
24
|
+
|
|
25
|
+
const fn = async () => {
|
|
26
|
+
const { data } = await axios({
|
|
27
|
+
method,
|
|
28
|
+
url: joinURL(launcherUrl, pathname),
|
|
29
|
+
data: method !== 'get' ? payload : {},
|
|
30
|
+
params: method === 'get' ? payload : {},
|
|
31
|
+
headers: {
|
|
32
|
+
'x-server-sig': toBase58(wallet.sign(stableStringify(payload))),
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return data;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const delay = 10 * 1000;
|
|
40
|
+
return pRetry(fn, {
|
|
41
|
+
retries: 3,
|
|
42
|
+
minTimeout: delay,
|
|
43
|
+
maxTimeout: delay,
|
|
44
|
+
onFailedAttempt: (error) => {
|
|
45
|
+
logger.error('failed to call launcher', { error, launcherUrl, pathname, payload });
|
|
46
|
+
// Exclude retrying for 4XX response codes
|
|
47
|
+
if (error.response && error.response.status >= 400 && error.response.status < 500) {
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const getLauncherSession = async (serverSk, params) => {
|
|
55
|
+
const { error } = schema.validate(params);
|
|
56
|
+
if (error) {
|
|
57
|
+
return { error: formatError(error) };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const { launcherSessionId, launcherUrl } = params;
|
|
61
|
+
try {
|
|
62
|
+
const result = await doRequest(serverSk, {
|
|
63
|
+
launcherUrl,
|
|
64
|
+
pathname: `/api/launches/${launcherSessionId}`,
|
|
65
|
+
payload: {},
|
|
66
|
+
method: 'get',
|
|
67
|
+
});
|
|
68
|
+
return { error: '', launcherSession: result.launch };
|
|
69
|
+
} catch (err) {
|
|
70
|
+
return { error: formatError(err) };
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const getLauncherUser = async (serverSk, params) => {
|
|
75
|
+
const { launcherSessionId, launcherUrl } = params;
|
|
76
|
+
const result = await doRequest(serverSk, {
|
|
77
|
+
launcherUrl,
|
|
78
|
+
pathname: `/api/launches/${launcherSessionId}/user`,
|
|
79
|
+
payload: {},
|
|
80
|
+
method: 'get',
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return result.user;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
module.exports = {
|
|
87
|
+
doRequest,
|
|
88
|
+
getLauncherSession,
|
|
89
|
+
getLauncherUser,
|
|
90
|
+
};
|
package/lib/lost-passport.js
CHANGED
package/lib/passport.js
CHANGED
|
@@ -29,7 +29,7 @@ const validatePassport = (d) => {
|
|
|
29
29
|
return value;
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
-
const createPassport = async ({ name, node, locale = 'en', teamDid, endpoint, role: inputRole } = {}) => {
|
|
32
|
+
const createPassport = async ({ name, node, locale = 'en', teamDid, endpoint, role: inputRole = null } = {}) => {
|
|
33
33
|
const passportNotFound = {
|
|
34
34
|
en: (x) => `The passport was not found: ${x}`,
|
|
35
35
|
zh: (x) => `未找到通行证: ${x}`,
|
|
@@ -66,7 +66,7 @@ const createPassportVC = ({
|
|
|
66
66
|
passport,
|
|
67
67
|
endpoint,
|
|
68
68
|
types = [],
|
|
69
|
-
tag,
|
|
69
|
+
tag = '',
|
|
70
70
|
ownerProfile,
|
|
71
71
|
preferredColor,
|
|
72
72
|
expirationDate,
|
package/lib/server.js
CHANGED
|
@@ -47,6 +47,7 @@ const {
|
|
|
47
47
|
createUserPassport,
|
|
48
48
|
getPassportClaimUrl,
|
|
49
49
|
} = require('./passport');
|
|
50
|
+
const { getLauncherSession } = require('./launcher');
|
|
50
51
|
const logger = require('./logger');
|
|
51
52
|
|
|
52
53
|
const secret = process.env.ABT_NODE_SESSION_SECRET;
|
|
@@ -245,6 +246,35 @@ const authenticateByNFT = async ({ node, claims, userDid, challenge, locale, isA
|
|
|
245
246
|
};
|
|
246
247
|
};
|
|
247
248
|
|
|
249
|
+
const authenticateByLauncher = async ({ node, claims, launcherSessionId, launcherUrl, chainHost }) => {
|
|
250
|
+
const info = await node.getNodeInfo();
|
|
251
|
+
const { error, launcherSession } = await getLauncherSession(info.sk, { launcherSessionId, launcherUrl });
|
|
252
|
+
if (error) {
|
|
253
|
+
throw new Error(error);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const claim = claims.find((x) => x.type === 'keyPair');
|
|
257
|
+
return {
|
|
258
|
+
role: SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER,
|
|
259
|
+
teamDid: info.did,
|
|
260
|
+
user: {
|
|
261
|
+
did: claim.userDid,
|
|
262
|
+
pk: claim.userPk,
|
|
263
|
+
},
|
|
264
|
+
extra: {
|
|
265
|
+
controller: {
|
|
266
|
+
nftId: launcherSession.nftDid,
|
|
267
|
+
nftOwner: launcherSession.userDid,
|
|
268
|
+
chainHost,
|
|
269
|
+
appMaxCount: 1,
|
|
270
|
+
launcherUrl,
|
|
271
|
+
launcherSessionId,
|
|
272
|
+
ownerDid: claim.userDid,
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
|
|
248
278
|
const authenticateBySession = async ({ node, userDid, locale, allowedRoles = ['owner', 'admin', 'member'] }) => {
|
|
249
279
|
const info = await node.getNodeInfo();
|
|
250
280
|
const user = await getUser(node, info.did, userDid);
|
|
@@ -297,7 +327,7 @@ const getAuthVcClaim =
|
|
|
297
327
|
|
|
298
328
|
const getAuthNFTClaim =
|
|
299
329
|
({ node }) =>
|
|
300
|
-
|
|
330
|
+
({ extraParams: { locale, launchType, nftId }, context: { didwallet } }) => {
|
|
301
331
|
checkWalletVersion({ didwallet, locale });
|
|
302
332
|
if (launchType === 'serverless') {
|
|
303
333
|
if (!nftId) {
|
|
@@ -535,7 +565,7 @@ const getOwnershipNFTClaim = async (node, locale) => {
|
|
|
535
565
|
};
|
|
536
566
|
};
|
|
537
567
|
|
|
538
|
-
const getServerlessNFTClaim =
|
|
568
|
+
const getServerlessNFTClaim = (nftId, locale) => {
|
|
539
569
|
return {
|
|
540
570
|
description: messages.requestBlockletSpaceNFT[locale],
|
|
541
571
|
address: nftId,
|
|
@@ -549,10 +579,12 @@ const ensureBlockletPermission = async ({
|
|
|
549
579
|
claims,
|
|
550
580
|
challenge,
|
|
551
581
|
locale,
|
|
552
|
-
blocklet,
|
|
553
|
-
isAuth,
|
|
554
582
|
chainHost,
|
|
583
|
+
isAuth = false,
|
|
584
|
+
blocklet = null,
|
|
555
585
|
allowedRoles = ['owner', 'admin', 'member'],
|
|
586
|
+
launcherUrl = '',
|
|
587
|
+
launcherSessionId = '',
|
|
556
588
|
}) => {
|
|
557
589
|
let result;
|
|
558
590
|
if (authMethod === 'vc') {
|
|
@@ -576,6 +608,14 @@ const ensureBlockletPermission = async ({
|
|
|
576
608
|
isAuth,
|
|
577
609
|
chainHost,
|
|
578
610
|
});
|
|
611
|
+
} else if (authMethod === 'launcher') {
|
|
612
|
+
result = await authenticateByLauncher({
|
|
613
|
+
node,
|
|
614
|
+
claims,
|
|
615
|
+
launcherUrl,
|
|
616
|
+
launcherSessionId,
|
|
617
|
+
chainHost,
|
|
618
|
+
});
|
|
579
619
|
} else {
|
|
580
620
|
result = await authenticateBySession({
|
|
581
621
|
node,
|
|
@@ -597,12 +637,12 @@ const ensureBlockletPermission = async ({
|
|
|
597
637
|
const createLaunchBlockletHandler =
|
|
598
638
|
(node, authMethod) =>
|
|
599
639
|
async ({ claims, challenge, userDid, updateSession, req, didwallet, extraParams }) => {
|
|
600
|
-
const { locale, blockletMetaUrl, title, description, chainHost } = extraParams;
|
|
640
|
+
const { locale, blockletMetaUrl, title, description, chainHost, launcherSessionId, launcherUrl } = extraParams;
|
|
601
641
|
logger.info('createLaunchBlockletHandler', extraParams);
|
|
602
642
|
|
|
603
|
-
const
|
|
604
|
-
if (!
|
|
605
|
-
logger.error('
|
|
643
|
+
const claim = claims.find((x) => x.type === 'keyPair');
|
|
644
|
+
if (!claim) {
|
|
645
|
+
logger.error('keyPair claim must be provided');
|
|
606
646
|
throw new Error(messages.missingKeyPair[locale]);
|
|
607
647
|
}
|
|
608
648
|
|
|
@@ -616,6 +656,11 @@ const createLaunchBlockletHandler =
|
|
|
616
656
|
throw new Error(messages.missingChainHost[locale]);
|
|
617
657
|
}
|
|
618
658
|
|
|
659
|
+
if (authMethod === 'launcher' && !(launcherSessionId && launcherUrl)) {
|
|
660
|
+
logger.error('launcherUrl and launcherSessionId must be provided');
|
|
661
|
+
throw new Error(messages.missingLauncherSession[locale]);
|
|
662
|
+
}
|
|
663
|
+
|
|
619
664
|
let blocklet;
|
|
620
665
|
let blockletWalletType;
|
|
621
666
|
if (blockletMetaUrl) {
|
|
@@ -644,13 +689,13 @@ const createLaunchBlockletHandler =
|
|
|
644
689
|
claims,
|
|
645
690
|
challenge,
|
|
646
691
|
locale,
|
|
647
|
-
isAuth: false,
|
|
648
692
|
chainHost,
|
|
649
693
|
blocklet,
|
|
694
|
+
launcherSessionId,
|
|
695
|
+
launcherUrl,
|
|
650
696
|
});
|
|
651
697
|
|
|
652
698
|
let controller;
|
|
653
|
-
|
|
654
699
|
let sessionToken = '';
|
|
655
700
|
if (authMethod === 'vc') {
|
|
656
701
|
sessionToken = createAuthToken({
|
|
@@ -660,52 +705,71 @@ const createLaunchBlockletHandler =
|
|
|
660
705
|
secret,
|
|
661
706
|
expiresIn: LAUNCH_BLOCKLET_TOKEN_EXPIRE,
|
|
662
707
|
});
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
}
|
|
708
|
+
} else if (role === SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER) {
|
|
709
|
+
// launch with serverless nft or launcher session
|
|
710
|
+
controller = extra.controller;
|
|
711
|
+
sessionToken = createBlockletControllerAuthToken({
|
|
712
|
+
did: userDid,
|
|
713
|
+
role,
|
|
714
|
+
controller,
|
|
715
|
+
secret,
|
|
716
|
+
expiresIn: EXTERNAL_LAUNCH_BLOCKLET_TOKEN_EXPIRE,
|
|
717
|
+
});
|
|
718
|
+
} else if (authMethod === 'nft') {
|
|
719
|
+
// launch with server nft
|
|
720
|
+
sessionToken = createAuthTokenByOwnershipNFT({
|
|
721
|
+
did: userDid,
|
|
722
|
+
role,
|
|
723
|
+
secret,
|
|
724
|
+
expiresIn: LAUNCH_BLOCKLET_TOKEN_EXPIRE,
|
|
725
|
+
});
|
|
682
726
|
}
|
|
683
727
|
|
|
684
728
|
if (sessionToken) {
|
|
685
729
|
await updateSession({ sessionToken }, true);
|
|
686
730
|
}
|
|
687
731
|
|
|
688
|
-
const appSk = toHex(
|
|
689
|
-
const
|
|
690
|
-
|
|
732
|
+
const appSk = toHex(claim.secret);
|
|
733
|
+
const wallet = getApplicationWallet(appSk, undefined, blockletWalletType);
|
|
734
|
+
const appDid = wallet.address;
|
|
735
|
+
const { id: sessionId } = await node.startSession({
|
|
736
|
+
data: {
|
|
737
|
+
appDid,
|
|
738
|
+
userDid,
|
|
739
|
+
ownerDid: claim.userDid,
|
|
740
|
+
ownerPk: claim.userPk,
|
|
741
|
+
lastLoginIp: get(req, 'headers[x-real-ip]') || '',
|
|
742
|
+
context: formatContext(req),
|
|
743
|
+
locale,
|
|
744
|
+
launcherSessionId,
|
|
745
|
+
launcherUrl,
|
|
746
|
+
},
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
await updateSession({ appDid, sessionId });
|
|
691
750
|
|
|
751
|
+
// FIXME: @zhenqiang do we still need this here? since the appDid changes each time
|
|
692
752
|
if (blocklet) {
|
|
693
753
|
// 检查是否已安装,这里不做升级的处理
|
|
694
754
|
const existedBlocklet = await node.getBlocklet({ did: appDid });
|
|
695
|
-
|
|
696
|
-
// 如果是 serverless, 并且已经消费过了,但是没有安装,则抛出异常
|
|
697
|
-
if (!existedBlocklet && role === SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER && isNFTConsumed(nft)) {
|
|
698
|
-
throw new Error(messages.nftAlreadyConsumed[locale]);
|
|
699
|
-
}
|
|
700
|
-
|
|
701
755
|
if (existedBlocklet) {
|
|
702
756
|
await updateSession({ isInstalled: true });
|
|
703
757
|
logger.info('blocklet already exists', { appDid });
|
|
704
758
|
return;
|
|
705
759
|
}
|
|
760
|
+
|
|
761
|
+
// 如果是 serverless, 并且已经消费过了,但是没有安装,则抛出异常
|
|
762
|
+
if (role === SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER) {
|
|
763
|
+
if (authMethod === 'nft' && isNFTConsumed(nft)) {
|
|
764
|
+
throw new Error(messages.nftAlreadyConsumed[locale]);
|
|
765
|
+
}
|
|
766
|
+
if (authMethod === 'launcher' && (await node.isLauncherSessionConsumed({ launcherUrl, launcherSessionId }))) {
|
|
767
|
+
throw new Error(messages.sessionAlreadyConsumed[locale]);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
706
770
|
}
|
|
707
771
|
|
|
708
|
-
logger.info('start install blocklet', { blockletMetaUrl, title, description });
|
|
772
|
+
logger.info('start install blocklet', { blockletMetaUrl, title, description, controller });
|
|
709
773
|
await node.installBlocklet(
|
|
710
774
|
{
|
|
711
775
|
url: blockletMetaUrl,
|
|
@@ -713,7 +777,6 @@ const createLaunchBlockletHandler =
|
|
|
713
777
|
description,
|
|
714
778
|
appSk,
|
|
715
779
|
skSource: didwallet?.version ? `${didwallet.os}-wallet-v${didwallet.version}` : '',
|
|
716
|
-
delay: 1000 * 4, // delay 4 seconds to download, wait for ws connection from frontend
|
|
717
780
|
downloadTokenList: extraParams?.previousWorkflowData?.downloadTokenList,
|
|
718
781
|
controller: role === SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER ? controller : null,
|
|
719
782
|
},
|
|
@@ -754,9 +817,9 @@ const createRotateKeyPairHandler =
|
|
|
754
817
|
const { locale, appDid } = extraParams;
|
|
755
818
|
logger.info('createRotateKeyPairHandler', extraParams);
|
|
756
819
|
|
|
757
|
-
const
|
|
758
|
-
if (!
|
|
759
|
-
logger.error('
|
|
820
|
+
const claim = claims.find((x) => x.type === 'keyPair');
|
|
821
|
+
if (!claim) {
|
|
822
|
+
logger.error('keyPair claim must be provided');
|
|
760
823
|
throw new Error(messages.missingKeyPair[locale]);
|
|
761
824
|
}
|
|
762
825
|
|
|
@@ -783,16 +846,47 @@ const createRotateKeyPairHandler =
|
|
|
783
846
|
await node.configBlocklet(
|
|
784
847
|
{
|
|
785
848
|
did: blocklet.meta.did,
|
|
786
|
-
configs: [{ key: 'BLOCKLET_APP_SK', value: toHex(
|
|
849
|
+
configs: [{ key: 'BLOCKLET_APP_SK', value: toHex(claim.secret), secure: true }],
|
|
787
850
|
skipHook: true,
|
|
788
851
|
},
|
|
789
852
|
formatContext(Object.assign(req, { user: { did: userDid, fullName: 'Owner', role: 'owner' } }))
|
|
790
853
|
);
|
|
791
854
|
};
|
|
792
855
|
|
|
856
|
+
const handleRestoreByLauncherSession = async ({ node, userDid, updateSession, extraParams }) => {
|
|
857
|
+
const { chainHost, locale, launcherUrl, launcherSessionId } = extraParams;
|
|
858
|
+
if (await node.isLauncherSessionConsumed({ launcherUrl, launcherSessionId })) {
|
|
859
|
+
throw new Error(messages.sessionAlreadyConsumed[locale]);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
const { launcherSession } = await node.getLauncherSession({ launcherUrl, launcherSessionId, external: false });
|
|
863
|
+
const controller = {
|
|
864
|
+
nftId: launcherSession.nftDid,
|
|
865
|
+
nftOwner: launcherSession.userDid,
|
|
866
|
+
chainHost,
|
|
867
|
+
appMaxCount: 1,
|
|
868
|
+
launcherUrl,
|
|
869
|
+
launcherSessionId,
|
|
870
|
+
ownerDid: userDid, // FIXME: @wangshijun is this incorrect
|
|
871
|
+
};
|
|
872
|
+
|
|
873
|
+
const sessionToken = createBlockletControllerAuthToken({
|
|
874
|
+
did: userDid,
|
|
875
|
+
role: SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER,
|
|
876
|
+
controller,
|
|
877
|
+
secret,
|
|
878
|
+
expiresIn: EXTERNAL_LAUNCH_BLOCKLET_TOKEN_EXPIRE,
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
await updateSession({ sessionToken }, true);
|
|
882
|
+
|
|
883
|
+
return controller;
|
|
884
|
+
};
|
|
885
|
+
|
|
793
886
|
const createRestoreByNftHandler =
|
|
794
887
|
(node, authMethod) =>
|
|
795
|
-
async ({ claims, challenge, userDid, updateSession, extraParams
|
|
888
|
+
async ({ claims, challenge, userDid, updateSession, extraParams }) => {
|
|
889
|
+
const { chainHost, locale, launcherUrl, launcherSessionId } = extraParams;
|
|
796
890
|
const { role, extra } = await ensureBlockletPermission({
|
|
797
891
|
authMethod,
|
|
798
892
|
node,
|
|
@@ -800,8 +894,9 @@ const createRestoreByNftHandler =
|
|
|
800
894
|
claims,
|
|
801
895
|
challenge,
|
|
802
896
|
locale,
|
|
803
|
-
isAuth: false,
|
|
804
897
|
chainHost,
|
|
898
|
+
launcherUrl,
|
|
899
|
+
launcherSessionId,
|
|
805
900
|
});
|
|
806
901
|
|
|
807
902
|
let sessionToken = '';
|
|
@@ -838,7 +933,7 @@ const createRestoreByNftHandler =
|
|
|
838
933
|
|
|
839
934
|
const createServerlessInstallGuard = (node) => {
|
|
840
935
|
return async ({ extraParams }) => {
|
|
841
|
-
const { locale, blockletMetaUrl } = extraParams;
|
|
936
|
+
const { locale, blockletMetaUrl, launcherUrl, launcherSessionId } = extraParams;
|
|
842
937
|
const [info, blocklet] = await Promise.all([
|
|
843
938
|
node.getNodeInfo(),
|
|
844
939
|
blockletMetaUrl ? node.getBlockletMetaFromUrl({ url: blockletMetaUrl, checkPrice: true }) : null,
|
|
@@ -851,6 +946,13 @@ const createServerlessInstallGuard = (node) => {
|
|
|
851
946
|
throw new Error(messages.notSupported[locale]);
|
|
852
947
|
}
|
|
853
948
|
}
|
|
949
|
+
|
|
950
|
+
// check if launcher session consumed
|
|
951
|
+
if (launcherUrl && launcherSessionId) {
|
|
952
|
+
if (await node.isLauncherSessionConsumed({ launcherUrl, launcherSessionId })) {
|
|
953
|
+
throw new Error(messages.sessionAlreadyConsumed[locale]);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
854
956
|
};
|
|
855
957
|
};
|
|
856
958
|
|
|
@@ -860,6 +962,7 @@ module.exports = {
|
|
|
860
962
|
authenticateByVc,
|
|
861
963
|
authenticateByNFT,
|
|
862
964
|
authenticateBySession,
|
|
965
|
+
authenticateByLauncher,
|
|
863
966
|
getRotateKeyPairClaims,
|
|
864
967
|
getOwnershipNFTClaim,
|
|
865
968
|
getLaunchBlockletClaims,
|
|
@@ -876,4 +979,5 @@ module.exports = {
|
|
|
876
979
|
getAuthPrincipalForMigrateAppToV2,
|
|
877
980
|
getAuthPrincipalForTransferAppOwnerShip,
|
|
878
981
|
createServerlessInstallGuard,
|
|
982
|
+
handleRestoreByLauncherSession,
|
|
879
983
|
};
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
const get = require('lodash/get');
|
|
2
2
|
|
|
3
|
-
const getServerAuthMethod = (info, type) => {
|
|
3
|
+
const getServerAuthMethod = (info, type, launcherSessionId = '', authorized = false) => {
|
|
4
|
+
if (launcherSessionId) {
|
|
5
|
+
return 'launcher';
|
|
6
|
+
}
|
|
7
|
+
|
|
4
8
|
if (type === 'serverless') {
|
|
5
9
|
return 'nft';
|
|
6
10
|
}
|
|
7
11
|
|
|
12
|
+
if (authorized) {
|
|
13
|
+
return 'session';
|
|
14
|
+
}
|
|
15
|
+
|
|
8
16
|
if (info.initialized) {
|
|
9
17
|
return 'vc';
|
|
10
18
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.16.11-beta-
|
|
6
|
+
"version": "1.16.11-beta-f9719c31",
|
|
7
7
|
"description": "Simple lib to manage auth in ABT Node",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -20,16 +20,17 @@
|
|
|
20
20
|
"author": "linchen <linchen1987@foxmail.com> (http://github.com/linchen1987)",
|
|
21
21
|
"license": "Apache-2.0",
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@abtnode/constant": "1.16.11-beta-
|
|
24
|
-
"@abtnode/logger": "1.16.11-beta-
|
|
25
|
-
"@abtnode/util": "1.16.11-beta-
|
|
26
|
-
"@arcblock/did": "1.18.
|
|
27
|
-
"@arcblock/vc": "1.18.
|
|
28
|
-
"@blocklet/constant": "1.16.11-beta-
|
|
29
|
-
"@blocklet/meta": "1.16.11-beta-
|
|
30
|
-
"@ocap/mcrypto": "1.18.
|
|
31
|
-
"@ocap/util": "1.18.
|
|
32
|
-
"@ocap/wallet": "1.18.
|
|
23
|
+
"@abtnode/constant": "1.16.11-beta-f9719c31",
|
|
24
|
+
"@abtnode/logger": "1.16.11-beta-f9719c31",
|
|
25
|
+
"@abtnode/util": "1.16.11-beta-f9719c31",
|
|
26
|
+
"@arcblock/did": "1.18.84",
|
|
27
|
+
"@arcblock/vc": "1.18.84",
|
|
28
|
+
"@blocklet/constant": "1.16.11-beta-f9719c31",
|
|
29
|
+
"@blocklet/meta": "1.16.11-beta-f9719c31",
|
|
30
|
+
"@ocap/mcrypto": "1.18.84",
|
|
31
|
+
"@ocap/util": "1.18.84",
|
|
32
|
+
"@ocap/wallet": "1.18.84",
|
|
33
|
+
"fs-extra": "^10.1.0",
|
|
33
34
|
"joi": "17.7.0",
|
|
34
35
|
"jsonwebtoken": "^9.0.0",
|
|
35
36
|
"lodash": "^4.17.21",
|
|
@@ -41,5 +42,5 @@
|
|
|
41
42
|
"devDependencies": {
|
|
42
43
|
"jest": "^27.5.1"
|
|
43
44
|
},
|
|
44
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "2d88c5df1a414e5b6a8d575984e1940de7573976"
|
|
45
46
|
}
|