@abtnode/auth 1.8.37 → 1.8.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/lib/auth.js +60 -16
  2. package/lib/server.js +29 -31
  3. package/package.json +14 -14
package/lib/auth.js CHANGED
@@ -15,6 +15,7 @@ const {
15
15
  ROLES,
16
16
  NODE_DATA_DIR_NAME,
17
17
  AUTH_CERT_TYPE,
18
+ WELLKNOWN_BLOCKLET_ADMIN_PATH,
18
19
  } = require('@abtnode/constant');
19
20
  const axios = require('@abtnode/util/lib/axios');
20
21
  const { extractUserAvatar, parseUserAvatar } = require('@abtnode/util/lib/user-avatar');
@@ -201,6 +202,22 @@ const messages = {
201
202
  en: 'Please provide server ownership NFT',
202
203
  zh: '请提供节点所有权 NFT',
203
204
  },
205
+ requestServerlessNFT: {
206
+ en: 'Please provide serverless NFT',
207
+ zh: '请提供无服务 NFT',
208
+ },
209
+ serverlessNftIdRequired: {
210
+ en: 'Serverless NFT ID is required',
211
+ zh: '无服务 NFT ID 是必须的',
212
+ },
213
+ nftAlreadyConsume: {
214
+ en: 'This NFT has already been used',
215
+ zh: '该 NFT 已经被使用过了',
216
+ },
217
+ nftAlreadyExpired: {
218
+ en: 'This NFT has expired',
219
+ zh: '该 NFT 已经过期',
220
+ },
204
221
  missingNftClaim: {
205
222
  en: 'Ownership NFT not provided',
206
223
  zh: '节点所有权 NFT 必须提供',
@@ -237,6 +254,11 @@ const messages = {
237
254
 
238
255
  const PASSPORT_STATUS_KEY = 'passport-status';
239
256
 
257
+ const TEAM_TYPE = {
258
+ NODE: 'node',
259
+ BLOCKLET: 'blocklet',
260
+ };
261
+
240
262
  const getPassportStatusEndpoint = ({ baseUrl, userDid, teamDid }) => {
241
263
  if (!baseUrl) {
242
264
  return null;
@@ -279,7 +301,7 @@ const getTeamInfo = async ({ node, nodeInfo, teamDid }) => {
279
301
  name = nodeInfo.name;
280
302
  description = nodeInfo.description;
281
303
  wallet = getNodeWallet(nodeInfo.sk);
282
- type = 'node';
304
+ type = TEAM_TYPE.NODE;
283
305
  passportColor = 'default';
284
306
  owner = nodeInfo.nodeOwner;
285
307
  dataDir = path.join(node.dataDirs.data, NODE_DATA_DIR_NAME);
@@ -290,7 +312,7 @@ const getTeamInfo = async ({ node, nodeInfo, teamDid }) => {
290
312
  description = blockletInfo.description;
291
313
  wallet = blockletInfo.wallet;
292
314
  passportColor = blockletInfo.passportColor;
293
- type = 'blocklet';
315
+ type = TEAM_TYPE.BLOCKLET;
294
316
  owner = get(blocklet, 'settings.owner');
295
317
  dataDir = blocklet.env.dataDir;
296
318
  }
@@ -493,7 +515,7 @@ const handleInvitationResponse = async ({
493
515
  preferredColor: passportColor,
494
516
  };
495
517
 
496
- if (issuerType === 'node') {
518
+ if (issuerType === TEAM_TYPE.NODE) {
497
519
  vcParams.tag = nodeInfo.did;
498
520
  }
499
521
 
@@ -731,7 +753,7 @@ const handleIssuePassportResponse = async ({
731
753
  preferredColor: passportColor,
732
754
  };
733
755
 
734
- if (issuerType === 'node') {
756
+ if (issuerType === TEAM_TYPE.NODE) {
735
757
  vcParams.tag = nodeInfo.did;
736
758
  }
737
759
 
@@ -763,7 +785,7 @@ const handleIssuePassportResponse = async ({
763
785
  node
764
786
  );
765
787
 
766
- if (name === ROLES.OWNER && issuerType === 'blocklet') {
788
+ if (name === ROLES.OWNER && issuerType === TEAM_TYPE.BLOCKLET) {
767
789
  logger.info('Bind owner for blocklet', { teamDid, userDid });
768
790
  await node.setBlockletInitialized({ did: teamDid, owner: { did: userDid, pk: userPk } });
769
791
  }
@@ -846,8 +868,14 @@ const getPassportStatus = async ({ node, teamDid, userDid, vcId, locale = 'en' }
846
868
  const { wallet: issuerWallet, name: issuerName, type: issuerType } = await getTeamInfo({ node, nodeInfo, teamDid });
847
869
 
848
870
  const actionLabel = {
849
- zh: `${issuerType === 'node' ? '管理节点' : '查看 Blocklet'}`,
850
- en: `${issuerType === 'node' ? 'Manage Node' : 'View Blocklet'}`,
871
+ open: {
872
+ zh: `${issuerType === TEAM_TYPE.NODE ? '管理节点' : '查看 Blocklet'}`,
873
+ en: `${issuerType === TEAM_TYPE.NODE ? 'Manage Node' : 'View Blocklet'}`,
874
+ },
875
+ manage: {
876
+ zh: '管理 Blocklet',
877
+ en: 'Manage Blocklet',
878
+ },
851
879
  };
852
880
 
853
881
  const user = await node.getUser({ teamDid, user: { did: userDid } });
@@ -875,18 +903,33 @@ const getPassportStatus = async ({ node, teamDid, userDid, vcId, locale = 'en' }
875
903
 
876
904
  const actionList = [];
877
905
  if (passport.endpoint) {
906
+ const claims = [];
907
+
908
+ // open blocklet
909
+ claims.push({
910
+ id: passport.endpoint,
911
+ type: 'navigate',
912
+ name: issuerType === TEAM_TYPE.NODE ? 'open-node' : 'open-blocklet',
913
+ scope: 'public',
914
+ label: actionLabel.open[locale],
915
+ });
916
+
917
+ // manage blocklet
918
+ const { name } = passport;
919
+ if ([ROLES.OWNER, ROLES.ADMIN].includes(name) && issuerType === TEAM_TYPE.BLOCKLET) {
920
+ claims.push({
921
+ id: joinUrl(passport.endpoint, WELLKNOWN_BLOCKLET_ADMIN_PATH),
922
+ type: 'navigate',
923
+ name: 'open-blocklet-admin',
924
+ scope: 'public',
925
+ label: actionLabel.manage[locale],
926
+ });
927
+ }
928
+
878
929
  actionList.push(
879
930
  ...createCredentialList({
880
931
  issuer,
881
- claims: [
882
- {
883
- id: passport.endpoint,
884
- type: 'navigate',
885
- name: issuerType === 'node' ? 'open-node' : 'open-blocklet',
886
- scope: 'public',
887
- label: actionLabel[locale],
888
- },
889
- ],
932
+ claims,
890
933
  })
891
934
  );
892
935
  }
@@ -976,4 +1019,5 @@ module.exports = {
976
1019
  validatePassportStatus,
977
1020
  setUserInfoHeaders,
978
1021
  createBlockletControllerAuthToken,
1022
+ TEAM_TYPE,
979
1023
  };
package/lib/server.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const get = require('lodash/get');
2
2
  const isEmpty = require('lodash/isEmpty');
3
3
  const last = require('lodash/last');
4
+ const { isNFTExpired, isNFTConsumed } = require('@abtnode/util/lib/nft');
4
5
  const Client = require('@ocap/client');
5
6
  const { fromPublicKey } = require('@ocap/wallet');
6
7
  const { fromBase58, toAddress } = require('@ocap/util');
@@ -12,7 +13,7 @@ const {
12
13
  VC_TYPE_NODE_PASSPORT,
13
14
  NFT_TYPE_SERVER_OWNERSHIP,
14
15
  SERVER_ROLES,
15
- NFT_TYPE_SERVER_USAGE_CREDENTIAL,
16
+ NFT_TYPE_SERVERLESS,
16
17
  } = require('@abtnode/constant');
17
18
  const { toExternalBlocklet } = require('@blocklet/meta/lib/did');
18
19
  const {
@@ -55,16 +56,6 @@ const getTrustedIssuers = (nodeInfo) => {
55
56
  return [nodeInfo.did, ...trustedPassports].filter(Boolean);
56
57
  };
57
58
 
58
- const getExternalPassport = async (nft) => {
59
- const data = JSON.parse(nft.data.value);
60
-
61
- return {
62
- name: SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER,
63
- appMaxCount: data.appMaxCount || 1,
64
- expireDate: last(data.expirationDate),
65
- };
66
- };
67
-
68
59
  const authenticateByVc = async ({
69
60
  node,
70
61
  locale,
@@ -153,7 +144,7 @@ const authenticateByVc = async ({
153
144
  return { role, user, teamDid, passport };
154
145
  };
155
146
 
156
- const authenticateByNFT = async ({ node, claims, userDid, challenge, locale }) => {
147
+ const authenticateByNFT = async ({ node, claims, userDid, challenge, locale, isAuth }) => {
157
148
  const info = await node.getNodeInfo();
158
149
  const client = new Client(info.launcher.chainHost);
159
150
 
@@ -193,8 +184,16 @@ const authenticateByNFT = async ({ node, claims, userDid, challenge, locale }) =
193
184
  throw new Error(messages.invalidNftIssuer[locale]);
194
185
  }
195
186
 
196
- if (state.tags.includes(NFT_TYPE_SERVER_USAGE_CREDENTIAL) && state.tags.includes(info.did)) {
197
- const passport = await getExternalPassport(state);
187
+ if (state.tags.includes(NFT_TYPE_SERVERLESS)) {
188
+ state.data.value = JSON.parse(state.data.value);
189
+
190
+ if (!isAuth && isNFTConsumed(state)) {
191
+ throw new Error(messages.nftAlreadyConsume[locale]);
192
+ }
193
+
194
+ if (!isAuth && isNFTExpired(state)) {
195
+ throw new Error(messages.nftAlreadyExpired[locale]);
196
+ }
198
197
 
199
198
  return {
200
199
  role: SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER,
@@ -202,8 +201,7 @@ const authenticateByNFT = async ({ node, claims, userDid, challenge, locale }) =
202
201
  controller: {
203
202
  nftId: address,
204
203
  nftOwner: state.owner,
205
- appMaxCount: passport.appMaxCount,
206
- expireDate: passport.expireDate,
204
+ appMaxCount: state.data.value.appMaxCount || 1,
207
205
  },
208
206
  },
209
207
  user: {
@@ -263,10 +261,14 @@ const getAuthVcClaim =
263
261
 
264
262
  const getAuthNFTClaim =
265
263
  ({ node }) =>
266
- async ({ extraParams: { locale, launchType }, context: { didwallet } }) => {
264
+ async ({ extraParams: { locale, launchType, nftId }, context: { didwallet } }) => {
267
265
  checkWalletVersion({ didwallet, locale });
268
266
  if (launchType === 'serverless') {
269
- return getUsageCredentialNFTClaim(node, locale);
267
+ if (!nftId) {
268
+ throw new Error(messages.serverlessNftIdRequired[locale]);
269
+ }
270
+
271
+ return getServerlessNFTClaim(node, nftId, locale);
270
272
  }
271
273
 
272
274
  return getOwnershipNFTClaim(node, locale);
@@ -318,7 +320,7 @@ const getOwnershipNFTClaim = async (node, locale) => {
318
320
  };
319
321
  };
320
322
 
321
- const getUsageCredentialNFTClaim = async (node, locale) => {
323
+ const getServerlessNFTClaim = async (node, nftId, locale) => {
322
324
  const info = await node.getNodeInfo();
323
325
  if (!info.ownerNft || !info.ownerNft.issuer) {
324
326
  throw new Error(messages.noNft[locale]);
@@ -331,13 +333,13 @@ const getUsageCredentialNFTClaim = async (node, locale) => {
331
333
  }
332
334
 
333
335
  return {
334
- description: messages.requestNft[locale],
336
+ description: messages.requestServerlessNFT[locale],
335
337
  trustedIssuers: [info.ownerNft.issuer],
336
- tag: info.did,
338
+ address: nftId,
337
339
  };
338
340
  };
339
341
 
340
- const ensureBlockletPermission = async ({ authMethod, node, userDid, claims, challenge, locale, blocklet }) => {
342
+ const ensureBlockletPermission = async ({ authMethod, node, userDid, claims, challenge, locale, blocklet, isAuth }) => {
341
343
  let result;
342
344
  if (authMethod === 'vc') {
343
345
  result = await authenticateByVc({
@@ -357,6 +359,7 @@ const ensureBlockletPermission = async ({ authMethod, node, userDid, claims, cha
357
359
  userDid,
358
360
  claims,
359
361
  challenge,
362
+ isAuth,
360
363
  });
361
364
  }
362
365
  const { teamDid, role } = result;
@@ -387,6 +390,7 @@ const createLaunchBlockletHandler =
387
390
  claims,
388
391
  challenge,
389
392
  locale,
393
+ isAuth: false,
390
394
  });
391
395
 
392
396
  const blocklet = await node.getBlockletMetaFromUrl({ url: blockletMetaUrl, checkPrice: true });
@@ -436,7 +440,7 @@ const createLaunchBlockletHandler =
436
440
 
437
441
  const blockletDid =
438
442
  role === SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER
439
- ? toExternalBlocklet(blocklet.meta.name, userDid, { didOnly: true })
443
+ ? toExternalBlocklet(blocklet.meta.name, controller.nftId, { didOnly: true })
440
444
  : blocklet.meta.did;
441
445
 
442
446
  await updateSession({
@@ -456,13 +460,7 @@ const createLaunchBlockletHandler =
456
460
  url: blockletMetaUrl,
457
461
  delay: 1000 * 4, // delay 4 seconds to download, wait for ws connection from frontend
458
462
  downloadTokenList: extraParams?.previousWorkflowData?.downloadTokenList,
459
- controller:
460
- role === SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER
461
- ? {
462
- ...controller,
463
- id: userDid,
464
- }
465
- : null,
463
+ controller: role === SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER ? controller : null,
466
464
  });
467
465
 
468
466
  await node.createAuditLog(
@@ -487,5 +485,5 @@ module.exports = {
487
485
  ensureBlockletPermission,
488
486
  getSetupBlockletClaims,
489
487
  getTrustedIssuers,
490
- getUsageCredentialNFTClaim,
488
+ getServerlessNFTClaim,
491
489
  };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.8.37",
6
+ "version": "1.8.39",
7
7
  "description": "Simple lib to manage auth in ABT Node",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -20,18 +20,18 @@
20
20
  "author": "linchen <linchen1987@foxmail.com> (http://github.com/linchen1987)",
21
21
  "license": "MIT",
22
22
  "dependencies": {
23
- "@abtnode/constant": "1.8.37",
24
- "@abtnode/logger": "1.8.37",
25
- "@abtnode/util": "1.8.37",
26
- "@arcblock/did": "1.18.15",
27
- "@arcblock/jwt": "^1.18.15",
28
- "@arcblock/vc": "1.18.15",
29
- "@blocklet/constant": "1.8.37",
30
- "@blocklet/meta": "1.8.37",
31
- "@ocap/client": "1.18.15",
32
- "@ocap/mcrypto": "1.18.15",
33
- "@ocap/util": "1.18.15",
34
- "@ocap/wallet": "1.18.15",
23
+ "@abtnode/constant": "1.8.39",
24
+ "@abtnode/logger": "1.8.39",
25
+ "@abtnode/util": "1.8.39",
26
+ "@arcblock/did": "1.18.18",
27
+ "@arcblock/jwt": "^1.18.18",
28
+ "@arcblock/vc": "1.18.18",
29
+ "@blocklet/constant": "1.8.39",
30
+ "@blocklet/meta": "1.8.39",
31
+ "@ocap/client": "1.18.18",
32
+ "@ocap/mcrypto": "1.18.18",
33
+ "@ocap/util": "1.18.18",
34
+ "@ocap/wallet": "1.18.18",
35
35
  "axios": "^0.27.2",
36
36
  "joi": "17.6.3",
37
37
  "jsonwebtoken": "^8.5.1",
@@ -42,5 +42,5 @@
42
42
  "devDependencies": {
43
43
  "jest": "^27.5.1"
44
44
  },
45
- "gitHead": "f26f451c6e2b1168b36f78269eafdf3f671236bf"
45
+ "gitHead": "cf2223c4d9a999993b2be1c943dd75b4221593c3"
46
46
  }