@verii/server-credentialagent 1.0.0-pre.1752076816

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 (395) hide show
  1. package/.localdev.e2e.env +40 -0
  2. package/.localdev.env +41 -0
  3. package/.standalone.env +5 -0
  4. package/LICENSE +202 -0
  5. package/NOTICE +1 -0
  6. package/README.md +19 -0
  7. package/docker/compose.yml +33 -0
  8. package/e2e/README.md +12 -0
  9. package/e2e/org-registration-and-issuing.e2e.test.js +624 -0
  10. package/jest.config.js +20 -0
  11. package/migrate-mongo.config.js +36 -0
  12. package/migrations/20210317133137-add-index-to-offers-repo.js +57 -0
  13. package/migrations/20210416145639-add-index-to-revocation-list.js +27 -0
  14. package/migrations/20210719120225-add_unique_did_index_to_tenant.js +45 -0
  15. package/migrations/20230524053029-add-vendorUserIdMappings-index.js +32 -0
  16. package/migrations/20230616111907-add-configuration-type-index.js +32 -0
  17. package/package.json +108 -0
  18. package/src/assets/public/favicon.ico +0 -0
  19. package/src/assets/public/logo192.png +0 -0
  20. package/src/assets/public/logo512.png +0 -0
  21. package/src/assets/public/manifest.json +28 -0
  22. package/src/assets/templates/app-redirect.hbs +16 -0
  23. package/src/config/config.js +44 -0
  24. package/src/config/core-config.js +143 -0
  25. package/src/config/holder-config.js +104 -0
  26. package/src/config/index.js +22 -0
  27. package/src/config/operator-config.js +64 -0
  28. package/src/controllers/autoload-holder-api-controllers.js +30 -0
  29. package/src/controllers/autoload-operator-api-controllers.js +31 -0
  30. package/src/controllers/autoload-root-api-controller.js +30 -0
  31. package/src/controllers/autoload-saasoperator-api-controllers.js +31 -0
  32. package/src/controllers/holder/autohooks.js +55 -0
  33. package/src/controllers/holder/get-exchange-progress/autohooks.js +27 -0
  34. package/src/controllers/holder/get-exchange-progress/controller.js +50 -0
  35. package/src/controllers/holder/inspect/autohooks.js +35 -0
  36. package/src/controllers/holder/inspect/get-presentation-request/controller.js +100 -0
  37. package/src/controllers/holder/inspect/schemas/holder-disclosure.schema.json +73 -0
  38. package/src/controllers/holder/inspect/schemas/index.js +33 -0
  39. package/src/controllers/holder/inspect/schemas/presentation-definition.v1.schema.json +461 -0
  40. package/src/controllers/holder/inspect/schemas/presentation-request.schema.json +279 -0
  41. package/src/controllers/holder/inspect/schemas/presentation-submission.schema.json +41 -0
  42. package/src/controllers/holder/inspect/schemas/siop-presentation-submission.schema.json +74 -0
  43. package/src/controllers/holder/inspect/schemas/velocity-presentation-submission.response.200.schema.json +36 -0
  44. package/src/controllers/holder/inspect/schemas/velocity-presentation-submission.schema.json +34 -0
  45. package/src/controllers/holder/inspect/submit-presentation/controller.js +89 -0
  46. package/src/controllers/holder/issue/autohooks.js +23 -0
  47. package/src/controllers/holder/issue/get-credential-manifest/controller.js +193 -0
  48. package/src/controllers/holder/issue/offers/autohooks.js +35 -0
  49. package/src/controllers/holder/issue/offers/controller.js +164 -0
  50. package/src/controllers/holder/issue/offers/credential-offers/controller.js +460 -0
  51. package/src/controllers/holder/issue/submit-identification/autohooks.js +37 -0
  52. package/src/controllers/holder/issue/submit-identification/controller.js +63 -0
  53. package/src/controllers/holder/oauth/autohooks.js +19 -0
  54. package/src/controllers/holder/oauth/controller.js +140 -0
  55. package/src/controllers/index.js +22 -0
  56. package/src/controllers/operator/tenants/_tenantId/autohooks.js +40 -0
  57. package/src/controllers/operator/tenants/_tenantId/check-credentials/autohooks.js +24 -0
  58. package/src/controllers/operator/tenants/_tenantId/check-credentials/controller-v0.8.js +200 -0
  59. package/src/controllers/operator/tenants/_tenantId/check-credentials/schemas/index.js +19 -0
  60. package/src/controllers/operator/tenants/_tenantId/check-credentials/schemas/vendor-credential.schema.json +244 -0
  61. package/src/controllers/operator/tenants/_tenantId/controller-v0.8.js +221 -0
  62. package/src/controllers/operator/tenants/_tenantId/disclosures/_id/autohooks.js +30 -0
  63. package/src/controllers/operator/tenants/_tenantId/disclosures/_id/controller-v0.8.js +271 -0
  64. package/src/controllers/operator/tenants/_tenantId/disclosures/_id/feeds/autohooks.js +45 -0
  65. package/src/controllers/operator/tenants/_tenantId/disclosures/_id/feeds/controller-v0.8.js +199 -0
  66. package/src/controllers/operator/tenants/_tenantId/disclosures/_id/feeds/schemas/add-feed.schema.js +14 -0
  67. package/src/controllers/operator/tenants/_tenantId/disclosures/_id/feeds/schemas/feed.schema.json +27 -0
  68. package/src/controllers/operator/tenants/_tenantId/disclosures/_id/feeds/schemas/index.js +25 -0
  69. package/src/controllers/operator/tenants/_tenantId/disclosures/_id/feeds/schemas/modify-feed-update-body.schema.js +18 -0
  70. package/src/controllers/operator/tenants/_tenantId/disclosures/_id/feeds/schemas/modify-feed.schema.json +19 -0
  71. package/src/controllers/operator/tenants/_tenantId/disclosures/autohooks.js +34 -0
  72. package/src/controllers/operator/tenants/_tenantId/disclosures/controller-v0.8.js +100 -0
  73. package/src/controllers/operator/tenants/_tenantId/disclosures/schemas/agent-disclosure-presentation-definition.schema.json +404 -0
  74. package/src/controllers/operator/tenants/_tenantId/disclosures/schemas/agent-disclosure.schema.js +24 -0
  75. package/src/controllers/operator/tenants/_tenantId/disclosures/schemas/index.js +29 -0
  76. package/src/controllers/operator/tenants/_tenantId/disclosures/schemas/new-agent-disclosure.schema.json +166 -0
  77. package/src/controllers/operator/tenants/_tenantId/disclosures/schemas/update-agent-disclosure.schema.js +20 -0
  78. package/src/controllers/operator/tenants/_tenantId/exchanges/_exchangeId/autohooks.js +30 -0
  79. package/src/controllers/operator/tenants/_tenantId/exchanges/_exchangeId/controller-v0.8.js +73 -0
  80. package/src/controllers/operator/tenants/_tenantId/exchanges/autohooks.js +19 -0
  81. package/src/controllers/operator/tenants/_tenantId/exchanges/controller-v0.8.js +150 -0
  82. package/src/controllers/operator/tenants/_tenantId/exchanges/schemas/get-exchange.response.body.json +147 -0
  83. package/src/controllers/operator/tenants/_tenantId/exchanges/schemas/index.js +21 -0
  84. package/src/controllers/operator/tenants/_tenantId/issued-credentials/autohooks.js +27 -0
  85. package/src/controllers/operator/tenants/_tenantId/issued-credentials/controller-v0.8.js +303 -0
  86. package/src/controllers/operator/tenants/_tenantId/issued-credentials/schemas/index.js +23 -0
  87. package/src/controllers/operator/tenants/_tenantId/issued-credentials/schemas/issued-credential.schema.json +115 -0
  88. package/src/controllers/operator/tenants/_tenantId/issued-credentials/schemas/revoke-credentials.schema.json +18 -0
  89. package/src/controllers/operator/tenants/_tenantId/keys/controller-v0.8.js +168 -0
  90. package/src/controllers/operator/tenants/_tenantId/offer-data/controller-v0.8.js +78 -0
  91. package/src/controllers/operator/tenants/_tenantId/offers/autohooks.js +34 -0
  92. package/src/controllers/operator/tenants/_tenantId/offers/controller-v0.8.js +253 -0
  93. package/src/controllers/operator/tenants/_tenantId/offers/schemas/index.js +23 -0
  94. package/src/controllers/operator/tenants/_tenantId/offers/schemas/new-vendor-offer.schema.js +47 -0
  95. package/src/controllers/operator/tenants/_tenantId/offers/schemas/vendor-offer.schema.json +56 -0
  96. package/src/controllers/operator/tenants/_tenantId/users/autohooks.js +24 -0
  97. package/src/controllers/operator/tenants/_tenantId/users/controller-v0.8.js +92 -0
  98. package/src/controllers/operator/tenants/_tenantId/users/schemas/index.js +23 -0
  99. package/src/controllers/operator/tenants/_tenantId/users/schemas/new-user.schema.json +13 -0
  100. package/src/controllers/operator/tenants/_tenantId/users/schemas/user.schema.json +16 -0
  101. package/src/controllers/operator/tenants/_tenantId/vc-api/credentials/autohooks.js +34 -0
  102. package/src/controllers/operator/tenants/_tenantId/vc-api/credentials/controller-v0.8.js +110 -0
  103. package/src/controllers/operator/tenants/_tenantId/vc-api/credentials/schemas/Credential.schema.js +18 -0
  104. package/src/controllers/operator/tenants/_tenantId/vc-api/credentials/schemas/IssueCredentialOptions.schema.json +42 -0
  105. package/src/controllers/operator/tenants/_tenantId/vc-api/credentials/schemas/IssueCredentialRequest.schema.json +13 -0
  106. package/src/controllers/operator/tenants/_tenantId/vc-api/credentials/schemas/IssueCredentialResponse.schema.json +19 -0
  107. package/src/controllers/operator/tenants/_tenantId/vc-api/credentials/schemas/LinkedDataProof.schema.json +43 -0
  108. package/src/controllers/operator/tenants/_tenantId/vc-api/credentials/schemas/VerifiableCredential.schema.js +16 -0
  109. package/src/controllers/operator/tenants/_tenantId/vc-api/credentials/schemas/index.js +31 -0
  110. package/src/controllers/operator/tenants/autohooks.js +65 -0
  111. package/src/controllers/operator/tenants/controller-v0.8.js +167 -0
  112. package/src/controllers/operator/tenants/schemas/index.js +41 -0
  113. package/src/controllers/operator/tenants/schemas/modify-secret.schema.json +11 -0
  114. package/src/controllers/operator/tenants/schemas/modify-tenant-v0.8.schema.json +44 -0
  115. package/src/controllers/operator/tenants/schemas/new-tenant-v0.8.schema.json +19 -0
  116. package/src/controllers/operator/tenants/schemas/new-tenant.response.200.schema.json +7 -0
  117. package/src/controllers/operator/tenants/schemas/secret-key-metadata.schema.json +31 -0
  118. package/src/controllers/operator/tenants/schemas/secret-key.schema.json +29 -0
  119. package/src/controllers/operator/tenants/schemas/secret-kid.schema.json +13 -0
  120. package/src/controllers/operator/tenants/schemas/secret-new-tenant-v0.8.schema.json +28 -0
  121. package/src/controllers/operator/tenants/schemas/secret-tenant-key-v0.8.schema.json +13 -0
  122. package/src/controllers/operator/tenants/schemas/tenant-key-v0.8.schema.json +14 -0
  123. package/src/controllers/operator/tenants/schemas/tenant-v0.8.schema.json +62 -0
  124. package/src/controllers/root/autohooks.js +23 -0
  125. package/src/controllers/root/controller.js +173 -0
  126. package/src/controllers/saasoperator/groups/_id/autohooks.js +9 -0
  127. package/src/controllers/saasoperator/groups/_id/controller.js +121 -0
  128. package/src/controllers/saasoperator/groups/autohooks.js +19 -0
  129. package/src/controllers/saasoperator/groups/controller.js +65 -0
  130. package/src/controllers/saasoperator/groups/schemas/group.schema.js +17 -0
  131. package/src/controllers/saasoperator/groups/schemas/index.js +4 -0
  132. package/src/controllers/saasoperator/groups/schemas/new-group.schema.js +13 -0
  133. package/src/entities/common/domains/get-json-at-path.js +28 -0
  134. package/src/entities/common/domains/index.js +17 -0
  135. package/src/entities/common/index.js +17 -0
  136. package/src/entities/credentials/domains/credential-format.js +22 -0
  137. package/src/entities/credentials/domains/index.js +19 -0
  138. package/src/entities/credentials/index.js +17 -0
  139. package/src/entities/deep-links/domains/extract-did.js +11 -0
  140. package/src/entities/deep-links/domains/index.js +20 -0
  141. package/src/entities/deep-links/domains/velocity-protocol-uri-to-http-uri.js +32 -0
  142. package/src/entities/deep-links/index.js +19 -0
  143. package/src/entities/disclosures/domains/assert-disclosure-active.js +21 -0
  144. package/src/entities/disclosures/domains/compute-disclosure-configuration-type.js +29 -0
  145. package/src/entities/disclosures/domains/constants.js +61 -0
  146. package/src/entities/disclosures/domains/errors.js +34 -0
  147. package/src/entities/disclosures/domains/get-disclosure-configuration-type.js +60 -0
  148. package/src/entities/disclosures/domains/index.js +32 -0
  149. package/src/entities/disclosures/domains/is-issuing-disclosure.js +23 -0
  150. package/src/entities/disclosures/domains/parse-body-to-disclosure.js +17 -0
  151. package/src/entities/disclosures/domains/validate-by-identification-method.js +69 -0
  152. package/src/entities/disclosures/domains/validate-commercial-entity.js +26 -0
  153. package/src/entities/disclosures/domains/validate-disclosure-by-configuration-type.js +47 -0
  154. package/src/entities/disclosures/domains/validate-disclosure-default-issuing.js +77 -0
  155. package/src/entities/disclosures/domains/validate-disclosure.js +37 -0
  156. package/src/entities/disclosures/domains/validate-feed.js +16 -0
  157. package/src/entities/disclosures/domains/validate-presentation-definition.js +54 -0
  158. package/src/entities/disclosures/domains/validate-vendor-endpoint.js +22 -0
  159. package/src/entities/disclosures/domains/validate-vendor-webhook.js +18 -0
  160. package/src/entities/disclosures/factories/disclosure-factory.js +94 -0
  161. package/src/entities/disclosures/factories/index.js +19 -0
  162. package/src/entities/disclosures/index.js +22 -0
  163. package/src/entities/disclosures/orchestrators/get-disclosure.js +18 -0
  164. package/src/entities/disclosures/orchestrators/index.js +20 -0
  165. package/src/entities/disclosures/orchestrators/update-disclosure-configuration-type.js +32 -0
  166. package/src/entities/disclosures/repos/index.js +20 -0
  167. package/src/entities/disclosures/repos/repo.js +118 -0
  168. package/src/entities/disclosures/repos/set-configuration-type.js +33 -0
  169. package/src/entities/exchanges/adapters/index.js +17 -0
  170. package/src/entities/exchanges/adapters/sign-exchange-response.js +45 -0
  171. package/src/entities/exchanges/domains/build-exchange-progress.js +56 -0
  172. package/src/entities/exchanges/domains/constants.js +24 -0
  173. package/src/entities/exchanges/domains/ensure-exchange-state-valid.js +35 -0
  174. package/src/entities/exchanges/domains/errors.js +33 -0
  175. package/src/entities/exchanges/domains/index.js +25 -0
  176. package/src/entities/exchanges/domains/states.js +43 -0
  177. package/src/entities/exchanges/domains/types.js +31 -0
  178. package/src/entities/exchanges/factories/disclosure-exchange-factory.js +46 -0
  179. package/src/entities/exchanges/factories/index.js +20 -0
  180. package/src/entities/exchanges/factories/offer-exchange-factory.js +48 -0
  181. package/src/entities/exchanges/index.js +23 -0
  182. package/src/entities/exchanges/orchestrators/build-exchange-request-deep-link.js +50 -0
  183. package/src/entities/exchanges/orchestrators/index.js +19 -0
  184. package/src/entities/exchanges/repos/exchange-repo-projections.js +45 -0
  185. package/src/entities/exchanges/repos/exchange-state-repo-extension.js +76 -0
  186. package/src/entities/exchanges/repos/index.js +20 -0
  187. package/src/entities/exchanges/repos/repo.js +44 -0
  188. package/src/entities/feeds/factories/feed-factory.js +47 -0
  189. package/src/entities/feeds/factories/index.js +19 -0
  190. package/src/entities/feeds/index.js +20 -0
  191. package/src/entities/feeds/repos/index.js +19 -0
  192. package/src/entities/feeds/repos/repo.js +95 -0
  193. package/src/entities/groups/domains/format-group.js +11 -0
  194. package/src/entities/groups/domains/index.js +3 -0
  195. package/src/entities/groups/factories/group-factory.js +40 -0
  196. package/src/entities/groups/factories/index.js +19 -0
  197. package/src/entities/groups/index.js +22 -0
  198. package/src/entities/groups/orchestrators/find-group-or-error.js +16 -0
  199. package/src/entities/groups/orchestrators/index.js +6 -0
  200. package/src/entities/groups/orchestrators/validate-did.js +24 -0
  201. package/src/entities/groups/orchestrators/validate-group-by-user.js +16 -0
  202. package/src/entities/groups/orchestrators/validate-group.js +39 -0
  203. package/src/entities/groups/repos/delete-tenant-extension.js +13 -0
  204. package/src/entities/groups/repos/index.js +19 -0
  205. package/src/entities/groups/repos/repo.js +38 -0
  206. package/src/entities/groups/repos/update-or-error-extension.js +46 -0
  207. package/src/entities/index.js +37 -0
  208. package/src/entities/keys/domains/constants.js +37 -0
  209. package/src/entities/keys/domains/index.js +21 -0
  210. package/src/entities/keys/domains/is-matching-private-key-kid.js +41 -0
  211. package/src/entities/keys/domains/validate-key.js +62 -0
  212. package/src/entities/keys/factories/index.js +19 -0
  213. package/src/entities/keys/factories/key-factory.js +56 -0
  214. package/src/entities/keys/index.js +22 -0
  215. package/src/entities/keys/orchestrators/index.js +3 -0
  216. package/src/entities/keys/orchestrators/validate-did-doc-keys.js +69 -0
  217. package/src/entities/metadata-list-allocations/index.js +19 -0
  218. package/src/entities/metadata-list-allocations/repos/index.js +19 -0
  219. package/src/entities/metadata-list-allocations/repos/repo.js +40 -0
  220. package/src/entities/notifications/domains/index.js +19 -0
  221. package/src/entities/notifications/domains/notification-types.js +25 -0
  222. package/src/entities/notifications/index.js +19 -0
  223. package/src/entities/offers/domains/build-clean-pii-filter.js +35 -0
  224. package/src/entities/offers/domains/build-deeplink-url.js +120 -0
  225. package/src/entities/offers/domains/build-offer.js +88 -0
  226. package/src/entities/offers/domains/build-qr-code-url.js +37 -0
  227. package/src/entities/offers/domains/constants.js +32 -0
  228. package/src/entities/offers/domains/filter-object-ids.js +34 -0
  229. package/src/entities/offers/domains/generate-issuing-challenge.js +26 -0
  230. package/src/entities/offers/domains/generate-link-code.js +35 -0
  231. package/src/entities/offers/domains/index.js +31 -0
  232. package/src/entities/offers/domains/post-validation-offers-handler.js +31 -0
  233. package/src/entities/offers/domains/prepare-linked-credentials-for-holder.js +36 -0
  234. package/src/entities/offers/domains/resolve-subject.js +142 -0
  235. package/src/entities/offers/domains/validate-offer-commercial-entity.js +24 -0
  236. package/src/entities/offers/domains/validate-offer.js +90 -0
  237. package/src/entities/offers/factories/index.js +19 -0
  238. package/src/entities/offers/factories/offer-factory.js +119 -0
  239. package/src/entities/offers/index.js +22 -0
  240. package/src/entities/offers/orchestrators/create-verifiable-credentials.js +131 -0
  241. package/src/entities/offers/orchestrators/finalize-exchange.js +44 -0
  242. package/src/entities/offers/orchestrators/index.js +23 -0
  243. package/src/entities/offers/orchestrators/load-credential-refs.js +57 -0
  244. package/src/entities/offers/orchestrators/load-credential-types-map.js +44 -0
  245. package/src/entities/offers/orchestrators/prepare-offers.js +35 -0
  246. package/src/entities/offers/orchestrators/trigger-issued-credentials-webhook.js +63 -0
  247. package/src/entities/offers/repos/clean-pii-extension.js +85 -0
  248. package/src/entities/offers/repos/index.js +20 -0
  249. package/src/entities/offers/repos/issued-credential-projection.js +44 -0
  250. package/src/entities/offers/repos/repo.js +177 -0
  251. package/src/entities/presentations/domains/build-identity-doc.js +120 -0
  252. package/src/entities/presentations/domains/build-request-response-schema.js +46 -0
  253. package/src/entities/presentations/domains/build-vendor-data.js +31 -0
  254. package/src/entities/presentations/domains/check-payment-requirement.js +30 -0
  255. package/src/entities/presentations/domains/errors.js +28 -0
  256. package/src/entities/presentations/domains/extract-fields-from-id-credential.js +35 -0
  257. package/src/entities/presentations/domains/index.js +26 -0
  258. package/src/entities/presentations/domains/merge-credential-check-results.js +24 -0
  259. package/src/entities/presentations/domains/validate-presentation.js +128 -0
  260. package/src/entities/presentations/index.js +20 -0
  261. package/src/entities/presentations/orchestrators/create-presentation-request.js +148 -0
  262. package/src/entities/presentations/orchestrators/deduplicate-disclosure-exchange.js +52 -0
  263. package/src/entities/presentations/orchestrators/handle-presentation-submission.js +47 -0
  264. package/src/entities/presentations/orchestrators/index.js +20 -0
  265. package/src/entities/presentations/orchestrators/match-identity-on-exchange.js +114 -0
  266. package/src/entities/presentations/orchestrators/share-identification-credentials.js +110 -0
  267. package/src/entities/presentations/orchestrators/share-presentation.js +234 -0
  268. package/src/entities/push-delegate/get-push-delegate.js +37 -0
  269. package/src/entities/push-delegate/index.js +17 -0
  270. package/src/entities/redirect/index.js +3 -0
  271. package/src/entities/redirect/orchestrators/index.js +3 -0
  272. package/src/entities/redirect/orchestrators/load-org-info.js +40 -0
  273. package/src/entities/revocation-list-allocations/index.js +19 -0
  274. package/src/entities/revocation-list-allocations/repos/index.js +19 -0
  275. package/src/entities/revocation-list-allocations/repos/repo.js +40 -0
  276. package/src/entities/schemas/index.js +19 -0
  277. package/src/entities/schemas/orchestrators/index.js +19 -0
  278. package/src/entities/schemas/orchestrators/load-schema-validation.js +73 -0
  279. package/src/entities/tenants/domains/build-service-ids.js +27 -0
  280. package/src/entities/tenants/domains/extract-service.js +27 -0
  281. package/src/entities/tenants/domains/index.js +21 -0
  282. package/src/entities/tenants/domains/validate-service-ids.js +35 -0
  283. package/src/entities/tenants/factories/index.js +19 -0
  284. package/src/entities/tenants/factories/tenant-factory.js +37 -0
  285. package/src/entities/tenants/index.js +22 -0
  286. package/src/entities/tenants/orchestrators/add-primary-address-to-tenant.js +47 -0
  287. package/src/entities/tenants/orchestrators/create-tenant.js +91 -0
  288. package/src/entities/tenants/orchestrators/index.js +22 -0
  289. package/src/entities/tenants/orchestrators/refresh-tenant-dids.js +146 -0
  290. package/src/entities/tenants/orchestrators/set-tenant-default-issuing-disclosure.js +31 -0
  291. package/src/entities/tenants/repos/index.js +20 -0
  292. package/src/entities/tenants/repos/insert-tenant-extension.js +33 -0
  293. package/src/entities/tenants/repos/repo.js +52 -0
  294. package/src/entities/tenants/repos/tenant-default-projection.js +33 -0
  295. package/src/entities/tokens/adapters/access-token.js +49 -0
  296. package/src/entities/tokens/adapters/index.js +19 -0
  297. package/src/entities/tokens/index.js +19 -0
  298. package/src/entities/users/factories/index.js +19 -0
  299. package/src/entities/users/factories/user-factory.js +36 -0
  300. package/src/entities/users/index.js +20 -0
  301. package/src/entities/users/repos/add-anonymous-user-repo-extension.js +23 -0
  302. package/src/entities/users/repos/find-or-insert-vendor-user-repo-extension.js +30 -0
  303. package/src/entities/users/repos/index.js +19 -0
  304. package/src/entities/users/repos/repo.js +50 -0
  305. package/src/fetchers/index.js +20 -0
  306. package/src/fetchers/operator/identify-fetcher.js +36 -0
  307. package/src/fetchers/operator/index.js +21 -0
  308. package/src/fetchers/operator/inspection-fetcher.js +35 -0
  309. package/src/fetchers/operator/issuing-fetcher.js +50 -0
  310. package/src/fetchers/operator/webhook-auth-header.js +45 -0
  311. package/src/fetchers/push-gateway/generate-push-gateway-token.js +40 -0
  312. package/src/fetchers/push-gateway/index.js +19 -0
  313. package/src/fetchers/push-gateway/push-fetcher.js +39 -0
  314. package/src/index.js +19 -0
  315. package/src/init-holder-server.js +108 -0
  316. package/src/init-operator-server.js +101 -0
  317. package/src/init-server.js +120 -0
  318. package/src/main-holder.js +18 -0
  319. package/src/main-operator.js +19 -0
  320. package/src/main.js +18 -0
  321. package/src/plugins/autoload-repos.js +28 -0
  322. package/src/plugins/disclosure-loader-plugin.js +56 -0
  323. package/src/plugins/ensure-disclosure-active-plugin.js +30 -0
  324. package/src/plugins/ensure-disclosure-configuration-type-plugin.js +29 -0
  325. package/src/plugins/ensure-tenant-default-issuing-disclosure-id-plugin.js +60 -0
  326. package/src/plugins/ensure-tenant-primary-address-plugin.js +44 -0
  327. package/src/plugins/exchange-error-handler-plugin.js +51 -0
  328. package/src/plugins/exchange-loader-plugin.js +50 -0
  329. package/src/plugins/group-loader-plugin.js +51 -0
  330. package/src/plugins/index.js +32 -0
  331. package/src/plugins/kms-plugin.js +57 -0
  332. package/src/plugins/tenant-loader-plugin.js +91 -0
  333. package/src/plugins/validate-cao-plugin.js +81 -0
  334. package/src/plugins/vendor-routes-auth-plugin.js +24 -0
  335. package/src/plugins/verify-access-token-plugin.js +88 -0
  336. package/src/standalone.js +24 -0
  337. package/src/start-app-server.js +38 -0
  338. package/test/combined/app-redirect.test.js +199 -0
  339. package/test/combined/helpers/credentialagent-build-fastify.js +29 -0
  340. package/test/combined/helpers/index.js +22 -0
  341. package/test/combined/helpers/nock-registrar-app-schema-name.js +50 -0
  342. package/test/combined/helpers/nock-registrar-get-organization-diddoc.js +26 -0
  343. package/test/combined/helpers/nock-registrar-get-organization-verified-profile.js +33 -0
  344. package/test/combined/manifest.json.test.js +55 -0
  345. package/test/combined/root-controller.test.js +42 -0
  346. package/test/combined/schemas/education-degree.schema.json +166 -0
  347. package/test/combined/schemas/employment-current-v1.1.schema.json +253 -0
  348. package/test/combined/schemas/open-badge-credential.schema.json +1285 -0
  349. package/test/combined/schemas/past-employment-position-with-uri-id.schema.js +22 -0
  350. package/test/combined/schemas/past-employment-position.schema.json +148 -0
  351. package/test/combined/schemas/will-always-validate.json +10 -0
  352. package/test/combined/validate-cao-plugin.test.js +155 -0
  353. package/test/get-push-delegate.test.js +54 -0
  354. package/test/helpers/jwt-vc-expectation.js +109 -0
  355. package/test/holder/build-request-response-schema.test.js +55 -0
  356. package/test/holder/credential-manifest-controller.test.js +3192 -0
  357. package/test/holder/e2e-issuing-controller.test.js +425 -0
  358. package/test/holder/get-exchange-progress-controller.test.js +521 -0
  359. package/test/holder/get-presentation-request.test.js +906 -0
  360. package/test/holder/helpers/credential-type-metadata.js +98 -0
  361. package/test/holder/helpers/credentialagent-holder-build-fastify.js +32 -0
  362. package/test/holder/helpers/generate-presentation.js +441 -0
  363. package/test/holder/helpers/generate-test-access-token.js +54 -0
  364. package/test/holder/helpers/jwt-access-token-expectation.js +32 -0
  365. package/test/holder/helpers/jwt-vc-expectation.js +115 -0
  366. package/test/holder/issuing-controller.test.js +7076 -0
  367. package/test/holder/oauth-token-controller.test.js +412 -0
  368. package/test/holder/presentation-submission.test.js +2365 -0
  369. package/test/holder/submit-identification.test.js +4815 -0
  370. package/test/operator/check-credentials-controller-v0.8.test.js +832 -0
  371. package/test/operator/credentials-revoke.test.js +536 -0
  372. package/test/operator/disclosures-controller-v0.8.test.js +4157 -0
  373. package/test/operator/exchanges-controller-v0.8.test.js +414 -0
  374. package/test/operator/exchanges-id-controller-v0.8.test.js +162 -0
  375. package/test/operator/feeds-controller-v0.8.test.js +659 -0
  376. package/test/operator/generate-push-gateway-token.test.js +116 -0
  377. package/test/operator/groups-controller.test.js +145 -0
  378. package/test/operator/groups-id-controller.test.js +287 -0
  379. package/test/operator/helpers/create-test-org-doc.js +60 -0
  380. package/test/operator/helpers/credentialagent-operator-build-fastify.js +32 -0
  381. package/test/operator/helpers/find-kms-key.js +31 -0
  382. package/test/operator/helpers/generate-primary-and-add-operator-to-primary.js +63 -0
  383. package/test/operator/helpers/init-agent-kms.js +22 -0
  384. package/test/operator/issued-credentials-controller-v0.8.test.js +398 -0
  385. package/test/operator/keys-controller-v0.8.test.js +1130 -0
  386. package/test/operator/offer-data-controller-v0.8.test.js +253 -0
  387. package/test/operator/offers-controller-v0.8.test.js +3026 -0
  388. package/test/operator/set-configuration-type-modifier.test.js +75 -0
  389. package/test/operator/swagger.test.js +37 -0
  390. package/test/operator/tenant-controller-v0.8.test.js +730 -0
  391. package/test/operator/tenant-loader-plugin.test.js +96 -0
  392. package/test/operator/tenants-controller-v0.8.test.js +2093 -0
  393. package/test/operator/users-controller-v0.8.test.js +137 -0
  394. package/test/operator/vc-api-credentials.test.js +963 -0
  395. package/verification.env +28 -0
