@abtnode/auth 1.16.23-beta-7b5b0175 → 1.16.23-beta-06c3a221

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
@@ -40,6 +40,7 @@ const createPassportSvg = require('./util/create-passport-svg');
40
40
  const messages = {
41
41
  description: getLocaleMap('description'),
42
42
  requestProfile: getLocaleMap('requestProfile'),
43
+ requestDidSpace: getLocaleMap('requestDidSpace'),
43
44
  requestNFT: getLocaleMap('requestNFT'),
44
45
  requestCredential: getLocaleMap('requestCredential'),
45
46
  requestPassport: getLocaleMap('requestPassport'),
@@ -807,7 +808,13 @@ const handleIssuePassportResponse = async ({
807
808
  const getVCFromClaims = ({ claims, challenge, trustedIssuers, vcTypes, locale = 'en', vcId }) => {
808
809
  const credential = claims
809
810
  .filter(Boolean) // FIXES: https://github.com/ArcBlock/did-connect/issues/74
810
- .find((x) => x.type === 'verifiableCredential' && vcTypes.some((item) => x.item.includes(item)));
811
+ .find(
812
+ (x) =>
813
+ x.type === 'verifiableCredential' &&
814
+ // 注意此处不要筛选出 did Spaces 的 verifiableCredential
815
+ x?.meta?.purpose !== 'DidSpace' &&
816
+ vcTypes.some((item) => x.item.includes(item))
817
+ );
811
818
 
812
819
  if (!credential || !credential.presentation) {
813
820
  return {};
@@ -0,0 +1,99 @@
1
+ const { getAppUrl, getAppName, getAppDescription } = require('@blocklet/meta/lib/util');
2
+ const { Joi } = require('@arcblock/validator');
3
+ const logger = require('@abtnode/logger')(require('../../../package.json').name);
4
+ const { DID_SPACES } = require('@blocklet/constant');
5
+ const { messages } = require('./auth');
6
+ const { getDidSpacesInfoByClaims, silentAuthorizationInConnect } = require('./util/spaces');
7
+
8
+ const ExtraParamsSchema = Joi.object({
9
+ referrer: Joi.string()
10
+ .uri({ scheme: ['http', 'https'] })
11
+ .required(),
12
+ appPid: Joi.DID().optional(),
13
+ });
14
+
15
+ /**
16
+ * @description
17
+ * @param {import('@abtnode/core').TNode} node
18
+ * @return {*}
19
+ */
20
+ function createConnectToDidSpacesRoute(node) {
21
+ return {
22
+ action: 'connect-to-did-spaces',
23
+ onStart: ({ extraParams }) => {
24
+ const { error } = ExtraParamsSchema.validate(extraParams, {
25
+ allowUnknown: true,
26
+ });
27
+ if (error) {
28
+ throw new Error(error);
29
+ }
30
+ },
31
+
32
+ onConnect: ({ extraParams: { locale } }) => {
33
+ return {
34
+ assetOrVC: {
35
+ description: messages.requestDidSpace[locale],
36
+ optional: false,
37
+ filters: [
38
+ {
39
+ tag: DID_SPACES.NFT_TAG, // 用于筛选 NFT
40
+ },
41
+ {
42
+ type: DID_SPACES.VC_TYPES, // 用于筛选 VC
43
+ },
44
+ ],
45
+ meta: {
46
+ purpose: 'DidSpace',
47
+ },
48
+ },
49
+ };
50
+ },
51
+
52
+ onAuth: async ({ claims, challenge, extraParams: { appPid, locale, referrer }, updateSession, request }) => {
53
+ /** @type {import('@abtnode/client').BlockletState} */
54
+ const blocklet = request.getBlocklet ? await request.getBlocklet() : await node.getBlocklet({ did: appPid });
55
+
56
+ const existsConnectSpaceClaim = claims.some(
57
+ (x) => x?.meta?.purpose === 'DidSpace' && ['asset', 'verifiableCredential'].includes(x.type)
58
+ );
59
+
60
+ if (!existsConnectSpaceClaim) {
61
+ logger.error('Unable to find claim for DID Spaces', { claims });
62
+ throw new Error('Unable to find claim for DID Spaces');
63
+ }
64
+
65
+ const didSpaceInfo = await getDidSpacesInfoByClaims({ claims });
66
+ const appUrl = getAppUrl(blocklet);
67
+ const { data } = await silentAuthorizationInConnect(didSpaceInfo, {
68
+ appInfo: {
69
+ appDid: blocklet.appDid,
70
+ appName: getAppName(blocklet),
71
+ appDescription: getAppDescription(blocklet),
72
+ appUrl,
73
+ scopes: 'list:object read:object write:object',
74
+ referrer,
75
+ },
76
+ verifyNFTParams: {
77
+ claims,
78
+ challenge,
79
+ locale,
80
+ },
81
+ });
82
+
83
+ /**
84
+ * @type {Omit<import('@abtnode/client').SpaceGatewayInput, 'protected'>}
85
+ */
86
+ const spaceGateway = {
87
+ did: data.did,
88
+ name: data.name,
89
+ endpoint: data.endpoint,
90
+ url: didSpaceInfo.didSpacesCoreUrl,
91
+ };
92
+
93
+ // 两边都要做同样的校验
94
+ await updateSession({ spaceGateway, ...data }, true);
95
+ },
96
+ };
97
+ }
98
+
99
+ module.exports = { createConnectToDidSpacesRoute };
@@ -0,0 +1,7 @@
1
+ const { default: axios } = require('axios');
2
+
3
+ axios.defaults.timeout = 10 * 1000; // 超时时间设为10s
4
+
5
+ const api = axios.create();
6
+
7
+ module.exports = { api };
@@ -0,0 +1,190 @@
1
+ const isUrl = require('is-url');
2
+ const Client = require('@ocap/client');
3
+ const { isValid } = require('@arcblock/did');
4
+ const { joinURL } = require('ufo');
5
+ const { api } = require('./api');
6
+
7
+ /**
8
+ * @description
9
+ * @param {string} endpoint
10
+ * @return {string}
11
+ */
12
+ function getDIDSpacesUrlFromDisplayUrl(endpoint) {
13
+ if (!isUrl(endpoint)) {
14
+ throw new Error(`Endpoint(${endpoint}) is not a valid url`);
15
+ }
16
+
17
+ return endpoint.replace(/\/api\/.+/, '');
18
+ }
19
+
20
+ /**
21
+ * @description
22
+ * @param {{
23
+ * chainHost: string,
24
+ * assetDid: string,
25
+ * }} { chainHost, assetDid }
26
+ * @return {Promise<{
27
+ * didSpacesCoreUrl: string,
28
+ * assetDid: string
29
+ * }>}
30
+ */
31
+ async function getDidSpaceInfoByAsset({ chainHost, assetDid }) {
32
+ if (!isValid(assetDid)) {
33
+ throw new Error(`Invalid asset did(${assetDid})`);
34
+ }
35
+
36
+ const client = new Client(chainHost);
37
+ /** @type {{ state: import('@ocap/client').AssetState }} */
38
+ const { state } = await client.getAssetState({ address: assetDid });
39
+
40
+ const didSpacesCoreUrl = getDIDSpacesUrlFromDisplayUrl(state.display.content);
41
+
42
+ return {
43
+ didSpacesCoreUrl,
44
+ assetDid,
45
+ };
46
+ }
47
+ /**
48
+ * @description
49
+ * @param {{ vcClaim: any }} { vcClaim }
50
+ * @return {{
51
+ * didSpacesCoreUrl: string,
52
+ * spaceDid: string,
53
+ * }}
54
+ */
55
+ async function getDidSpaceInfoByVC({ vcClaim }) {
56
+ const vcData = JSON.parse(JSON.parse(vcClaim.presentation).verifiableCredential[0]);
57
+
58
+ /**
59
+ * @type {{
60
+ * appAuth: {
61
+ * appUrl?: string,
62
+ * spaceDid: string,
63
+ * endpoint?: string,
64
+ * }
65
+ * }}
66
+ * */
67
+ const { appAuth } = vcData.credentialSubject;
68
+ const spacesAnyUrl = appAuth.appUrl || appAuth.endpoint;
69
+
70
+ if (!spacesAnyUrl) {
71
+ throw new Error('Unable to find any valid space paths');
72
+ }
73
+
74
+ const didSpacesCoreUrl = await getDidSpacesCoreUrl(spacesAnyUrl);
75
+
76
+ return {
77
+ didSpacesCoreUrl,
78
+ spaceDid: appAuth.spaceDid,
79
+ };
80
+ }
81
+
82
+ /**
83
+ * @description
84
+ * @param {string} spacesAnyUrl
85
+ * @param {{ timeout: number }} [options={ timeout: 5000 }]
86
+ * @return {Promise<string>}
87
+ */
88
+ async function getDidSpacesCoreUrl(spacesAnyUrl, options = { timeout: 5000 }) {
89
+ const u = new URL(spacesAnyUrl);
90
+
91
+ /** @type {{ data: import('@blocklet/sdk').WindowBlocklet }} */
92
+ const { data: blockletMeta } = await api.get(joinURL(u.origin, '/__blocklet__.js?type=json'), {
93
+ timeout: options.timeout,
94
+ });
95
+
96
+ /** @type {string} */
97
+ const mountPoint = blockletMeta.componentMountPoints.find(
98
+ (x) => x.did === 'z8iZnaYxnkMD5AKRjTKiCb8pQr1ut8UantAcf'
99
+ )?.mountPoint;
100
+
101
+ if (!mountPoint) {
102
+ throw new Error(`MountPoint(${mountPoint}) not found`);
103
+ }
104
+
105
+ return joinURL(u.origin, mountPoint);
106
+ }
107
+
108
+ /**
109
+ * @typedef {{
110
+ * didSpacesCoreUrl: string,
111
+ * spaceDid?: string,
112
+ * assetDid?: string, // spaceDid, assetDid 必定会存在一个
113
+ * }} DidSpaceInfo
114
+ * @typedef {{
115
+ * appDid: string,
116
+ * appName: string,
117
+ * appDescription: string,
118
+ * scopes: string,
119
+ * appUrl: string,
120
+ * referer: string,
121
+ * }} DidSpaceExtraParams
122
+ * @typedef {{
123
+ * claims: any[],
124
+ * challenge: any,
125
+ * locale: 'en' | 'zh',
126
+ * }} DidSpaceVerifyNFTParams
127
+ */
128
+
129
+ /**
130
+ * @description
131
+ * @param {{ claims: any[] }} { claims }
132
+ * @return {Promise<DidSpaceInfo>}
133
+ */
134
+ function getDidSpacesInfoByClaims({ claims }) {
135
+ const assetOrVcClaim = claims.find(
136
+ (x) => x?.meta?.purpose === 'DidSpace' && ['asset', 'verifiableCredential'].includes(x.type)
137
+ );
138
+ if (!assetOrVcClaim) {
139
+ // 说明 DID Spaces 对于应用来说不是必要的
140
+ return null;
141
+ }
142
+
143
+ const isAssetClaim = assetOrVcClaim.type === 'asset';
144
+ if (isAssetClaim) {
145
+ return getDidSpaceInfoByAsset({
146
+ chainHost: getChainHostByAssetChainId(assetOrVcClaim.assetChainId),
147
+ assetDid: assetOrVcClaim.asset,
148
+ });
149
+ }
150
+ return getDidSpaceInfoByVC({ vcClaim: assetOrVcClaim });
151
+ }
152
+
153
+ /**
154
+ * @description
155
+ * @param {DidSpaceInfo} didSpaceInfo
156
+ * @param {{
157
+ * extrasParams: DidSpaceExtraParams,
158
+ * verifyNFTParams: DidSpaceVerifyNFTParams
159
+ * }} { extrasParams, verifyNFTParams }
160
+ * @return {*}
161
+ */
162
+ async function silentAuthorizationInConnect(didSpaceInfo, data) {
163
+ const silentAuthorizationUrl = joinURL(didSpaceInfo.didSpacesCoreUrl, '/api/space/silent-authorization');
164
+
165
+ const res = await api.put(silentAuthorizationUrl, data, {
166
+ params: {
167
+ spaceDid: didSpaceInfo.spaceDid,
168
+ assetDid: didSpaceInfo.assetDid,
169
+ },
170
+ });
171
+
172
+ return res;
173
+ }
174
+ const CHAIN_HOST_MAP = {
175
+ beta: 'https://beta.abtnetwork.io/api/',
176
+ 'xenon-2020-01-15': 'https://abtnetwork.io/api/',
177
+ };
178
+ function getChainHostByAssetChainId(assetChainId) {
179
+ if (assetChainId in CHAIN_HOST_MAP) {
180
+ return CHAIN_HOST_MAP[assetChainId];
181
+ }
182
+
183
+ throw new Error(`Unknown asset chain id(${assetChainId})`);
184
+ }
185
+
186
+ module.exports = {
187
+ getDIDSpacesUrlFromDisplayUrl,
188
+ getDidSpacesInfoByClaims,
189
+ silentAuthorizationInConnect,
190
+ };
package/locales/en.js CHANGED
@@ -2,6 +2,7 @@
2
2
  module.exports = {
3
3
  description: 'Connect your DID Wallet',
4
4
  requestProfile: 'Please provide following information to continue',
5
+ requestDidSpace: 'Please authorize DID Space to continue',
5
6
  requestNFT: 'Please present NFT to exchange for passport',
6
7
  requestCredential: 'Please provide credential',
7
8
  requestPassport: 'Please provide passport',
package/locales/zh.js CHANGED
@@ -2,6 +2,7 @@
2
2
  module.exports = {
3
3
  description: '连接 DID 钱包',
4
4
  requestProfile: '请提供如下信息以继续',
5
+ requestDidSpace: '请授权应用读写你的 DID Space',
5
6
  requestNFT: '请提供 NFT 以换取通行证',
6
7
  requestCredential: '请提供凭证',
7
8
  requestPassport: '请提供通行证',
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.16.23-beta-7b5b0175",
6
+ "version": "1.16.23-beta-06c3a221",
7
7
  "description": "Simple lib to manage auth in ABT Node",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -20,29 +20,34 @@
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.23-beta-7b5b0175",
24
- "@abtnode/logger": "1.16.23-beta-7b5b0175",
25
- "@abtnode/util": "1.16.23-beta-7b5b0175",
23
+ "@abtnode/constant": "1.16.23-beta-06c3a221",
24
+ "@abtnode/logger": "1.16.23-beta-06c3a221",
25
+ "@abtnode/util": "1.16.23-beta-06c3a221",
26
26
  "@arcblock/did": "1.18.108",
27
- "@arcblock/nft-display": "2.9.15",
27
+ "@arcblock/nft-display": "2.9.17",
28
+ "@arcblock/validator": "^1.18.108",
28
29
  "@arcblock/vc": "1.18.108",
29
- "@blocklet/constant": "1.16.23-beta-7b5b0175",
30
- "@blocklet/meta": "1.16.23-beta-7b5b0175",
30
+ "@blocklet/constant": "1.16.23-beta-06c3a221",
31
+ "@blocklet/meta": "1.16.23-beta-06c3a221",
32
+ "@ocap/client": "^1.18.108",
31
33
  "@ocap/mcrypto": "1.18.108",
32
34
  "@ocap/util": "1.18.108",
33
35
  "@ocap/wallet": "1.18.108",
36
+ "axios": "^0.27.2",
34
37
  "flat": "^5.0.2",
35
38
  "fs-extra": "^11.2.0",
39
+ "is-url": "^1.2.4",
36
40
  "joi": "17.11.0",
37
41
  "jsonwebtoken": "^9.0.0",
38
42
  "lodash": "^4.17.21",
39
43
  "p-retry": "4.6.1",
40
44
  "semver": "^7.3.8",
41
45
  "transliteration": "^2.3.5",
46
+ "ufo": "^1.3.2",
42
47
  "url-join": "^4.0.1"
43
48
  },
44
49
  "devDependencies": {
45
50
  "jest": "^29.7.0"
46
51
  },
47
- "gitHead": "d1386002a4a10a65bf53cfca9754ea077c1d5983"
52
+ "gitHead": "10d2351eb9a0a222781d803798e15390622e421b"
48
53
  }