@verii/server-credentialagent 1.1.0-pre.1765354771 → 1.1.0-pre.1765745740

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verii/server-credentialagent",
3
- "version": "1.1.0-pre.1765354771",
3
+ "version": "1.1.0-pre.1765745740",
4
4
  "description": "Credential Agent application",
5
5
  "main": "src/index.js",
6
6
  "repository": "https://github.com/LFDT-Verii/core",
@@ -40,31 +40,31 @@
40
40
  "@fastify/view": "^11.0.0",
41
41
  "@spencejs/spence-factories": "^0.10.2",
42
42
  "@spencejs/spence-mongo-repos": "^0.10.2",
43
- "@verii/auth": "1.1.0-pre.1765354771",
44
- "@verii/base-contract-io": "1.1.0-pre.1765354771",
45
- "@verii/blockchain-functions": "1.1.0-pre.1765354771",
46
- "@verii/common-fetchers": "1.1.0-pre.1765354771",
47
- "@verii/common-functions": "1.1.0-pre.1765354771",
48
- "@verii/common-schemas": "1.1.0-pre.1765354771",
49
- "@verii/config": "1.1.0-pre.1765354771",
50
- "@verii/contract-permissions": "1.1.0-pre.1765354771",
51
- "@verii/crypto": "1.1.0-pre.1765354771",
52
- "@verii/csv-parser": "1.1.0-pre.1765354771",
53
- "@verii/db-kms": "1.1.0-pre.1765354771",
54
- "@verii/did-doc": "1.1.0-pre.1765354771",
55
- "@verii/fastify-plugins": "1.1.0-pre.1765354771",
56
- "@verii/http-client": "1.1.0-pre.1765354771",
57
- "@verii/jwt": "1.1.0-pre.1765354771",
58
- "@verii/metadata-registration": "1.1.0-pre.1765354771",
59
- "@verii/organizations-registry": "1.1.0-pre.1765354771",
60
- "@verii/rest-queries": "1.1.0-pre.1765354771",
61
- "@verii/server-provider": "1.1.0-pre.1765354771",
62
- "@verii/spencer-mongo-extensions": "1.1.0-pre.1765354771",
63
- "@verii/test-regexes": "1.1.0-pre.1765354771",
64
- "@verii/validation": "1.1.0-pre.1765354771",
65
- "@verii/vc-checks": "1.1.0-pre.1765354771",
66
- "@verii/verii-issuing": "1.1.0-pre.1765354771",
67
- "@verii/verii-verification": "1.1.0-pre.1765354771",
43
+ "@verii/auth": "1.1.0-pre.1765745740",
44
+ "@verii/base-contract-io": "1.1.0-pre.1765745740",
45
+ "@verii/blockchain-functions": "1.1.0-pre.1765745740",
46
+ "@verii/common-fetchers": "1.1.0-pre.1765745740",
47
+ "@verii/common-functions": "1.1.0-pre.1765745740",
48
+ "@verii/common-schemas": "1.1.0-pre.1765745740",
49
+ "@verii/config": "1.1.0-pre.1765745740",
50
+ "@verii/contract-permissions": "1.1.0-pre.1765745740",
51
+ "@verii/crypto": "1.1.0-pre.1765745740",
52
+ "@verii/csv-parser": "1.1.0-pre.1765745740",
53
+ "@verii/db-kms": "1.1.0-pre.1765745740",
54
+ "@verii/did-doc": "1.1.0-pre.1765745740",
55
+ "@verii/fastify-plugins": "1.1.0-pre.1765745740",
56
+ "@verii/http-client": "1.1.0-pre.1765745740",
57
+ "@verii/jwt": "1.1.0-pre.1765745740",
58
+ "@verii/metadata-registration": "1.1.0-pre.1765745740",
59
+ "@verii/organizations-registry": "1.1.0-pre.1765745740",
60
+ "@verii/rest-queries": "1.1.0-pre.1765745740",
61
+ "@verii/server-provider": "1.1.0-pre.1765745740",
62
+ "@verii/spencer-mongo-extensions": "1.1.0-pre.1765745740",
63
+ "@verii/test-regexes": "1.1.0-pre.1765745740",
64
+ "@verii/validation": "1.1.0-pre.1765745740",
65
+ "@verii/vc-checks": "1.1.0-pre.1765745740",
66
+ "@verii/verii-issuing": "1.1.0-pre.1765745740",
67
+ "@verii/verii-verification": "1.1.0-pre.1765745740",
68
68
  "ajv": "8.17.1",