@@ -0,0 +1,2365 @@
1
+ /**
2
+ * Copyright 2023 Velocity Team
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ const mockVerifyCredentials = jest.fn();
18
+ const mockSendPush = jest.fn().mockResolvedValue(undefined);
19
+ // eslint-disable-next-line import/order
20
+ const buildFastify = require('./helpers/credentialagent-holder-build-fastify');
21
+ const { subHours, getUnixTime } = require('date-fns/fp');
22
+ const { ObjectId } = require('mongodb');
23
+ const { mongoDb } = require('@spencejs/spence-mongo-repos');
24
+ const { mapWithIndex, wait } = require('@verii/common-functions');
25
+ const { KeyPurposes, generateKeyPair } = require('@verii/crypto');
26
+ const {
27
+ jwtVerify,
28
+ decodeCredentialJwt,
29
+ tamperJwt,
30
+ generateDocJwt,
31
+ } = require('@verii/jwt');
32
+ const { sampleOrganizationProfile1 } = require('@verii/sample-data');
33
+ const { errorResponseMatcher } = require('@verii/tests-helpers');
34
+ const { VnfProtocolVersions } = require('@verii/vc-checks');
35
+ const { map, isString } = require('lodash/fp');
36
+ const { nanoid } = require('nanoid');
37
+ const nock = require('nock');
38
+ const metadataRegistration = require('@verii/metadata-registration');
39
+ const { getDidUriFromJwk } = require('@verii/did-doc');
40
+ const { ISO_DATETIME_FORMAT } = require('@verii/test-regexes');
41
+ const { generatePresentation } = require('./helpers/generate-presentation');
42
+ const {
43
+ jwtAccessTokenExpectation,
44
+ } = require('./helpers/jwt-access-token-expectation');
45
+ const {
46
+ initTenantFactory,
47
+ initDisclosureFactory,
48
+ initDisclosureExchangeFactory,
49
+ initKeysFactory,
50
+ initUserFactory,
51
+ initFeedFactory,
52
+ ExchangeProtocols,
53
+ ExchangeStates,
54
+ ExchangeTypes,
55
+ VendorEndpoint,
56
+ NotificationTypes,
57
+ } = require('../../src/entities');
58
+
59
+ jest.mock('@verii/metadata-registration');
60
+ jest.mock('@verii/verifiable-credentials', () => ({
61
+ ...jest.requireActual('@verii/verifiable-credentials'),
62
+ verifyCredentials: mockVerifyCredentials,
63
+ }));
64
+ jest.mock('../../src/fetchers', () => {
65
+ const actualFetchers = jest.requireActual('../../src/fetchers');
66
+ return {
67
+ ...actualFetchers,
68
+ sendPush: mockSendPush,
69
+ };
70
+ });
71
+
72
+ describe('presentation submission', () => {
73
+ let fastify;
74
+ let persistDisclosure;
75
+ let persistTenant;
76
+ let persistDisclosureExchange;
77
+ let persistVendorUserIdMapping;
78
+ let tenant;
79
+ let persistKey;
80
+ let persistFeed;
81
+ let disclosure;
82
+ let orgDidDoc;
83
+ let tenantKeyPair;
84
+ let tenantDltKey;
85
+ let vendorUserIdMapping;
86
+ let disclosureExchange;
87
+ let disclosureExchange2;
88
+
89
+ const checkResult = {
90
+ credentialChecks: {
91
+ TRUSTED_ISSUER: 'NOT_CHECKED',
92
+ TRUSTED_HOLDER: 'NOT_CHECKED',
93
+ UNEXPIRED: 'NOT_CHECKED',
94
+ UNTAMPERED: 'NOT_CHECKED',
95
+ UNREVOKED: 'NOT_CHECKED',
96
+ },
97
+ };
98
+
99
+ const checkResultPaymentRequired = {
100
+ credentialChecks: {
101
+ TRUSTED_HOLDER: 'NOT_CHECKED',
102
+ TRUSTED_ISSUER: 'VOUCHER_RESERVE_EXHAUSTED',
103
+ UNEXPIRED: 'NOT_CHECKED',
104
+ UNTAMPERED: 'NOT_CHECKED',
105
+ UNREVOKED: 'NOT_CHECKED',
106
+ },
107
+ };
108
+
109
+ beforeAll(async () => {
110
+ fastify = buildFastify();
111
+ await fastify.ready();
112
+ ({ persistDisclosure } = initDisclosureFactory(fastify));
113
+ ({ persistDisclosureExchange } = initDisclosureExchangeFactory(fastify));
114
+ ({ persistTenant } = initTenantFactory(fastify));
115
+ ({ persistKey } = initKeysFactory(fastify));
116
+ ({ persistVendorUserIdMapping } = initUserFactory(fastify));
117
+ ({ persistFeed } = initFeedFactory(fastify));
118
+ });
119
+
120
+ beforeEach(async () => {
121
+ jest.resetAllMocks();
122
+ nock.cleanAll();
123
+ fastify.resetOverrides();
124
+
125
+ await mongoDb().collection('disclosures').deleteMany({});
126
+ await mongoDb().collection('tenants').deleteMany({});
127
+ await mongoDb().collection('keys').deleteMany({});
128
+ await mongoDb().collection('exchanges').deleteMany({});
129
+ await mongoDb().collection('vendorUserIdMappings').deleteMany({});
130
+ await mongoDb().collection('feeds').deleteMany({});
131
+
132
+ mockVerifyCredentials.mockImplementation(async ({ credentials }) => {
133
+ return credentials.map((credential) => ({
134
+ credential: decodeCredentialJwt(credential),
135
+ credentialChecks: {
136
+ TRUSTED_HOLDER: 'NOT_CHECKED',
137
+ TRUSTED_ISSUER: 'NOT_CHECKED',
138
+ UNEXPIRED: 'NOT_CHECKED',
139
+ UNTAMPERED: 'NOT_CHECKED',
140
+ UNREVOKED: 'NOT_CHECKED',
141
+ },
142
+ }));
143
+ });
144
+ tenantKeyPair = generateKeyPair({
145
+ format: 'jwk',
146
+ });
147
+ tenant = await persistTenant();
148
+ orgDidDoc = {
149
+ id: tenant.did,
150
+ publicKey: [
151
+ { id: `${tenant.did}#key-1`, publicKeyJwk: tenantKeyPair.publicKey },
152
+ ],
153
+ service: [
154
+ {
155
+ id: `${tenant.did}#service-1`,
156
+ type: 'BasicProfileInformation',
157
+ ...sampleOrganizationProfile1,
158
+ },
159
+ ],
160
+ };
161
+ await persistKey({
162
+ tenant,
163
+ kidFragment: `#${orgDidDoc.publicKey[0].id.split('#')[1]}`,
164
+ keyPair: tenantKeyPair,
165
+ });
166
+ tenantDltKey = await persistKey({
167
+ tenant,
168
+ kidFragment: '#ID1',
169
+ keyPair: tenantKeyPair,
170
+ purposes: [KeyPurposes.DLT_TRANSACTIONS],
171
+ });
172
+
173
+ await persistKey({
174
+ tenant,
175
+ kidFragment: '#exchanges-1',
176
+ keyPair: tenantKeyPair,
177
+ purposes: [KeyPurposes.EXCHANGES],
178
+ });
179
+ disclosure = await persistDisclosure({
180
+ tenant,
181
+ vendorEndpoint: VendorEndpoint.RECEIVE_CHECKED_CREDENTIALS,
182
+ });
183
+ vendorUserIdMapping = await persistVendorUserIdMapping({ tenant });
184
+ metadataRegistration.initVerificationCoupon.mockImplementation(() => ({
185
+ getCoupon: () => Promise.resolve(42),
186
+ }));
187
+ metadataRegistration.initRevocationRegistry.mockImplementation(() => ({
188
+ getRevokedStatus: () => Promise.resolve(0),
189
+ }));
190
+
191
+ nock('http://oracle.localhost.test')
192
+ .get('/api/v0.6/credential-types', () => {
193
+ return true;
194
+ })
195
+ .reply(
196
+ 200,
197
+ [
198
+ {
199
+ credentialType: 'Passport',
200
+ issuerCategory: 'ContactIssuer',
201
+ },
202
+ ],
203
+ { 'cache-control': 'max-age=3600' }
204
+ );
205
+
206
+ disclosureExchange = await persistDisclosureExchange({
207
+ disclosure,
208
+ tenant,
209
+ protocolMetadata: {
210
+ protocol: ExchangeProtocols.VNF_API,
211
+ },
212
+ });
213
+ disclosureExchange2 = await persistDisclosureExchange({
214
+ disclosure,
215
+ tenant,
216
+ protocolMetadata: {
217
+ protocol: ExchangeProtocols.VNF_API,
218
+ },
219
+ });
220
+ });
221
+
222
+ afterAll(async () => {
223
+ await fastify.close();
224
+ nock.cleanAll();
225
+ nock.restore();
226
+ });
227
+
228
+ const tenantUrl = ({ did }, suffix) => `/api/holder/v0.6/org/${did}${suffix}`;
229
+ const inspectUrl = ({ did }, suffix) =>
230
+ `${tenantUrl({ did }, `/inspect${suffix}`)}`;
231
+
232
+ const mockVendorUrl = 'http://mockvendor.localhost.test';
233
+ const sendCredentialsCheckedEndpoint = `/inspection/${VendorEndpoint.RECEIVE_CHECKED_CREDENTIALS}`;
234
+ const sendCredentialsUncheckedEndpoint = `/inspection/${VendorEndpoint.RECEIVE_UNCHECKED_CREDENTIALS}`;
235
+ const sendCredentialsPayload = { body: { numProcessed: 3 } };
236
+
237
+ describe('base presentation submission tests', () => {
238
+ it('should 400 if the jwt is invalid', async () => {
239
+ const response = await fastify.injectJson({
240
+ method: 'POST',
241
+ url: inspectUrl(tenant, '/submit-presentation'),
242
+ payload: { exchange_id: disclosureExchange._id, jwt_vp: 'BAD JWT' },
243
+ });
244
+ expect(response.statusCode).toEqual(400);
245
+ });
246
+ it('should 400 if the presentation_submission is invalid', async () => {
247
+ // eslint-disable-next-line camelcase
248
+ const presentationBuilder = (
249
+ await generatePresentation(disclosureExchange)
250
+ ).delete('presentation_submission.descriptor_map');
251
+ const response = await fastify.injectJson({
252
+ method: 'POST',
253
+ url: inspectUrl(tenant, '/submit-presentation'),
254
+ payload: {
255
+ exchange_id: disclosureExchange._id,
256
+ jwt_vp: await presentationBuilder.selfSign(),
257
+ },
258
+ });
259
+
260
+ expect(response.statusCode).toEqual(400);
261
+ });
262
+ it('should 400 if the exchange id mismatched', async () => {
263
+ // eslint-disable-next-line camelcase
264
+ const jwt_vp = await (
265
+ await generatePresentation({
266
+ ...disclosureExchange,
267
+ _id: new ObjectId(),
268
+ })
269
+ ).selfSign();
270
+ const response = await fastify.injectJson({
271
+ method: 'POST',
272
+ url: inspectUrl(tenant, '/submit-presentation'),
273
+ // eslint-disable-next-line camelcase
274
+ payload: { exchange_id: disclosureExchange._id, jwt_vp },
275
+ });
276
+
277
+ expect(response.statusCode).toEqual(400);
278
+ });
279
+ it('should 400 if the disclosure id mismatched', async () => {
280
+ // eslint-disable-next-line camelcase
281
+ const jwt_vp = await (
282
+ await generatePresentation({
283
+ ...disclosureExchange,
284
+ disclosureId: new ObjectId(),
285
+ })
286
+ ).selfSign();
287
+ const response = await fastify.injectJson({
288
+ method: 'POST',
289
+ url: inspectUrl(tenant, '/submit-presentation'),
290
+ // eslint-disable-next-line camelcase
291
+ payload: { exchange_id: disclosureExchange._id, jwt_vp },
292
+ });
293
+
294
+ expect(response.statusCode).toEqual(400);
295
+ });
296
+ it('should 400 if the contains an input descriptor isnt a jwt_vc format', async () => {
297
+ const builder = await generatePresentation(disclosureExchange);
298
+
299
+ // eslint-disable-next-line camelcase
300
+ const jwt_vp = await builder
301
+ .override({
302
+ presentation_submission: {
303
+ descriptor_map: [
304
+ {
305
+ id: nanoid(),
306
+ path: '$.verifiableCredential[0]',
307
+ format: 'jwt',
308
+ },
309
+ {
310
+ id: nanoid(),
311
+ path: '$.verifiableCredential[1]',
312
+ format: 'jwt',
313
+ },
314
+ {
315
+ id: nanoid(),
316
+ path: '$.verifiableCredential[2]',
317
+ format: 'jwt',
318
+ },
319
+ ],
320
+ },
321
+ })
322
+ .selfSign();
323
+ const response = await fastify.injectJson({
324
+ method: 'POST',
325
+ url: inspectUrl(tenant, '/submit-presentation'),
326
+ // eslint-disable-next-line camelcase
327
+ payload: { exchange_id: disclosureExchange._id, jwt_vp },
328
+ });
329
+
330
+ expect(response.statusCode).toEqual(400);
331
+ });
332
+
333
+ it('should 200 not send notification if the presentation submission with unchecked credentials', async () => {
334
+ const presentationDisclosure = await persistDisclosure({
335
+ tenant,
336
+ sendPushOnVerification: true,
337
+ vendorEndpoint: VendorEndpoint.RECEIVE_UNCHECKED_CREDENTIALS,
338
+ });
339
+ const pushDelegate = {
340
+ pushToken: 'randomToken',
341
+ pushUrl: 'http://secreturl.com',
342
+ };
343
+ const disclosurePresentationExchange = await persistDisclosureExchange({
344
+ disclosure: presentationDisclosure,
345
+ tenant,
346
+ pushDelegate,
347
+ protocolMetadata: {
348
+ protocol: ExchangeProtocols.VNF_API,
349
+ },
350
+ });
351
+ const presentationBuilder = await generatePresentation(
352
+ disclosurePresentationExchange
353
+ );
354
+ let webhookPayload;
355
+
356
+ nock(mockVendorUrl)
357
+ .post(sendCredentialsUncheckedEndpoint)
358
+ .reply(200, (uri, body) => {
359
+ webhookPayload = body;
360
+ return sendCredentialsPayload;
361
+ });
362
+
363
+ const response = await fastify.inject({
364
+ method: 'POST',
365
+ url: inspectUrl(tenant, '/submit-presentation'),
366
+ payload: {
367
+ exchange_id: disclosurePresentationExchange._id,
368
+ vp_jwt: await presentationBuilder.selfSign(),
369
+ },
370
+ });
371
+
372
+ expect(response.statusCode).toEqual(200);
373
+ expect(mockSendPush).toBeCalledTimes(0);
374
+
375
+ expect(response.statusCode).toEqual(200);
376
+ expect(webhookPayload).toEqual({
377
+ exchangeId: disclosurePresentationExchange._id,
378
+ presentationId: presentationBuilder.presentation.id,
379
+ vendorDisclosureId: presentationDisclosure.vendorDisclosureId,
380
+ vendorOrganizationId: tenant.vendorOrganizationId,
381
+ tenantDID: tenant.did,
382
+ tenantId: tenant._id,
383
+ sendPushOnVerification: true,
384
+ rawCredentials: mapWithIndex(
385
+ ({ vc }, i) => ({
386
+ id: vc.id,
387
+ rawCredential:
388
+ presentationBuilder.presentation.verifiableCredential[i],
389
+ }),
390
+ presentationBuilder.credentials
391
+ ),
392
+ credentials: map(
393
+ ({ vc }) => ({
394
+ ...vc,
395
+ issuer: isString(vc.issuer) ? { id: vc.issuer } : vc.issuer,
396
+ issuanceDate: expect.stringMatching(ISO_DATETIME_FORMAT),
397
+ }),
398
+ presentationBuilder.credentials
399
+ ),
400
+ });
401
+ });
402
+
403
+ it("should 200 and update feedTimestamp of user's latest feed", async () => {
404
+ const presentationDisclosure = await persistDisclosure({
405
+ tenant,
406
+ sendPushOnVerification: true,
407
+ vendorEndpoint: VendorEndpoint.RECEIVE_UNCHECKED_CREDENTIALS,
408
+ feed: true,
409
+ });
410
+ const leastRecentFeed = await persistFeed({
411
+ tenant,
412
+ disclosure: presentationDisclosure,
413
+ vendorUserId: vendorUserIdMapping.vendorUserId,
414
+ });
415
+ expect(leastRecentFeed.feedTimestamp).toBeUndefined();
416
+ await wait(100);
417
+ const mostRecentFeed = await persistFeed({
418
+ tenant,
419
+ disclosure: presentationDisclosure,
420
+ vendorUserId: vendorUserIdMapping.vendorUserId,
421
+ });
422
+ const pushDelegate = {
423
+ pushToken: 'randomToken',
424
+ pushUrl: 'http://secreturl.com',
425
+ };
426
+ const disclosurePresentationExchange = await persistDisclosureExchange({
427
+ disclosure: presentationDisclosure,
428
+ tenant,
429
+ pushDelegate,
430
+ protocolMetadata: {
431
+ protocol: ExchangeProtocols.VNF_API,
432
+ },
433
+ });
434
+ const presentationBuilder = await generatePresentation(
435
+ disclosurePresentationExchange
436
+ );
437
+ let webhookPayload;
438
+
439
+ nock(mockVendorUrl)
440
+ .post(sendCredentialsUncheckedEndpoint)
441
+ .reply(200, (uri, body) => {
442
+ webhookPayload = body;
443
+ return sendCredentialsPayload;
444
+ });
445
+
446
+ const signedJwt = await generateDocJwt({}, tenantKeyPair.privateKey, {
447
+ kid: '#exchanges-1',
448
+ subject: vendorUserIdMapping._id,
449
+ issuer: tenant.did,
450
+ audience: tenant.did,
451
+ expiresIn: '1h',
452
+ });
453
+
454
+ const response = await fastify.inject({
455
+ method: 'POST',
456
+ url: inspectUrl(tenant, '/submit-presentation'),
457
+ headers: {
458
+ Authorization: `Bearer ${signedJwt}`,
459
+ },
460
+ payload: {
461
+ exchange_id: disclosurePresentationExchange._id,
462
+ vp_jwt: await presentationBuilder.selfSign(),
463
+ },
464
+ });
465
+
466
+ expect(response.statusCode).toEqual(200);
467
+ expect(mockSendPush).toBeCalledTimes(0);
468
+
469
+ expect(response.statusCode).toEqual(200);
470
+ expect(webhookPayload).toEqual({
471
+ exchangeId: disclosurePresentationExchange._id,
472
+ presentationId: presentationBuilder.presentation.id,
473
+ vendorDisclosureId: presentationDisclosure.vendorDisclosureId,
474
+ vendorOrganizationId: tenant.vendorOrganizationId,
475
+ tenantDID: tenant.did,
476
+ tenantId: tenant._id,
477
+ sendPushOnVerification: true,
478
+ rawCredentials: mapWithIndex(
479
+ ({ vc }, i) => ({
480
+ id: vc.id,
481
+ rawCredential:
482
+ presentationBuilder.presentation.verifiableCredential[i],
483
+ }),
484
+ presentationBuilder.credentials
485
+ ),
486
+ credentials: map(
487
+ ({ vc }) => ({
488
+ ...vc,
489
+ issuer: isString(vc.issuer) ? { id: vc.issuer } : vc.issuer,
490
+ issuanceDate: expect.stringMatching(ISO_DATETIME_FORMAT),
491
+ }),
492
+ presentationBuilder.credentials
493
+ ),
494
+ });
495
+ const dbLeastRecentFeed = await mongoDb()
496
+ .collection('feeds')
497
+ .findOne({
498
+ _id: new ObjectId(leastRecentFeed._id),
499
+ });
500
+ const dbMostRecentFeed = await mongoDb()
501
+ .collection('feeds')
502
+ .findOne({
503
+ _id: new ObjectId(mostRecentFeed._id),
504
+ });
505
+ expect(dbLeastRecentFeed.feedTimestamp).toBeUndefined();
506
+ expect(dbMostRecentFeed.feedTimestamp).toEqual(expect.any(Date));
507
+ });
508
+
509
+ it('should 200 and use custom webhook url from tenant for receive unchecked credential', async () => {
510
+ const webhookUrl = 'https://webhook.com';
511
+ const newTenant = await persistTenant({ webhookUrl });
512
+
513
+ const keyPair = generateKeyPair({
514
+ format: 'jwk',
515
+ });
516
+ const newOrgDidDoc = {
517
+ id: newTenant.did,
518
+ publicKey: [
519
+ { id: `${newTenant.did}#key-1`, publicKeyJwk: keyPair.publicKey },
520
+ ],
521
+ service: [
522
+ {
523
+ id: `${newTenant.did}#service-1`,
524
+ type: 'BasicProfileInformation',
525
+ ...sampleOrganizationProfile1,
526
+ },
527
+ ],
528
+ };
529
+ await persistKey({
530
+ tenant: newTenant,
531
+ kidFragment: `#${newOrgDidDoc.publicKey[0].id.split('#')[1]}`,
532
+ keyPair,
533
+ });
534
+
535
+ const presentationDisclosure = await persistDisclosure({
536
+ tenant: newTenant,
537
+ vendorEndpoint: VendorEndpoint.RECEIVE_UNCHECKED_CREDENTIALS,
538
+ });
539
+ const disclosurePresentationExchange = await persistDisclosureExchange({
540
+ disclosure: presentationDisclosure,
541
+ tenant: newTenant,
542
+ protocolMetadata: {
543
+ protocol: ExchangeProtocols.VNF_API,
544
+ },
545
+ });
546
+ const presentationBuilder = await generatePresentation(
547
+ disclosurePresentationExchange
548
+ );
549
+
550
+ const webhookNock = await nock(webhookUrl)
551
+ .post(sendCredentialsUncheckedEndpoint)
552
+ .reply(200);
553
+
554
+ const response = await fastify.inject({
555
+ method: 'POST',
556
+ url: inspectUrl(newTenant, '/submit-presentation'),
557
+ payload: {
558
+ exchange_id: disclosurePresentationExchange._id,
559
+ vp_jwt: await presentationBuilder.selfSign(),
560
+ },
561
+ });
562
+
563
+ expect(webhookNock.isDone()).toEqual(true);
564
+ expect(response.statusCode).toEqual(200);
565
+ });
566
+
567
+ it('should 200 and send notification when receiving a self signed presentation with sendPushOnVerification and pushDelegate', async () => {
568
+ const presentationDisclosure = await persistDisclosure({
569
+ tenant,
570
+ sendPushOnVerification: true,
571
+ vendorEndpoint: VendorEndpoint.RECEIVE_CHECKED_CREDENTIALS,
572
+ });
573
+ const pushDelegate = {
574
+ pushToken: 'randomToken',
575
+ pushUrl: 'http://secreturl.com',
576
+ };
577
+ const disclosurePresentationExchange = await persistDisclosureExchange({
578
+ disclosure: presentationDisclosure,
579
+ tenant,
580
+ pushDelegate,
581
+ protocolMetadata: {
582
+ protocol: ExchangeProtocols.VNF_API,
583
+ },
584
+ });
585
+ const presentationBuilder = await generatePresentation(
586
+ disclosurePresentationExchange
587
+ );
588
+ let webhookPayload;
589
+
590
+ nock(mockVendorUrl)
591
+ .post(sendCredentialsCheckedEndpoint)
592
+ .reply(200, (uri, body) => {
593
+ webhookPayload = body;
594
+ return sendCredentialsPayload;
595
+ });
596
+
597
+ const response = await fastify.inject({
598
+ method: 'POST',
599
+ url: inspectUrl(tenant, '/submit-presentation'),
600
+ payload: {
601
+ exchange_id: disclosurePresentationExchange._id,
602
+ vp_jwt: await presentationBuilder.selfSign(),
603
+ },
604
+ });
605
+
606
+ expect(response.statusCode).toEqual(200);
607
+ expect(mockSendPush).toBeCalledTimes(1);
608
+ expect(mockSendPush).toBeCalledWith(
609
+ {
610
+ data: {
611
+ exchangeId: expect.any(ObjectId),
612
+ serviceEndpoint: pushDelegate.pushUrl,
613
+ notificationType: NotificationTypes.PRESENTATION_VERIFIFED,
614
+ issuer: tenant.did,
615
+ },
616
+ pushToken: pushDelegate.pushToken,
617
+ id: mockSendPush.mock.calls[0][0].id,
618
+ },
619
+ pushDelegate,
620
+ expect.any(Object)
621
+ );
622
+
623
+ expect(response.statusCode).toEqual(200);
624
+ expect(webhookPayload).toEqual({
625
+ exchangeId: disclosurePresentationExchange._id,
626
+ presentationId: presentationBuilder.presentation.id,
627
+ vendorDisclosureId: presentationDisclosure.vendorDisclosureId,
628
+ vendorOrganizationId: tenant.vendorOrganizationId,
629
+ tenantDID: tenant.did,
630
+ tenantId: tenant._id,
631
+ sendPushOnVerification: true,
632
+ paymentRequired: false,
633
+ rawCredentials: mapWithIndex(
634
+ ({ vc }, i) => ({
635
+ id: vc.id,
636
+ rawCredential:
637
+ presentationBuilder.presentation.verifiableCredential[i],
638
+ }),
639
+ presentationBuilder.credentials
640
+ ),
641
+ credentials: map(
642
+ ({ vc }) => ({
643
+ ...vc,
644
+ issuer: isString(vc.issuer) ? { id: vc.issuer } : vc.issuer,
645
+ issuanceDate: expect.stringMatching(ISO_DATETIME_FORMAT),
646
+ ...checkResult,
647
+ }),
648
+ presentationBuilder.credentials
649
+ ),
650
+ });
651
+ });
652
+
653
+ it('should 200 and send notification if the did:jwk signed presentation submission', async () => {
654
+ const presentationDisclosure = await persistDisclosure({
655
+ tenant,
656
+ vendorEndpoint: VendorEndpoint.RECEIVE_CHECKED_CREDENTIALS,
657
+ });
658
+ const disclosurePresentationExchange = await persistDisclosureExchange({
659
+ disclosure: presentationDisclosure,
660
+ tenant,
661
+ protocolMetadata: {
662
+ protocol: ExchangeProtocols.VNF_API,
663
+ },
664
+ });
665
+ const presentationBuilder = await generatePresentation(
666
+ disclosurePresentationExchange
667
+ );
668
+ let webhookPayload;
669
+ const holderKeyPair = generateKeyPair({ format: 'jwk' });
670
+ const holderDidJwk = getDidUriFromJwk(holderKeyPair.publicKey);
671
+ const holderKid = `${holderDidJwk}#0`;
672
+
673
+ const holderSignedPresentation = await presentationBuilder.sign(
674
+ holderKid,
675
+ holderKeyPair.privateKey,
676
+ holderDidJwk
677
+ );
678
+ nock(mockVendorUrl)
679
+ .post(sendCredentialsCheckedEndpoint)
680
+ .reply(200, (uri, body) => {
681
+ webhookPayload = body;
682
+ return sendCredentialsPayload;
683
+ });
684
+
685
+ const response = await fastify.inject({
686
+ method: 'POST',
687
+ url: inspectUrl(tenant, '/submit-presentation'),
688
+ headers: {
689
+ 'x-vnf-protocol-version': `${VnfProtocolVersions.VNF_PROTOCOL_VERSION_2}`,
690
+ }, // actually irrelevant as verifiable-credentials calls are all mocked out
691
+ payload: {
692
+ exchange_id: disclosurePresentationExchange._id,
693
+ vp_jwt: holderSignedPresentation,
694
+ },
695
+ });
696
+
697
+ expect(response.statusCode).toEqual(200);
698
+ expect(mockSendPush).toBeCalledTimes(0);
699
+
700
+ expect(response.statusCode).toEqual(200);
701
+ expect(webhookPayload).toEqual({
702
+ exchangeId: disclosurePresentationExchange._id,
703
+ presentationId: presentationBuilder.presentation.id,
704
+ vendorDisclosureId: presentationDisclosure.vendorDisclosureId,
705
+ tenantDID: tenant.did,
706
+ tenantId: tenant._id,
707
+ sendPushOnVerification: false,
708
+ paymentRequired: false,
709
+ rawCredentials: mapWithIndex(
710
+ ({ vc }, i) => ({
711
+ id: vc.id,
712
+ rawCredential:
713
+ presentationBuilder.presentation.verifiableCredential[i],
714
+ }),
715
+ presentationBuilder.credentials
716
+ ),
717
+ credentials: map(
718
+ ({ vc }) => ({
719
+ ...vc,
720
+ issuer: isString(vc.issuer) ? { id: vc.issuer } : vc.issuer,
721
+ issuanceDate: expect.stringMatching(ISO_DATETIME_FORMAT),
722
+ ...checkResult,
723
+ }),
724
+ presentationBuilder.credentials
725
+ ),
726
+ });
727
+
728
+ expect(mockVerifyCredentials.mock.calls).toEqual([
729
+ [
730
+ {
731
+ credentials: expect.any(Array),
732
+ relyingParty: {
733
+ dltOperatorKMSKeyId: new ObjectId(tenantDltKey._id),
734
+ },
735
+ expectedHolderDid: holderDidJwk,
736
+ },
737
+ expect.any(Object),
738
+ expect.any(Object),
739
+ ],
740
+ ]);
741
+ });
742
+
743
+ it('should 200 and not send notifications if the presentation submission with sendPushOnVerification true and no pushToken', async () => {
744
+ const presentationDisclosure = await persistDisclosure({
745
+ tenant,
746
+ sendPushOnVerification: true,
747
+ vendorEndpoint: VendorEndpoint.RECEIVE_CHECKED_CREDENTIALS,
748
+ });
749
+ const pushDelegate = {
750
+ pushUrl: 'http://secreturl.com',
751
+ };
752
+ const disclosurePresentationExchange = await persistDisclosureExchange({
753
+ disclosure: presentationDisclosure,
754
+ tenant,
755
+ pushDelegate,
756
+ protocolMetadata: {
757
+ protocol: ExchangeProtocols.VNF_API,
758
+ },
759
+ });
760
+ const presentationBuilder = await generatePresentation(
761
+ disclosurePresentationExchange
762
+ );
763
+ let webhookPayload;
764
+
765
+ nock(mockVendorUrl)
766
+ .post(sendCredentialsCheckedEndpoint)
767
+ .reply(200, (uri, body) => {
768
+ webhookPayload = body;
769
+ return sendCredentialsPayload;
770
+ });
771
+
772
+ const response = await fastify.inject({
773
+ method: 'POST',
774
+ url: inspectUrl(tenant, '/submit-presentation'),
775
+ payload: {
776
+ exchange_id: disclosurePresentationExchange._id,
777
+ vp_jwt: await presentationBuilder.selfSign(),
778
+ },
779
+ });
780
+
781
+ expect(response.statusCode).toEqual(200);
782
+ expect(mockSendPush).toBeCalledTimes(0);
783
+
784
+ expect(webhookPayload).toEqual({
785
+ exchangeId: disclosurePresentationExchange._id,
786
+ presentationId: presentationBuilder.presentation.id,
787
+ vendorDisclosureId: presentationDisclosure.vendorDisclosureId,
788
+ vendorOrganizationId: tenant.vendorOrganizationId,
789
+ tenantDID: tenant.did,
790
+ tenantId: tenant._id,
791
+ paymentRequired: false,
792
+ sendPushOnVerification: true,
793
+ rawCredentials: mapWithIndex(
794
+ ({ vc }, i) => ({
795
+ id: vc.id,
796
+ rawCredential:
797
+ presentationBuilder.presentation.verifiableCredential[i],
798
+ }),
799
+ presentationBuilder.credentials
800
+ ),
801
+ credentials: map(
802
+ ({ vc }) => ({
803
+ ...vc,
804
+ issuer: isString(vc.issuer) ? { id: vc.issuer } : vc.issuer,
805
+ issuanceDate: expect.stringMatching(ISO_DATETIME_FORMAT),
806
+ ...checkResult,
807
+ }),
808
+ presentationBuilder.credentials
809
+ ),
810
+ });
811
+ });
812
+
813
+ it('should 200 and not send notifications if the presentation submission with sendPushOnVerification true and no pushUrl', async () => {
814
+ const presentationDisclosure = await persistDisclosure({
815
+ tenant,
816
+ sendPushOnVerification: true,
817
+ vendorEndpoint: VendorEndpoint.RECEIVE_CHECKED_CREDENTIALS,
818
+ });
819
+ const pushDelegate = {
820
+ pushToken: 'token',
821
+ };
822
+ const disclosurePresentationExchange = await persistDisclosureExchange({
823
+ disclosure: presentationDisclosure,
824
+ tenant,
825
+ pushDelegate,
826
+ protocolMetadata: {
827
+ protocol: ExchangeProtocols.VNF_API,
828
+ },
829
+ });
830
+ const presentationBuilder = await generatePresentation(
831
+ disclosurePresentationExchange
832
+ );
833
+ let webhookPayload;
834
+
835
+ nock(mockVendorUrl)
836
+ .post(sendCredentialsCheckedEndpoint)
837
+ .reply(200, (uri, body) => {
838
+ webhookPayload = body;
839
+ return sendCredentialsPayload;
840
+ });
841
+
842
+ const response = await fastify.inject({
843
+ method: 'POST',
844
+ url: inspectUrl(tenant, '/submit-presentation'),
845
+ payload: {
846
+ exchange_id: disclosurePresentationExchange._id,
847
+ vp_jwt: await presentationBuilder.selfSign(),
848
+ },
849
+ });
850
+
851
+ expect(response.statusCode).toEqual(200);
852
+ expect(mockSendPush).toBeCalledTimes(0);
853
+
854
+ expect(webhookPayload).toEqual({
855
+ exchangeId: disclosurePresentationExchange._id,
856
+ presentationId: presentationBuilder.presentation.id,
857
+ vendorDisclosureId: presentationDisclosure.vendorDisclosureId,
858
+ vendorOrganizationId: tenant.vendorOrganizationId,
859
+ paymentRequired: false,
860
+ tenantDID: tenant.did,
861
+ tenantId: tenant._id,
862
+ sendPushOnVerification: true,
863
+ rawCredentials: mapWithIndex(
864
+ ({ vc }, i) => ({
865
+ id: vc.id,
866
+ rawCredential:
867
+ presentationBuilder.presentation.verifiableCredential[i],
868
+ }),
869
+ presentationBuilder.credentials
870
+ ),
871
+ credentials: map(
872
+ ({ vc }) => ({
873
+ ...vc,
874
+ issuer: isString(vc.issuer) ? { id: vc.issuer } : vc.issuer,
875
+ issuanceDate: expect.stringMatching(ISO_DATETIME_FORMAT),
876
+ ...checkResult,
877
+ }),
878
+ presentationBuilder.credentials
879
+ ),
880
+ });
881
+ });
882
+
883
+ it('should 200 and use custom webhook url from tenant for receive checked credential', async () => {
884
+ const webhookUrl = 'https://webhook.com';
885
+ const newTenant = await persistTenant({ webhookUrl });
886
+
887
+ const keyPair = generateKeyPair({
888
+ format: 'jwk',
889
+ });
890
+
891
+ orgDidDoc = {
892
+ id: newTenant.did,
893
+ publicKey: [
894
+ { id: `${newTenant.did}#key-1`, publicKeyJwk: keyPair.publicKey },
895
+ ],
896
+ service: [
897
+ {
898
+ id: `${newTenant.did}#service-1`,
899
+ type: 'BasicProfileInformation',
900
+ ...sampleOrganizationProfile1,
901
+ },
902
+ ],
903
+ };
904
+ await persistKey({
905
+ tenant: newTenant,
906
+ kidFragment: `#${orgDidDoc.publicKey[0].id.split('#')[1]}`,
907
+ keyPair,
908
+ });
909
+
910
+ const presentationDisclosure = await persistDisclosure({
911
+ tenant: newTenant,
912
+ vendorEndpoint: VendorEndpoint.RECEIVE_CHECKED_CREDENTIALS,
913
+ });
914
+ const disclosurePresentationExchange = await persistDisclosureExchange({
915
+ disclosure: presentationDisclosure,
916
+ tenant: newTenant,
917
+ protocolMetadata: {
918
+ protocol: ExchangeProtocols.VNF_API,
919
+ },
920
+ });
921
+ const presentationBuilder = await generatePresentation(
922
+ disclosurePresentationExchange
923
+ );
924
+
925
+ const nockWebhook = await nock(webhookUrl)
926
+ .post(sendCredentialsCheckedEndpoint)
927
+ .reply(200);
928
+
929
+ const response = await fastify.inject({
930
+ method: 'POST',
931
+ url: inspectUrl(newTenant, '/submit-presentation'),
932
+ payload: {
933
+ exchange_id: disclosurePresentationExchange._id,
934
+ vp_jwt: await presentationBuilder.selfSign(),
935
+ },
936
+ });
937
+
938
+ expect(nockWebhook.isDone()).toEqual(true);
939
+ expect(response.statusCode).toEqual(200);
940
+ });
941
+
942
+ it('should 400 if the input descriptor path doesnt dereference anything', async () => {
943
+ const builder = await generatePresentation(disclosureExchange);
944
+
945
+ // eslint-disable-next-line camelcase
946
+ const jwt_vp = await builder
947
+ .override({
948
+ presentation_submission: {
949
+ descriptor_map: [
950
+ {
951
+ id: nanoid(),
952
+ path: '$.verifiableCredential[0]',
953
+ format: 'jwt_vc',
954
+ },
955
+ {
956
+ id: nanoid(),
957
+ path: '$.verifiableCredential[1]',
958
+ format: 'jwt_vc',
959
+ },
960
+ {
961
+ id: nanoid(),
962
+ path: '$.verifiableCredential[4]',
963
+ format: 'jwt_vc',
964
+ },
965
+ ],
966
+ },
967
+ })
968
+ .selfSign();
969
+ const response = await fastify.injectJson({
970
+ method: 'POST',
971
+ url: inspectUrl(tenant, '/submit-presentation'),
972
+ // eslint-disable-next-line camelcase
973
+ payload: { exchange_id: disclosureExchange._id, jwt_vp },
974
+ });
975
+
976
+ expect(response.statusCode).toEqual(400);
977
+ });
978
+
979
+ it('should 400 id disclosure deactivated', async () => {
980
+ fastify.overrides.reqConfig = (config) => ({
981
+ ...config,
982
+ enableDeactivatedDisclosure: true,
983
+ });
984
+ disclosure = await persistDisclosure({
985
+ tenant,
986
+ deactivationDate: '2000-12-01T00:00:00.000Z',
987
+ vendorEndpoint: VendorEndpoint.RECEIVE_CHECKED_CREDENTIALS,
988
+ });
989
+ disclosureExchange = await persistDisclosureExchange({
990
+ disclosure,
991
+ tenant,
992
+ protocolMetadata: {
993
+ protocol: ExchangeProtocols.VNF_API,
994
+ },
995
+ });
996
+ const presentationBuilder = await generatePresentation(
997
+ disclosureExchange
998
+ );
999
+ const response = await fastify.injectJson({
1000
+ method: 'POST',
1001
+ url: inspectUrl(tenant, '/submit-presentation'),
1002
+ payload: {
1003
+ exchange_id: disclosureExchange._id,
1004
+ jwt_vp: await presentationBuilder.selfSign(),
1005
+ },
1006
+ });
1007
+ expect(response.statusCode).toEqual(400);
1008
+ expect(response.json).toEqual(
1009
+ errorResponseMatcher({
1010
+ error: 'Bad Request',
1011
+ errorCode: 'disclosure_not_active',
1012
+ message: 'Disclosure is not active',
1013
+ statusCode: 400,
1014
+ })
1015
+ );
1016
+ });
1017
+
1018
+ it('should 200 if the presentation submission maps to a standard vp', async () => {
1019
+ const presentationBuilder = await generatePresentation(
1020
+ disclosureExchange
1021
+ );
1022
+ let webhookPayload;
1023
+
1024
+ nock(mockVendorUrl)
1025
+ .post(sendCredentialsCheckedEndpoint)
1026
+ .reply(200, (uri, body) => {
1027
+ webhookPayload = body;
1028
+ return sendCredentialsPayload;
1029
+ });
1030
+
1031
+ const response = await fastify.injectJson({
1032
+ method: 'POST',
1033
+ url: inspectUrl(tenant, '/submit-presentation'),
1034
+ payload: {
1035
+ exchange_id: disclosureExchange._id,
1036
+ jwt_vp: await presentationBuilder.selfSign(),
1037
+ },
1038
+ });
1039
+ expect(response.statusCode).toEqual(200);
1040
+ expect(response.json).toEqual({
1041
+ exchange: {
1042
+ id: disclosureExchange._id,
1043
+ type: ExchangeTypes.DISCLOSURE,
1044
+ disclosureComplete: true,
1045
+ exchangeComplete: true,
1046
+ },
1047
+ token: expect.any(String),
1048
+ });
1049
+
1050
+ expect(
1051
+ await jwtVerify(response.json.token, tenantKeyPair.publicKey)
1052
+ ).toEqual(
1053
+ jwtAccessTokenExpectation(tenant, null, {
1054
+ jti: disclosureExchange._id.toString(),
1055
+ sub: expect.stringMatching(/anonymous|/),
1056
+ })
1057
+ );
1058
+ const dbExchange = await mongoDb()
1059
+ .collection('exchanges')
1060
+ .findOne({ _id: new ObjectId(disclosureExchange._id) });
1061
+ expect(dbExchange).toEqual({
1062
+ ...disclosureExchange,
1063
+ _id: new ObjectId(disclosureExchange._id),
1064
+ tenantId: new ObjectId(tenant._id),
1065
+ disclosureId: new ObjectId(disclosure._id),
1066
+ events: [
1067
+ { state: ExchangeStates.NEW, timestamp: expect.any(Date) },
1068
+ {
1069
+ state: ExchangeStates.DISCLOSURE_RECEIVED,
1070
+ timestamp: expect.any(Date),
1071
+ },
1072
+ {
1073
+ state: ExchangeStates.DISCLOSURE_CHECKED,
1074
+ timestamp: expect.any(Date),
1075
+ },
1076
+ {
1077
+ state: ExchangeStates.COMPLETE,
1078
+ timestamp: expect.any(Date),
1079
+ },
1080
+ ],
1081
+ presentationId: presentationBuilder.presentation.id,
1082
+ disclosureConsentedAt: expect.any(Date),
1083
+ createdAt: expect.any(Date),
1084
+ updatedAt: expect.any(Date),
1085
+ });
1086
+ expect(webhookPayload).toEqual({
1087
+ exchangeId: disclosureExchange._id,
1088
+ presentationId: presentationBuilder.presentation.id,
1089
+ vendorDisclosureId: disclosure.vendorDisclosureId,
1090
+ vendorOrganizationId: tenant.vendorOrganizationId,
1091
+ tenantDID: tenant.did,
1092
+ tenantId: tenant._id,
1093
+ paymentRequired: false,
1094
+ sendPushOnVerification: false,
1095
+ rawCredentials: mapWithIndex(
1096
+ ({ vc }, i) => ({
1097
+ id: vc.id,
1098
+ rawCredential:
1099
+ presentationBuilder.presentation.verifiableCredential[i],
1100
+ }),
1101
+ presentationBuilder.credentials
1102
+ ),
1103
+ credentials: map(
1104
+ ({ vc }) => ({
1105
+ ...vc,
1106
+ issuer: isString(vc.issuer) ? { id: vc.issuer } : vc.issuer,
1107
+ issuanceDate: expect.stringMatching(ISO_DATETIME_FORMAT),
1108
+ ...checkResult,
1109
+ }),
1110
+ presentationBuilder.credentials
1111
+ ),
1112
+ });
1113
+ });
1114
+
1115
+ it('123 should 200 and add to jwt token expiration date based on authTokensExpireIn', async () => {
1116
+ const { insertedId } = await mongoDb()
1117
+ .collection('disclosures')
1118
+ .insertOne({
1119
+ description: 'Clerk',
1120
+ types: [
1121
+ { type: 'PastEmploymentPosition' },
1122
+ { type: 'CurrentEmploymentPosition' },
1123
+ ],
1124
+ vendorEndpoint: VendorEndpoint.RECEIVE_CHECKED_CREDENTIALS,
1125
+ tenantId: new ObjectId(tenant._id),
1126
+ vendorDisclosureId: 'HR-PKG-USPS-CLRK',
1127
+ purpose: 'Job Application',
1128
+ duration: '6y',
1129
+ termsUrl: 'https://www.lipsum.com/feed/html',
1130
+ sendPushOnVerification: false,
1131
+ authTokensExpireIn: 10,
1132
+ createdAt: new Date(),
1133
+ updatedAt: new Date(),
1134
+ });
1135
+ disclosure = await mongoDb()
1136
+ .collection('disclosures')
1137
+ .findOne({ _id: insertedId });
1138
+ disclosureExchange = await persistDisclosureExchange({
1139
+ disclosure,
1140
+ tenant,
1141
+ protocolMetadata: {
1142
+ protocol: ExchangeProtocols.VNF_API,
1143
+ },
1144
+ });
1145
+ const presentationBuilder = await generatePresentation(
1146
+ disclosureExchange
1147
+ );
1148
+ const dbDisclosureBeforeRequest = await mongoDb()
1149
+ .collection('disclosures')
1150
+ .findOne({
1151
+ _id: new ObjectId(disclosureExchange.disclosureId.toString()),
1152
+ });
1153
+ expect(dbDisclosureBeforeRequest.configurationType).toBeUndefined();
1154
+
1155
+ nock(mockVendorUrl)
1156
+ .post(sendCredentialsCheckedEndpoint)
1157
+ .reply(200, () => {
1158
+ return sendCredentialsPayload;
1159
+ });
1160
+
1161
+ const mockDate = new Date('2023-10-05T12:00:00');
1162
+ jest
1163
+ .useFakeTimers({
1164
+ doNotFake: [
1165
+ 'hrtime',
1166
+ 'nextTick',
1167
+ 'performance',
1168
+ 'queueMicrotask',
1169
+ 'requestAnimationFrame',
1170
+ 'cancelAnimationFrame',
1171
+ 'requestIdleCallback',
1172
+ 'cancelIdleCallback',
1173
+ 'setImmediate',
1174
+ 'clearImmediate',
1175
+ 'setInterval',
1176
+ 'clearInterval',
1177
+ 'setTimeout',
1178
+ 'clearTimeout',
1179
+ ],
1180
+ })
1181
+ .setSystemTime(mockDate);
1182
+ // const spy = jest.spyOn(global, 'Date').mockReturnValue(mockDate);
1183
+ const response = await fastify.injectJson({
1184
+ method: 'POST',
1185
+ url: inspectUrl(tenant, '/submit-presentation'),
1186
+ payload: {
1187
+ exchange_id: disclosureExchange._id,
1188
+ jwt_vp: await presentationBuilder.selfSign(),
1189
+ },
1190
+ });
1191
+ expect(response.statusCode).toEqual(200);
1192
+ expect(response.json).toEqual({
1193
+ exchange: {
1194
+ id: disclosureExchange._id,
1195
+ type: ExchangeTypes.DISCLOSURE,
1196
+ disclosureComplete: true,
1197
+ exchangeComplete: true,
1198
+ },
1199
+ token: expect.any(String),
1200
+ });
1201
+
1202
+ expect(
1203
+ await jwtVerify(response.json.token, tenantKeyPair.publicKey)
1204
+ ).toEqual(
1205
+ jwtAccessTokenExpectation(tenant, null, {
1206
+ jti: disclosureExchange._id.toString(),
1207
+ sub: expect.stringMatching(/anonymous|/),
1208
+ })
1209
+ );
1210
+ const dbDisclosureAfterRequest = await mongoDb()
1211
+ .collection('disclosures')
1212
+ .findOne({
1213
+ _id: new ObjectId(disclosureExchange.disclosureId.toString()),
1214
+ });
1215
+
1216
+ expect(dbDisclosureAfterRequest.configurationType).toEqual('inspection');
1217
+ jest.useRealTimers();
1218
+ });
1219
+
1220
+ it('should 200 and set configuration type to disclosure', async () => {
1221
+ const { insertedId } = await mongoDb()
1222
+ .collection('disclosures')
1223
+ .insertOne({
1224
+ description: 'Clerk',
1225
+ types: [
1226
+ { type: 'PastEmploymentPosition' },
1227
+ { type: 'CurrentEmploymentPosition' },
1228
+ ],
1229
+ vendorEndpoint: VendorEndpoint.RECEIVE_CHECKED_CREDENTIALS,
1230
+ tenantId: new ObjectId(tenant._id),
1231
+ vendorDisclosureId: 'HR-PKG-USPS-CLRK',
1232
+ purpose: 'Job Application',
1233
+ duration: '6y',
1234
+ termsUrl: 'https://www.lipsum.com/feed/html',
1235
+ sendPushOnVerification: false,
1236
+ authTokensExpireIn: 10080,
1237
+ createdAt: new Date(),
1238
+ updatedAt: new Date(),
1239
+ });
1240
+ disclosure = await mongoDb()
1241
+ .collection('disclosures')
1242
+ .findOne({ _id: insertedId });
1243
+ disclosureExchange = await persistDisclosureExchange({
1244
+ disclosure,
1245
+ tenant,
1246
+ protocolMetadata: {
1247
+ protocol: ExchangeProtocols.VNF_API,
1248
+ },
1249
+ });
1250
+ const presentationBuilder = await generatePresentation(
1251
+ disclosureExchange
1252
+ );
1253
+ const dbDisclosureBeforeRequest = await mongoDb()
1254
+ .collection('disclosures')
1255
+ .findOne({
1256
+ _id: new ObjectId(disclosureExchange.disclosureId.toString()),
1257
+ });
1258
+ expect(dbDisclosureBeforeRequest.configurationType).toBeUndefined();
1259
+
1260
+ nock(mockVendorUrl)
1261
+ .post(sendCredentialsCheckedEndpoint)
1262
+ .reply(200, () => {
1263
+ return sendCredentialsPayload;
1264
+ });
1265
+
1266
+ const response = await fastify.injectJson({
1267
+ method: 'POST',
1268
+ url: inspectUrl(tenant, '/submit-presentation'),
1269
+ payload: {
1270
+ exchange_id: disclosureExchange._id,
1271
+ jwt_vp: await presentationBuilder.selfSign(),
1272
+ },
1273
+ });
1274
+ expect(response.statusCode).toEqual(200);
1275
+ expect(response.json).toEqual({
1276
+ exchange: {
1277
+ id: disclosureExchange._id,
1278
+ type: ExchangeTypes.DISCLOSURE,
1279
+ disclosureComplete: true,
1280
+ exchangeComplete: true,
1281
+ },
1282
+ token: expect.any(String),
1283
+ });
1284
+
1285
+ expect(
1286
+ await jwtVerify(response.json.token, tenantKeyPair.publicKey)
1287
+ ).toEqual(
1288
+ jwtAccessTokenExpectation(tenant, null, {
1289
+ jti: disclosureExchange._id.toString(),
1290
+ sub: expect.stringMatching(/anonymous|/),
1291
+ })
1292
+ );
1293
+
1294
+ const dbDisclosureAfterRequest = await mongoDb()
1295
+ .collection('disclosures')
1296
+ .findOne({
1297
+ _id: new ObjectId(disclosureExchange.disclosureId.toString()),
1298
+ });
1299
+
1300
+ expect(dbDisclosureAfterRequest.configurationType).toEqual('inspection');
1301
+ });
1302
+
1303
+ it('should 200 with credentials status payment required', async () => {
1304
+ mockVerifyCredentials.mockImplementationOnce(async ({ credentials }) => {
1305
+ return credentials.map((credential) => ({
1306
+ credential: decodeCredentialJwt(credential),
1307
+ credentialChecks: {
1308
+ TRUSTED_HOLDER: 'NOT_CHECKED',
1309
+ TRUSTED_ISSUER: 'VOUCHER_RESERVE_EXHAUSTED',
1310
+ UNEXPIRED: 'NOT_CHECKED',
1311
+ UNTAMPERED: 'NOT_CHECKED',
1312
+ UNREVOKED: 'NOT_CHECKED',
1313
+ },
1314
+ }));
1315
+ });
1316
+ const presentationBuilder = await generatePresentation(
1317
+ disclosureExchange
1318
+ );
1319
+ let webhookPayload;
1320
+
1321
+ nock(mockVendorUrl)
1322
+ .post(sendCredentialsCheckedEndpoint)
1323
+ .reply(200, (uri, body) => {
1324
+ webhookPayload = body;
1325
+ return sendCredentialsPayload;
1326
+ });
1327
+
1328
+ const response = await fastify.injectJson({
1329
+ method: 'POST',
1330
+ url: inspectUrl(tenant, '/submit-presentation'),
1331
+ payload: {
1332
+ exchange_id: disclosureExchange._id,
1333
+ jwt_vp: await presentationBuilder.selfSign(),
1334
+ },
1335
+ });
1336
+ expect(response.statusCode).toEqual(200);
1337
+ expect(response.json).toEqual({
1338
+ exchange: {
1339
+ id: disclosureExchange._id,
1340
+ type: ExchangeTypes.DISCLOSURE,
1341
+ disclosureComplete: true,
1342
+ exchangeComplete: true,
1343
+ },
1344
+ token: expect.any(String),
1345
+ });
1346
+
1347
+ expect(
1348
+ await jwtVerify(response.json.token, tenantKeyPair.publicKey)
1349
+ ).toEqual(
1350
+ jwtAccessTokenExpectation(tenant, null, {
1351
+ jti: disclosureExchange._id.toString(),
1352
+ sub: expect.stringMatching(/anonymous|/),
1353
+ })
1354
+ );
1355
+
1356
+ const dbExchange = await mongoDb()
1357
+ .collection('exchanges')
1358
+ .findOne({ _id: new ObjectId(disclosureExchange._id) });
1359
+ expect(dbExchange).toEqual({
1360
+ ...disclosureExchange,
1361
+ _id: new ObjectId(disclosureExchange._id),
1362
+ tenantId: new ObjectId(tenant._id),
1363
+ disclosureId: new ObjectId(disclosure._id),
1364
+ events: [
1365
+ { state: ExchangeStates.NEW, timestamp: expect.any(Date) },
1366
+ {
1367
+ state: ExchangeStates.DISCLOSURE_RECEIVED,
1368
+ timestamp: expect.any(Date),
1369
+ },
1370
+ {
1371
+ state: ExchangeStates.DISCLOSURE_CHECKED,
1372
+ timestamp: expect.any(Date),
1373
+ },
1374
+ {
1375
+ state: ExchangeStates.COMPLETE,
1376
+ timestamp: expect.any(Date),
1377
+ },
1378
+ ],
1379
+ presentationId: presentationBuilder.presentation.id,
1380
+ disclosureConsentedAt: expect.any(Date),
1381
+ createdAt: expect.any(Date),
1382
+ updatedAt: expect.any(Date),
1383
+ });
1384
+ expect(webhookPayload).toEqual({
1385
+ exchangeId: disclosureExchange._id,
1386
+ presentationId: presentationBuilder.presentation.id,
1387
+ vendorDisclosureId: disclosure.vendorDisclosureId,
1388
+ vendorOrganizationId: tenant.vendorOrganizationId,
1389
+ sendPushOnVerification: false,
1390
+ paymentRequired: true,
1391
+ tenantDID: tenant.did,
1392
+ tenantId: tenant._id,
1393
+ rawCredentials: mapWithIndex(
1394
+ ({ vc }, i) => ({
1395
+ id: vc.id,
1396
+ rawCredential:
1397
+ presentationBuilder.presentation.verifiableCredential[i],
1398
+ }),
1399
+ presentationBuilder.credentials
1400
+ ),
1401
+ credentials: map(
1402
+ ({ vc }) => ({
1403
+ ...vc,
1404
+ issuer: isString(vc.issuer) ? { id: vc.issuer } : vc.issuer,
1405
+ issuanceDate: expect.stringMatching(ISO_DATETIME_FORMAT),
1406
+ ...checkResultPaymentRequired,
1407
+ }),
1408
+ presentationBuilder.credentials
1409
+ ),
1410
+ });
1411
+ });
1412
+
1413
+ it('should 200 with credentials UNTAMPERED be VOUCHER_RESERVE_EXHAUSTED status payment required true', async () => {
1414
+ const credentialChecks = {
1415
+ TRUSTED_HOLDER: 'NOT_CHECKED',
1416
+ TRUSTED_ISSUER: 'NOT_CHECKED',
1417
+ UNEXPIRED: 'NOT_CHECKED',
1418
+ UNTAMPERED: 'VOUCHER_RESERVE_EXHAUSTED',
1419
+ UNREVOKED: 'NOT_CHECKED',
1420
+ };
1421
+
1422
+ mockVerifyCredentials.mockImplementationOnce(async ({ credentials }) => {
1423
+ return credentials.map((credential) => ({
1424
+ credential: decodeCredentialJwt(credential),
1425
+ credentialChecks,
1426
+ }));
1427
+ });
1428
+ const presentationBuilder = await generatePresentation(
1429
+ disclosureExchange
1430
+ );
1431
+ let webhookPayload;
1432
+
1433
+ nock(mockVendorUrl)
1434
+ .post(sendCredentialsCheckedEndpoint)
1435
+ .reply(200, (uri, body) => {
1436
+ webhookPayload = body;
1437
+ return sendCredentialsPayload;
1438
+ });
1439
+
1440
+ const response = await fastify.injectJson({
1441
+ method: 'POST',
1442
+ url: inspectUrl(tenant, '/submit-presentation'),
1443
+ payload: {
1444
+ exchange_id: disclosureExchange._id,
1445
+ jwt_vp: await presentationBuilder.selfSign(),
1446
+ },
1447
+ });
1448
+ expect(response.statusCode).toEqual(200);
1449
+
1450
+ expect(webhookPayload).toEqual({
1451
+ exchangeId: disclosureExchange._id,
1452
+ presentationId: presentationBuilder.presentation.id,
1453
+ vendorDisclosureId: disclosure.vendorDisclosureId,
1454
+ vendorOrganizationId: tenant.vendorOrganizationId,
1455
+ sendPushOnVerification: false,
1456
+ paymentRequired: true,
1457
+ tenantDID: tenant.did,
1458
+ tenantId: tenant._id,
1459
+ rawCredentials: mapWithIndex(
1460
+ ({ vc }, i) => ({
1461
+ id: vc.id,
1462
+ rawCredential:
1463
+ presentationBuilder.presentation.verifiableCredential[i],
1464
+ }),
1465
+ presentationBuilder.credentials
1466
+ ),
1467
+ credentials: map(
1468
+ ({ vc }) => ({
1469
+ ...vc,
1470
+ issuer: isString(vc.issuer) ? { id: vc.issuer } : vc.issuer,
1471
+ issuanceDate: expect.stringMatching(ISO_DATETIME_FORMAT),
1472
+ credentialChecks,
1473
+ }),
1474
+ presentationBuilder.credentials
1475
+ ),
1476
+ });
1477
+ });
1478
+
1479
+ it('should 200 with credentials UNTAMPERED be NOT_CHECKED status payment required false', async () => {
1480
+ const credentialChecks = {
1481
+ TRUSTED_HOLDER: 'NOT_CHECKED',
1482
+ TRUSTED_ISSUER: 'NOT_CHECKED',
1483
+ UNEXPIRED: 'NOT_CHECKED',
1484
+ UNTAMPERED: 'NOT_CHECKED',
1485
+ UNREVOKED: 'NOT_CHECKED',
1486
+ };
1487
+
1488
+ mockVerifyCredentials.mockImplementationOnce(async ({ credentials }) => {
1489
+ return credentials.map((credential) => ({
1490
+ credential: decodeCredentialJwt(credential),
1491
+ credentialChecks,
1492
+ }));
1493
+ });
1494
+ const presentationBuilder = await generatePresentation(
1495
+ disclosureExchange
1496
+ );
1497
+ let webhookPayload;
1498
+
1499
+ nock(mockVendorUrl)
1500
+ .post(sendCredentialsCheckedEndpoint)
1501
+ .reply(200, (uri, body) => {
1502
+ webhookPayload = body;
1503
+ return sendCredentialsPayload;
1504
+ });
1505
+
1506
+ const response = await fastify.injectJson({
1507
+ method: 'POST',
1508
+ url: inspectUrl(tenant, '/submit-presentation'),
1509
+ payload: {
1510
+ exchange_id: disclosureExchange._id,
1511
+ jwt_vp: await presentationBuilder.selfSign(),
1512
+ },
1513
+ });
1514
+ expect(response.statusCode).toEqual(200);
1515
+
1516
+ expect(webhookPayload).toEqual({
1517
+ exchangeId: disclosureExchange._id,
1518
+ presentationId: presentationBuilder.presentation.id,
1519
+ vendorDisclosureId: disclosure.vendorDisclosureId,
1520
+ vendorOrganizationId: tenant.vendorOrganizationId,
1521
+ sendPushOnVerification: false,
1522
+ paymentRequired: false,
1523
+ tenantDID: tenant.did,
1524
+ tenantId: tenant._id,
1525
+ rawCredentials: mapWithIndex(
1526
+ ({ vc }, i) => ({
1527
+ id: vc.id,
1528
+ rawCredential:
1529
+ presentationBuilder.presentation.verifiableCredential[i],
1530
+ }),
1531
+ presentationBuilder.credentials
1532
+ ),
1533
+ credentials: map(
1534
+ ({ vc }) => ({
1535
+ ...vc,
1536
+ issuer: isString(vc.issuer) ? { id: vc.issuer } : vc.issuer,
1537
+ issuanceDate: expect.stringMatching(ISO_DATETIME_FORMAT),
1538
+ credentialChecks,
1539
+ }),
1540
+ presentationBuilder.credentials
1541
+ ),
1542
+ });
1543
+ });
1544
+
1545
+ it('should be backwards compatible with the vp_jwt prop', async () => {
1546
+ const presentationBuilder = await generatePresentation(
1547
+ disclosureExchange
1548
+ );
1549
+ let webhookPayload;
1550
+
1551
+ nock(mockVendorUrl)
1552
+ .post(sendCredentialsCheckedEndpoint)
1553
+ .reply(200, (uri, body) => {
1554
+ webhookPayload = body;
1555
+ return sendCredentialsPayload;
1556
+ });
1557
+
1558
+ const response = await fastify.injectJson({
1559
+ method: 'POST',
1560
+ url: inspectUrl(tenant, '/submit-presentation'),
1561
+ payload: {
1562
+ exchange_id: disclosureExchange._id,
1563
+ vp_jwt: await presentationBuilder.selfSign(),
1564
+ },
1565
+ });
1566
+
1567
+ expect(response.statusCode).toEqual(200);
1568
+ const dbExchange = await mongoDb()
1569
+ .collection('exchanges')
1570
+ .findOne({ _id: new ObjectId(disclosureExchange._id) });
1571
+ expect(dbExchange).toEqual({
1572
+ ...disclosureExchange,
1573
+ _id: new ObjectId(disclosureExchange._id),
1574
+ tenantId: new ObjectId(tenant._id),
1575
+ disclosureId: new ObjectId(disclosure._id),
1576
+ events: [
1577
+ { state: ExchangeStates.NEW, timestamp: expect.any(Date) },
1578
+ {
1579
+ state: ExchangeStates.DISCLOSURE_RECEIVED,
1580
+ timestamp: expect.any(Date),
1581
+ },
1582
+ {
1583
+ state: ExchangeStates.DISCLOSURE_CHECKED,
1584
+ timestamp: expect.any(Date),
1585
+ },
1586
+ {
1587
+ state: ExchangeStates.COMPLETE,
1588
+ timestamp: expect.any(Date),
1589
+ },
1590
+ ],
1591
+ presentationId: presentationBuilder.presentation.id,
1592
+ disclosureConsentedAt: expect.any(Date),
1593
+ createdAt: expect.any(Date),
1594
+ updatedAt: expect.any(Date),
1595
+ });
1596
+ expect(webhookPayload).toEqual({
1597
+ exchangeId: disclosureExchange._id,
1598
+ presentationId: presentationBuilder.presentation.id,
1599
+ vendorDisclosureId: disclosure.vendorDisclosureId,
1600
+ vendorOrganizationId: tenant.vendorOrganizationId,
1601
+ tenantDID: tenant.did,
1602
+ tenantId: tenant._id,
1603
+ sendPushOnVerification: false,
1604
+ paymentRequired: false,
1605
+ rawCredentials: mapWithIndex(
1606
+ ({ vc }, i) => ({
1607
+ id: vc.id,
1608
+ rawCredential:
1609
+ presentationBuilder.presentation.verifiableCredential[i],
1610
+ }),
1611
+ presentationBuilder.credentials
1612
+ ),
1613
+ credentials: map(
1614
+ ({ vc }) => ({
1615
+ ...vc,
1616
+ issuer: isString(vc.issuer) ? { id: vc.issuer } : vc.issuer,
1617
+ issuanceDate: expect.stringMatching(ISO_DATETIME_FORMAT),
1618
+ ...checkResult,
1619
+ }),
1620
+ presentationBuilder.credentials
1621
+ ),
1622
+ });
1623
+ });
1624
+ it('should 409 if the presentation submission has already occurred', async () => {
1625
+ const nockContext = nock(mockVendorUrl)
1626
+ .post(sendCredentialsCheckedEndpoint)
1627
+ .times(2)
1628
+ .reply(200, sendCredentialsPayload);
1629
+
1630
+ const presentationBuilder = await generatePresentation(
1631
+ disclosureExchange
1632
+ );
1633
+
1634
+ const selfSignedPresentation = await presentationBuilder.selfSign();
1635
+ const responses = await Promise.all([
1636
+ fastify.injectJson({
1637
+ method: 'POST',
1638
+ url: inspectUrl(tenant, '/submit-presentation'),
1639
+ payload: {
1640
+ exchange_id: disclosureExchange._id,
1641
+ jwt_vp: selfSignedPresentation,
1642
+ },
1643
+ }),
1644
+ fastify.injectJson({
1645
+ method: 'POST',
1646
+ url: inspectUrl(tenant, '/submit-presentation'),
1647
+ payload: {
1648
+ exchange_id: disclosureExchange._id,
1649
+ jwt_vp: selfSignedPresentation,
1650
+ },
1651
+ }),
1652
+ ]);
1653
+
1654
+ expect(map('statusCode', responses)).toEqual(
1655
+ expect.arrayContaining([200, 409])
1656
+ );
1657
+
1658
+ const dbExchange = await mongoDb()
1659
+ .collection('exchanges')
1660
+ .findOne({ _id: new ObjectId(disclosureExchange._id) });
1661
+ expect(dbExchange).toEqual({
1662
+ ...disclosureExchange,
1663
+ _id: new ObjectId(disclosureExchange._id),
1664
+ tenantId: new ObjectId(tenant._id),
1665
+ disclosureId: new ObjectId(disclosure._id),
1666
+ events: [
1667
+ { state: ExchangeStates.NEW, timestamp: expect.any(Date) },
1668
+ {
1669
+ state: ExchangeStates.DISCLOSURE_RECEIVED,
1670
+ timestamp: expect.any(Date),
1671
+ },
1672
+ {
1673
+ state: ExchangeStates.DISCLOSURE_CHECKED,
1674
+ timestamp: expect.any(Date),
1675
+ },
1676
+ {
1677
+ state: ExchangeStates.COMPLETE,
1678
+ timestamp: expect.any(Date),
1679
+ },
1680
+ ],
1681
+ presentationId: presentationBuilder.presentation.id,
1682
+ disclosureConsentedAt: expect.any(Date),
1683
+ createdAt: expect.any(Date),
1684
+ updatedAt: expect.any(Date),
1685
+ });
1686
+ expect(nockContext.pendingMocks()).toHaveLength(1);
1687
+ });
1688
+ it('should 200 on 2 different presentation submissions', async () => {
1689
+ const nockContext = nock(mockVendorUrl)
1690
+ .post(sendCredentialsCheckedEndpoint)
1691
+ .times(2)
1692
+ .reply(200, sendCredentialsPayload);
1693
+
1694
+ const presentationBuilder = await generatePresentation(
1695
+ disclosureExchange
1696
+ );
1697
+ const presentationBuilder2 = await generatePresentation(
1698
+ disclosureExchange2
1699
+ );
1700
+ const response = await fastify.injectJson({
1701
+ method: 'POST',
1702
+ url: inspectUrl(tenant, '/submit-presentation'),
1703
+ payload: {
1704
+ exchange_id: disclosureExchange._id,
1705
+ jwt_vp: await presentationBuilder.selfSign(),
1706
+ },
1707
+ });
1708
+
1709
+ const response2 = await fastify.injectJson({
1710
+ method: 'POST',
1711
+ url: inspectUrl(tenant, '/submit-presentation'),
1712
+ payload: {
1713
+ exchange_id: disclosureExchange2._id,
1714
+ jwt_vp: await presentationBuilder2.selfSign(),
1715
+ },
1716
+ });
1717
+
1718
+ expect(response.statusCode).toEqual(200);
1719
+ expect(response2.statusCode).toEqual(200);
1720
+
1721
+ const dbExchange = await mongoDb()
1722
+ .collection('exchanges')
1723
+ .findOne({ _id: new ObjectId(disclosureExchange._id) });
1724
+ expect(dbExchange).toEqual({
1725
+ ...disclosureExchange,
1726
+ _id: new ObjectId(disclosureExchange._id),
1727
+ tenantId: new ObjectId(tenant._id),
1728
+ disclosureId: new ObjectId(disclosure._id),
1729
+ events: [
1730
+ { state: ExchangeStates.NEW, timestamp: expect.any(Date) },
1731
+ {
1732
+ state: ExchangeStates.DISCLOSURE_RECEIVED,
1733
+ timestamp: expect.any(Date),
1734
+ },
1735
+ {
1736
+ state: ExchangeStates.DISCLOSURE_CHECKED,
1737
+ timestamp: expect.any(Date),
1738
+ },
1739
+ {
1740
+ state: ExchangeStates.COMPLETE,
1741
+ timestamp: expect.any(Date),
1742
+ },
1743
+ ],
1744
+ presentationId: presentationBuilder.presentation.id,
1745
+ disclosureConsentedAt: expect.any(Date),
1746
+ createdAt: expect.any(Date),
1747
+ updatedAt: expect.any(Date),
1748
+ });
1749
+
1750
+ const dbExchange2 = await mongoDb()
1751
+ .collection('exchanges')
1752
+ .findOne({ _id: new ObjectId(disclosureExchange2._id) });
1753
+ expect(dbExchange2).toEqual({
1754
+ ...disclosureExchange,
1755
+ _id: new ObjectId(disclosureExchange2._id),
1756
+ tenantId: new ObjectId(tenant._id),
1757
+ disclosureId: new ObjectId(disclosure._id),
1758
+ events: [
1759
+ { state: ExchangeStates.NEW, timestamp: expect.any(Date) },
1760
+ {
1761
+ state: ExchangeStates.DISCLOSURE_RECEIVED,
1762
+ timestamp: expect.any(Date),
1763
+ },
1764
+ {
1765
+ state: ExchangeStates.DISCLOSURE_CHECKED,
1766
+ timestamp: expect.any(Date),
1767
+ },
1768
+ {
1769
+ state: ExchangeStates.COMPLETE,
1770
+ timestamp: expect.any(Date),
1771
+ },
1772
+ ],
1773
+ presentationId: presentationBuilder2.presentation.id,
1774
+ disclosureConsentedAt: expect.any(Date),
1775
+ createdAt: expect.any(Date),
1776
+ updatedAt: expect.any(Date),
1777
+ });
1778
+ expect(nockContext.pendingMocks()).toHaveLength(0);
1779
+ });
1780
+ it('should 200 if the presentation submission maps to a standard vp and contains a vendorOriginContext', async () => {
1781
+ const presentationBuilder = await generatePresentation(
1782
+ disclosureExchange
1783
+ );
1784
+ presentationBuilder.presentation.vendorOriginContext = '123';
1785
+
1786
+ let webhookPayload;
1787
+ nock(mockVendorUrl)
1788
+ .post(sendCredentialsCheckedEndpoint)
1789
+ .reply(200, (uri, body) => {
1790
+ webhookPayload = body;
1791
+ return sendCredentialsPayload;
1792
+ });
1793
+
1794
+ const response = await fastify.injectJson({
1795
+ method: 'POST',
1796
+ url: inspectUrl(tenant, '/submit-presentation'),
1797
+ payload: {
1798
+ exchange_id: disclosureExchange._id,
1799
+ jwt_vp: await presentationBuilder.selfSign(),
1800
+ },
1801
+ });
1802
+
1803
+ expect(response.statusCode).toEqual(200);
1804
+ expect(response.json).toEqual({
1805
+ exchange: {
1806
+ id: disclosureExchange._id,
1807
+ type: 'DISCLOSURE',
1808
+ disclosureComplete: true,
1809
+ exchangeComplete: true,
1810
+ },
1811
+ token: expect.any(String),
1812
+ });
1813
+
1814
+ expect(
1815
+ await jwtVerify(response.json.token, tenantKeyPair.publicKey)
1816
+ ).toEqual(
1817
+ jwtAccessTokenExpectation(tenant, null, {
1818
+ jti: disclosureExchange._id.toString(),
1819
+ sub: expect.stringMatching(/anonymous|/),
1820
+ })
1821
+ );
1822
+
1823
+ const dbExchange = await mongoDb()
1824
+ .collection('exchanges')
1825
+ .findOne({ _id: new ObjectId(disclosureExchange._id) });
1826
+ expect(dbExchange).toEqual({
1827
+ ...disclosureExchange,
1828
+ _id: new ObjectId(disclosureExchange._id),
1829
+ tenantId: new ObjectId(tenant._id),
1830
+ disclosureId: new ObjectId(disclosure._id),
1831
+ events: [
1832
+ { state: ExchangeStates.NEW, timestamp: expect.any(Date) },
1833
+ {
1834
+ state: ExchangeStates.DISCLOSURE_RECEIVED,
1835
+ timestamp: expect.any(Date),
1836
+ },
1837
+ {
1838
+ state: ExchangeStates.DISCLOSURE_CHECKED,
1839
+ timestamp: expect.any(Date),
1840
+ },
1841
+ {
1842
+ state: ExchangeStates.COMPLETE,
1843
+ timestamp: expect.any(Date),
1844
+ },
1845
+ ],
1846
+ presentationId: presentationBuilder.presentation.id,
1847
+ disclosureConsentedAt: expect.any(Date),
1848
+ createdAt: expect.any(Date),
1849
+ updatedAt: expect.any(Date),
1850
+ });
1851
+ expect(webhookPayload).toEqual({
1852
+ exchangeId: disclosureExchange._id,
1853
+ presentationId: presentationBuilder.presentation.id,
1854
+ vendorDisclosureId: disclosure.vendorDisclosureId,
1855
+ vendorOrganizationId: tenant.vendorOrganizationId,
1856
+ tenantDID: tenant.did,
1857
+ tenantId: tenant._id,
1858
+ sendPushOnVerification: false,
1859
+ paymentRequired: false,
1860
+ vendorOriginContext: '123',
1861
+ rawCredentials: mapWithIndex(
1862
+ ({ vc }, i) => ({
1863
+ id: vc.id,
1864
+ rawCredential:
1865
+ presentationBuilder.presentation.verifiableCredential[i],
1866
+ }),
1867
+ presentationBuilder.credentials
1868
+ ),
1869
+ credentials: map(
1870
+ ({ vc }) => ({
1871
+ ...vc,
1872
+ issuer: isString(vc.issuer) ? { id: vc.issuer } : vc.issuer,
1873
+ issuanceDate: expect.stringMatching(ISO_DATETIME_FORMAT),
1874
+ ...checkResult,
1875
+ }),
1876
+ presentationBuilder.credentials
1877
+ ),
1878
+ });
1879
+ });
1880
+ });
1881
+
1882
+ describe('presentation @context validation enabled test suite', () => {
1883
+ beforeEach(async () => {
1884
+ fastify.overrides.reqConfig = (config) => ({
1885
+ ...config,
1886
+ enablePresentationContextValidation: true,
1887
+ });
1888
+ });
1889
+
1890
+ it('should 400 when @context is a string and is incorrect value', async () => {
1891
+ const presentationDocument = (
1892
+ await generatePresentation(disclosureExchange)
1893
+ ).override({ '@context': 'foo' });
1894
+
1895
+ const response = await fastify.injectJson({
1896
+ method: 'POST',
1897
+ url: inspectUrl(tenant, '/submit-presentation'),
1898
+ payload: {
1899
+ exchange_id: disclosureExchange._id,
1900
+ jwt_vp: await presentationDocument.selfSign(),
1901
+ },
1902
+ });
1903
+
1904
+ expect(response.statusCode).toEqual(400);
1905
+ expect(response.json).toEqual(
1906
+ errorResponseMatcher({
1907
+ error: 'Bad Request',
1908
+ message: 'presentation @context is not set correctly',
1909
+ statusCode: 400,
1910
+ errorCode: 'presentation_invalid',
1911
+ })
1912
+ );
1913
+ });
1914
+ it('should 400 when @context is an array and is incorrect value', async () => {
1915
+ const presentationDocument = (
1916
+ await generatePresentation(disclosureExchange)
1917
+ ).override({ '@context': ['foo'] });
1918
+
1919
+ const response = await fastify.injectJson({
1920
+ method: 'POST',
1921
+ url: inspectUrl(tenant, '/submit-presentation'),
1922
+ payload: {
1923
+ exchange_id: disclosureExchange._id,
1924
+ jwt_vp: await presentationDocument.selfSign(),
1925
+ },
1926
+ });
1927
+
1928
+ expect(response.statusCode).toEqual(400);
1929
+ expect(response.json).toEqual(
1930
+ errorResponseMatcher({
1931
+ error: 'Bad Request',
1932
+ message: 'presentation @context is not set correctly',
1933
+ statusCode: 400,
1934
+ errorCode: 'presentation_invalid',
1935
+ })
1936
+ );
1937
+ });
1938
+ it('should 400 when @context is an empty array', async () => {
1939
+ const presentationDocument = (
1940
+ await generatePresentation(disclosureExchange)
1941
+ ).override({ '@context': [] });
1942
+
1943
+ const response = await fastify.injectJson({
1944
+ method: 'POST',
1945
+ url: inspectUrl(tenant, '/submit-presentation'),
1946
+ payload: {
1947
+ exchange_id: disclosureExchange._id,
1948
+ jwt_vp: await presentationDocument.selfSign(),
1949
+ },
1950
+ });
1951
+
1952
+ expect(response.statusCode).toEqual(400);
1953
+ expect(response.json).toEqual(
1954
+ errorResponseMatcher({
1955
+ error: 'Bad Request',
1956
+ message: 'presentation @context is not set correctly',
1957
+ statusCode: 400,
1958
+ errorCode: 'presentation_invalid',
1959
+ })
1960
+ );
1961
+ });
1962
+ it('should 200 when @context is a string and is correct value', async () => {
1963
+ const presentationBuilder = (
1964
+ await generatePresentation(disclosureExchange)
1965
+ ).override({
1966
+ '@context': 'https://www.w3.org/2018/credentials/v1',
1967
+ });
1968
+
1969
+ nock(mockVendorUrl)
1970
+ .post(sendCredentialsCheckedEndpoint)
1971
+ .reply(200, sendCredentialsPayload);
1972
+
1973
+ const response = await fastify.injectJson({
1974
+ method: 'POST',
1975
+ url: inspectUrl(tenant, '/submit-presentation'),
1976
+ payload: {
1977
+ exchange_id: disclosureExchange._id,
1978
+ jwt_vp: await presentationBuilder.selfSign(),
1979
+ },
1980
+ });
1981
+
1982
+ expect(response.statusCode).toEqual(200);
1983
+ });
1984
+ it('should 200 when @context is an array and is correct value', async () => {
1985
+ const presentationBuilder = (
1986
+ await generatePresentation(disclosureExchange)
1987
+ ).override({
1988
+ '@context': ['https://www.w3.org/2018/credentials/v1'],
1989
+ });
1990
+
1991
+ nock(mockVendorUrl)
1992
+ .post(sendCredentialsCheckedEndpoint)
1993
+ .reply(200, sendCredentialsPayload);
1994
+
1995
+ const response = await fastify.injectJson({
1996
+ method: 'POST',
1997
+ url: inspectUrl(tenant, '/submit-presentation'),
1998
+ payload: {
1999
+ exchange_id: disclosureExchange._id,
2000
+ jwt_vp: await presentationBuilder.selfSign(),
2001
+ },
2002
+ });
2003
+
2004
+ expect(response.statusCode).toEqual(200);
2005
+ });
2006
+ });
2007
+
2008
+ describe('presentation submission authorization test suite', () => {
2009
+ it('should 401 if the disclosure feed is true, but no auth token', async () => {
2010
+ disclosure = await persistDisclosure({
2011
+ tenant,
2012
+ vendorEndpoint: VendorEndpoint.RECEIVE_CHECKED_CREDENTIALS,
2013
+ feed: true,
2014
+ });
2015
+ disclosureExchange = await persistDisclosureExchange({
2016
+ disclosure,
2017
+ tenant,
2018
+ protocolMetadata: {
2019
+ protocol: ExchangeProtocols.VNF_API,
2020
+ },
2021
+ });
2022
+ const builder = await generatePresentation(disclosureExchange);
2023
+
2024
+ // eslint-disable-next-line camelcase
2025
+ const jwt_vp = await builder.selfSign();
2026
+ const response = await fastify.injectJson({
2027
+ method: 'POST',
2028
+ url: inspectUrl(tenant, '/submit-presentation'),
2029
+ // eslint-disable-next-line camelcase
2030
+ payload: { exchange_id: disclosureExchange._id, jwt_vp },
2031
+ });
2032
+
2033
+ expect(response.statusCode).toEqual(401);
2034
+ expect(response.json).toEqual(
2035
+ errorResponseMatcher({
2036
+ error: 'Unauthorized',
2037
+ errorCode: 'unauthorized',
2038
+ message: 'Unauthorized',
2039
+ statusCode: 401,
2040
+ })
2041
+ );
2042
+ });
2043
+ it('should 401 if user is not found', async () => {
2044
+ disclosure = await persistDisclosure({
2045
+ tenant,
2046
+ vendorEndpoint: VendorEndpoint.RECEIVE_CHECKED_CREDENTIALS,
2047
+ feed: true,
2048
+ });
2049
+ disclosureExchange = await persistDisclosureExchange({
2050
+ disclosure,
2051
+ tenant,
2052
+ protocolMetadata: {
2053
+ protocol: ExchangeProtocols.VNF_API,
2054
+ },
2055
+ });
2056
+
2057
+ const signedJwt = await generateDocJwt(
2058
+ { foo: 'foo' },
2059
+ tenantKeyPair.privateKey,
2060
+ {
2061
+ subject: 'foo',
2062
+ issuer: 'did:foo:foo',
2063
+ audience: 'did:foo:foo',
2064
+ expiresIn: '1h',
2065
+ }
2066
+ );
2067
+ const builder = await generatePresentation(disclosureExchange);
2068
+
2069
+ // eslint-disable-next-line camelcase
2070
+ const jwt_vp = await builder.selfSign();
2071
+ const response = await fastify.injectJson({
2072
+ method: 'POST',
2073
+ url: inspectUrl(tenant, '/submit-presentation'),
2074
+ headers: {
2075
+ Authorization: `Bearer ${signedJwt}`,
2076
+ },
2077
+ // eslint-disable-next-line camelcase
2078
+ payload: { exchange_id: disclosureExchange._id, jwt_vp },
2079
+ });
2080
+
2081
+ expect(response.statusCode).toEqual(401);
2082
+ expect(response.json).toEqual(
2083
+ errorResponseMatcher({
2084
+ error: 'Unauthorized',
2085
+ errorCode: 'unauthorized',
2086
+ message: 'Unauthorized',
2087
+ statusCode: 401,
2088
+ })
2089
+ );
2090
+ });
2091
+ it('should 401 if auth jwt is tampered', async () => {
2092
+ disclosure = await persistDisclosure({
2093
+ tenant,
2094
+ vendorEndpoint: VendorEndpoint.RECEIVE_CHECKED_CREDENTIALS,
2095
+ feed: true,
2096
+ });
2097
+ disclosureExchange = await persistDisclosureExchange({
2098
+ disclosure,
2099
+ tenant,
2100
+ protocolMetadata: {
2101
+ protocol: ExchangeProtocols.VNF_API,
2102
+ },
2103
+ });
2104
+
2105
+ const signedJwt = await generateDocJwt(
2106
+ { foo: 'foo' },
2107
+ tenantKeyPair.privateKey,
2108
+ {
2109
+ subject: vendorUserIdMapping._id,
2110
+ issuer: 'did:foo:foo',
2111
+ audience: 'did:foo:foo',
2112
+ expiresIn: '1h',
2113
+ }
2114
+ );
2115
+ const tamperedJwt = tamperJwt(signedJwt, { foo: 'bar' });
2116
+ const builder = await generatePresentation(disclosureExchange);
2117
+
2118
+ // eslint-disable-next-line camelcase
2119
+ const jwt_vp = await builder.selfSign();
2120
+ const response = await fastify.injectJson({
2121
+ method: 'POST',
2122
+ url: inspectUrl(tenant, '/submit-presentation'),
2123
+ headers: {
2124
+ Authorization: `Bearer ${tamperedJwt}`,
2125
+ },
2126
+ // eslint-disable-next-line camelcase
2127
+ payload: { exchange_id: disclosureExchange._id, jwt_vp },
2128
+ });
2129
+
2130
+ expect(response.statusCode).toEqual(401);
2131
+ expect(response.json).toEqual(
2132
+ errorResponseMatcher({
2133
+ error: 'Unauthorized',
2134
+ errorCode: 'unauthorized',
2135
+ message: 'Unauthorized',
2136
+ statusCode: 401,
2137
+ })
2138
+ );
2139
+ });
2140
+ it('should 401 if auth jwt is expired', async () => {
2141
+ disclosure = await persistDisclosure({
2142
+ tenant,
2143
+ vendorEndpoint: VendorEndpoint.RECEIVE_CHECKED_CREDENTIALS,
2144
+ feed: true,
2145
+ });
2146
+ disclosureExchange = await persistDisclosureExchange({
2147
+ disclosure,
2148
+ tenant,
2149
+ protocolMetadata: {
2150
+ protocol: ExchangeProtocols.VNF_API,
2151
+ },
2152
+ });
2153
+
2154
+ const signedJwt = await generateDocJwt(
2155
+ { foo: 'foo' },
2156
+ tenantKeyPair.privateKey,
2157
+ {
2158
+ kid: `${tenant.did}#key-1`,
2159
+ subject: vendorUserIdMapping._id,
2160
+ issuer: tenant.did,
2161
+ audience: tenant.did,
2162
+ exp: getUnixTime(subHours(1)(new Date())),
2163
+ }
2164
+ );
2165
+ const builder = await generatePresentation(disclosureExchange);
2166
+
2167
+ // eslint-disable-next-line camelcase
2168
+ const jwt_vp = await builder.selfSign();
2169
+ const response = await fastify.injectJson({
2170
+ method: 'POST',
2171
+ url: inspectUrl(tenant, '/submit-presentation'),
2172
+ headers: {
2173
+ Authorization: `Bearer ${signedJwt}`,
2174
+ },
2175
+ // eslint-disable-next-line camelcase
2176
+ payload: { exchange_id: disclosureExchange._id, jwt_vp },
2177
+ });
2178
+
2179
+ expect(response.statusCode).toEqual(401);
2180
+ expect(response.json).toEqual(
2181
+ errorResponseMatcher({
2182
+ error: 'Unauthorized',
2183
+ errorCode: 'unauthorized',
2184
+ message: 'Unauthorized',
2185
+ statusCode: 401,
2186
+ })
2187
+ );
2188
+ });
2189
+ it('should 401 if auth jwt audience is not the tenant', async () => {
2190
+ disclosure = await persistDisclosure({
2191
+ tenant,
2192
+ vendorEndpoint: VendorEndpoint.RECEIVE_CHECKED_CREDENTIALS,
2193
+ feed: true,
2194
+ });
2195
+ disclosureExchange = await persistDisclosureExchange({
2196
+ disclosure,
2197
+ tenant,
2198
+ protocolMetadata: {
2199
+ protocol: ExchangeProtocols.VNF_API,
2200
+ },
2201
+ });
2202
+
2203
+ const signedJwt = await generateDocJwt(
2204
+ { foo: 'foo' },
2205
+ tenantKeyPair.privateKey,
2206
+ {
2207
+ subject: vendorUserIdMapping._id,
2208
+ issuer: tenant.did,
2209
+ audience: 'did:foo:foo',
2210
+ expiresIn: '1h',
2211
+ }
2212
+ );
2213
+ const builder = await generatePresentation(disclosureExchange);
2214
+
2215
+ // eslint-disable-next-line camelcase
2216
+ const jwt_vp = await builder.selfSign();
2217
+ const response = await fastify.injectJson({
2218
+ method: 'POST',
2219
+ url: inspectUrl(tenant, '/submit-presentation'),
2220
+ headers: {
2221
+ Authorization: `Bearer ${signedJwt}`,
2222
+ },
2223
+ // eslint-disable-next-line camelcase
2224
+ payload: { exchange_id: disclosureExchange._id, jwt_vp },
2225
+ });
2226
+
2227
+ expect(response.statusCode).toEqual(401);
2228
+ expect(response.json).toEqual(
2229
+ errorResponseMatcher({
2230
+ error: 'Unauthorized',
2231
+ errorCode: 'unauthorized',
2232
+ message: 'Unauthorized',
2233
+ statusCode: 401,
2234
+ })
2235
+ );
2236
+ });
2237
+ it('should 401 if auth jwt issuer is not the tenant', async () => {
2238
+ disclosure = await persistDisclosure({
2239
+ tenant,
2240
+ vendorEndpoint: VendorEndpoint.RECEIVE_CHECKED_CREDENTIALS,
2241
+ feed: true,
2242
+ });
2243
+ disclosureExchange = await persistDisclosureExchange({
2244
+ disclosure,
2245
+ tenant,
2246
+ protocolMetadata: {
2247
+ protocol: ExchangeProtocols.VNF_API,
2248
+ },
2249
+ });
2250
+
2251
+ const signedJwt = await generateDocJwt(
2252
+ { foo: 'foo' },
2253
+ tenantKeyPair.privateKey,
2254
+ {
2255
+ subject: vendorUserIdMapping._id,
2256
+ issuer: 'did:foo:foo',
2257
+ audience: tenant.did,
2258
+ expiresIn: '1h',
2259
+ }
2260
+ );
2261
+ const builder = await generatePresentation(disclosureExchange);
2262
+
2263
+ // eslint-disable-next-line camelcase
2264
+ const jwt_vp = await builder.selfSign();
2265
+ const response = await fastify.injectJson({
2266
+ method: 'POST',
2267
+ url: inspectUrl(tenant, '/submit-presentation'),
2268
+ headers: {
2269
+ Authorization: `Bearer ${signedJwt}`,
2270
+ },
2271
+ // eslint-disable-next-line camelcase
2272
+ payload: { exchange_id: disclosureExchange._id, jwt_vp },
2273
+ });
2274
+
2275
+ expect(response.statusCode).toEqual(401);
2276
+ expect(response.json).toEqual(
2277
+ errorResponseMatcher({
2278
+ error: 'Unauthorized',
2279
+ errorCode: 'unauthorized',
2280
+ message: 'Unauthorized',
2281
+ statusCode: 401,
2282
+ })
2283
+ );
2284
+ });
2285
+ it('should 200 if auth jwt is valid', async () => {
2286
+ const vendorNock = nock(mockVendorUrl);
2287
+
2288
+ disclosure = await persistDisclosure({
2289
+ tenant,
2290
+ vendorEndpoint: VendorEndpoint.RECEIVE_UNCHECKED_CREDENTIALS,
2291
+ feed: true,
2292
+ });
2293
+ disclosureExchange = await persistDisclosureExchange({
2294
+ disclosure,
2295
+ tenant,
2296
+ protocolMetadata: {
2297
+ protocol: ExchangeProtocols.VNF_API,
2298
+ },
2299
+ });
2300
+
2301
+ vendorNock.post(sendCredentialsUncheckedEndpoint).reply(200);
2302
+
2303
+ const signedJwt = await generateDocJwt(
2304
+ { foo: 'foo' },
2305
+ tenantKeyPair.privateKey,
2306
+ {
2307
+ kid: '#exchanges-1',
2308
+ subject: vendorUserIdMapping._id,
2309
+ issuer: tenant.did,
2310
+ audience: tenant.did,
2311
+ expiresIn: '1h',
2312
+ }
2313
+ );
2314
+ const builder = await generatePresentation(disclosureExchange);
2315
+
2316
+ // eslint-disable-next-line camelcase
2317
+ const jwt_vp = await builder.selfSign();
2318
+ const response = await fastify.injectJson({
2319
+ method: 'POST',
2320
+ url: inspectUrl(tenant, '/submit-presentation'),
2321
+ headers: {
2322
+ Authorization: `Bearer ${signedJwt}`,
2323
+ },
2324
+ // eslint-disable-next-line camelcase
2325
+ payload: { exchange_id: disclosureExchange._id, jwt_vp },
2326
+ });
2327
+
2328
+ expect(response.statusCode).toEqual(200);
2329
+ expect(vendorNock.isDone()).toEqual(true);
2330
+ });
2331
+ it('should 200 if disclosure.field is false', async () => {
2332
+ const vendorNock = nock(mockVendorUrl);
2333
+
2334
+ disclosure = await persistDisclosure({
2335
+ tenant,
2336
+ vendorEndpoint: VendorEndpoint.RECEIVE_UNCHECKED_CREDENTIALS,
2337
+ feed: false,
2338
+ });
2339
+ disclosureExchange = await persistDisclosureExchange({
2340
+ disclosure,
2341
+ tenant,
2342
+ protocolMetadata: {
2343
+ protocol: ExchangeProtocols.VNF_API,
2344
+ },
2345
+ });
2346
+
2347
+ vendorNock.post(sendCredentialsUncheckedEndpoint).reply(200);
2348
+
2349
+ const builder = await generatePresentation(disclosureExchange);
2350
+
2351
+ // eslint-disable-next-line camelcase
2352
+ const jwt_vp = await builder.selfSign();
2353
+ const response = await fastify.injectJson({
2354
+ method: 'POST',
2355
+ url: inspectUrl(tenant, '/submit-presentation'),
2356
+ headers: {},
2357
+ // eslint-disable-next-line camelcase
2358
+ payload: { exchange_id: disclosureExchange._id, jwt_vp },
2359
+ });
2360
+
2361
+ expect(response.statusCode).toEqual(200);
2362
+ expect(vendorNock.isDone()).toEqual(true);
2363
+ });
2364
+ });
2365
+ });