@velocitycareerlabs/server-webwallet 1.25.0-dev-build.12642c864

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 (193) hide show
  1. package/.localdev.env +57 -0
  2. package/.standalone.env +22 -0
  3. package/LICENSE +248 -0
  4. package/README.md +11 -0
  5. package/jest.config.js +20 -0
  6. package/migrate-mongo.config.js +23 -0
  7. package/package.json +72 -0
  8. package/src/config/config.js +74 -0
  9. package/src/controllers/accounts/autohooks.js +28 -0
  10. package/src/controllers/accounts/controller.js +209 -0
  11. package/src/controllers/accounts/schemas/index.js +6 -0
  12. package/src/controllers/accounts/schemas/webwallet-accounts-request.schema.js +25 -0
  13. package/src/controllers/accounts/schemas/webwallet-accounts-response.schema.js +72 -0
  14. package/src/controllers/accounts/schemas/webwallet-accounts-update-request.schema.js +23 -0
  15. package/src/controllers/accounts/schemas/webwallet-test-personas-response.schema.js +110 -0
  16. package/src/controllers/consent/autohooks.js +14 -0
  17. package/src/controllers/consent/controller.js +91 -0
  18. package/src/controllers/consent/schemas/index.js +3 -0
  19. package/src/controllers/consent/schemas/webwallet-consent-response.schema.js +23 -0
  20. package/src/controllers/credentials/autohooks.js +36 -0
  21. package/src/controllers/credentials/controller.js +92 -0
  22. package/src/controllers/credentials/schemas/index.js +4 -0
  23. package/src/controllers/credentials/schemas/webwallet-credential-categories-response.schema.js +21 -0
  24. package/src/controllers/credentials/schemas/webwallet-credentials-response.schema.js +29 -0
  25. package/src/controllers/disclosures/autohooks.js +21 -0
  26. package/src/controllers/disclosures/controller.js +168 -0
  27. package/src/controllers/disclosures/schemas/index.js +7 -0
  28. package/src/controllers/disclosures/schemas/webwallet-accept-presentation-request-body.schema.js +28 -0
  29. package/src/controllers/disclosures/schemas/webwallet-accept-presentation-response.schema.js +16 -0
  30. package/src/controllers/disclosures/schemas/webwallet-get-disclosures-response.schema.js +18 -0
  31. package/src/controllers/disclosures/schemas/webwallet-get-presentation-request-query.schema.js +21 -0
  32. package/src/controllers/disclosures/schemas/webwallet-get-presentation-request-response.schema.js +16 -0
  33. package/src/controllers/feedback/autohooks.js +11 -0
  34. package/src/controllers/feedback/controller.js +48 -0
  35. package/src/controllers/issuing/common-schemas/index.js +10 -0
  36. package/src/controllers/issuing/common-schemas/webwallet-get-credential-manifest-query.schema.js +27 -0
  37. package/src/controllers/issuing/common-schemas/webwallet-get-credential-manifest-response.schema.js +31 -0
  38. package/src/controllers/issuing/deep-link/autohooks.js +51 -0
  39. package/src/controllers/issuing/deep-link/controller.js +192 -0
  40. package/src/controllers/issuing/deep-link/schemas/index.js +6 -0
  41. package/src/controllers/issuing/deep-link/schemas/webwallet-deep-link-accept-offers-request.schema.js +30 -0
  42. package/src/controllers/issuing/deep-link/schemas/webwallet-deep-link-accept-offers-response.schema.js +36 -0
  43. package/src/controllers/issuing/deep-link/schemas/webwallet-deep-link-offers-request.schema.js +28 -0
  44. package/src/controllers/issuing/deep-link/schemas/webwallet-deep-link-offers-response.schema.js +48 -0
  45. package/src/controllers/issuing/identity-credentials/autohooks.js +42 -0
  46. package/src/controllers/issuing/identity-credentials/controller.js +188 -0
  47. package/src/controllers/issuing/identity-credentials/schemas/index.js +6 -0
  48. package/src/controllers/issuing/identity-credentials/schemas/webwallet-identity-credentials-confirm-body.schema.js +20 -0
  49. package/src/controllers/issuing/identity-credentials/schemas/webwallet-identity-credentials-confirm-response.schema.js +37 -0
  50. package/src/controllers/issuing/identity-credentials/schemas/webwallet-identity-credentials-request-code-body.schema.js +14 -0
  51. package/src/controllers/issuing/identity-credentials/schemas/webwallet-identity-credentials-request-code-params.schema.js +19 -0
  52. package/src/controllers/linkedin/autohooks.js +16 -0
  53. package/src/controllers/linkedin/controller.js +276 -0
  54. package/src/controllers/linkedin/schemas/index.js +3 -0
  55. package/src/controllers/linkedin/schemas/webwallet-linkedin-me-response.schema.js +14 -0
  56. package/src/controllers/root/controller.js +29 -0
  57. package/src/entities/.gitkeep +0 -0
  58. package/src/entities/accounts/constants.js +5 -0
  59. package/src/entities/accounts/domains/extract-auth0-id-token-or-access-token.js +22 -0
  60. package/src/entities/accounts/domains/index.js +5 -0
  61. package/src/entities/accounts/domains/validate-account-permissions.js +36 -0
  62. package/src/entities/accounts/domains/validate-logo-size.js +25 -0
  63. package/src/entities/accounts/index.js +5 -0
  64. package/src/entities/accounts/repos/accounts.repo.js +63 -0
  65. package/src/entities/credentials/domains/index.js +3 -0
  66. package/src/entities/credentials/domains/offer-to-credential-mongo-dto.js +43 -0
  67. package/src/entities/credentials/index.js +6 -0
  68. package/src/entities/credentials/orchestrators/index.js +3 -0
  69. package/src/entities/credentials/orchestrators/load-additional-render-info.js +65 -0
  70. package/src/entities/credentials/repos/credentials.repo.js +132 -0
  71. package/src/entities/credentials/schemas/index.js +4 -0
  72. package/src/entities/credentials/schemas/webwallet-credential-response.schema.js +138 -0
  73. package/src/entities/credentials/schemas/webwallet-credentials-response.schemas.js +95 -0
  74. package/src/entities/credentials/schemas/webwallet-display-descriptor-response.schema.js +106 -0
  75. package/src/entities/disclosures/index.js +4 -0
  76. package/src/entities/disclosures/repos/disclosures.repo.js +31 -0
  77. package/src/entities/disclosures/schemas/index.js +10 -0
  78. package/src/entities/disclosures/schemas/webwallet-disclosure.schema.js +39 -0
  79. package/src/entities/disclosures/schemas/webwallet-presentation-request-field.schema.js +45 -0
  80. package/src/entities/disclosures/schemas/webwallet-presentation-request-filter.schema.js +30 -0
  81. package/src/entities/disclosures/schemas/webwallet-presentation-request-format.schema.js +36 -0
  82. package/src/entities/disclosures/schemas/webwallet-presentation-request-input-descriptors.schema.js +111 -0
  83. package/src/entities/disclosures/schemas/webwallet-presentation-request-schema.schema.js +14 -0
  84. package/src/entities/disclosures/schemas/webwallet-presentation-request-submission-requirements.schema.js +37 -0
  85. package/src/entities/disclosures/schemas/webwallet-presentation-request.schema.js +79 -0
  86. package/src/entities/index.js +6 -0
  87. package/src/entities/issuing/domain/constants.js +8 -0
  88. package/src/entities/issuing/domain/does-user-have-fresh-pending-verification.js +26 -0
  89. package/src/entities/issuing/domain/get-credentials-from-offers.js +18 -0
  90. package/src/entities/issuing/domain/index.js +5 -0
  91. package/src/entities/issuing/index.js +5 -0
  92. package/src/entities/issuing/orchestrators/build-issuing-input-credentials.js +35 -0
  93. package/src/entities/issuing/orchestrators/generate-offers-by-deep-link.js +74 -0
  94. package/src/entities/issuing/orchestrators/get-identity-issuer.js +35 -0
  95. package/src/entities/issuing/orchestrators/get-identity-offers-by-deeplink.js +74 -0
  96. package/src/entities/issuing/orchestrators/index.js +6 -0
  97. package/src/entities/issuing/schemas/index.js +9 -0
  98. package/src/entities/issuing/schemas/webwallet-credential-manifest.schema.js +198 -0
  99. package/src/errors/error-codes.js +12 -0
  100. package/src/fetchers/career-wallet/create-account-fetcher.js +7 -0
  101. package/src/fetchers/career-wallet/create-did-fetcher.js +17 -0
  102. package/src/fetchers/career-wallet/get-app-config.js +11 -0
  103. package/src/fetchers/career-wallet/get-consents.js +13 -0
  104. package/src/fetchers/career-wallet/get-credential-categories.js +7 -0
  105. package/src/fetchers/career-wallet/get-personas.js +7 -0
  106. package/src/fetchers/career-wallet/index.js +12 -0
  107. package/src/fetchers/career-wallet/post-consent.js +9 -0
  108. package/src/fetchers/career-wallet/send-feedback.js +14 -0
  109. package/src/fetchers/career-wallet/sign-fetcher.js +14 -0
  110. package/src/fetchers/career-wallet/verify-id-credential-confirm-code.js +19 -0
  111. package/src/fetchers/career-wallet/verify-id-credential-request-code.js +20 -0
  112. package/src/fetchers/index.js +4 -0
  113. package/src/fetchers/lib-api/get-credential-display-schema.js +12 -0
  114. package/src/fetchers/lib-api/index.js +3 -0
  115. package/src/fetchers/linkedin/create-linkedin-post.js +45 -0
  116. package/src/fetchers/linkedin/get-access-token.js +26 -0
  117. package/src/fetchers/linkedin/get-linkedin-user-email.js +13 -0
  118. package/src/fetchers/linkedin/get-linkedin-user-id.js +16 -0
  119. package/src/fetchers/linkedin/index.js +9 -0
  120. package/src/fetchers/linkedin/register-image-to-upload.js +30 -0
  121. package/src/fetchers/linkedin/revoke-linkedin-access.js +20 -0
  122. package/src/fetchers/linkedin/upload-image-to-linkedin.js +16 -0
  123. package/src/index.js +15 -0
  124. package/src/init-server.js +108 -0
  125. package/src/plugins/crypto-services/index.js +5 -0
  126. package/src/plugins/crypto-services/jwt-sign-service-impl.js +72 -0
  127. package/src/plugins/crypto-services/jwt-verify-service-impl.js +21 -0
  128. package/src/plugins/crypto-services/key-service-impl.js +28 -0
  129. package/src/plugins/fetch-errors-handler-plugin.js +64 -0
  130. package/src/plugins/index.js +4 -0
  131. package/src/plugins/vnf-sdk-plugin.js +53 -0
  132. package/src/standalone.js +8 -0
  133. package/test/accounts-controller.test.js +618 -0
  134. package/test/consent-controller.test.js +185 -0
  135. package/test/credentials-controller.test.js +307 -0
  136. package/test/crypro-services/jwt-sign-service-impl.test.js +83 -0
  137. package/test/crypro-services/jwt-verify-service-impl.test.js +27 -0
  138. package/test/crypro-services/key-service-impl.test.js +76 -0
  139. package/test/crypro-services/mocks/index.js +4 -0
  140. package/test/crypro-services/mocks/jwt-mock.js +15 -0
  141. package/test/crypro-services/mocks/public-jwk.js +14 -0
  142. package/test/disclosures-controller/disclosure-credentials.test.js +428 -0
  143. package/test/disclosures-controller/get-disclosures.test.js +169 -0
  144. package/test/disclosures-controller/mocks/get-credential-manifest.js +20 -0
  145. package/test/disclosures-controller/mocks/index.js +6 -0
  146. package/test/disclosures-controller/mocks/presentation-request.js +32 -0
  147. package/test/disclosures-controller/mocks/presentation-submission.js +21 -0
  148. package/test/disclosures-controller/mocks/submission-result.js +19 -0
  149. package/test/factories/accounts.js +25 -0
  150. package/test/factories/credentials.js +66 -0
  151. package/test/factories/disclosures.js +106 -0
  152. package/test/feedback-controller.test.js +125 -0
  153. package/test/fetch-errors-handler-plugin.test.js +97 -0
  154. package/test/filter-deleted-credentials-extension.test.js +82 -0
  155. package/test/helpers/.env.test +10 -0
  156. package/test/helpers/nock-consent-add.js +16 -0
  157. package/test/helpers/nock-consents-get.js +15 -0
  158. package/test/helpers/nock-feedback.js +9 -0
  159. package/test/helpers/nock-linkedin-access-token.js +9 -0
  160. package/test/helpers/nock-linkedin-email.js +15 -0
  161. package/test/helpers/nock-linkedin-image-register.js +28 -0
  162. package/test/helpers/nock-linkedin-me.js +13 -0
  163. package/test/helpers/nock-linkedin-post.js +37 -0
  164. package/test/helpers/nock-linkedin-revoke.js +9 -0
  165. package/test/helpers/nock-test-personas.js +13 -0
  166. package/test/helpers/webwallet-build-fastify.js +17 -0
  167. package/test/issuing-controller/issuing-by-deeplink-empty-offers.test.js +142 -0
  168. package/test/issuing-controller/issuing-by-deeplink-failed-offers.test.js +142 -0
  169. package/test/issuing-controller/issuing-by-deeplink.test.js +492 -0
  170. package/test/issuing-controller/issuing-identity-credentials.test.js +377 -0
  171. package/test/issuing-controller/mocks/accept-offers-response.js +90 -0
  172. package/test/issuing-controller/mocks/accepted-credentials.js +47 -0
  173. package/test/issuing-controller/mocks/credential-manifest-presentation.js +72 -0
  174. package/test/issuing-controller/mocks/credential-manifest.js +45 -0
  175. package/test/issuing-controller/mocks/identity-issuing/accept-offers.js +22 -0
  176. package/test/issuing-controller/mocks/identity-issuing/confirm-verification-code.js +5 -0
  177. package/test/issuing-controller/mocks/identity-issuing/get-app-config.js +52 -0
  178. package/test/issuing-controller/mocks/identity-issuing/get-organization.js +118 -0
  179. package/test/issuing-controller/mocks/identity-issuing/index.js +6 -0
  180. package/test/issuing-controller/mocks/index.js +6 -0
  181. package/test/issuing-controller/mocks/issuers.js +126 -0
  182. package/test/issuing-controller/mocks/offers.js +48 -0
  183. package/test/issuing-controller/mocks/schema.js +107 -0
  184. package/test/linkedin-controller.test.js +452 -0
  185. package/test/mocks/credential-categories.js +115 -0
  186. package/test/mocks/credentials.js +15 -0
  187. package/test/mocks/didjwk.js +25 -0
  188. package/test/mocks/index.js +7 -0
  189. package/test/mocks/issuers.js +88 -0
  190. package/test/mocks/jwk.js +53 -0
  191. package/test/mocks/schema.js +107 -0
  192. package/test/root.test.js +42 -0
  193. package/test/vcl-sdk-plugin.test.js +86 -0