69
69
  "canonicalize": "^2.1.0",
70
70
  "date-fns": "~4.1.0",
@@ -83,10 +83,10 @@
83
83
  },
84
84
  "devDependencies": {
85
85
  "@spencejs/spence-config": "0.10.2",
86
- "@verii/endpoints-organizations-registrar": "1.1.0-pre.1765354771",
87
- "@verii/sample-data": "1.1.0-pre.1765354771",
86
+ "@verii/endpoints-organizations-registrar": "1.1.0-pre.1765745740",
87
+ "@verii/sample-data": "1.1.0-pre.1765745740",
88
88
  "@verii/test-regexes": "0.5.0-build",
89
- "@verii/tests-helpers": "1.1.0-pre.1765354771",
89
+ "@verii/tests-helpers": "1.1.0-pre.1765745740",
90
90
  "cheerio": "1.1.2",
91
91
  "dotenv": "16.6.1",
92
92
  "eslint": "8.57.1",
@@ -107,5 +107,5 @@
107
107
  "prettier": "2.8.8",
108
108
  "qs": "6.14.0"
109
109
  },
110
- "gitHead": "95b0dfb9c8d00b85e3c0ca240ea38048505a8681"
110
+ "gitHead": "46b3c80a3277ce91fff1e698ac201602b6141d18"
111
111
  }
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * Copyright 2023 Velocity Team
3
3
  *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * Licensed under the Apache License, Version 2.0 (the 'License');
5
5
  * you may not use this file except in compliance with the License.
6
6
  * You may obtain a copy of the License at
7
7
  *
8
8
  * http://www.apache.org/licenses/LICENSE-2.0
9
9
  *
10
10
  * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * distributed under the License is distributed on an 'AS IS' BASIS,
12
12
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
@@ -22,6 +22,7 @@ const {
22
22
  forEach,
23
23
  isEmpty,
24
24
  values,
25
+ identity,
25
26
  } = require('lodash/fp');
26
27
  const { nanoid } = require('nanoid');
27
28
  const fastifyView = require('@fastify/view');
@@ -81,6 +82,10 @@ const appRedirectController = async (fastify) => {
81
82
  items: { type: 'string' },
82
83
  },
83
84
  inspectorDid: { type: 'array', items: { type: 'string' } },
85
+ providers: {
86
+ type: 'array',
87
+ items: { type: 'string' },
88
+ },
84
89
  },
85
90
  required: ['request_uri', 'exchange_type'],
86
91
  },
@@ -124,9 +129,13 @@ const appRedirectController = async (fastify) => {
124
129
  };
125
130
 
