@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,192 @@
1
+ const {
2
+ VCLDeepLink,
3
+ VCLCredentialManifestDescriptorByDeepLink,
4
+ VCLToken,
5
+ VCLFinalizeOffersDescriptor,
6
+ VCLIssuingType,
7
+ VCLCredentialManifest,
8
+ VCLJwt,
9
+ VCLVerifiedProfile,
10
+ VCLDidJwk,
11
+ } = require('@velocitycareerlabs/vnf-nodejs-wallet-sdk');
12
+ const {
13
+ loadAdditionalRenderInfo,
14
+ getCredentialsFromOffers,
15
+ buildIssuingInputCredentials,
16
+ generateOffersByDeepLink,
17
+ } = require('../../../entities');
18
+ const {
19
+ validateAccountPermissions,
20
+ } = require('../../../entities/accounts/domains');
21
+
22
+ const issuingController = async (fastify) => {
23
+ fastify.get(
24
+ '/get-credential-manifest',
25
+ {
26
+ preValidation: fastify.verifyAccessToken(),
27
+ schema: fastify.autoSchema({
28
+ tags: ['webwallet'],
29
+ querystring: {
30
+ $ref: 'https://velocitycareerlabs.io/webwallet-get-credential-manifest-query.schema.json',
31
+ },
32
+ response: {
33
+ 200: {
34
+ $ref: 'https://velocitycareerlabs.io/webwallet-get-credential-manifest-response.schema.json',
35
+ },
36
+ '4xx': { $ref: 'error#' },
37
+ },
38
+ }),
39
+ },
40
+ async (req) => {
41
+ const credentialManifest = await req.vclSdk.getCredentialManifest(
42
+ new VCLCredentialManifestDescriptorByDeepLink(
43
+ new VCLDeepLink(req.query.link)
44
+ )
45
+ );
46
+
47
+ return {
48
+ credentialManifest: {
49
+ presentation: credentialManifest.jwt.payload,
50
+ jwt: credentialManifest.jwt.encodedJwt,
51
+ verifiedProfile: credentialManifest.verifiedProfile.payload,
52
+ link: credentialManifest.deepLink.value,
53
+ },
54
+ };
55
+ }
56
+ );
57
+
58
+ fastify.post(
59
+ '/offers',
60
+ {
61
+ preValidation: fastify.verifyAccessToken(),
62
+ schema: fastify.autoSchema({
63
+ tags: ['webwallet'],
64
+ body: {
65
+ $ref: 'https://velocitycareerlabs.io/webwallet-offers-by-deep-link-request.schema.json',
66
+ },
67
+ response: {
68
+ 200: {
69
+ $ref: 'https://velocitycareerlabs.io/webwallet-offers-by-deep-link-response.schema.json',
70
+ },
71
+ '4xx': { $ref: 'error#' },
72
+ },
73
+ }),
74
+ },
75
+ async (req) => {
76
+ const {
77
+ body: {
78
+ credentialManifest,
79
+ inputCredentials: inputCredentialsIds = [],
80
+ },
81
+ } = req;
82
+
83
+ const { offers } = await generateOffersByDeepLink(
84
+ credentialManifest,
85
+ VCLIssuingType.Career,
86
+ [],
87
+ await buildIssuingInputCredentials(inputCredentialsIds, req),
88
+ req
89
+ );
90
+
91
+ const offersPayloadArr = offers.all.map((offer) => offer.payload);
92
+
93
+ const additionalRenderInfo = await loadAdditionalRenderInfo(
94
+ offersPayloadArr,
95
+ req
96
+ );
97
+
98
+ return {
99
+ credentialManifest,
100
+ offers: offersPayloadArr,
101
+ token: offers.sessionToken.value,
102
+ challenge: offers.challenge,
103
+ ...additionalRenderInfo,
104
+ };
105
+ }
106
+ );
107
+
108
+ fastify.post(
109
+ '/accept-offers',
110
+ {
111
+ preValidation: fastify.verifyAccessToken(),
112
+ schema: fastify.autoSchema({
113
+ tags: ['webwallet'],
114
+ body: {
115
+ $ref: 'https://velocitycareerlabs.io/webwallet-accept-deep-link-offers-request.schema.json',
116
+ },
117
+ response: {
118
+ 200: {
119
+ $ref: 'https://velocitycareerlabs.io/webwallet-accept-deep-link-offers-response.schema.json',
120
+ },
121
+ '4xx': { $ref: 'error#' },
122
+ },
123
+ }),
124
+ },
125
+ async (req) => {
126
+ const {
127
+ body: { credentialManifest, offerIds, token, challenge },
128
+ } = req;
129
+
130
+ const account = await req.repos.accounts.findOne({
131
+ filter: { userId: req.user.sub },
132
+ });
133
+
134
+ validateAccountPermissions(account);
135
+
136
+ const { did: userDid } = account;
137
+
138
+ const didJwk = getDidJwkFromAccount(account);
139
+
140
+ const {
141
+ vclSdk,
142
+ config: { careerWalletAdminAccessToken },
143
+ } = req;
144
+
145
+ const vclCredentialManifest = new VCLCredentialManifest(
146
+ VCLJwt.fromEncodedJwt(credentialManifest.jwt),
147
+ credentialManifest.vendorOriginContext,
148
+ new VCLVerifiedProfile(credentialManifest.verifiedProfile),
149
+ new VCLDeepLink(credentialManifest.link),
150
+ didJwk,
151
+ careerWalletAdminAccessToken
152
+ );
153
+ const sessionToken = new VCLToken(token);
154
+ const verifiableCredentials = await vclSdk.finalizeOffers(
155
+ new VCLFinalizeOffersDescriptor(
156
+ vclCredentialManifest,
157
+ challenge,
158
+ offerIds,
159
+ []
160
+ ),
161
+ sessionToken
162
+ );
163
+
164
+ let savedCredentials;
165
+ if (verifiableCredentials.passedCredentials?.length) {
166
+ const credentials = getCredentialsFromOffers(
167
+ req.user.sub,
168
+ userDid,
169
+ verifiableCredentials.passedCredentials
170
+ );
171
+
172
+ // eslint-disable-next-line no-unused-vars
173
+ savedCredentials = await req.repos.credentials.insertMany(credentials);
174
+ }
175
+
176
+ return {
177
+ passedCredentials: savedCredentials || [],
178
+ failedCredentials: verifiableCredentials.failedCredentials || [],
179
+ };
180
+ }
181
+ );
182
+
183
+ const getDidJwkFromAccount = (account) => {
184
+ return account.didKeyMetadatum &&
185
+ Array.isArray(account.didKeyMetadatum) &&
186
+ account.didKeyMetadatum.length > 0
187
+ ? VCLDidJwk.fromJSON(account.didKeyMetadatum[0])
188
+ : null;
189
+ };
190
+ };
191
+
192
+ module.exports = issuingController;
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ ...require('./webwallet-deep-link-offers-request.schema'),
3
+ ...require('./webwallet-deep-link-offers-response.schema'),
4
+ ...require('./webwallet-deep-link-accept-offers-response.schema'),
5
+ ...require('./webwallet-deep-link-accept-offers-request.schema'),
6
+ };
@@ -0,0 +1,30 @@
1
+ const webWalletDeepLinkAcceptOffersRequestSchema = {
2
+ $id: 'https://velocitycareerlabs.io/webwallet-accept-deep-link-offers-request.schema.json',
3
+ title: 'webwallet-accept-deep-link-offers-request',
4
+ description: 'the request for webwallet accept offers endpoint',
5
+ type: 'object',
6
+ properties: {
7
+ credentialManifest: {
8
+ type: 'object',
9
+ properties: {
10
+ jwt: { type: 'string' },
11
+ link: { type: 'string' },
12
+ },
13
+ required: ['jwt'],
14
+ additionalProperties: true,
15
+ },
16
+ token: { type: 'string' },
17
+ offerIds: {
18
+ type: 'array',
19
+ items: {
20
+ type: 'string',
21
+ },
22
+ },
23
+ challenge: { type: 'string' },
24
+ },
25
+ required: ['credentialManifest', 'token', 'offerIds'],
26
+ };
27
+
28
+ module.exports = {
29
+ webWalletDeepLinkAcceptOffersRequestSchema,
30
+ };
@@ -0,0 +1,36 @@
1
+ const webWalletDeepLinkAcceptOffersResponseSchema = {
2
+ $id: 'https://velocitycareerlabs.io/webwallet-accept-deep-link-offers-response.schema.json',
3
+ title: 'webwallet-accept-deep-link-offers-response',
4
+ description: 'the body of response for webwallet accept offers endpoint',
5
+ type: 'object',
6
+ properties: {
7
+ passedCredentials: {
8
+ type: 'array',
9
+ items: {
10
+ $ref: 'https://velocitycareerlabs.io/webwallet-credential-response.schema.json',
11
+ },
12
+ },
13
+ failedCredentials: {
14
+ type: 'array',
15
+ items: {
16
+ type: 'object',
17
+ properties: {
18
+ signedJwt: {
19
+ type: 'object',
20
+ properties: {
21
+ header: { type: 'string' },
22
+ payload: { type: 'string' },
23
+ signature: { type: 'string' },
24
+ },
25
+ },
26
+ encodedJwt: { type: 'string' },
27
+ },
28
+ },
29
+ },
30
+ },
31
+ required: ['passedCredentials', 'failedCredentials'],
32
+ };
33
+
34
+ module.exports = {
35
+ webWalletDeepLinkAcceptOffersResponseSchema,
36
+ };
@@ -0,0 +1,28 @@
1
+ const webWalletDeepLinkOffersRequestSchema = {
2
+ $id: 'https://velocitycareerlabs.io/webwallet-offers-by-deep-link-request.schema.json',
3
+ title: 'webwallet-offers-by-deep-link-request',
4
+ description: 'the request for webwallet get offers endpoint',
5
+ type: 'object',
6
+ properties: {
7
+ credentialManifest: {
8
+ type: 'object',
9
+ properties: {
10
+ jwt: { type: 'string' },
11
+ link: { type: 'string' },
12
+ },
13
+ required: ['jwt'],
14
+ additionalProperties: true,
15
+ },
16
+ inputCredentials: {
17
+ type: 'array',
18
+ items: {
19
+ type: 'string',
20
+ },
21
+ },
22
+ },
23
+ required: ['credentialManifest'],
24
+ };
25
+
26
+ module.exports = {
27
+ webWalletDeepLinkOffersRequestSchema,
28
+ };
@@ -0,0 +1,48 @@
1
+ const webWalletDeepLinkOffersResponseSchema = {
2
+ $id: 'https://velocitycareerlabs.io/webwallet-offers-by-deep-link-response.schema.json',
3
+ title: 'webwallet-offers-by-deep-link-response',
4
+ description: 'the body of response for webwallet get offers endpoint',
5
+ type: 'object',
6
+ properties: {
7
+ credentialManifest: {
8
+ type: 'object',
9
+ properties: {
10
+ jwt: { type: 'string' },
11
+ link: { type: 'string' },
12
+ },
13
+ required: ['jwt'],
14
+ additionalProperties: true,
15
+ },
16
+ offers: {
17
+ type: 'array',
18
+ items: {
19
+ $ref: 'https://velocitycareerlabs.io/holder-offer.schema.json',
20
+ },
21
+ },
22
+ challenge: { type: 'string' },
23
+ token: { type: 'string' },
24
+ issuers: {
25
+ type: 'object',
26
+ additionalProperties: {
27
+ $ref: 'organization-profile',
28
+ },
29
+ },
30
+ displayDescriptors: {
31
+ type: 'object',
32
+ additionalProperties: {
33
+ $ref: 'https://velocitycareerlabs.io/webwallet-display-descriptor-response.schema.json',
34
+ },
35
+ },
36
+ },
37
+ required: [
38
+ 'credentialManifest',
39
+ 'offers',
40
+ 'token',
41
+ 'issuers',
42
+ 'displayDescriptors',
43
+ ],
44
+ };
45
+
46
+ module.exports = {
47
+ webWalletDeepLinkOffersResponseSchema,
48
+ };
@@ -0,0 +1,42 @@
1
+ const { oauthPlugin } = require('@velocitycareerlabs/auth');
2
+
3
+ const {
4
+ newOfferRelatedResourceSchema,
5
+ holderOfferSchema,
6
+ } = require('@velocitycareerlabs/common-schemas');
7
+
8
+ const {
9
+ webWalletCredentialResponseSchema,
10
+ webWalletCredentialManifestSchema,
11
+ } = require('../../../entities');
12
+ const {
13
+ webWalletIdentityCredentialsRequestCodeParamsSchema,
14
+ webWalletIdentityCredentialsRequestCodeBodySchema,
15
+ webWalletIdentityCredentialsConfirmBodySchema,
16
+ webWalletIdentityCredentialsConfirmResponseSchema,
17
+ } = require('./schemas');
18
+ const {
19
+ webWalletGetCredentialManifestQuerySchema,
20
+ webWalletGetCredentialManifestResponseSchema,
21
+ } = require('../common-schemas');
22
+
23
+ module.exports = async (fastify) => {
24
+ fastify
25
+ .addSchema(newOfferRelatedResourceSchema)
26
+ .addSchema(holderOfferSchema)
27
+ .addSchema(webWalletCredentialManifestSchema)
28
+ .addSchema(webWalletGetCredentialManifestQuerySchema)
29
+ .addSchema(webWalletGetCredentialManifestResponseSchema)
30
+ .addSchema(webWalletCredentialResponseSchema)
31
+ .addSchema(webWalletIdentityCredentialsRequestCodeParamsSchema)
32
+ .addSchema(webWalletIdentityCredentialsRequestCodeBodySchema)
33
+ .addSchema(webWalletIdentityCredentialsConfirmBodySchema)
34
+ .addSchema(webWalletIdentityCredentialsConfirmResponseSchema)
35
+ .autoSchemaPreset({
36
+ tags: ['webwallet'],
37
+ })
38
+ .register(oauthPlugin, {
39
+ domain: fastify.config.auth0Domain,
40
+ audience: [fastify.config.auth0WebWalletAudience],
41
+ });
42
+ };
@@ -0,0 +1,188 @@
1
+ const newError = require('http-errors');
2
+ const { get } = require('lodash/fp');
3
+ const md5 = require('blueimp-md5');
4
+ const { jwtDecode } = require('@velocitycareerlabs/jwt');
5
+
6
+ const {
7
+ getIdentityIssuerService,
8
+ getIdentityOffersByDeepLink,
9
+ getCredentialsFromOffers,
10
+ doesUserHaveFreshPendingVerification,
11
+ } = require('../../../entities');
12
+ const {
13
+ verifyIdCredentialRequestCode,
14
+ verifyIdCredentialConfirmCode,
15
+ } = require('../../../fetchers');
16
+
17
+ const issueIdentityCredentialsController = async (fastify) => {
18
+ fastify.post(
19
+ '/request-code/:credentialType',
20
+ {
21
+ preValidation: fastify.verifyAccessToken(),
22
+ schema: fastify.autoSchema({
23
+ tags: ['webwallet'],
24
+ params: {
25
+ $ref: 'https://velocitycareerlabs.io/webwallet-identity-credentials-request-code-params.schema.json',
26
+ },
27
+ body: {
28
+ $ref: 'https://velocitycareerlabs.io/webwallet-identity-credentials-request-code-body.schema.json',
29
+ },
30
+ response: {
31
+ 204: {
32
+ type: 'null',
33
+ },
34
+ '4xx': { $ref: 'error#' },
35
+ },
36
+ }),
37
+ },
38
+ async (req, reply) => {
39
+ const {
40
+ params: { credentialType },
41
+ body: { value },
42
+ repos,
43
+ } = req;
44
+
45
+ /**
46
+ * @type {import('../../../entities/accounts/repos/accounts.repo').Account}
47
+ */
48
+ const existingAccount = await repos.accounts.findOne(
49
+ {
50
+ filter: { userId: req.user.sub },
51
+ },
52
+ { verificationInfo: 1 }
53
+ );
54
+
55
+ if (
56
+ !doesUserHaveFreshPendingVerification(
57
+ existingAccount,
58
+ value,
59
+ credentialType
60
+ )
61
+ ) {
62
+ const nonOfIdCredentialsVerified =
63
+ get(`verificationInfo.${credentialType}.status`, existingAccount) !==
64
+ 'verified';
65
+
66
+ await verifyIdCredentialRequestCode(credentialType, value, req);
67
+
68
+ await repos.accounts.update(existingAccount._id, {
69
+ [`verificationInfo.${credentialType}`]: {
70
+ status: nonOfIdCredentialsVerified ? 'pending' : 'verified',
71
+ codeSentAt: new Date(),
72
+ codeSentTo: md5(value),
73
+ },
74
+ });
75
+ }
76
+
77
+ reply.code(204);
78
+ }
79
+ );
80
+
81
+ fastify.post(
82
+ '/confirm',
83
+ {
84
+ preValidation: fastify.verifyAccessToken(),
85
+ schema: fastify.autoSchema({
86
+ tags: ['webwallet'],
87
+ body: {
88
+ $ref: 'https://velocitycareerlabs.io/webwallet-identity-credentials-confirm-body.schema.json',
89
+ },
90
+ response: {
91
+ 200: {
92
+ $ref: 'https://velocitycareerlabs.io/webwallet-identity-credentials-confirm-response.schema.json',
93
+ },
94
+ '4xx': { $ref: 'error#' },
95
+ },
96
+ }),
97
+ },
98
+ async (req) => {
99
+ const {
100
+ body: { verificationCode, type },
101
+ } = req;
102
+
103
+ const { token } = await verifyIdCredentialConfirmCode(
104
+ verificationCode,
105
+ req
106
+ );
107
+
108
+ const identityIssuer = await getIdentityIssuerService(type, req);
109
+
110
+ if (!identityIssuer) {
111
+ throw newError(400, 'Failed to find identity issuer', {
112
+ errorCode: 'verification_error',
113
+ });
114
+ }
115
+
116
+ const deepLink = buildCredentialManifestDeepLink(
117
+ identityIssuer.serviceEndpoint,
118
+ token,
119
+ fastify.config.deepLinkProtocol
120
+ );
121
+
122
+ const {
123
+ acceptedOffers = [],
124
+ offers = [],
125
+ credentialManifest = {},
126
+ } = await getIdentityOffersByDeepLink(deepLink, req);
127
+
128
+ if (
129
+ acceptedOffers?.failedCredentials?.length ||
130
+ !acceptedOffers?.passedCredentials?.length
131
+ ) {
132
+ throw newError(400, 'Failed to finalize offers', {
133
+ errorCode: 'verification_error',
134
+ });
135
+ }
136
+
137
+ const { disclosureId, issuerName } = getPropsFromCredentialManifest(
138
+ credentialManifest.jwt.encodedJwt
139
+ );
140
+
141
+ const { did: userDid, _id: id } = await req.repos.accounts.findOne({
142
+ filter: { userId: req.user.sub },
143
+ });
144
+
145
+ const credentials = getCredentialsFromOffers(
146
+ req.user.sub,
147
+ userDid,
148
+ acceptedOffers.passedCredentials
149
+ );
150
+
151
+ const savedCredentials = await req.repos.credentials.insertMany(
152
+ credentials
153
+ );
154
+
155
+ await req.repos.accounts.update(id, {
156
+ [`verificationInfo.${type}`]: {
157
+ status: 'verified',
158
+ codeSentAt: null,
159
+ codeSentTo: null,
160
+ },
161
+ });
162
+
163
+ return {
164
+ credentials: savedCredentials,
165
+ // trackingData is for Mixpanel tracking events
166
+ trackingData: { offers, disclosureId, issuerName },
167
+ };
168
+ }
169
+ );
170
+ };
171
+
172
+ const buildCredentialManifestDeepLink = (serviceEndpoint, token, protocol) => {
173
+ const requestUri = encodeURIComponent(
174
+ `${serviceEndpoint}?vendorOriginContext=${token}`
175
+ );
176
+
177
+ return `${protocol}issue?request_uri=${requestUri}&vendorOriginContext=${token}`;
178
+ };
179
+
180
+ const getPropsFromCredentialManifest = (encodedJwt) => {
181
+ const decodedJwt = jwtDecode(encodedJwt);
182
+ return {
183
+ disclosureId: decodedJwt.payload.presentation_definition.id.split('.')[1],
184
+ issuerName: decodedJwt.payload.metadata.client_name,
185
+ };
186
+ };
187
+
188
+ module.exports = issueIdentityCredentialsController;
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ ...require('./webwallet-identity-credentials-request-code-params.schema'),
3
+ ...require('./webwallet-identity-credentials-request-code-body.schema'),
4
+ ...require('./webwallet-identity-credentials-confirm-body.schema'),
5
+ ...require('./webwallet-identity-credentials-confirm-response.schema'),
6
+ };
@@ -0,0 +1,20 @@
1
+ const { keys } = require('lodash/fp');
2
+ const { identityClaimTypes } = require('../../../../entities/issuing');
3
+
4
+ const webWalletIdentityCredentialsConfirmBodySchema = {
5
+ $id: 'https://velocitycareerlabs.io/webwallet-identity-credentials-confirm-body.schema.json',
6
+ title: 'webwallet-identity-credentials-confirm-body',
7
+ type: 'object',
8
+ properties: {
9
+ verificationCode: { type: 'string' },
10
+ type: {
11
+ type: 'string',
12
+ enum: keys(identityClaimTypes),
13
+ },
14
+ },
15
+ required: ['verificationCode', 'type'],
16
+ };
17
+
18
+ module.exports = {
19
+ webWalletIdentityCredentialsConfirmBodySchema,
20
+ };
@@ -0,0 +1,37 @@
1
+ const webWalletIdentityCredentialsConfirmResponseSchema = {
2
+ $id: 'https://velocitycareerlabs.io/webwallet-identity-credentials-confirm-response.schema.json',
3
+ title: 'webwallet-identity-credentials-confirm-response',
4
+ description:
5
+ 'the response for webwallet identity credentials confirm endpoint',
6
+ type: 'object',
7
+ properties: {
8
+ credentials: {
9
+ type: 'array',
10
+ items: {
11
+ $ref: 'https://velocitycareerlabs.io/webwallet-credential-response.schema.json',
12
+ },
13
+ },
14
+ trackingData: {
15
+ type: 'object',
16
+ properties: {
17
+ disclosureId: {
18
+ type: 'string',
19
+ },
20
+ offers: {
21
+ type: 'array',
22
+ items: {
23
+ $ref: 'https://velocitycareerlabs.io/holder-offer.schema.json',
24
+ },
25
+ },
26
+ issuerName: {
27
+ type: 'string',
28
+ },
29
+ },
30
+ },
31
+ },
32
+ required: ['credentials', 'trackingData'],
33
+ };
34
+
35
+ module.exports = {
36
+ webWalletIdentityCredentialsConfirmResponseSchema,
37
+ };
@@ -0,0 +1,14 @@
1
+ const webWalletIdentityCredentialsRequestCodeBodySchema = {
2
+ $id: 'https://velocitycareerlabs.io/webwallet-identity-credentials-request-code-body.schema.json',
3
+ title: 'webwallet-identity-credentials-request-code-body',
4
+ type: 'object',
5
+ description: 'The actual email address or phone number',
6
+ properties: {
7
+ value: { type: 'string' },
8
+ },
9
+ required: ['value'],
10
+ };
11
+
12
+ module.exports = {
13
+ webWalletIdentityCredentialsRequestCodeBodySchema,
14
+ };
@@ -0,0 +1,19 @@
1
+ const { keys } = require('lodash/fp');
2
+ const { identityClaimTypes } = require('../../../../entities/issuing');
3
+
4
+ const webWalletIdentityCredentialsRequestCodeParamsSchema = {
5
+ $id: 'https://velocitycareerlabs.io/webwallet-identity-credentials-request-code-params.schema.json',
6
+ title: 'webwallet-identity-credentials-request-code-params',
7
+ type: 'object',
8
+ description: 'Either `phone` or `email`',
9
+ properties: {
10
+ credentialType: {
11
+ type: 'string',
12
+ enum: keys(identityClaimTypes),
13
+ },
14
+ },
15
+ };
16
+
17
+ module.exports = {
18
+ webWalletIdentityCredentialsRequestCodeParamsSchema,
19
+ };
@@ -0,0 +1,16 @@
1
+ const { oauthPlugin } = require('@velocitycareerlabs/auth');
2
+ const multipart = require('@fastify/multipart');
3
+ const { webWalletLinkedinMeResponseSchema } = require('./schemas');
4
+
5
+ module.exports = async (fastify) => {
6
+ fastify
7
+ .register(multipart, { attachFieldsToBody: 'keyValues' })
8
+ .register(oauthPlugin, {
9
+ domain: fastify.config.auth0Domain,
10
+ audience: [fastify.config.auth0WebWalletAudience],
11
+ });
12
+
13
+ fastify.addSchema(webWalletLinkedinMeResponseSchema).autoSchemaPreset({
14
+ tags: ['webwallet'],
15
+ });
16
+ };