@abtnode/auth 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/lib/auth.js CHANGED
@@ -6,8 +6,11 @@ const get = require('lodash/get');
6
6
  const { verifyPresentation, createCredentialList } = require('@arcblock/vc');
7
7
  const formatContext = require('@abtnode/util/lib/format-context');
8
8
  const getNodeWallet = require('@abtnode/util/lib/get-app-wallet');
9
+ const { getChainClient } = require('@abtnode/util/lib/get-chain-client');
10
+ const { fromPublicKey } = require('@ocap/wallet');
11
+ const { fromBase58, toAddress } = require('@ocap/util');
12
+ const { toTypeInfo, isFromPublicKey } = require('@arcblock/did');
9
13
  const Mcrypto = require('@ocap/mcrypto');
10
- const Client = require('@ocap/client');
11
14
  const getBlockletInfo = require('@blocklet/meta/lib/info');
12
15
  const {
13
16
  PASSPORT_STATUS,
@@ -41,6 +44,10 @@ const messages = {
41
44
  en: 'Please provide following information to continue',
42
45
  zh: '请提供如下信息以继续',
43
46
  },
47
+ requestNFT: {
48
+ en: 'Please present NFT to exchange for passport',
49
+ zh: '请提供 NFT 以换取通行证',
50
+ },
44
51
  requestCredential: {
45
52
  en: 'Please provide credential',
46
53
  zh: '请提供凭证',
@@ -72,6 +79,10 @@ const messages = {
72
79
  en: 'This node is not initialized, login is disabled',
73
80
  zh: '节点初始化未完成,禁止任何 DID 登录',
74
81
  },
82
+ appNotInitialized: {
83
+ en: 'This application is not initialized',
84
+ zh: '应用未初始化未完成',
85
+ },
75
86
  alreadyInitiated: {
76
87
  en: 'This Node already have an owner verified',
77
88
  zh: '该节点已经绑定所有者',
@@ -80,6 +91,10 @@ const messages = {
80
91
  en: 'You are not allowed to login to this node',
81
92
  zh: '你没有权限登录该节点',
82
93
  },
94
+ notAppOwner: {
95
+ en: 'You are not the owner of this application',
96
+ zh: '你不是该应用的所有者',
97
+ },
83
98
  missingCredentialClaim: {
84
99
  en: 'Credential is not provided',
85
100
  zh: '请提供凭证',
@@ -214,6 +229,10 @@ const messages = {
214
229
  en: 'Invalid server ownership NFT issuer',
215
230
  zh: '无效的节点所有权 NFT 颁发者',
216
231
  },
232
+ invalidNftParent: {
233
+ en: 'Unexpected NFT Collection',
234
+ zh: '无效的 NFT 集合',
235
+ },
217
236
  tagNotMatch: {
218
237
  en: 'This NFT is for another blocklet server',
219
238
  zh: '您所提供的所有权 NFT 不属于当前节点',
@@ -234,6 +253,10 @@ const messages = {
234
253
  en: 'This NFT has expired',
235
254
  zh: '该 NFT 已经过期',
236
255
  },
256
+ nftAlreadyUsed: {
257
+ en: 'This NFT has already been connected to another user',
258
+ zh: '该 NFT 已经被起它用户使用过',
259
+ },
237
260
  missingNftClaim: {
238
261
  en: 'Ownership NFT not provided',
239
262
  zh: '节点所有权 NFT 必须提供',
@@ -266,6 +289,10 @@ const messages = {
266
289
  en: 'tag is required',
267
290
  zh: 'tag 不能为空',
268
291
  },
292
+ appIsRunning: {
293
+ en: 'Please stop the app before transferring ownership',
294
+ zh: '转移所有权之前请停止应用',
295
+ },
269
296
  };
270
297
 
271
298
  const PASSPORT_STATUS_KEY = 'passport-status';
@@ -288,7 +315,7 @@ const getRandomMessage = (len = 16) => {
288
315
  return hex.replace(/^0x/, '').toUpperCase();
289
316
  };
290
317
 
291
- const getApplicationInfo = async ({ node, nodeInfo, teamDid }) => {
318
+ const getApplicationInfo = async ({ node, nodeInfo = {}, teamDid }) => {
292
319
  let type;
293
320
  let name;
294
321
  let wallet;
@@ -454,7 +481,7 @@ const handleInvitationReceive = async ({
454
481
 
455
482
  if (get(nodeInfo, 'ownerNft.holder')) {
456
483
  // 这种情况下是 Transfer 有 Owner NFT 的 Blocklet Server
457
- const client = new Client(nodeInfo.launcher.chainHost);
484
+ const client = getChainClient(nodeInfo.launcher.chainHost);
458
485
  const ownerNftDid = get(nodeInfo, 'ownerNft.did');
459
486
 
460
487
  const { state: assetState } = await client.getAssetState({ address: ownerNftDid });
@@ -516,6 +543,11 @@ const handleInvitationReceive = async ({
516
543
  const user = await getUser(node, teamDid, userDid);
517
544
 
518
545
  if (role === 'owner') {
546
+ if (issuerType === TEAM_TYPE.blocklet) {
547
+ // should not be here
548
+ throw new Error('not allowed to transfer application ownership');
549
+ }
550
+
519
551
  if (user && user.role === 'owner') {
520
552
  throw new Error(messages.alreadyTransferred[locale](userDid));
521
553
  }
@@ -1052,9 +1084,48 @@ const setUserInfoHeaders = (req) => {
1052
1084
  }
1053
1085
  };
1054
1086
 
1087
+ const verifyNFT = async ({ claims, challenge, chainHost, locale }) => {
1088
+ const client = getChainClient(chainHost);
1089
+ const claim = claims.find((x) => x.type === 'asset');
1090
+ if (!claim) {
1091
+ throw new Error(messages.missingNftClaim[locale]);
1092
+ }
1093
+
1094
+ const fields = ['asset', 'ownerProof', 'ownerPk', 'ownerDid'];
1095
+ for (const field of fields) {
1096
+ if (!claim[field]) {
1097
+ throw new Error(messages.invalidNftClaim[locale]);
1098
+ }
1099
+ }
1100
+
1101
+ const address = claim.asset;
1102
+ const ownerDid = toAddress(claim.ownerDid);
1103
+ const ownerPk = fromBase58(claim.ownerPk);
1104
+ const ownerProof = fromBase58(claim.ownerProof);
1105
+ if (isFromPublicKey(ownerDid, ownerPk) === false) {
1106
+ throw new Error(messages.invalidNftHolder[locale]);
1107
+ }
1108
+
1109
+ const owner = fromPublicKey(ownerPk, toTypeInfo(ownerDid));
1110
+ if (owner.verify(challenge, ownerProof) === false) {
1111
+ throw new Error(messages.invalidNftProof[locale]);
1112
+ }
1113
+
1114
+ const { state } = await client.getAssetState({ address }, { ignoreFields: ['context'] });
1115
+ if (!state) {
1116
+ throw new Error(messages.invalidNft[locale]);
1117
+ }
1118
+ if (state.owner !== ownerDid) {
1119
+ throw new Error(messages.invalidNftHolder[locale]);
1120
+ }
1121
+
1122
+ return state;
1123
+ };
1124
+
1055
1125
  module.exports = {
1056
1126
  getUser,
1057
1127
  getApplicationInfo,
1128
+ verifyNFT,
1058
1129
  createAuthToken,
1059
1130
  createAuthTokenByOwnershipNFT,
1060
1131
  beforeInvitationRequest,
package/lib/passport.js CHANGED
@@ -63,6 +63,7 @@ const createPassportVC = ({
63
63
  tag,
64
64
  ownerProfile,
65
65
  preferredColor,
66
+ expirationDate,
66
67
  } = {}) => {
67
68
  validatePassport(passport);
68
69
 
@@ -87,6 +88,7 @@ const createPassportVC = ({
87
88
  }),
88
89
  },
89
90
  },
91
+ expirationDate,
90
92
  endpoint,
91
93
  tag,
92
94
  });
package/lib/server.js CHANGED
@@ -6,11 +6,9 @@ const uniq = require('lodash/uniq');
6
6
  const pRetry = require('p-retry');
7
7
  const { isNFTExpired, isNFTConsumed } = require('@abtnode/util/lib/nft');
8
8
  const axios = require('@abtnode/util/lib/axios');
9
- const Client = require('@ocap/client');
10
- const { fromPublicKey } = require('@ocap/wallet');
11
9
  const { types } = require('@ocap/mcrypto');
12
- const { fromBase58, toAddress, toHex } = require('@ocap/util');
13
- const { toTypeInfo, isFromPublicKey, DidType, isEthereumType } = require('@arcblock/did');
10
+ const { toHex } = require('@ocap/util');
11
+ const { DidType, isEthereumType } = require('@arcblock/did');
14
12
  const urlPathFriendly = require('@blocklet/meta/lib/url-path-friendly').default;
15
13
  const getApplicationWallet = require('@blocklet/meta/lib/wallet');
16
14
  const { slugify } = require('transliteration');
@@ -38,6 +36,7 @@ const {
38
36
  createBlockletControllerAuthToken,
39
37
  checkWalletVersion,
40
38
  checkWalletVersionForMigrateAppToV2,
39
+ verifyNFT,
41
40
  } = require('./auth');
42
41
  const {
43
42
  validatePassport,
@@ -193,41 +192,9 @@ const authenticateByVc = async ({
193
192
 
194
193
  const authenticateByNFT = async ({ node, claims, userDid, challenge, locale, isAuth, chainHost }) => {
195
194
  const info = await node.getNodeInfo();
196
- // serverless 应用通过 querystring 传递 chainHost
197
- const client = new Client(chainHost || info.launcher.chainHost);
198
-
199
- const claim = claims.find((x) => x.type === 'asset');
200
- if (!claim) {
201
- throw new Error(messages.missingNftClaim[locale]);
202
- }
203
195
 
204
- const fields = ['asset', 'ownerProof', 'ownerPk', 'ownerDid'];
205
- for (const field of fields) {
206
- if (!claim[field]) {
207
- throw new Error(messages.invalidNftClaim[locale]);
208
- }
209
- }
210
-
211
- const address = claim.asset;
212
- const ownerDid = toAddress(claim.ownerDid);
213
- const ownerPk = fromBase58(claim.ownerPk);
214
- const ownerProof = fromBase58(claim.ownerProof);
215
- if (isFromPublicKey(ownerDid, ownerPk) === false) {
216
- throw new Error(messages.invalidNftHolder[locale]);
217
- }
218
-
219
- const owner = fromPublicKey(ownerPk, toTypeInfo(ownerDid));
220
- if (owner.verify(challenge, ownerProof) === false) {
221
- throw new Error(messages.invalidNftProof[locale]);
222
- }
223
-
224
- const { state } = await client.getAssetState({ address }, { ignoreFields: ['context'] });
225
- if (!state) {
226
- throw new Error(messages.invalidNft[locale]);
227
- }
228
- if (state.owner !== ownerDid) {
229
- throw new Error(messages.invalidNftHolder[locale]);
230
- }
196
+ // serverless 应用通过 querystring 传递 chainHost
197
+ const state = await verifyNFT({ claims, challenge, chainHost: chainHost || info.launcher.chainHost, locale });
231
198
 
232
199
  const trustedLaunchers = await getLauncherAppIdList(get(info, 'launcher.url'));
233
200
  if (!trustedLaunchers.includes(state.issuer)) {
@@ -246,7 +213,7 @@ const authenticateByNFT = async ({ node, claims, userDid, challenge, locale, isA
246
213
  nft: state,
247
214
  extra: {
248
215
  controller: {
249
- nftId: address,
216
+ nftId: state.address,
250
217
  nftOwner: state.owner,
251
218
  chainHost,
252
219
  appMaxCount: state.data.value.appMaxCount || 1,
@@ -265,7 +232,15 @@ const authenticateByNFT = async ({ node, claims, userDid, challenge, locale, isA
265
232
  }
266
233
 
267
234
  const user = await getUser(node, info.did, userDid);
268
- return { role: ROLES.OWNER, teamDid: info.did, nft: state, user, passport: null, ownerDid, ownerNFT: address };
235
+ return {
236
+ role: ROLES.OWNER,
237
+ teamDid: info.did,
238
+ nft: state,
239
+ user,
240
+ passport: null,
241
+ ownerDid: state.owner,
242
+ ownerNFT: state.address,
243
+ };
269
244
  };
270
245
 
271
246
  const authenticateBySession = async ({ node, userDid, locale, allowedRoles = ['owner', 'admin', 'member'] }) => {
@@ -372,17 +347,21 @@ const getAuthPrincipalForMigrateAppToV2 = (node) => async (param) => {
372
347
  };
373
348
  };
374
349
 
350
+ const getAuthPrincipalForTransferAppOwnerShip = getAuthPrincipalForMigrateAppToV2;
351
+
375
352
  const getKeyPairClaim =
376
- (node, declare = true) =>
353
+ (node, opts = {}) =>
377
354
  async ({ extraParams: { locale, appDid, wt = 'default', title }, context: { didwallet } }) => {
378
355
  checkWalletVersion({ didwallet, locale });
379
356
 
357
+ const { declare = true } = opts;
358
+
380
359
  const description = {
381
360
  en: 'Please generate a new key-pair for this application',
382
361
  zh: '请为应用创建新的钥匙对',
383
362
  };
384
363
 
385
- let appName = title;
364
+ let appName = title || opts.title;
386
365
  let migrateFrom = '';
387
366
 
388
367
  let type;
@@ -416,7 +395,7 @@ const getKeyPairClaim =
416
395
  const result = {
417
396
  mfa: !process.env.DID_CONNECT_MFA_DISABLED,
418
397
  description: description[locale] || description.en,
419
- moniker: (urlPathFriendly(slugify(appName)) || 'application').toLowerCase(),
398
+ moniker: (urlPathFriendly(slugify(appName || 'application')) || 'application').toLowerCase(),
420
399
  declare: !!declare,
421
400
  migrateFrom: declare ? migrateFrom : '',
422
401
  chainInfo,
@@ -502,7 +481,7 @@ const getLaunchBlockletClaims = (node, authMethod) => {
502
481
  return claims;
503
482
  };
504
483
 
505
- const getSetupBlockletClaims = () => {
484
+ const getAppDidOwnerClaims = () => {
506
485
  const description = {
507
486
  en: 'Sign following message to prove that you are the owner of the app',
508
487
  zh: '签名如下消息以证明你是应用的拥有者',
@@ -713,7 +692,7 @@ const createLaunchBlockletHandler =
713
692
 
714
693
  // 如果是 serverless, 并且已经消费过了,但是没有安装,则抛出异常
715
694
  if (!existedBlocklet && role === SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER && isNFTConsumed(nft)) {
716
- throw new Error(messages.nftAlreadyConsume[locale]);
695
+ throw new Error(messages.nftAlreadyConsumed[locale]);
717
696
  }
718
697
 
719
698
  if (existedBlocklet) {
@@ -853,10 +832,11 @@ module.exports = {
853
832
  createRestoreOnServerlessHandler,
854
833
  ensureBlockletPermission,
855
834
  getBlockletPermissionChecker,
856
- getSetupBlockletClaims,
835
+ getAppDidOwnerClaims,
857
836
  getTrustedIssuers,
858
837
  getAuthNFTClaim,
859
838
  getServerlessNFTClaim,
860
839
  getLauncherAppIdList,
861
840
  getAuthPrincipalForMigrateAppToV2,
841
+ getAuthPrincipalForTransferAppOwnerShip,
862
842
  };
@@ -3,14 +3,18 @@ const { getNftBGColor, DEFAULT_COLOR, getNftBGColorFromDid } = require('./passpo
3
3
  /**
4
4
  * Generate Passport SVG
5
5
  *
6
- * @param {string} title passport title
7
- * @param {string} issuer issuer name
8
- * @param {string} issuerDid
9
- * @param {boolean} ownerName
10
- * @param {string} preferredColor
11
- * @param {boolean} ownerAvatarUrl
12
- * @param {boolean} revoked 是否撤销
13
- * @param {boolean} isDataUrl 返回生成 data url
6
+ * @param {object} params
7
+ * @param {string} params.title passport title
8
+ * @param {string} params.issuer issuer name
9
+ * @param {string} params.issuerDid
10
+ * @param {string} params.ownerName
11
+ * @param {string} params.ownerAvatarUrl
12
+ * @param {string} [params.preferredColor]
13
+ * @param {string} [params.width]
14
+ * @param {string} [params.height]
15
+ * @param {boolean} [params.ownerAvatarUrl]
16
+ * @param {boolean} [params.revoked] 是否撤销
17
+ * @param {boolean} [params.isDataUrl] 返回生成 data url
14
18
  * @returns {string} svg xml or image data url
15
19
  */
16
20
  const createPassportSvg = ({
@@ -18,13 +22,13 @@ const createPassportSvg = ({
18
22
  title = '',
19
23
  issuerDid = '',
20
24
  ownerName = '',
21
- preferredColor = 'default',
22
25
  ownerAvatarUrl = '',
23
- revoked,
24
- isDataUrl,
26
+ preferredColor = 'default',
27
+ revoked = false,
28
+ isDataUrl = false,
25
29
  width = '100%',
26
30
  height = '100%',
27
- } = {}) => {
31
+ }) => {
28
32
  let colors;
29
33
  if (preferredColor === 'default') {
30
34
  colors = DEFAULT_COLOR;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.16.5",
6
+ "version": "1.16.6-beta-8be2fe37",
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.16.5",
24
- "@abtnode/logger": "1.16.5",
25
- "@abtnode/util": "1.16.5",
26
- "@arcblock/did": "1.18.68",
27
- "@arcblock/jwt": "^1.18.68",
28
- "@arcblock/vc": "1.18.68",
29
- "@blocklet/constant": "1.16.5",
30
- "@blocklet/meta": "1.16.5",
31
- "@ocap/client": "1.18.68",
32
- "@ocap/mcrypto": "1.18.68",
33
- "@ocap/util": "1.18.68",
34
- "@ocap/wallet": "1.18.68",
23
+ "@abtnode/constant": "1.16.6-beta-8be2fe37",
24
+ "@abtnode/logger": "1.16.6-beta-8be2fe37",
25
+ "@abtnode/util": "1.16.6-beta-8be2fe37",
26
+ "@arcblock/did": "1.18.71",
27
+ "@arcblock/jwt": "^1.18.71",
28
+ "@arcblock/vc": "1.18.71",
29
+ "@blocklet/constant": "1.16.6-beta-8be2fe37",
30
+ "@blocklet/meta": "1.16.6-beta-8be2fe37",
31
+ "@ocap/client": "1.18.71",
32
+ "@ocap/mcrypto": "1.18.71",
33
+ "@ocap/util": "1.18.71",
34
+ "@ocap/wallet": "1.18.71",
35
35
  "axios": "^0.27.2",
36
36
  "joi": "17.7.0",
37
37
  "jsonwebtoken": "^9.0.0",
@@ -44,5 +44,5 @@
44
44
  "devDependencies": {
45
45
  "jest": "^27.5.1"
46
46
  },
47
- "gitHead": "229effc24ce7e12a0dbe551b2cc57825c3803745"
47
+ "gitHead": "27229a1a62850e95907b7a53e0259a809d99377b"
48
48
  }