@velocitycareerlabs/server-careerwallet 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 (252) hide show
  1. package/.localdev.env +47 -0
  2. package/.standalone.env +13 -0
  3. package/LICENSE +248 -0
  4. package/jest.config.js +20 -0
  5. package/migrate-mongo.config.js +25 -0
  6. package/migrations/20211017180227-create-personas.js +478 -0
  7. package/migrations/20211026185916-create-vanessa.js +79 -0
  8. package/migrations/20211026185917-update-personas.js +30 -0
  9. package/migrations/20211108124410-remove-surplus-personas.js +33 -0
  10. package/migrations/20211108132353-fix-vanessa-and-sheila.js +25 -0
  11. package/migrations/20220222123110-add-career-wallet-app-config.js +6 -0
  12. package/migrations/20220411104157-add-min-app-versions.js +15 -0
  13. package/migrations/20220419131726-create-nicole-flores-persona.js +63 -0
  14. package/migrations/20220515114034-update-persona-id-credentials.js +628 -0
  15. package/migrations/20220608093743-disable-mainnet-holderapp-id-verification.js +21 -0
  16. package/migrations/20220609063708-enable-mainnet-holderapp-id-verification.js +21 -0
  17. package/migrations/20220623091507-add-push-url.js +43 -0
  18. package/migrations/20220624133205-set-min-app-versions-to-11.js +16 -0
  19. package/migrations/20220710125326-set-min-app-version-to-0.10.7.js +16 -0
  20. package/migrations/20220811103500-add-verification-service-disclosure-deeplink.js +45 -0
  21. package/migrations/20220811123751-add-holderapp-dids-to-config.js +74 -0
  22. package/migrations/20220818072306-add-holderapp-endpoints-to-config.js +21 -0
  23. package/migrations/20220825090656-update-deeplink.js +46 -0
  24. package/migrations/20221003151823-app-config-add-public-verification-api.js +18 -0
  25. package/migrations/20221116085242-add-holderapp-sdk-to-config.js +15 -0
  26. package/migrations/20221121091030-update-holderapp-deeplink.js +19 -0
  27. package/migrations/20221128103425-update-holderapp-presentation-template.js +19 -0
  28. package/migrations/20221221091436-app-config-add-oauth.js +13 -0
  29. package/migrations/20221226205900-app-config-add-presentation-extension-api.js +18 -0
  30. package/migrations/20230120113141-update-holderapp-cashSiquence.js +15 -0
  31. package/migrations/20230123084103-update-holderapp-cacheSequence.js +15 -0
  32. package/migrations/20230214083430-update-holderapp-cache-Sequence.js +15 -0
  33. package/migrations/20230225173335-set-min-app-version-1.5.1.js +30 -0
  34. package/migrations/20230323120629-add-sunil-singh-persona.js +87 -0
  35. package/migrations/20230329081529-add-personas-by-env.js +294 -0
  36. package/migrations/20230329103219-remove-unused-personas.js +14 -0
  37. package/migrations/20230504090208-disable-yoti-migration.js +21 -0
  38. package/migrations/20230504123425-set-min-app-version-1.8.1.js +38 -0
  39. package/migrations/20230504185047-enable-yoti-migration.js +21 -0
  40. package/migrations/20230524053203-add-devices-index.js +16 -0
  41. package/migrations/20230704000002-add-common-holder-endpoints-to-config.js +16 -0
  42. package/migrations/20230704000003-add-linkedin-holder-endpoints-to-config.js +18 -0
  43. package/migrations/20230704104055-update-push-url-and-yoti-url.js +18 -0
  44. package/migrations/20230705000001-add-liburl-to-holderapp-config.js +20 -0
  45. package/migrations/20230814113134-app-config-add-oauth-client-id.js +18 -0
  46. package/migrations/20230821154136-yoti-new-session-url-fix.js +19 -0
  47. package/migrations/20230907134442-update-holderapp-cache-Sequence.js +15 -0
  48. package/migrations/20230919180000-set-holderapp-min-app-versions-1.15.0.js +16 -0
  49. package/migrations/20231011083137-update-holderapp-cache-sequence-6.js +15 -0
  50. package/migrations/20231102083252-set-holderapp-min-app-versions-1.14.0.js +16 -0
  51. package/migrations/20231108202229-set-holderapp-min-app-versions-1.15.0.js +16 -0
  52. package/migrations/20231115143332-holderapp-isDirectIssuerCheckOn-true.js +16 -0
  53. package/migrations/20231120100020-insert-didKeyMetadatum-for-old-accounts.js +59 -0
  54. package/migrations/20231207142742-remove-devices-from-accounts.js +46 -0
  55. package/migrations/20231226090805-app-config-set-direct-issuer-check.js +13 -0
  56. package/migrations/202312271524-set-holderapp-app-version-1.15.1.js +16 -0
  57. package/migrations/20232211171700-app-config-update-base-urls.js +18 -0
  58. package/migrations/20240102093506-holderapp-revert-version-1.15.0.js +16 -0
  59. package/migrations/202401041618111-holderapp-isDebugOn-false.js +17 -0
  60. package/migrations/20240131095122-test-personas-add-did.js +102 -0
  61. package/migrations/202402051547000-update-sdk.js +18 -0
  62. package/migrations/20240206101448-update-personas-vc-to-v2.js +137 -0
  63. package/migrations/202402061233000-set-xVnfProtocolVersion-2.js +13 -0
  64. package/migrations/202402081240000-set-xVnfProtocolVersion-1.js +13 -0
  65. package/migrations/202402131319-set-holderapp-min-versions-1.17.js +16 -0
  66. package/migrations/202402141152-set-holderapp-min-version-1.16.js +16 -0
  67. package/migrations/20240221123501-transform-account-keys-to-stringified-jwk.js +81 -0
  68. package/migrations/202402290955-update-holderapp-to-verifyMyCreds.js +36 -0
  69. package/migrations/20240311134223-update-keyid-persona.js +66 -0
  70. package/migrations/20240312141618-vl-7409-persona-update-maria-williams.js +82 -0
  71. package/migrations/202403181733000-remove-devices-from-all-accounts.js +27 -0
  72. package/migrations/20240401091041-set-holderapp-min-version-1.18.1.js +16 -0
  73. package/migrations/202404071847-app-config-update-yoti-url.js +19 -0
  74. package/migrations/20240724063405-vl-3827-new-yoti-session-url.js +19 -0
  75. package/migrations/20240731112302-vl-8160-add-persona-keys.js +101 -0
  76. package/migrations/20240911115206-vanessa-lin-id-credentials.js +57 -0
  77. package/migrations/202409201219-add-isWalletAvailable-field.js +13 -0
  78. package/migrations/202409221012-set-isWalletAvailable-true.js +13 -0
  79. package/migrations/20240922114643-holderapp-isWalletAvailable-refactor.js +36 -0
  80. package/migrations/20240923132213-update-android-ios-app-config.js +36 -0
  81. package/migrations/20240926061732-inc-cacheSequence-to-7.js +15 -0
  82. package/migrations/20240926073339-inc-cacheSequence-to-7-fix.js +18 -0
  83. package/migrations/202410081217-adam-smith-id-credntials.js +57 -0
  84. package/migrations/202410101243-adam_smith-id-credential.js +57 -0
  85. package/migrations/20241015124307-persona-key-id-type-fix.js +26 -0
  86. package/migrations/202501291027000-set-holderapp-did-web.js +37 -0
  87. package/migrations/environments/dev.env +34 -0
  88. package/migrations/environments/localdev.env +22 -0
  89. package/migrations/environments/prod.env +18 -0
  90. package/migrations/environments/qa.env +54 -0
  91. package/migrations/environments/staging.env +31 -0
  92. package/migrations/environments/test.env +18 -0
  93. package/package.json +84 -0
  94. package/src/assets/category-icons/assessment.png +0 -0
  95. package/src/assets/category-icons/badge.png +0 -0
  96. package/src/assets/category-icons/certification.png +0 -0
  97. package/src/assets/category-icons/education.png +0 -0
  98. package/src/assets/category-icons/employment.png +0 -0
  99. package/src/assets/category-icons/gig.png +0 -0
  100. package/src/assets/category-icons/identity.png +0 -0
  101. package/src/assets/category-icons/pharmacy.png +0 -0
  102. package/src/assets/category-icons/training.png +0 -0
  103. package/src/assets/credentialCategories.json +119 -0
  104. package/src/config/config.js +156 -0
  105. package/src/controllers/api/v0.6/accounts/autohooks.js +36 -0
  106. package/src/controllers/api/v0.6/accounts/controller.js +288 -0
  107. package/src/controllers/api/v0.6/accounts/schemas/careerwallet-accounts-didkeymetadatum-response.schema.js +41 -0
  108. package/src/controllers/api/v0.6/accounts/schemas/careerwallet-accounts-request.schema.js +49 -0
  109. package/src/controllers/api/v0.6/accounts/schemas/careerwallet-accounts-response.schema.js +74 -0
  110. package/src/controllers/api/v0.6/accounts/schemas/careerwallet-get-account-response.schema.js +72 -0
  111. package/src/controllers/api/v0.6/accounts/schemas/index.js +6 -0
  112. package/src/controllers/api/v0.6/careerwallet/appconfig/controller.js +25 -0
  113. package/src/controllers/api/v0.6/careerwallet/appconfig/repo.js +40 -0
  114. package/src/controllers/api/v0.6/careerwallet/autohooks.js +5 -0
  115. package/src/controllers/api/v0.6/careerwallet/consents/controller.js +66 -0
  116. package/src/controllers/api/v0.6/careerwallet/consents/latest/autohooks.js +7 -0
  117. package/src/controllers/api/v0.6/careerwallet/consents/latest/controller.js +76 -0
  118. package/src/controllers/api/v0.6/careerwallet/consents/repo.js +24 -0
  119. package/src/controllers/api/v0.6/careerwallet/consents/schemas/careerwallet-consent-response.schema.js +23 -0
  120. package/src/controllers/api/v0.6/careerwallet/consents/schemas/index.js +3 -0
  121. package/src/controllers/api/v0.6/create_did_key/autohooks.js +10 -0
  122. package/src/controllers/api/v0.6/create_did_key/controller.js +84 -0
  123. package/src/controllers/api/v0.6/create_did_key/schemas/index.js +4 -0
  124. package/src/controllers/api/v0.6/create_did_key/schemas/jwk-did-request.schema.js +20 -0
  125. package/src/controllers/api/v0.6/create_did_key/schemas/jwk-did-response.schema.js +41 -0
  126. package/src/controllers/api/v0.6/create_jwk/autohooks.js +10 -0
  127. package/src/controllers/api/v0.6/create_jwk/controller.js +46 -0
  128. package/src/controllers/api/v0.6/create_jwk/schemas/index.js +3 -0
  129. package/src/controllers/api/v0.6/create_jwk/schemas/jwk-response.schema.js +33 -0
  130. package/src/controllers/api/v0.6/credential-categories/controller.js +35 -0
  131. package/src/controllers/api/v0.6/devices/autohooks.js +11 -0
  132. package/src/controllers/api/v0.6/devices/controller.js +323 -0
  133. package/src/controllers/api/v0.6/devices/repo.js +27 -0
  134. package/src/controllers/api/v0.6/devices/schemas/device.schema.json +43 -0
  135. package/src/controllers/api/v0.6/devices/schemas/index.js +3 -0
  136. package/src/controllers/api/v0.6/feedback/controller.js +24 -0
  137. package/src/controllers/api/v0.6/feedback/schemas/index.js +3 -0
  138. package/src/controllers/api/v0.6/feedback/schemas/new-feedback.schema.js +47 -0
  139. package/src/controllers/api/v0.6/jwt/autohooks.js +10 -0
  140. package/src/controllers/api/v0.6/jwt/controller.js +44 -0
  141. package/src/controllers/api/v0.6/jwt/schemas/index.js +4 -0
  142. package/src/controllers/api/v0.6/jwt/schemas/jwt-request.schema.js +40 -0
  143. package/src/controllers/api/v0.6/jwt/schemas/jwt-response.schema.js +14 -0
  144. package/src/controllers/api/v0.6/oauth/controller.js +131 -0
  145. package/src/controllers/api/v0.6/push/controller.js +296 -0
  146. package/src/controllers/api/v0.6/push/firebase-initializer.js +21 -0
  147. package/src/controllers/api/v0.6/push/notification-types.js +37 -0
  148. package/src/controllers/api/v0.6/push/push-gateway-auth.js +123 -0
  149. package/src/controllers/api/v0.6/push/repo.js +24 -0
  150. package/src/controllers/api/v0.6/verification-offers/repo.js +25 -0
  151. package/src/controllers/api/v0.6/verify/autohooks.js +15 -0
  152. package/src/controllers/api/v0.6/verify/controller.js +348 -0
  153. package/src/controllers/api/v0.6/verify/repo.js +23 -0
  154. package/src/controllers/api/v0.6/verify/verification-credential-types.js +6 -0
  155. package/src/controllers/jwt/autohooks.js +10 -0
  156. package/src/controllers/jwt/controller.js +106 -0
  157. package/src/controllers/jwt/schemas/index.js +31 -0
  158. package/src/controllers/jwt/schemas/jwt-decode.response.200.schema.json +20 -0
  159. package/src/controllers/jwt/schemas/jwt-decode.schema.json +15 -0
  160. package/src/controllers/jwt/schemas/jwt-sign.response.200.schema.json +14 -0
  161. package/src/controllers/jwt/schemas/jwt-sign.schema.json +40 -0
  162. package/src/controllers/jwt/schemas/jwt-verify.response.200.schema.json +17 -0
  163. package/src/controllers/jwt/schemas/jwt-verify.schema.json +19 -0
  164. package/src/controllers/reference/autohooks.js +5 -0
  165. package/src/controllers/reference/countries/controller.js +81 -0
  166. package/src/controllers/reference/personas/controller.js +91 -0
  167. package/src/controllers/reference/personas/repo.js +29 -0
  168. package/src/controllers/reference/personas/schemas/index.js +3 -0
  169. package/src/controllers/reference/personas/schemas/persona.schema.json +108 -0
  170. package/src/controllers/root/controller.js +27 -0
  171. package/src/entities/accounts/constants.js +18 -0
  172. package/src/entities/accounts/domain/index.js +3 -0
  173. package/src/entities/accounts/domain/merge-scopes.js +9 -0
  174. package/src/entities/accounts/index.js +5 -0
  175. package/src/entities/accounts/repos/accounts.repo.js +64 -0
  176. package/src/entities/accounts/repos/id-token-claims-extension.js +31 -0
  177. package/src/entities/accounts/repos/index.js +3 -0
  178. package/src/entities/devices/constants.js +7 -0
  179. package/src/entities/devices/index.js +3 -0
  180. package/src/entities/feedback/domain/generate-feedback-email.js +23 -0
  181. package/src/entities/feedback/domain/index.js +3 -0
  182. package/src/entities/feedback/index.js +3 -0
  183. package/src/entities/index.js +9 -0
  184. package/src/entities/key-pairs/index.js +4 -0
  185. package/src/entities/key-pairs/orchestrators/generate-jwk.js +28 -0
  186. package/src/entities/key-pairs/orchestrators/get-key-pair.js +58 -0
  187. package/src/entities/key-pairs/orchestrators/index.js +4 -0
  188. package/src/entities/key-pairs/repos/index.js +3 -0
  189. package/src/entities/key-pairs/repos/key-pairs.repo.js +23 -0
  190. package/src/entities/oauth/constants.js +13 -0
  191. package/src/entities/oauth/domain/build-access-token.js +14 -0
  192. package/src/entities/oauth/domain/index.js +10 -0
  193. package/src/entities/oauth/domain/validate-audience.js +13 -0
  194. package/src/entities/oauth/domain/validate-client-id.js +13 -0
  195. package/src/entities/oauth/domain/validate-credential.js +16 -0
  196. package/src/entities/oauth/domain/validate-presentation.js +17 -0
  197. package/src/entities/oauth/domain/validate-refresh-token.js +17 -0
  198. package/src/entities/oauth/domain/validate-scope.js +30 -0
  199. package/src/entities/oauth/domain/verify-presentation.js +24 -0
  200. package/src/entities/oauth/index.js +4 -0
  201. package/src/entities/pushes/domains/errors.js +11 -0
  202. package/src/entities/pushes/domains/index.js +3 -0
  203. package/src/entities/pushes/index.js +3 -0
  204. package/src/entities/verification-code-attempts/index.js +3 -0
  205. package/src/entities/verification-code-attempts/orchestrators/index.js +3 -0
  206. package/src/entities/verification-code-attempts/orchestrators/validate-verification-code-attempts.js +79 -0
  207. package/src/entities/verification-code-attempts/repo.js +99 -0
  208. package/src/entities/verifications/constants.js +8 -0
  209. package/src/entities/verifications/index.js +3 -0
  210. package/src/helpers/caching-constants.js +6 -0
  211. package/src/helpers/index.js +3 -0
  212. package/src/index.js +15 -0
  213. package/src/init-server.js +88 -0
  214. package/src/plugins/index.js +4 -0
  215. package/src/plugins/vcl-verification-version-plugin.js +10 -0
  216. package/src/plugins/verify-access-token-plugin.js +116 -0
  217. package/src/standalone.js +8 -0
  218. package/test/accounts-controller.test.js +893 -0
  219. package/test/accounts-repo.test.js +92 -0
  220. package/test/careerwallet-config-controller.test.js +142 -0
  221. package/test/careerwallet-consents-controller.test.js +409 -0
  222. package/test/create_did_key-controller.test.js +397 -0
  223. package/test/create_jwk-controller.test.js +188 -0
  224. package/test/credential-categories-controller.test.js +29 -0
  225. package/test/credential-icons-controller.test.js +36 -0
  226. package/test/devices-controller.test.js +1025 -0
  227. package/test/factories/accounts-factory.js +15 -0
  228. package/test/factories/career-wallet-config-factory.js +60 -0
  229. package/test/factories/consents-career-wallet-factory.js +21 -0
  230. package/test/factories/devices-factory.js +16 -0
  231. package/test/factories/key-pairs-factory.js +18 -0
  232. package/test/factories/notifications-factory.js +16 -0
  233. package/test/factories/persona-factory.js +17 -0
  234. package/test/factories/refresh-tokens-factory.js +14 -0
  235. package/test/factories/verification-code-attempts-factory.js +17 -0
  236. package/test/factories/verification-factory.js +15 -0
  237. package/test/factories/verification-offer-factory.js +20 -0
  238. package/test/feedback-controller.test.js +225 -0
  239. package/test/helpers/.env.test +39 -0
  240. package/test/helpers/access-token.js +59 -0
  241. package/test/helpers/careerwallet-build-fastify.js +20 -0
  242. package/test/helpers/yoti.js +96 -0
  243. package/test/id-verification-controller.test.js +27 -0
  244. package/test/jwt-controller.test.js +1519 -0
  245. package/test/oauth-controller.test.js +639 -0
  246. package/test/push-controller.test.js +733 -0
  247. package/test/push-gateway-auth.test.js +208 -0
  248. package/test/reference-countries-controller.test.js +45 -0
  249. package/test/reference-personas-controller.test.js +179 -0
  250. package/test/root.test.js +21 -0
  251. package/test/swagger.test.js +21 -0
  252. package/test/verification-controller.test.js +1372 -0