126
131
  const validateInspectorDid = ({ exchangeType, inspectorDid }) => {
127
- if (exchangeType === EXCHANGE_TYPE.inspect && isEmpty(inspectorDid)) {
132
+ if (
133
+ (exchangeType === EXCHANGE_TYPE.inspect ||
134
+ exchangeType === EXCHANGE_TYPE.claim) &&
135
+ isEmpty(inspectorDid)
136
+ ) {
128
137
  throw new Error.BadRequest(
129
- 'inspectorDid should be present for exchange_type = "inspect"'
138
+ `inspectorDid should be present for exchange_type = "${exchangeType}"`
130
139
  );
131
140
  }
132
141
  if (exchangeType === EXCHANGE_TYPE.issue && !isEmpty(inspectorDid)) {
@@ -143,30 +152,101 @@ const processingLinks = (context) => {
143
152
  request_uri: requestUriItems,
144
153
  inspectorDid: inspectorDidItems = [],
145
154
  vendorOriginContext: vendorOriginContextItems = [],
155
+ providers: providersItems,
146
156
  },
147
157
  } = context;
148
158
 
159
+ const parsedProviders = parseProviders(providersItems, exchangeType);
160
+
149
161
  const parsedLinks = flow(
150
162
  (items) => zip(items, range(0, items.length)),
151
163
  map(([value, index]) => ({
152
164
  requestUri: value,
153
165
  vendorOriginContext: vendorOriginContextItems[index],
154
166
  inspectorDid: inspectorDidItems[index],
167
+ ...(parsedProviders ? { providers: parsedProviders } : {}),
155
168
  }))
156
169
  )(requestUriItems);
157
170
 
158
171
  const deeplink = createDeepLinkUrl(exchangeType, context);
159
- forEach(({ requestUri, inspectorDid, vendorOriginContext }) => {
160
- flow(
161
- appendSearchParam('request_uri', requestUri),
162
- appendSearchParam('inspectorDid', inspectorDid),
163
- appendSearchParam('vendorOriginContext', vendorOriginContext)
164
- )(deeplink);
165
- }, parsedLinks);
172
+ forEach(
173
+ ({
174
+ requestUri,
175
+ inspectorDid,
176
+ vendorOriginContext,
177
+ providers: providersItem,
178
+ }) => {
179
+ const appendParams = flow(
180
+ appendSearchParam('request_uri', requestUri),
181
+ appendSearchParam('inspectorDid', inspectorDid),
182
+ appendSearchParam('vendorOriginContext', vendorOriginContext),
183
+ providersItem
184
+ ? appendSearchParam('providers', JSON.stringify(providersItem))
185
+ : identity
186
+ );
187
+
188
+ appendParams(deeplink);
189
+ },
190
+ parsedLinks
191
+ );
166
192
 
167
193
  return { deeplink };
168
194
  };
169
195
 
196
+ const parseProviders = (providersItems, exchangeType) => {
197
+ if (exchangeType !== EXCHANGE_TYPE.claim) {
198
+ return null;
199
+ }
200
+
201
+ if (!providersItems) {
202
+ throw new Error.BadRequest(
203
+ 'providers parameter is required for exchange_type = "claim.wizard"'
204
+ );
205
+ }
206
+
207
+ const normalizedProviders = Array.isArray(providersItems)
208
+ ? providersItems
209
+ : [providersItems];
210
+
211
+ let parsedProviders;
212
+
213
+ try {
214
+ parsedProviders = normalizedProviders.map((provider) => {
215
+ if (typeof provider === 'string') {
216
+ return JSON.parse(provider);
217
+ }
218
+ return provider;
219
+ });
220
+ } catch (error) {
221
+ throw new Error.BadRequest('Invalid JSON format for providers parameter');
222
+ }
223
+
224
+ const PROVIDERS_REQUIRED_PROPS = ['name', 'id', 'logo', 'category'];
225
+
226
+ return parsedProviders.map((item, index) => {
227
+ if (!item || typeof item !== 'object') {
228
+ throw new Error.BadRequest(
229
+ `Provider at index ${index} must be an object`
230
+ );
231
+ }
232
+
233
+ for (const prop of PROVIDERS_REQUIRED_PROPS) {
234
+ if (!item[prop] || typeof item[prop] !== 'string') {
235
+ throw new Error.BadRequest(
236
+ `Provider at index ${index} missing required property: ${prop}`
237
+ );
238
+ }
239
+ }
240
+
241
+ return {
242
+ name: item.name,
243
+ logo: item.logo,
244
+ category: item.category,
245
+ id: item.id,
246
+ };
247
+ });
248
+ };
249
+
170
250
  // eslint-disable-next-line better-mutation/no-mutation
171
251
  appRedirectController.prefixOverride = '';
172
252
 
@@ -17,6 +17,7 @@
17
17
  const EXCHANGE_TYPE = {
18
18
  inspect: 'inspect',
19
19
  issue: 'issue',
20
+ claim: 'claim.wizard',
20
21
  };
21
22
 
22
23
  module.exports = {
@@ -126,6 +126,18 @@ describe('app redirect controller test', () => {
126
126
  );
127
127
  });
128
128
 
129
+ it('should 400 if exchange_type is claim.wizard and inspectorDid not provided', async () => {
130
+ setupNock();
131
+ const response = await fastify.injectJson({
132
+ method: 'GET',
133
+ url: `${appRedirectUrl}?request_uri=uri&exchange_type=claim.wizard`,
134
+ });
135
+ expect(response.statusCode).toEqual(400);
136
+ expect(response.json.message).toEqual(
137
+ 'inspectorDid should be present for exchange_type = "claim.wizard"'
138
+ );
139
+ });
140
+
129
141
  it('should 400 if exchange_type is not one of allowed values', async () => {
130
142
  setupNock();
131
143
  const response = await fastify.injectJson({
@@ -187,7 +199,7 @@ describe('app redirect controller test', () => {
187
199
  expect(scriptTag.attr('nonce')).toEqual(nonceFromCspHeader);
188
200
  });
189
201
 
190
- it('should include vnf wallet selection mount point', async () => {
202
+ it('should include vnf wallet selection mount point if inspect', async () => {
191
203
  const url =
192
204
  // eslint-disable-next-line max-len
193
205
  'http%3A%2F%2Flocalhost.test%2Fapi%2Fholder%2Fv0.6%2Forg%2Fdid%3Aion%3A4131209321321323123e%2Fissue%2Fget-credential-manifest%3Fexchange_id%3D5f123eab4362bb2e%26credential_types%3DPastEmploymentPosition%26id%3DsecretId';
@@ -205,4 +217,116 @@ describe('app redirect controller test', () => {
205
217
  expect(scriptTag.attr('data-deeplink')).toEqual(deeplink);
206
218
  expect(scriptTag.attr('data-automode')).toEqual('');
207
219
  });
220
+
221
+ it('should include vnf wallet selection mount point if claim.wizard with one provider', async () => {
222
+ setupNock();
223
+ const url =
224
+ // eslint-disable-next-line max-len
225
+ 'http%3A%2F%2Flocalhost.test%2Fapi%2Fholder%2Fv0.6%2Forg%2Fdid%3Aion%3A4131209321321323123e%2Fissue%2Fget-credential-manifest%3Fexchange_id%3D5f123eab4362bb2e%26credential_types%3DPastEmploymentPosition%26id%3DsecretId';
226
+ const response = await fastify.injectJson({
227
+ method: 'GET',
228
+ // eslint-disable-next-line max-len
229
+ url: `${appRedirectUrl}?request_uri=${url}&exchange_type=claim.wizard&inspectorDid=321123&providers=%7B%22logo%22%3A%22https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fa%2Faa%2FLinkedIn_2021.svg%22%2C%22name%22%3A%22LinkedIn%22%2C%22category%22%3A%22Personal%20Records%22%2C%22id%22%3A%22a9f1063c-06b7-476a-8410-9ff6e427e637%22%7D`,
230
+ });
231
+
232
+ expect(response.statusCode).toEqual(200);
233
+ const $ = cheerio.load(response.body);
234
+
235
+ const scriptTag = $('html > body > #vnf-wallet-selection');
236
+ const deeplink =
237
+ // eslint-disable-next-line max-len
238
+ 'velocity-test://claim.wizard?request_uri=http%3A%2F%2Flocalhost.test%2Fapi%2Fholder%2Fv0.6%2Forg%2Fdid%3Aion%3A4131209321321323123e%2Fissue%2Fget-credential-manifest%3Fexchange_id%3D5f123eab4362bb2e%26credential_types%3DPastEmploymentPosition%26id%3DsecretId&inspectorDid=321123&providers=%5B%7B%22name%22%3A%22LinkedIn%22%2C%22logo%22%3A%22https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fa%2Faa%2FLinkedIn_2021.svg%22%2C%22category%22%3A%22Personal+Records%22%2C%22id%22%3A%22a9f1063c-06b7-476a-8410-9ff6e427e637%22%7D%5D';
239
+ expect(scriptTag.attr('data-deeplink')).toEqual(deeplink);
240
+ expect(scriptTag.attr('data-automode')).toEqual('');
241
+ });
242
+
243
+ it('should throw if claim.wizard and provider is missing required properties: id', async () => {
244
+ setupNock();
245
+ const url =
246
+ // eslint-disable-next-line max-len
247
+ 'http%3A%2F%2Flocalhost.test%2Fapi%2Fholder%2Fv0.6%2Forg%2Fdid%3Aion%3A4131209321321323123e%2Fissue%2Fget-credential-manifest%3Fexchange_id%3D5f123eab4362bb2e%26credential_types%3DPastEmploymentPosition%26id%3DsecretId';
248
+ const response = await fastify.injectJson({
249
+ method: 'GET',
250
+ // eslint-disable-next-line max-len
251
+ url: `${appRedirectUrl}?request_uri=${url}&exchange_type=claim.wizard&inspectorDid=321123&providers=%7B%22logo%22%3A%22https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fa%2Faa%2FLinkedIn_2021.svg%22%2C%22name%22%3A%22LinkedIn%22%7D`,
252
+ });
253
+
254
+ expect(response.statusCode).toEqual(400);
255
+ expect(response.json.message).toEqual(
256
+ 'Provider at index 0 missing required property: id'
257
+ );
258
+ });
259
+
260
+ it('should throw if claim.wizard and provider is missing required properties: name', async () => {
261
+ setupNock();
262
+ const url =
263
+ // eslint-disable-next-line max-len
264
+ 'http%3A%2F%2Flocalhost.test%2Fapi%2Fholder%2Fv0.6%2Forg%2Fdid%3Aion%3A4131209321321323123e%2Fissue%2Fget-credential-manifest%3Fexchange_id%3D5f123eab4362bb2e%26credential_types%3DPastEmploymentPosition%26id%3DsecretId';
265
+ const response = await fastify.injectJson({
266
+ method: 'GET',
267
+ // eslint-disable-next-line max-len
268
+ url: `${appRedirectUrl}?request_uri=${url}&exchange_type=claim.wizard&inspectorDid=321123&providers=%7B%22logo%22%3A%22https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fa%2Faa%2FLinkedIn_2021.svg%22%2C%22id%22%3A%22LinkedIn%22%7D`,
269
+ });
270
+
271
+ expect(response.statusCode).toEqual(400);
272
+ expect(response.json.message).toEqual(
273
+ 'Provider at index 0 missing required property: name'
274
+ );
275
+ });
276
+
277
+ it('should throw if claim.wizard and provider is missing required properties: logo', async () => {
278
+ setupNock();
279
+ const url =
280
+ // eslint-disable-next-line max-len
281
+ 'http%3A%2F%2Flocalhost.test%2Fapi%2Fholder%2Fv0.6%2Forg%2Fdid%3Aion%3A4131209321321323123e%2Fissue%2Fget-credential-manifest%3Fexchange_id%3D5f123eab4362bb2e%26credential_types%3DPastEmploymentPosition%26id%3DsecretId';
282
+ const response = await fastify.injectJson({
283
+ method: 'GET',
284
+ // eslint-disable-next-line max-len
285
+ url: `${appRedirectUrl}?request_uri=${url}&exchange_type=claim.wizard&inspectorDid=321123&providers=%7B%22name%22%3A%22LinkedIn%22%2C%22category%22%3A%22Personal%20Records%22%2C%22id%22%3A%22a9f1063c-06b7-476a-8410-9ff6e427e637%22%7D`,
286
+ });
287
+
288
+ expect(response.statusCode).toEqual(400);
289
+ expect(response.json.message).toEqual(
290
+ 'Provider at index 0 missing required property: logo'
291
+ );
292
+ });
293
+
294
+ it('should include vnf wallet selection mount point if claim.wizard with multiple providers', async () => {
295
+ setupNock();
296
+ const url =
297
+ // eslint-disable-next-line max-len
298
+ 'http%3A%2F%2Flocalhost.test%2Fapi%2Fholder%2Fv0.6%2Forg%2Fdid%3Aion%3A4131209321321323123e%2Fissue%2Fget-credential-manifest%3Fexchange_id%3D5f123eab4362bb2e%26credential_types%3DPastEmploymentPosition%26id%3DsecretId';
299
+ const response = await fastify.injectJson({
300
+ method: 'GET',
301
+ // eslint-disable-next-line max-len
302
+ url: `${appRedirectUrl}?request_uri=${url}&exchange_type=claim.wizard&inspectorDid=321123&providers=%7B%22logo%22%3A%22https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fa%2Faa%2FLinkedIn_2021.svg%22%2C%22name%22%3A%22LinkedIn%22%2C%22category%22%3A%22Personal%20Records%22%2C%22id%22%3A%22a9f1063c-06b7-476a-8410-9ff6e427e637%22%7D&providers=%7B%22logo%22%3A%22https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fa%2Faa%2FLinkedIn_2021.svg%22%2C%22name%22%3A%22LinkedIn%22%2C%22category%22%3A%22Personal%20Records%22%2C%22id%22%3A%22a9f1063c-06b7-476a-8410-9ff6e427e637%22%7D`,
303
+ });
304
+
305
+ expect(response.statusCode).toEqual(200);
306
+ const $ = cheerio.load(response.body);
307
+
308
+ const scriptTag = $('html > body > #vnf-wallet-selection');
309
+ const deeplink =
310
+ // eslint-disable-next-line max-len
311
+ 'velocity-test://claim.wizard?request_uri=http%3A%2F%2Flocalhost.test%2Fapi%2Fholder%2Fv0.6%2Forg%2Fdid%3Aion%3A4131209321321323123e%2Fissue%2Fget-credential-manifest%3Fexchange_id%3D5f123eab4362bb2e%26credential_types%3DPastEmploymentPosition%26id%3DsecretId&inspectorDid=321123&providers=%5B%7B%22name%22%3A%22LinkedIn%22%2C%22logo%22%3A%22https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fa%2Faa%2FLinkedIn_2021.svg%22%2C%22category%22%3A%22Personal+Records%22%2C%22id%22%3A%22a9f1063c-06b7-476a-8410-9ff6e427e637%22%7D%2C%7B%22name%22%3A%22LinkedIn%22%2C%22logo%22%3A%22https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fa%2Faa%2FLinkedIn_2021.svg%22%2C%22category%22%3A%22Personal+Records%22%2C%22id%22%3A%22a9f1063c-06b7-476a-8410-9ff6e427e637%22%7D%5D';
312
+ expect(scriptTag.attr('data-deeplink')).toEqual(deeplink);
313
+ expect(scriptTag.attr('data-automode')).toEqual('');
314
+ });
315
+
316
+ it('should throw an error if claim.wizard and empty providers', async () => {
317
+ setupNock();
318
+ const url =
319
+ // eslint-disable-next-line max-len
320
+ 'http%3A%2F%2Flocalhost.test%2Fapi%2Fholder%2Fv0.6%2Forg%2Fdid%3Aion%3A4131209321321323123e%2Fissue%2Fget-credential-manifest%3Fexchange_id%3D5f123eab4362bb2e%26credential_types%3DPastEmploymentPosition%26id%3DsecretId';
321
+ const response = await fastify.injectJson({
322
+ method: 'GET',
323
+ // eslint-disable-next-line max-len
324
+ url: `${appRedirectUrl}?request_uri=${url}&exchange_type=claim.wizard&inspectorDid=321123`,
325
+ });
326
+
327
+ expect(response.statusCode).toEqual(400);
328
+ expect(response.json.message).toEqual(
329
+ 'providers parameter is required for exchange_type = "claim.wizard"'
330
+ );
331
+ });
208
332
  });