@abtnode/auth 1.8.68-beta-500af7e5 → 1.8.68

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 +3 -19
  2. package/lib/server.js +79 -276
  3. package/package.json +14 -15
package/lib/auth.js CHANGED
@@ -156,22 +156,6 @@ const messages = {
156
156
  en: 'Invalid Params',
157
157
  zh: '无效的参数',
158
158
  },
159
- missingKeyPair: {
160
- en: 'Missing app key pair',
161
- zh: '缺少应用钥匙对',
162
- },
163
- missingBlockletUrl: {
164
- en: 'Missing blocklet url',
165
- zh: '缺少应用下载地址',
166
- },
167
- missingBlockletDid: {
168
- en: 'Missing blocklet did',
169
- zh: '缺少应用 ID',
170
- },
171
- missingChainHost: {
172
- en: 'Missing chain host',
173
- zh: '缺少链的端点地址',
174
- },
175
159
  invalidBlocklet: {
176
160
  en: 'Invalid Blocklet',
177
161
  zh: '无效的 Blocklet',
@@ -825,9 +809,9 @@ const handleIssuePassportResponse = async ({
825
809
  };
826
810
 
827
811
  const getVCFromClaims = async ({ claims, challenge, trustedIssuers, vcTypes, locale = 'en', vcId }) => {
828
- const credential = claims
829
- .filter(Boolean) // FIXES: https://github.com/ArcBlock/did-connect/issues/74
830
- .find((x) => x.type === 'verifiableCredential' && vcTypes.some((item) => x.item.includes(item)));
812
+ const credential = claims.find(
813
+ (x) => x.type === 'verifiableCredential' && vcTypes.some((item) => x.item.includes(item))
814
+ );
831
815
 
832
816
  if (!credential || !credential.presentation) {
833
817
  return {};
package/lib/server.js CHANGED
@@ -1,16 +1,11 @@
1
1
  const get = require('lodash/get');
2
- const pick = require('lodash/pick');
3
2
  const isEmpty = require('lodash/isEmpty');
4
3
  const last = require('lodash/last');
5
4
  const { isNFTExpired, isNFTConsumed } = require('@abtnode/util/lib/nft');
6
5
  const Client = require('@ocap/client');
7
6
  const { fromPublicKey } = require('@ocap/wallet');
8
- const { fromBase58, toAddress, toHex } = require('@ocap/util');
7
+ const { fromBase58, toAddress } = require('@ocap/util');
9
8
  const { toTypeInfo, isFromPublicKey } = require('@arcblock/did');
10
- const urlFriendly = require('@blocklet/meta/lib/url-friendly').default;
11
- const { slugify } = require('transliteration');
12
- const { getChainInfo } = require('@blocklet/meta/lib/util');
13
- const getBlockletInfo = require('@blocklet/meta/lib/info');
14
9
  const formatContext = require('@abtnode/util/lib/format-context');
15
10
  const {
16
11
  ROLES,
@@ -19,7 +14,6 @@ const {
19
14
  NFT_TYPE_SERVER_OWNERSHIP,
20
15
  SERVER_ROLES,
21
16
  NFT_TYPE_SERVERLESS,
22
- MAIN_CHAIN_ENDPOINT,
23
17
  } = require('@abtnode/constant');
24
18
  const { toExternalBlocklet } = require('@blocklet/meta/lib/did');
25
19
  const {
@@ -225,16 +219,6 @@ const authenticateByNFT = async ({ node, claims, userDid, challenge, locale, isA
225
219
  return { role: ROLES.OWNER, teamDid: info.did, nft: state, user, passport: null, ownerDid, ownerNFT: address };
226
220
  };
227
221
 
228
- const authenticateBySession = async ({ node, userDid, locale, allowedRoles = ['owner', 'admin', 'member'] }) => {
229
- const info = await node.getNodeInfo();
230
- const user = await getUser(node, info.did, userDid);
231
- if (!user) {
232
- throw new Error(messages.userNotFound[locale]);
233
- }
234
- const passport = (user.passports || []).find((x) => x.status === 'valid' && allowedRoles.includes(x.role));
235
- return { role: passport ? passport.role : ROLES.GUEST, teamDid: info.did, user, passport: null };
236
- };
237
-
238
222
  const getAuthVcClaim =
239
223
  ({ node, launchBlocklet, blocklet, options }) =>
240
224
  async ({ extraParams: { locale, passportId }, context: { didwallet } }) => {
@@ -289,117 +273,28 @@ const getAuthNFTClaim =
289
273
  return getOwnershipNFTClaim(node, locale);
290
274
  };
291
275
 
292
- const getKeyPairClaim =
293
- (node) =>
294
- async ({ extraParams: { locale, appDid, title }, context: { didwallet } }) => {
295
- checkWalletVersion({ didwallet, locale });
296
-
297
- const description = {
298
- en: 'Please generate a new key-pair for this application',
299
- zh: '请为应用创建新的钥匙对',
300
- };
301
-
302
- let appName = title;
303
- let migrateFrom = '';
304
-
305
- // We are rotating a key-pair for existing application
306
- if (appDid) {
307
- const blocklet = await node.getBlocklet({ did: appDid, attachRuntimeInfo: false });
308
- if (!blocklet) {
309
- throw new Error(messages.invalidBlocklet[locale]);
310
- }
311
-
312
- const info = await node.getNodeInfo();
313
- const { name, wallet } = getBlockletInfo(blocklet, info.sk);
314
- appName = name;
315
- migrateFrom = wallet.address;
316
- }
317
-
318
- return {
319
- mfa: !process.env.DID_CONNECT_MFA_DISABLED,
320
- description: description[locale] || description.en,
321
- moniker: (urlFriendly(slugify(appName)) || 'application').toLowerCase(),
322
- migrateFrom,
323
- targetType: {
324
- role: 'application',
325
- hash: 'sha3',
326
- key: 'ed25519',
327
- encoding: 'base58',
328
- },
329
- };
330
- };
331
-
332
- const getRotateKeyPairClaims = (node) => {
333
- return [
334
- {
335
- authPrincipal: async ({ extraParams: { locale, appDid } }) => {
336
- const description = {
337
- en: 'Please select ',
338
- zh: '请为应用创建新的钥匙对',
339
- };
340
-
341
- let chainInfo = { host: 'none', id: 'none', type: 'arcblock' };
342
-
343
- if (!appDid) {
344
- throw new Error(messages.missingBlockletDid[locale]);
345
- }
346
-
347
- const blocklet = await node.getBlocklet({ did: appDid, attachRuntimeInfo: false });
348
- if (!blocklet) {
349
- throw new Error(messages.invalidBlocklet[locale]);
350
- }
351
-
352
- // Try to use blocklet chain config if possible
353
- // Since migration happens on the chain the app holds some actual assets
354
- // We must ensure it happens on that chain
355
- chainInfo = getChainInfo(blocklet.configObj);
356
-
357
- // Fallback to main chain, since it is the default registry for all DID
358
- if (chainInfo.host === 'none') {
359
- chainInfo = { host: MAIN_CHAIN_ENDPOINT, id: 'main', type: 'arcblock' };
360
- }
361
-
362
- return {
363
- chainInfo,
364
- description: description[locale] || description.en,
365
- target: '',
366
- };
367
- },
368
- },
369
- {
370
- keyPair: getKeyPairClaim(node),
371
- },
372
- ];
373
- };
374
-
375
276
  const getLaunchBlockletClaims = (node, authMethod) => {
376
- const claims = {
377
- blockletAppKeypair: ['keyPair', getKeyPairClaim(node)],
378
- };
379
-
380
277
  if (authMethod === 'vc') {
381
- claims.serverPassport = ['verifiableCredential', getAuthVcClaim({ node, launchBlocklet: true })];
382
- }
383
-
384
- if (authMethod === 'nft') {
385
- claims.serverNFT = ['asset', getAuthNFTClaim({ node })];
278
+ return {
279
+ serverPassport: ['verifiableCredential', getAuthVcClaim({ node, launchBlocklet: true })],
280
+ };
386
281
  }
387
282
 
388
- return claims;
283
+ return {
284
+ serverNFT: ['asset', getAuthNFTClaim({ node })],
285
+ };
389
286
  };
390
287
 
391
- // FIXME: @wangshijun should be changed to blocklet appSK owner claim?
392
288
  const getSetupBlockletClaims = (node, authMethod, blocklet) => {
393
- const claims = {};
394
-
395
289
  if (authMethod === 'vc') {
396
- claims.serverPassport = ['verifiableCredential', getAuthVcClaim({ node, blocklet })];
397
- }
398
- if (authMethod === 'nft') {
399
- claims.serverNFT = ['asset', getAuthNFTClaim({ node })];
290
+ return {
291
+ serverPassport: ['verifiableCredential', getAuthVcClaim({ node, blocklet })],
292
+ };
400
293
  }
401
294
 
402
- return claims;
295
+ return {
296
+ serverNFT: ['asset', getAuthNFTClaim({ node })],
297
+ };
403
298
  };
404
299
 
405
300
  const getOwnershipNFTClaim = async (node, locale) => {
@@ -453,7 +348,6 @@ const ensureBlockletPermission = async ({
453
348
  blocklet,
454
349
  isAuth,
455
350
  chainHost,
456
- allowedRoles = ['owner', 'admin', 'member'],
457
351
  }) => {
458
352
  let result;
459
353
  if (authMethod === 'vc') {
@@ -467,7 +361,7 @@ const ensureBlockletPermission = async ({
467
361
  launchBlocklet: true,
468
362
  blocklet,
469
363
  });
470
- } else if (authMethod === 'nft') {
364
+ } else {
471
365
  result = await authenticateByNFT({
472
366
  node,
473
367
  locale,
@@ -477,16 +371,9 @@ const ensureBlockletPermission = async ({
477
371
  isAuth,
478
372
  chainHost,
479
373
  });
480
- } else {
481
- result = await authenticateBySession({
482
- node,
483
- userDid,
484
- locale,
485
- allowedRoles,
486
- });
487
374
  }
488
-
489
375
  const { teamDid, role } = result;
376
+
490
377
  const permissions = await node.getPermissionsByRole({ teamDid, role: { name: role } });
491
378
  if (!permissions.some((item) => ['mutate_blocklets'].includes(item.name))) {
492
379
  throw new Error(messages.notAuthorized[locale]);
@@ -498,38 +385,17 @@ const ensureBlockletPermission = async ({
498
385
  const createLaunchBlockletHandler =
499
386
  (node, authMethod) =>
500
387
  async ({ claims, challenge, userDid, updateSession, req, extraParams }) => {
501
- const { locale, blockletMetaUrl, title, description, chainHost } = extraParams;
388
+ const { locale, blockletMetaUrl, chainHost } = extraParams;
502
389
  logger.info('createLaunchBlockletHandler', extraParams);
503
390
 
504
- const keyPair = claims.find((x) => x.type === 'keyPair');
505
- if (!keyPair) {
506
- logger.error('app keyPair must be provided');
507
- throw new Error(messages.missingKeyPair[locale]);
508
- }
509
-
510
- if (!blockletMetaUrl && !title && !description) {
511
- logger.error('blockletMetaUrl | title + description must be provided');
512
- throw new Error(messages.missingBlockletUrl[locale]);
391
+ if (!blockletMetaUrl) {
392
+ logger.error('blockletMetaUrl must be provided');
393
+ throw new Error(messages.invalidParams[locale]);
513
394
  }
514
395
 
515
396
  if (authMethod === 'nft' && !chainHost) {
516
397
  logger.error('chainHost must be provided');
517
- throw new Error(messages.missingChainHost[locale]);
518
- }
519
-
520
- let blocklet;
521
- if (blockletMetaUrl) {
522
- blocklet = await node.getBlockletMetaFromUrl({ url: blockletMetaUrl, checkPrice: true });
523
- if (!blocklet.meta) {
524
- throw new Error(messages.invalidBlocklet[locale]);
525
- }
526
-
527
- if (!blocklet.isFree) {
528
- if (isEmpty(extraParams?.previousWorkflowData?.downloadTokenList)) {
529
- logger.error('downloadTokenList must be provided');
530
- throw new Error(messages.invalidParams[locale]);
531
- }
532
- }
398
+ throw new Error(messages.invalidParams[locale]);
533
399
  }
534
400
 
535
401
  const { role, passport, user, extra, nft } = await ensureBlockletPermission({
@@ -541,9 +407,22 @@ const createLaunchBlockletHandler =
541
407
  locale,
542
408
  isAuth: false,
543
409
  chainHost,
544
- blocklet,
545
410
  });
546
411
 
412
+ const blocklet = await node.getBlockletMetaFromUrl({ url: blockletMetaUrl, checkPrice: true });
413
+ if (!blocklet.meta) {
414
+ throw new Error(messages.invalidBlocklet[locale]);
415
+ }
416
+
417
+ if (!blocklet.isFree) {
418
+ if (isEmpty(extraParams?.previousWorkflowData?.downloadTokenList)) {
419
+ logger.error('downloadTokenList must be provided');
420
+ throw new Error(messages.invalidParams[locale]);
421
+ }
422
+ }
423
+
424
+ const { did } = blocklet.meta;
425
+
547
426
  let controller;
548
427
 
549
428
  let sessionToken = '';
@@ -555,152 +434,76 @@ const createLaunchBlockletHandler =
555
434
  secret,
556
435
  expiresIn: LAUNCH_BLOCKLET_TOKEN_EXPIRE,
557
436
  });
437
+ } else if (role === SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER) {
438
+ controller = extra.controller;
439
+ sessionToken = createBlockletControllerAuthToken({
440
+ did: userDid,
441
+ role,
442
+ controller,
443
+ secret,
444
+ expiresIn: EXTERNAL_LAUNCH_BLOCKLET_TOKEN_EXPIRE,
445
+ });
446
+ } else {
447
+ sessionToken = createAuthTokenByOwnershipNFT({
448
+ did: userDid,
449
+ role,
450
+ secret,
451
+ expiresIn: LAUNCH_BLOCKLET_TOKEN_EXPIRE,
452
+ });
558
453
  }
559
- if (authMethod === 'nft') {
560
- if (role === SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER) {
561
- controller = extra.controller;
562
- sessionToken = createBlockletControllerAuthToken({
563
- did: userDid,
564
- role,
565
- controller,
566
- secret,
567
- expiresIn: EXTERNAL_LAUNCH_BLOCKLET_TOKEN_EXPIRE,
568
- });
569
- } else {
570
- sessionToken = createAuthTokenByOwnershipNFT({
571
- did: userDid,
572
- role,
573
- secret,
574
- expiresIn: LAUNCH_BLOCKLET_TOKEN_EXPIRE,
575
- });
576
- }
577
- }
578
-
579
- if (sessionToken) {
580
- await updateSession({ sessionToken }, true);
581
- }
582
-
583
- if (blocklet) {
584
- const blockletDid =
585
- role === SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER
586
- ? toExternalBlocklet(blocklet.meta.name, controller.nftId, { didOnly: true })
587
- : blocklet.meta.did;
588
-
589
- await updateSession({ blockletDid });
590
-
591
- // 检查是否已安装,这里不做升级的处理
592
- const existedBlocklet = await node.getBlocklet({ did: blockletDid, attachRuntimeInfo: false });
593
-
594
- // 如果是 serverless, 并且已经消费过了,但是没有安装,则抛出异常
595
- if (!existedBlocklet && role === SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER && isNFTConsumed(nft)) {
596
- throw new Error(messages.nftAlreadyConsume[locale]);
597
- }
598
-
599
- if (existedBlocklet) {
600
- await updateSession({ isInstalled: true });
601
- logger.info('blocklet already exists', { blockletDid });
602
- return;
603
- }
604
- }
605
-
606
- logger.info('start install blocklet', { blockletMetaUrl, title, description });
607
- const tmp = await node.installBlocklet(
608
- {
609
- url: blockletMetaUrl,
610
- title,
611
- description,
612
- appSk: toHex(keyPair.secret),
613
- delay: 1000 * 4, // delay 4 seconds to download, wait for ws connection from frontend
614
- downloadTokenList: extraParams?.previousWorkflowData?.downloadTokenList,
615
- controller: role === SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER ? controller : null,
616
- },
617
- formatContext(Object.assign(req, { user: { ...pick(user, ['did', 'fullName']), role } }))
618
- );
619
454
 
620
- await updateSession({ blockletDid: tmp.meta.did });
621
- };
455
+ await updateSession({ sessionToken }, true);
622
456
 
623
- const getBlockletPermissionChecker =
624
- (node, allowedRoles = ['owner', 'admin', 'member']) =>
625
- async ({ userDid, extraParams }) => {
626
- const { locale = 'en', connectedDid } = extraParams;
627
-
628
- if (!connectedDid || userDid !== connectedDid) {
629
- throw new Error(
630
- {
631
- en: 'Please use current connected wallet to install blocklet',
632
- zh: '请使用当前登录的钱包来安装应用',
633
- }[locale]
634
- );
635
- }
457
+ const blockletDid =
458
+ role === SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER
459
+ ? toExternalBlocklet(blocklet.meta.name, controller.nftId, { didOnly: true })
460
+ : blocklet.meta.did;
636
461
 
637
- const info = await node.getNodeInfo();
638
- const user = await getUser(node, info.did, userDid);
639
- const passport = (user.passports || []).find((x) => x.status === 'valid' && allowedRoles.includes(x.role));
640
- if (!passport) {
641
- throw new Error(
642
- {
643
- en: 'You do not have permission to install blocklets on this server',
644
- zh: '你无权在此节点上安装应用',
645
- }[locale]
646
- );
647
- }
648
- };
462
+ await updateSession({
463
+ blockletDid,
464
+ });
649
465
 
650
- const createRotateKeyPairHandler =
651
- (node, authMethod) =>
652
- async ({ claims, userDid, req, extraParams }) => {
653
- const { locale, appDid } = extraParams;
654
- logger.info('createRotateKeyPairHandler', extraParams);
655
-
656
- const keyPair = claims.find((x) => x.type === 'keyPair');
657
- if (!keyPair) {
658
- logger.error('app keyPair must be provided');
659
- throw new Error(messages.missingKeyPair[locale]);
660
- }
466
+ // 检查是否已安装,这里不做升级的处理
467
+ const existedBlocklet = await node.getBlocklet({ did: blockletDid, attachRuntimeInfo: false });
661
468
 
662
- if (!appDid) {
663
- logger.error('appDid must be provided');
664
- throw new Error(messages.missingBlockletDid[locale]);
469
+ // 如果是 serverless, 并且已经消费过了,但是没有安装,则抛出异常
470
+ if (!existedBlocklet && role === SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER && isNFTConsumed(nft)) {
471
+ throw new Error(messages.nftAlreadyConsume[locale]);
665
472
  }
666
473
 
667
- const blocklet = await node.getBlocklet({ did: appDid, attachRuntimeInfo: false });
668
- if (!blocklet) {
669
- throw new Error(messages.invalidBlocklet[locale]);
474
+ if (existedBlocklet) {
475
+ await updateSession({ isInstalled: true });
476
+ logger.info('blocklet already exists', { blockletDid });
477
+ return;
670
478
  }
671
479
 
672
- const { role, user } = await ensureBlockletPermission({
673
- authMethod,
674
- node,
675
- userDid,
676
- locale,
677
- allowedRoles: ['owner'],
480
+ const tmp = await node.installBlocklet({
481
+ url: blockletMetaUrl,
482
+ delay: 1000 * 4, // delay 4 seconds to download, wait for ws connection from frontend
483
+ downloadTokenList: extraParams?.previousWorkflowData?.downloadTokenList,
484
+ controller: role === SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER ? controller : null,
678
485
  });
679
486
 
680
- await node.configBlocklet(
487
+ await node.createAuditLog(
681
488
  {
682
- did: blocklet.meta.did,
683
- configs: [{ key: 'BLOCKLET_APP_SK', value: toHex(keyPair.secret), secure: true }],
684
- skipHook: true,
685
- skipDidDocument: true,
489
+ action: 'installBlocklet',
490
+ args: { url: blockletMetaUrl },
491
+ context: formatContext(Object.assign(req, { user })),
492
+ result: tmp,
686
493
  },
687
- formatContext(Object.assign(req, { user: { ...pick(user, ['did', 'fullName']), role } }))
494
+ node
688
495
  );
496
+ logger.info('start install blocklet', { blockletDid, bundleDid: did });
689
497
  };
690
498
 
691
499
  module.exports = {
692
500
  getAuthVcClaim,
693
- getKeyPairClaim,
694
501
  authenticateByVc,
695
502
  authenticateByNFT,
696
- authenticateBySession,
697
- getRotateKeyPairClaims,
698
503
  getOwnershipNFTClaim,
699
504
  getLaunchBlockletClaims,
700
505
  createLaunchBlockletHandler,
701
- createRotateKeyPairHandler,
702
506
  ensureBlockletPermission,
703
- getBlockletPermissionChecker,
704
507
  getSetupBlockletClaims,
705
508
  getTrustedIssuers,
706
509
  getServerlessNFTClaim,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.8.68-beta-500af7e5",
6
+ "version": "1.8.68",
7
7
  "description": "Simple lib to manage auth in ABT Node",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -20,28 +20,27 @@
20
20
  "author": "linchen <linchen1987@foxmail.com> (http://github.com/linchen1987)",
21
21
  "license": "MIT",
22
22
  "dependencies": {
23
- "@abtnode/constant": "1.8.68-beta-500af7e5",
24
- "@abtnode/logger": "1.8.68-beta-500af7e5",
25
- "@abtnode/util": "1.8.68-beta-500af7e5",
26
- "@arcblock/did": "1.18.57",
27
- "@arcblock/jwt": "^1.18.57",
28
- "@arcblock/vc": "1.18.57",
29
- "@blocklet/constant": "1.8.68-beta-500af7e5",
30
- "@blocklet/meta": "1.8.68-beta-500af7e5",
31
- "@ocap/client": "1.18.57",
32
- "@ocap/mcrypto": "1.18.57",
33
- "@ocap/util": "1.18.57",
34
- "@ocap/wallet": "1.18.57",
23
+ "@abtnode/constant": "1.8.68",
24
+ "@abtnode/logger": "1.8.68",
25
+ "@abtnode/util": "1.8.68",
26
+ "@arcblock/did": "1.18.42",
27
+ "@arcblock/jwt": "^1.18.42",
28
+ "@arcblock/vc": "1.18.42",
29
+ "@blocklet/constant": "1.8.68",
30
+ "@blocklet/meta": "1.8.68",
31
+ "@ocap/client": "1.18.42",
32
+ "@ocap/mcrypto": "1.18.42",
33
+ "@ocap/util": "1.18.42",
34
+ "@ocap/wallet": "1.18.42",
35
35
  "axios": "^0.27.2",
36
36
  "joi": "17.7.0",
37
37
  "jsonwebtoken": "^9.0.0",
38
38
  "lodash": "^4.17.21",
39
39
  "semver": "^7.3.8",
40
- "transliteration": "^2.3.5",
41
40
  "url-join": "^4.0.1"
42
41
  },
43
42
  "devDependencies": {
44
43
  "jest": "^27.5.1"
45
44
  },
46
- "gitHead": "9070621373f317a10ff0d289323bf725e30d3521"
45
+ "gitHead": "1392044ac5677bde567797adeb9a6d3f0b9264b8"
47
46
  }