@@ -0,0 +1,397 @@
1
+ const AWS = require('aws-sdk');
2
+ const { ObjectId } = require('mongodb');
3
+ const { mongoDb } = require('@spencejs/spence-mongo-repos');
4
+ const { generateKeyPair } = require('@velocitycareerlabs/crypto');
5
+ const { hexFromJwk, generateDocJwt } = require('@velocitycareerlabs/jwt');
6
+ const { errorResponseMatcher } = require('@velocitycareerlabs/tests-helpers');
7
+ const buildFastify = require('./helpers/careerwallet-build-fastify');
8
+ const accountsFactory = require('./factories/accounts-factory');
9
+ const initKeyPairsRepo = require('../src/entities/key-pairs/repos/key-pairs.repo');
10
+ const initAccountsRepo = require('../src/entities/accounts/repos/accounts.repo');
11
+
12
+ describe('create_did_key controller test suite', () => {
13
+ const aliasName = 'alias/my-key-alias';
14
+ let validBearer;
15
+ let withoutScopeBearer;
16
+ let fastify;
17
+ let keyPairsRepo;
18
+ let accountRepo;
19
+ let testClient;
20
+ let persistAccounts;
21
+ let privateKey;
22
+ let accountId;
23
+
24
+ beforeAll(async () => {
25
+ testClient = new AWS.KMS({
26
+ credentials: new AWS.Credentials('tests-kei-id', 'tests-key'),
27
+ region: 'us-west-1',
28
+ endpoint: 'http://localhost:4566',
29
+ });
30
+ const createKeyResponse = await testClient.createKey().promise();
31
+ const createAliasParams = {
32
+ AliasName: aliasName,
33
+ TargetKeyId: createKeyResponse.KeyMetadata.Arn,
34
+ };
35
+ await testClient.createAlias(createAliasParams).promise();
36
+ const { publicKey, privateKey: pk } = generateKeyPair({ format: 'jwk' });
37
+ privateKey = pk;
38
+ fastify = await buildFastify({
39
+ holderAppServerAccessTokenPublicKey: publicKey,
40
+ holderAppServerAccessTokenSigningKey: hexFromJwk(privateKey),
41
+ awsRegion: 'us-west-1',
42
+ awsEndpoint: 'http://localhost:4566',
43
+ managedAccountsKeyId: aliasName,
44
+ oauthVerificationDisabledEndpoints: [], // enabled for all endpoints
45
+ });
46
+ await fastify.ready();
47
+
48
+ keyPairsRepo = initKeyPairsRepo(fastify)({
49
+ log: fastify.log,
50
+ config: fastify.config,
51
+ });
52
+ accountRepo = initAccountsRepo(fastify)({
53
+ log: fastify.log,
54
+ config: fastify.config,
55
+ });
56
+ ({ persistAccounts } = accountsFactory(fastify));
57
+ withoutScopeBearer = await generateDocJwt(
58
+ { scope: 'test', sub: 'abc' },
59
+ privateKey
60
+ );
61
+ });
62
+
63
+ beforeEach(async () => {
64
+ jest.clearAllMocks();
65
+ await mongoDb().collection('keyPairs').deleteMany({});
66
+ await mongoDb().collection('accounts').deleteMany({});
67
+ const account = await persistAccounts();
68
+ accountId = account._id;
69
+ validBearer = await generateDocJwt(
70
+ { scope: 'key:create test', sub: account._id },
71
+ privateKey
72
+ );
73
+ });
74
+
75
+ afterAll(async () => {
76
+ await testClient.deleteAlias({ AliasName: aliasName }).promise();
77
+
78
+ await mongoDb().collection('keyPair').deleteMany({});
79
+ await mongoDb().collection('accounts').deleteMany({});
80
+
81
+ await fastify.close();
82
+ });
83
+
84
+ it('should return 401 if no bearer token is provided', async () => {
85
+ const response = await fastify.injectJson({
86
+ method: 'POST',
87
+ url: '/api/v0.6/create_did_key',
88
+ });
89
+ expect(response.statusCode).toEqual(401);
90
+ });
91
+
92
+ it('should return 401 if the bearer token is invalid', async () => {
93
+ const response = await fastify.injectJson({
94
+ method: 'POST',
95
+ url: '/api/v0.6/create_did_key',
96
+ headers: {
97
+ Authorization: `Bearer ${validBearer}111`,
98
+ },
99
+ });
100
+ expect(response.statusCode).toEqual(401);
101
+ });
102
+
103
+ it('should return 401 if no scope', async () => {
104
+ const response = await fastify.injectJson({
105
+ method: 'POST',
106
+ url: '/api/v0.6/create_did_key',
107
+ headers: {
108
+ Authorization: `Bearer ${withoutScopeBearer}`,
109
+ },
110
+ });
111
+ expect(response.statusCode).toEqual(403);
112
+ expect(response.json).toStrictEqual(
113
+ errorResponseMatcher({
114
+ error: 'Forbidden',
115
+ message: 'User must have one of the following scopes: ["key:create"]',
116
+ errorCode: 'missing_error_code',
117
+ statusCode: 403,
118
+ })
119
+ );
120
+ });
121
+
122
+ it('should return 401 if no sub in the payload of access token', async () => {
123
+ const bearer = await generateDocJwt(
124
+ { scope: 'key:create test' },
125
+ privateKey
126
+ );
127
+ const response = await fastify.injectJson({
128
+ method: 'POST',
129
+ url: '/api/v0.6/create_did_key',
130
+ headers: {
131
+ Authorization: `Bearer ${bearer}`,
132
+ },
133
+ });
134
+ expect(response.statusCode).toEqual(401);
135
+ expect(response.json).toStrictEqual(
136
+ errorResponseMatcher({
137
+ message: 'invalid authorization header',
138
+ errorCode: 'missing_error_code',
139
+ error: 'Unauthorized',
140
+ statusCode: 401,
141
+ })
142
+ );
143
+ });
144
+
145
+ it('should return 400 if the request body is invalid', async () => {
146
+ const response = await fastify.injectJson({
147
+ method: 'POST',
148
+ url: '/api/v0.6/create_did_key',
149
+ payload: {},
150
+ headers: {
151
+ Authorization: `Bearer ${validBearer}`,
152
+ },
153
+ });
154
+ expect(response.statusCode).toEqual(400);
155
+ expect(response.json).toStrictEqual(
156
+ errorResponseMatcher({
157
+ errorCode: 'request_validation_failed',
158
+ message: "body must have required property 'crv'",
159
+ statusCode: 400,
160
+ error: 'Bad Request',
161
+ })
162
+ );
163
+ });
164
+
165
+ it('should return 400 if the crv is invalid', async () => {
166
+ const response = await fastify.injectJson({
167
+ method: 'POST',
168
+ url: '/api/v0.6/create_did_key',
169
+ payload: {
170
+ crv: 'abc',
171
+ },
172
+ headers: {
173
+ Authorization: `Bearer ${validBearer}`,
174
+ },
175
+ });
176
+ expect(response.statusCode).toEqual(400);
177
+ expect(response.json).toStrictEqual(
178
+ errorResponseMatcher({
179
+ errorCode: 'request_validation_failed',
180
+ message: 'body/crv must be equal to one of the allowed values',
181
+ statusCode: 400,
182
+ error: 'Bad Request',
183
+ })
184
+ );
185
+ });
186
+
187
+ it('should return 400 if account already has crv', async () => {
188
+ const account = await persistAccounts({
189
+ didKeyMetadatum: [
190
+ {
191
+ didKeyId: 'abc1',
192
+ didMethod: 'did:jwk',
193
+ kid: 'kid1',
194
+ publicJwk: {
195
+ crv: 'P-256',
196
+ kty: 'EC',
197
+ x: 'x',
198
+ y: 'y',
199
+ },
200
+ },
201
+ ],
202
+ });
203
+ const bearer = await generateDocJwt(
204
+ { scope: 'key:create test', sub: account._id },
205
+ privateKey
206
+ );
207
+ const response = await fastify.injectJson({
208
+ method: 'POST',
209
+ url: '/api/v0.6/create_did_key',
210
+ payload: {
211
+ crv: 'P-256',
212
+ didMethod: 'did:jwk',
213
+ didKeyId: 'abc1',
214
+ },
215
+ headers: {
216
+ Authorization: `Bearer ${bearer}`,
217
+ },
218
+ });
219
+ expect(response.statusCode).toEqual(400);
220
+ expect(response.json).toStrictEqual(
221
+ errorResponseMatcher({
222
+ errorCode: 'account_did_exist',
223
+ message: 'DID key with crv already exists',
224
+ statusCode: 400,
225
+ error: 'Bad Request',
226
+ })
227
+ );
228
+ });
229
+
230
+ it('should create a new did and didKeyMetadatum keys', async () => {
231
+ const response = await fastify.injectJson({
232
+ method: 'POST',
233
+ url: '/api/v0.6/create_did_key',
234
+ payload: {
235
+ crv: 'P-256',
236
+ didMethod: 'did:jwk',
237
+ didKeyId: 'abc1',
238
+ },
239
+ headers: {
240
+ Authorization: `Bearer ${validBearer}`,
241
+ },
242
+ });
243
+ expect(response.statusCode).toEqual(200);
244
+ expect(response.json).toStrictEqual({
245
+ keyId: expect.any(String),
246
+ kid: expect.any(String),
247
+ did: expect.any(String),
248
+ publicJwk: {
249
+ crv: 'P-256',
250
+ kty: 'EC',
251
+ x: expect.any(String),
252
+ y: expect.any(String),
253
+ },
254
+ });
255
+
256
+ const keyPair = await keyPairsRepo.findOne({
257
+ filter: {
258
+ _id: response.json.keyId,
259
+ },
260
+ });
261
+ expect(keyPair).toEqual({
262
+ _id: new ObjectId(response.json.keyId),
263
+ encryptedPrivateKey: expect.anything(),
264
+ publicKey: expect.anything(),
265
+ createdAt: expect.anything(),
266
+ updatedAt: expect.anything(),
267
+ });
268
+
269
+ const decryptResult = await testClient
270
+ .decrypt({
271
+ CiphertextBlob: keyPair.encryptedPrivateKey.buffer,
272
+ })
273
+ .promise();
274
+ expect(decryptResult.Plaintext).toBeDefined();
275
+
276
+ const accounts = await accountRepo.findOne({
277
+ filter: {
278
+ _id: accountId,
279
+ },
280
+ });
281
+
282
+ expect(accounts).toEqual({
283
+ _id: new ObjectId(accountId),
284
+ accountType: expect.any(String),
285
+ createdAt: expect.any(Date),
286
+ refreshToken: expect.any(String),
287
+ scope: ['jwt', 'key:create', 'inbox', 'account'],
288
+ updatedAt: expect.any(Date),
289
+ didKeyMetadatum: [
290
+ {
291
+ ...response.json,
292
+ keyId: new ObjectId(response.json.keyId),
293
+ },
294
+ ],
295
+ });
296
+ });
297
+
298
+ it('should create a new did and didKeyMetadatum keys 2', async () => {
299
+ const response1 = await fastify.injectJson({
300
+ method: 'POST',
301
+ url: '/api/v0.6/create_did_key',
302
+ payload: {
303
+ crv: 'secp256k1',
304
+ didMethod: 'did:jwk',
305
+ },
306
+ headers: {
307
+ Authorization: `Bearer ${validBearer}`,
308
+ },
309
+ });
310
+ const response2 = await fastify.injectJson({
311
+ method: 'POST',
312
+ url: '/api/v0.6/create_did_key',
313
+ payload: {
314
+ crv: 'P-256',
315
+ didMethod: 'did:jwk',
316
+ },
317
+ headers: {
318
+ Authorization: `Bearer ${validBearer}`,
319
+ },
320
+ });
321
+ expect(response1.statusCode).toEqual(200);
322
+ expect(response1.json).toStrictEqual({
323
+ keyId: expect.any(String),
324
+ kid: expect.any(String),
325
+ did: expect.any(String),
326
+ publicJwk: {
327
+ crv: 'secp256k1',
328
+ kty: 'EC',
329
+ x: expect.any(String),
330
+ y: expect.any(String),
331
+ },
332
+ });
333
+ expect(response2.statusCode).toEqual(200);
334
+ expect(response2.json).toStrictEqual({
335
+ keyId: expect.any(String),
336
+ kid: expect.any(String),
337
+ did: expect.any(String),
338
+ publicJwk: {
339
+ crv: 'P-256',
340
+ kty: 'EC',
341
+ x: expect.any(String),
342
+ y: expect.any(String),
343
+ },
344
+ });
345
+
346
+ const keyPair1 = await keyPairsRepo.findOne({
347
+ filter: {
348
+ _id: response1.json.keyId,
349
+ },
350
+ });
351
+ expect(keyPair1).toEqual({
352
+ _id: new ObjectId(response1.json.keyId),
353
+ encryptedPrivateKey: expect.anything(),
354
+ publicKey: expect.anything(),
355
+ createdAt: expect.anything(),
356
+ updatedAt: expect.anything(),
357
+ });
358
+
359
+ const keyPair2 = await keyPairsRepo.findOne({
360
+ filter: {
361
+ _id: response2.json.keyId,
362
+ },
363
+ });
364
+ expect(keyPair2).toEqual({
365
+ _id: new ObjectId(response2.json.keyId),
366
+ encryptedPrivateKey: expect.anything(),
367
+ publicKey: expect.anything(),
368
+ createdAt: expect.anything(),
369
+ updatedAt: expect.anything(),
370
+ });
371
+
372
+ const accounts = await accountRepo.findOne({
373
+ filter: {
374
+ _id: accountId,
375
+ },
376
+ });
377
+
378
+ expect(accounts).toEqual({
379
+ _id: new ObjectId(accountId),
380
+ accountType: expect.any(String),
381
+ createdAt: expect.any(Date),
382
+ refreshToken: expect.any(String),
383
+ scope: ['jwt', 'key:create', 'inbox', 'account'],
384
+ updatedAt: expect.any(Date),
385
+ didKeyMetadatum: [
386
+ {
387
+ ...response1.json,
388
+ keyId: new ObjectId(response1.json.keyId),
389
+ },
390
+ {
391
+ ...response2.json,
392
+ keyId: new ObjectId(response2.json.keyId),
393
+ },
394
+ ],
395
+ });
396
+ });
397
+ });
@@ -0,0 +1,188 @@
1
+ const AWS = require('aws-sdk');
2
+ const { ObjectId } = require('mongodb');
3
+ const { mongoDb } = require('@spencejs/spence-mongo-repos');
4
+ const { generateKeyPair } = require('@velocitycareerlabs/crypto');
5
+ const { hexFromJwk, generateDocJwt } = require('@velocitycareerlabs/jwt');
6
+ const { errorResponseMatcher } = require('@velocitycareerlabs/tests-helpers');
7
+ const buildFastify = require('./helpers/careerwallet-build-fastify');
8
+ const initKeyPairsRepo = require('../src/entities/key-pairs/repos/key-pairs.repo');
9
+
10
+ describe('create_jwk controller test suite', () => {
11
+ const aliasName = 'alias/my-key-alias';
12
+ let validBearer;
13
+ let withoutScopeBearer;
14
+ let fastify;
15
+ let jwkRepo;
16
+ let testClient;
17
+
18
+ beforeAll(async () => {
19
+ testClient = new AWS.KMS({
20
+ credentials: new AWS.Credentials('tests-kei-id', 'tests-key'),
21
+ region: 'us-west-1',
22
+ endpoint: 'http://localhost:4566',
23
+ });
24
+ const createKeyResponse = await testClient.createKey().promise();
25
+ const createAliasParams = {
26
+ AliasName: aliasName,
27
+ TargetKeyId: createKeyResponse.KeyMetadata.Arn,
28
+ };
29
+ await testClient.createAlias(createAliasParams).promise();
30
+ const { publicKey, privateKey } = generateKeyPair({ format: 'jwk' });
31
+ fastify = await buildFastify({
32
+ holderAppServerAccessTokenPublicKey: publicKey,
33
+ holderAppServerAccessTokenSigningKey: hexFromJwk(privateKey),
34
+ awsRegion: 'us-west-1',
35
+ awsEndpoint: 'http://localhost:4566',
36
+ managedAccountsKeyId: aliasName,
37
+ oauthVerificationDisabledEndpoints: [], // enabled for all endpoints
38
+ });
39
+ await fastify.ready();
40
+
41
+ jwkRepo = initKeyPairsRepo(fastify)({
42
+ log: fastify.log,
43
+ config: fastify.config,
44
+ });
45
+
46
+ validBearer = await generateDocJwt(
47
+ { scope: 'key:create test', sub: 'abc' },
48
+ privateKey
49
+ );
50
+ withoutScopeBearer = await generateDocJwt(
51
+ { scope: 'test', sub: 'abc' },
52
+ privateKey
53
+ );
54
+ });
55
+
56
+ beforeEach(async () => {
57
+ jest.clearAllMocks();
58
+ await mongoDb().collection('keyPairs').deleteMany({});
59
+ });
60
+
61
+ afterAll(async () => {
62
+ await testClient.deleteAlias({ AliasName: aliasName }).promise();
63
+ await fastify.close();
64
+ });
65
+
66
+ it('should return 401 if no bearer token is provided', async () => {
67
+ const response = await fastify.injectJson({
68
+ method: 'POST',
69
+ url: '/api/v0.6/create_jwk',
70
+ });
71
+ expect(response.statusCode).toEqual(401);
72
+ });
73
+
74
+ it('should return 401 if the bearer token is invalid', async () => {
75
+ const response = await fastify.injectJson({
76
+ method: 'POST',
77
+ url: '/api/v0.6/create_jwk',
78
+ headers: {
79
+ Authorization: `Bearer ${validBearer}111`,
80
+ },
81
+ });
82
+ expect(response.statusCode).toEqual(401);
83
+ });
84
+
85
+ it('should return 401 if no scope', async () => {
86
+ const response = await fastify.injectJson({
87
+ method: 'POST',
88
+ url: '/api/v0.6/create_jwk',
89
+ headers: {
90
+ Authorization: `Bearer ${withoutScopeBearer}`,
91
+ },
92
+ });
93
+ expect(response.statusCode).toEqual(403);
94
+ expect(response.json).toStrictEqual(
95
+ errorResponseMatcher({
96
+ error: 'Forbidden',
97
+ message: 'User must have one of the following scopes: ["key:create"]',
98
+ errorCode: 'missing_error_code',
99
+ statusCode: 403,
100
+ })
101
+ );
102
+ });
103
+
104
+ it('should return 400 if the request body is invalid', async () => {
105
+ const response = await fastify.injectJson({
106
+ method: 'POST',
107
+ url: '/api/v0.6/create_jwk',
108
+ payload: {},
109
+ headers: {
110
+ Authorization: `Bearer ${validBearer}`,
111
+ },
112
+ });
113
+ expect(response.statusCode).toEqual(400);
114
+ expect(response.json).toStrictEqual(
115
+ errorResponseMatcher({
116
+ errorCode: 'request_validation_failed',
117
+ message: "body must have required property 'crv'",
118
+ statusCode: 400,
119
+ error: 'Bad Request',
120
+ })
121
+ );
122
+ });
123
+
124
+ it('should return 400 if the crv is invalid', async () => {
125
+ const response = await fastify.injectJson({
126
+ method: 'POST',
127
+ url: '/api/v0.6/create_jwk',
128
+ payload: {
129
+ crv: 'abc',
130
+ },
131
+ headers: {
132
+ Authorization: `Bearer ${validBearer}`,
133
+ },
134
+ });
135
+ expect(response.statusCode).toEqual(400);
136
+ expect(response.json).toStrictEqual(
137
+ errorResponseMatcher({
138
+ errorCode: 'request_validation_failed',
139
+ message: 'body/crv must be equal to one of the allowed values',
140
+ statusCode: 400,
141
+ error: 'Bad Request',
142
+ })
143
+ );
144
+ });
145
+
146
+ it('should create a new jwk', async () => {
147
+ const response = await fastify.injectJson({
148
+ method: 'POST',
149
+ url: '/api/v0.6/create_jwk',
150
+ payload: {
151
+ crv: 'P-256',
152
+ },
153
+ headers: {
154
+ Authorization: `Bearer ${validBearer}`,
155
+ },
156
+ });
157
+ expect(response.statusCode).toEqual(200);
158
+ expect(response.json).toStrictEqual({
159
+ keyId: expect.any(String),
160
+ jwk: {
161
+ crv: 'P-256',
162
+ kty: 'EC',
163
+ x: expect.any(String),
164
+ y: expect.any(String),
165
+ },
166
+ });
167
+
168
+ const keyPair = await jwkRepo.findOne({
169
+ filter: {
170
+ _id: response.json.keyId,
171
+ },
172
+ });
173
+ expect(keyPair).toEqual({
174
+ _id: new ObjectId(response.json.keyId),
175
+ encryptedPrivateKey: expect.anything(),
176
+ publicKey: response.json.jwk,
177
+ createdAt: expect.anything(),
178
+ updatedAt: expect.anything(),
179
+ });
180
+
181
+ const decryptResult = await testClient
182
+ .decrypt({
183
+ CiphertextBlob: keyPair.encryptedPrivateKey.buffer,
184
+ })
185
+ .promise();
186
+ expect(decryptResult.Plaintext).toBeDefined();
187
+ });
188
+ });
@@ -0,0 +1,29 @@
1
+ const buildFastify = require('./helpers/careerwallet-build-fastify');
2
+
3
+ describe('Credential categories', () => {
4
+ let fastify;
5
+
6
+ beforeAll(async () => {
7
+ fastify = buildFastify();
8
+ await fastify.ready();
9
+ });
10
+
11
+ afterAll(async () => {
12
+ await fastify.close();
13
+ });
14
+
15
+ it('should return credential categories', async () => {
16
+ const response = await fastify.injectJson({
17
+ method: 'GET',
18
+ url: 'api/v0.6/credential-categories',
19
+ });
20
+
21
+ expect(response.statusCode).toEqual(200);
22
+ expect(response.headers).toMatchObject({
23
+ 'content-type': 'application/json; charset=utf-8',
24
+ });
25
+ expect(response.json).toEqual(
26
+ require('../src/assets/credentialCategories.json')
27
+ );
28
+ });
29
+ });
@@ -0,0 +1,36 @@
1
+ const buildFastify = require('./helpers/careerwallet-build-fastify');
2
+
3
+ describe('Credential Icons', () => {
4
+ let fastify;
5
+
6
+ beforeAll(async () => {
7
+ fastify = buildFastify();
8
+ await fastify.ready();
9
+ });
10
+
11
+ afterAll(async () => {
12
+ await fastify.close();
13
+ });
14
+
15
+ it('should return 404 if unknown category', async () => {
16
+ const response = await fastify.inject({
17
+ method: 'GET',
18
+ url: 'category-icons/doesnt-exist.png',
19
+ });
20
+
21
+ expect(response.statusCode).toEqual(404);
22
+ });
23
+
24
+ it('should return category icons', async () => {
25
+ const response = await fastify.inject({
26
+ method: 'GET',
27
+ url: 'category-icons/employment.png',
28
+ });
29
+
30
+ expect(response.statusCode).toEqual(200);
31
+ expect(response.headers).toMatchObject({
32
+ 'content-type': 'image/png',
33
+ });
34
+ expect(response.rawPayload).toHaveLength(1194);
35
+ });
36
+ });