@@ -0,0 +1,276 @@
1
+ const {
2
+ getLinkedInAccessToken,
3
+ getLinkedInUserId,
4
+ getLinkedInUserEmail,
5
+ registerImageToUpload,
6
+ uploadImageToLinkedin,
7
+ createLinkedinPost,
8
+ revokeLinkedinAccess,
9
+ } = require('../../fetchers/linkedin');
10
+
11
+ /**
12
+ * @param {import('fastify').FastifyInstance} fastify
13
+ */
14
+ const linkedinController = async (fastify) => {
15
+ fastify.post(
16
+ '/authorize',
17
+ {
18
+ preValidation: fastify.verifyAccessToken(),
19
+ schema: fastify.autoSchema({
20
+ tags: ['webwallet'],
21
+ body: {
22
+ type: 'object',
23
+ properties: {
24
+ code: { type: 'string' },
25
+ redirectUri: { type: 'string' },
26
+ },
27
+ required: ['code'],
28
+ },
29
+ response: {
30
+ 200: {
31
+ type: 'object',
32
+ properties: {
33
+ expiresIn: { type: 'number' },
34
+ },
35
+ },
36
+ 400: {
37
+ type: 'object',
38
+ properties: {
39
+ error: { type: 'string' },
40
+ },
41
+ },
42
+ },
43
+ }),
44
+ },
45
+ async (req, reply) => {
46
+ const { access_token: accessToken, expires_in: expiresIn } =
47
+ await getLinkedInAccessToken(
48
+ { code: req.body.code, redirectUri: req.body.redirectUri || '' },
49
+ req
50
+ );
51
+
52
+ if (!accessToken || !expiresIn) {
53
+ reply.code(400);
54
+ return { error: 'Failed to get LinkedIn access token' };
55
+ }
56
+
57
+ await req.repos.accounts.updateUsingFilter(
58
+ {
59
+ filter: {
60
+ userId: req.user.sub,
61
+ },
62
+ },
63
+ {
64
+ linkedin: {
65
+ accessToken,
66
+ tokenExpiresAt: new Date(new Date().getTime() + expiresIn * 1000),
67
+ },
68
+ }
69
+ );
70
+
71
+ reply.code(200);
72
+ return { expiresIn };
73
+ }
74
+ );
75
+
76
+ fastify.get(
77
+ '/me',
78
+ {
79
+ preValidation: fastify.verifyAccessToken(),
80
+ schema: fastify.autoSchema({
81
+ tags: ['webwallet'],
82
+ response: {
83
+ 200: {
84
+ $ref: 'https://velocitycareerlabs.io/webwallet-linkedin-me-response.schema.json',
85
+ },
86
+ },
87
+ }),
88
+ },
89
+ async (req, reply) => {
90
+ const {
91
+ repos: { accounts },
92
+ user,
93
+ } = req;
94
+
95
+ const account = await accounts.findOne({
96
+ filter: { userId: user.sub },
97
+ });
98
+
99
+ if (!account.linkedin?.accessToken) {
100
+ reply.code(204);
101
+ return {};
102
+ }
103
+ try {
104
+ const {
105
+ id: linkedinUserId,
106
+ localizedLastName,
107
+ localizedFirstName,
108
+ } = await getLinkedInUserId(account.linkedin.accessToken, req);
109
+
110
+ const { elements } = await getLinkedInUserEmail(
111
+ account.linkedin.accessToken,
112
+ req
113
+ );
114
+ reply.code(200);
115
+
116
+ return {
117
+ userId: linkedinUserId,
118
+ userName: `${localizedFirstName} ${localizedLastName}`,
119
+ userEmail: elements[0]['handle~'].emailAddress,
120
+ };
121
+ } catch (error) {
122
+ if (error?.response.statusCode === 401) {
123
+ await accounts.updateUsingFilter(
124
+ {
125
+ filter: { userId: user.sub },
126
+ },
127
+ {
128
+ linkedin: {},
129
+ }
130
+ );
131
+ }
132
+ throw error;
133
+ }
134
+ }
135
+ );
136
+
137
+ fastify.post(
138
+ '/revoke',
139
+ {
140
+ preValidation: fastify.verifyAccessToken(),
141
+ schema: fastify.autoSchema({
142
+ tags: ['webwallet'],
143
+ }),
144
+ },
145
+ async (req, reply) => {
146
+ const {
147
+ repos: { accounts },
148
+ user,
149
+ } = req;
150
+
151
+ const { linkedin } = await accounts.findOne({
152
+ filter: { userId: user.sub },
153
+ });
154
+ if (linkedin?.accessToken) {
155
+ await revokeLinkedinAccess(linkedin.accessToken, req);
156
+ await accounts.updateUsingFilter(
157
+ {
158
+ filter: { userId: user.sub },
159
+ },
160
+ {
161
+ linkedin: {},
162
+ }
163
+ );
164
+ }
165
+ reply.code(204);
166
+ }
167
+ );
168
+
169
+ fastify.post(
170
+ '/feed',
171
+ {
172
+ preValidation: fastify.verifyAccessToken(),
173
+ schema: fastify.autoSchema({
174
+ body: {
175
+ type: 'object',
176
+ patternProperties: {
177
+ 'message[0-9]+': { type: 'string' },
178
+ 'logo[0-9]+': { type: 'object' },
179
+ 'credential[0-9]+': { type: 'string' },
180
+ },
181
+ },
182
+ }),
183
+ },
184
+ async (req, reply) => {
185
+ const {
186
+ repos: { accounts },
187
+ user,
188
+ } = req;
189
+
190
+ const posts = [];
191
+ Object.keys(req.body).forEach((key) => {
192
+ const [prop, index] = key.split(/[[\]]/).filter(Boolean);
193
+ if (!posts[index]) {
194
+ // eslint-disable-next-line better-mutation/no-mutation
195
+ posts[index] = {};
196
+ }
197
+ // eslint-disable-next-line better-mutation/no-mutation
198
+ posts[index][prop] = req.body[key];
199
+ });
200
+
201
+ const requiredProps = ['message', 'logo', 'credential'];
202
+ posts.forEach((post, i) => {
203
+ requiredProps.forEach((prop) => {
204
+ if (!Object.prototype.propertyIsEnumerable.call(post, prop)) {
205
+ reply
206
+ .code(400)
207
+ .send({ error: `Missing required property: ${prop}${i}` });
208
+ }
209
+ });
210
+ });
211
+ let linkedinAccessToken;
212
+ try {
213
+ const {
214
+ linkedin: { accessToken },
215
+ } = await accounts.findOne({
216
+ filter: { userId: user.sub },
217
+ });
218
+ linkedinAccessToken = accessToken;
219
+ } catch (error) {
220
+ reply.code(400);
221
+ return { error: 'LinkedIn access token not found' };
222
+ }
223
+
224
+ try {
225
+ const { id: linkedinUserId } = await getLinkedInUserId(
226
+ linkedinAccessToken,
227
+ req
228
+ );
229
+
230
+ const linkedinPosts = await Promise.all(
231
+ posts.map(async (params) => {
232
+ let imageData;
233
+ if (params.logo) {
234
+ const uploadLinkData = await registerImageToUpload(
235
+ { accessToken: linkedinAccessToken, linkedinUserId },
236
+ req
237
+ );
238
+
239
+ imageData = {
240
+ uploadUrl:
241
+ uploadLinkData.value.uploadMechanism[
242
+ 'com.linkedin.digitalmedia.uploading.MediaUploadHttpRequest'
243
+ ].uploadUrl,
244
+ asset: uploadLinkData.value.asset,
245
+ };
246
+
247
+ await uploadImageToLinkedin({
248
+ accessToken: linkedinAccessToken,
249
+ file: params.logo,
250
+ uploadUrl: imageData.uploadUrl,
251
+ });
252
+ }
253
+ return createLinkedinPost(
254
+ {
255
+ accessToken: linkedinAccessToken,
256
+ linkedinUserId,
257
+ text: params.message,
258
+ asset: imageData?.asset,
259
+ },
260
+ req
261
+ );
262
+ })
263
+ );
264
+
265
+ reply.code(201);
266
+
267
+ return linkedinPosts;
268
+ } catch (error) {
269
+ reply.code(400);
270
+ return { error: error.message };
271
+ }
272
+ }
273
+ );
274
+ };
275
+
276
+ module.exports = linkedinController;
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ ...require('./webwallet-linkedin-me-response.schema'),
3
+ };
@@ -0,0 +1,14 @@
1
+ const webWalletLinkedinMeResponseSchema = {
2
+ $id: 'https://velocitycareerlabs.io/webwallet-linkedin-me-response.schema.json',
3
+ title: 'webwallet-linkedin-me-response',
4
+ description: 'the body of response for webwallet linkedin me endpoint',
5
+ type: 'object',
6
+ properties: {
7
+ userId: { type: 'string' },
8
+ userName: { type: 'string' },
9
+ userEmail: { type: 'string' },
10
+ },
11
+ required: ['userId', 'userEmail', 'userName'],
12
+ };
13
+
14
+ module.exports = { webWalletLinkedinMeResponseSchema };
@@ -0,0 +1,29 @@
1
+ const rootRoutes = async (fastify) => {
2
+ fastify.get(
3
+ '/',
4
+ {
5
+ schema: {
6
+ description: 'Healthcheck and version info',
7
+ responses: {
8
+ 200: {
9
+ description: 'Successful response',
10
+ content: { 'text/plain': { schema: { type: 'string' } } },
11
+ },
12
+ },
13
+ tags: ['healthchecks'],
14
+ },
15
+ },
16
+ async (_res, reply) => {
17
+ reply
18
+ .status(200)
19
+ .send(
20
+ `Welcome to Webwallet Server\nVersion: ${fastify.config.version}\n`
21
+ );
22
+ }
23
+ );
24
+ };
25
+
26
+ // eslint-disable-next-line better-mutation/no-mutation
27
+ rootRoutes.prefixOverride = '';
28
+
29
+ module.exports = rootRoutes;
File without changes
@@ -0,0 +1,5 @@
1
+ const accountTypes = ['perm', 'temp'];
2
+
3
+ module.exports = {
4
+ accountTypes,
5
+ };
@@ -0,0 +1,22 @@
1
+ const newError = require('http-errors');
2
+
3
+ const extractAuth0IdTokenOrAccessToken = (req) => {
4
+ const {
5
+ body: {
6
+ account: { id_token: auth0IdToken },
7
+ },
8
+ headers: { authorization = '' },
9
+ } = req;
10
+
11
+ const [, auth0AccessToken] = authorization.split(' ');
12
+
13
+ if (!auth0IdToken && !auth0AccessToken) {
14
+ throw new newError.Unauthorized('No authorization token provided');
15
+ }
16
+
17
+ return auth0IdToken || auth0AccessToken;
18
+ };
19
+
20
+ module.exports = {
21
+ extractAuth0IdTokenOrAccessToken,
22
+ };
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ ...require('./validate-account-permissions'),
3
+ ...require('./validate-logo-size'),
4
+ ...require('./extract-auth0-id-token-or-access-token'),
5
+ };
@@ -0,0 +1,36 @@
1
+ const newError = require('http-errors');
2
+ const { get, includes } = require('lodash/fp');
3
+ const { WebWalletServerError } = require('../../../errors/error-codes');
4
+
5
+ /**
6
+ * @param {import("../repos/accounts.repo").Account} account
7
+ * @throws {Error}
8
+ */
9
+ const validateAccountPermissions = (account) => {
10
+ const emailStatus = get('verificationInfo.email.status', account);
11
+ const phoneStatus = get('verificationInfo.phone.status', account);
12
+
13
+ if (emailStatus !== 'verified') {
14
+ throw newError(403, 'Email verification is required.', {
15
+ errorCode: WebWalletServerError,
16
+ });
17
+ }
18
+
19
+ if (!includes(phoneStatus, ['skipped', 'verified'])) {
20
+ throw newError(403, 'Phone verification is required.', {
21
+ errorCode: WebWalletServerError,
22
+ });
23
+ }
24
+
25
+ if (!account.profileName) {
26
+ throw newError(403, 'Profile is not completed.', {
27
+ errorCode: WebWalletServerError,
28
+ });
29
+ }
30
+
31
+ return true;
32
+ };
33
+
34
+ module.exports = {
35
+ validateAccountPermissions,
36
+ };
@@ -0,0 +1,25 @@
1
+ const newError = require('http-errors');
2
+
3
+ /**
4
+ * @param {import('fastify').FastifyRequest} request
5
+ */
6
+ const validateLogoSize = async (request) => {
7
+ const { logo } = request.body.account;
8
+
9
+ if (!logo) {
10
+ return;
11
+ }
12
+
13
+ const buffer = Buffer.from(logo, 'base64');
14
+
15
+ const maxSizeInBytes = 100 * 1024; // 100 KB
16
+ if (buffer.length > maxSizeInBytes) {
17
+ throw newError.BadRequest(
18
+ 'Logo size is too large. Maximum size is 100 KB.'
19
+ );
20
+ }
21
+ };
22
+
23
+ module.exports = {
24
+ validateLogoSize,
25
+ };
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ accountsRepoPlugin: require('./repos/accounts.repo'),
3
+ ...require('./constants'),
4
+ ...require('./domains'),
5
+ };
@@ -0,0 +1,63 @@
1
+ const {
2
+ autoboxIdsExtension,
3
+ repoFactory,
4
+ } = require('@spencejs/spence-mongo-repos');
5
+
6
+ /**
7
+ * @typedef {Object} Account
8
+ * @property {string} id
9
+ * @property {string} userId
10
+ * @property {string} did
11
+ * @property {Object} didKeyMetadatum
12
+ * @property {string} careerWalletAccountId
13
+ * @property {string} accountType
14
+ * @property {string} profileName
15
+ * @property {string} logo
16
+ * @property {Object} [verificationInfo]
17
+ * @property {boolean} verificationInfo.createAccountMessageDisplayed
18
+ * @property {Object} [verificationInfo.email]
19
+ * @property {string} verificationInfo.email.status
20
+ * @property {string} verificationInfo.email.codeSentTo
21
+ * @property {string} verificationInfo.email.codeSentAt
22
+ * @property {Object} [verificationInfo.phone]
23
+ * @property {string} verificationInfo.phone.status
24
+ * @property {string} verificationInfo.phone.codeSentTo
25
+ * @property {string} verificationInfo.phone.codeSentAt
26
+ * @property {Object} linkedin
27
+ * @property {string} accessToken
28
+ * @property {string} tokenExpiresAt
29
+ * @property {string} createdAt
30
+ * @property {string} updatedAt
31
+ */
32
+ module.exports = (app, _, next = () => {}) => {
33
+ next();
34
+ return repoFactory(
35
+ {
36
+ name: 'accounts',
37
+ entityName: 'accounts',
38
+ defaultProjection: {
39
+ _id: 1,
40
+ userId: 1,
41
+ did: 1,
42
+ didKeyMetadatum: 1,
43
+ careerWalletAccountId: 1,
44
+ accountType: 1,
45
+ profileName: 1,
46
+ logo: 1,
47
+ consent: 1,
48
+ 'verificationInfo.createAccountMessageDisplayed': 1,
49
+ 'verificationInfo.email.status': 1,
50
+ 'verificationInfo.email.codeSentTo': -1,
51
+ 'verificationInfo.email.codeSentAt': 1,
52
+ 'verificationInfo.phone.status': 1,
53
+ 'verificationInfo.phone.codeSentTo': -1,
54
+ 'verificationInfo.phone.codeSentAt': 1,
55
+ linkedin: 1,
56
+ createdAt: 1,
57
+ updatedAt: 1,
58
+ },
59
+ extensions: [autoboxIdsExtension],
60
+ },
61
+ app
62
+ );
63
+ };
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ ...require('./offer-to-credential-mongo-dto'),
3
+ };
@@ -0,0 +1,43 @@
1
+ const offerToCredentialMongoDTO = (auth0UserId, userDid, credential) => {
2
+ const {
3
+ vc: {
4
+ type,
5
+ id: did,
6
+ credentialStatus,
7
+ linkCodeCommitment,
8
+ issuer,
9
+ contentHash,
10
+ credentialSchema,
11
+ vnfProtocolVersion,
12
+ '@context': context,
13
+ credentialSubject,
14
+ relatedResource,
15
+ },
16
+ jti,
17
+ iss,
18
+ encodedCredential,
19
+ } = credential;
20
+
21
+ return {
22
+ type,
23
+ did,
24
+ auth0UserId,
25
+ userDid,
26
+ credentialStatus,
27
+ linkCodeCommitment,
28
+ issuer,
29
+ relatedResource,
30
+ contentHash,
31
+ credentialSchema,
32
+ vnfProtocolVersion,
33
+ context,
34
+ credentialSubject,
35
+ jti,
36
+ iss,
37
+ encodedCredential,
38
+ };
39
+ };
40
+
41
+ module.exports = {
42
+ offerToCredentialMongoDTO,
43
+ };
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ credentialsRepoPlugin: require('./repos/credentials.repo'),
3
+ ...require('./domains'),
4
+ ...require('./schemas'),
5
+ ...require('./orchestrators'),
6
+ };
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ ...require('./load-additional-render-info'),
3
+ };
@@ -0,0 +1,65 @@
1
+ const { flow, map, uniq, fromPairs, compact } = require('lodash/fp');
2
+
3
+ const {
4
+ VCLOrganizationsSearchDescriptor,
5
+ VCLFilter,
6
+ } = require('@velocitycareerlabs/vnf-nodejs-wallet-sdk');
7
+ const {
8
+ normalizeJsonSchemaName,
9
+ } = require('@velocitycareerlabs/common-functions');
10
+ const { getCredentialDisplaySchema } = require('../../../fetchers/lib-api');
11
+
12
+ const loadAdditionalRenderInfo = async (credentials, context) => {
13
+ return {
14
+ issuers: await loadIssuersInfo(credentials, context),
15
+ displayDescriptors: await loadCredentialTypeDisplaySchemas(
16
+ credentials,
17
+ context
18
+ ),
19
+ };
20
+ };
21
+
22
+ const loadIssuersInfo = async (credentials, context) => {
23
+ const issuerIds = flow(map('issuer.id'), uniq)(credentials);
24
+
25
+ const issuers = await Promise.all(
26
+ issuerIds.map(async (id) => {
27
+ const organizations = await context.vclSdk.searchForOrganizations(
28
+ new VCLOrganizationsSearchDescriptor(new VCLFilter(id))
29
+ );
30
+ return organizations.all?.[0]?.payload;
31
+ })
32
+ );
33
+
34
+ return issuers.reduce((acc, issuer, index) => {
35
+ acc[issuerIds[index]] = issuer;
36
+ return acc;
37
+ }, {});
38
+ };
39
+
40
+ const getCredentialSchemaByType = async (credentialTypes, context) => {
41
+ const schemas = await Promise.all(
42
+ map(
43
+ (type) =>
44
+ getCredentialDisplaySchema(normalizeJsonSchemaName(type), context).then(
45
+ (data) => [type, data]
46
+ ),
47
+ credentialTypes
48
+ )
49
+ );
50
+
51
+ return fromPairs(schemas);
52
+ };
53
+
54
+ const loadCredentialTypeDisplaySchemas = async (credentials, context) => {
55
+ const credentialTypes = flow(map('type.0'), compact, uniq)(credentials);
56
+
57
+ return getCredentialSchemaByType(credentialTypes, context);
58
+ };
59
+
60
+ module.exports = {
61
+ loadAdditionalRenderInfo,
62
+ loadIssuersInfo,
63
+ loadCredentialTypeDisplaySchemas,
64
+ getCredentialSchemaByType,
65
+ };