diff --git a/.eslintrc.js b/.eslintrc.js index c9fa9626..07a0a1b5 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -28,6 +28,8 @@ module.exports = { '@typescript-eslint/no-explicit-any': 'error', '@typescript-eslint/no-var-requires': 'error', '@typescript-eslint/no-empty-interface': 'warn', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-explicit-any': 'off', 'no-console': 'off', quotes: ['error', 'single'], 'linebreak-style': ['error', 'unix'], @@ -38,7 +40,7 @@ module.exports = { 'cucumber/no-arrow-functions': 2, 'import/default': 'warn', // Support legacy modules without default export 'import/extensions': 'off', - 'max-len': ["warn", { "code": 120 }], + 'max-len': ["off", { "code": 120 }], }, settings: { "import/resolver": { @@ -62,7 +64,7 @@ module.exports = { rules: { '@typescript-eslint/no-var-requires': 'off', } - }, + }, { // properly handle generated DTO and autofix it files: [ 'src/interface/**/*.ts' diff --git a/audit-resolve.json b/audit-resolve.json index 37a938ea..a3771ed6 100644 --- a/audit-resolve.json +++ b/audit-resolve.json @@ -71,6 +71,122 @@ "decision": "ignore", "madeAt": 1636084373888, "expiresAt": 1638676347746 + }, + "1006904|hapi-swagger>swagger-ui-dist": { + "decision": "fix", + "madeAt": 1645585036388 + }, + "1004946|@mojaloop/event-sdk>@grpc/proto-loader>yargs>string-width>strip-ansi>ansi-regex": { + "decision": "ignore", + "madeAt": 1645585509117, + "expiresAt": 1648177505123 + }, + "1004946|@mojaloop/event-sdk>@grpc/proto-loader>yargs>cliui>string-width>strip-ansi>ansi-regex": { + "decision": "ignore", + "madeAt": 1645585509117, + "expiresAt": 1648177505123 + }, + "1006852|fido2-lib>node-jose>node-forge": { + "decision": "fix", + "madeAt": 1645585193032 + }, + "1006854|fido2-lib>node-jose>node-forge": { + "decision": "fix", + "madeAt": 1645585193032 + }, + "1006898|fido2-lib>node-jose>node-forge": { + "decision": "fix", + "madeAt": 1645585193032 + }, + "1006865|@mojaloop/central-services-shared>axios>follow-redirects": { + "decision": "ignore", + "madeAt": 1645585510312, + "expiresAt": 1648177505123 + }, + "1006865|axios>follow-redirects": { + "decision": "fix", + "madeAt": 1645585411869 + }, + "1007026|@mojaloop/central-services-shared>axios>follow-redirects": { + "decision": "ignore", + "madeAt": 1645585510312, + "expiresAt": 1648177505123 + }, + "1007026|axios>follow-redirects": { + "decision": "fix", + "madeAt": 1645585411869 + }, + "1006899|openapi-typescript>node-fetch": { + "decision": "fix", + "madeAt": 1645585431603 + }, + "1006899|@mojaloop/central-services-shared>widdershins>node-fetch": { + "decision": "fix", + "madeAt": 1645585431603 + }, + "1006899|@mojaloop/event-sdk>grpc>@mapbox/node-pre-gyp>node-fetch": { + "decision": "fix", + "madeAt": 1645585431603 + }, + "1004854|@mojaloop/central-services-shared>widdershins>openapi-sampler>json-pointer": { + "decision": "ignore", + "madeAt": 1645585478508, + "expiresAt": 1648177016953 + }, + "1004869|@mojaloop/central-services-shared>widdershins>swagger2openapi>better-ajv-errors>jsonpointer": { + "decision": "ignore", + "madeAt": 1645585479620, + "expiresAt": 1648177016953 + }, + "1004869|@mojaloop/central-services-shared>widdershins>swagger2openapi>oas-validator>better-ajv-errors>jsonpointer": { + "decision": "ignore", + "madeAt": 1645585479620, + "expiresAt": 1648177016953 + }, + "1004876|hapi-swagger>swagger-parser>z-schema>validator": { + "decision": "ignore", + "madeAt": 1645585480639, + "expiresAt": 1648177016953 + }, + "1004946|@mojaloop/central-services-shared>widdershins>yargs>string-width>strip-ansi>ansi-regex": { + "decision": "ignore", + "madeAt": 1645585481655, + "expiresAt": 1648177016953 + }, + "1004946|@mojaloop/central-services-shared>widdershins>yargs>cliui>string-width>strip-ansi>ansi-regex": { + "decision": "ignore", + "madeAt": 1645585481655, + "expiresAt": 1648177016953 + }, + "1005383|@mojaloop/central-services-shared>shins>sanitize-html": { + "decision": "ignore", + "madeAt": 1645585482701, + "expiresAt": 1648177016953 + }, + "1005384|@mojaloop/central-services-shared>shins>sanitize-html": { + "decision": "ignore", + "madeAt": 1645585482701, + "expiresAt": 1648177016953 + }, + "1005534|@mojaloop/central-services-shared>widdershins>yargs>yargs-parser": { + "decision": "ignore", + "madeAt": 1645585483849, + "expiresAt": 1648177016953 + }, + "1006846|@mojaloop/central-services-shared>shins>sanitize-html>postcss": { + "decision": "ignore", + "madeAt": 1645585485233, + "expiresAt": 1648177016953 + }, + "1006886|@mojaloop/central-services-shared>shins>markdown-it": { + "decision": "ignore", + "madeAt": 1645585486383, + "expiresAt": 1648177016953 + }, + "1007017|@mojaloop/central-services-shared>widdershins>swagger2openapi>oas-validator>ajv": { + "decision": "ignore", + "madeAt": 1645585487352, + "expiresAt": 1648177016953 } }, "rules": {}, diff --git a/docker/ml-testing-toolkit/spec_files/api_definitions/thirdparty_pisp/api_spec.yaml b/docker/ml-testing-toolkit/spec_files/api_definitions/thirdparty_pisp/api_spec.yaml index 8ace83dc..f064bbc0 100644 --- a/docker/ml-testing-toolkit/spec_files/api_definitions/thirdparty_pisp/api_spec.yaml +++ b/docker/ml-testing-toolkit/spec_files/api_definitions/thirdparty_pisp/api_spec.yaml @@ -11,7 +11,7 @@ info: servers: - url: / paths: - '/consents/{ID}': + /consents/{ID}: parameters: - $ref: '#/components/parameters/ID' - $ref: '#/components/parameters/Content-Type' @@ -182,7 +182,7 @@ paths: $ref: '#/components/responses/501' '503': $ref: '#/components/responses/503' - '/consents/{ID}/error': + /consents/{ID}/error: parameters: - $ref: '#/components/parameters/ID' - $ref: '#/components/parameters/Content-Type' @@ -230,7 +230,7 @@ paths: $ref: '#/components/responses/501' '503': $ref: '#/components/responses/503' - '/participants/{Type}/{ID}': + /participants/{Type}/{ID}: parameters: - $ref: '#/components/parameters/Type' - $ref: '#/components/parameters/ID' @@ -399,7 +399,7 @@ paths: $ref: '#/components/responses/501' '503': $ref: '#/components/responses/503' - '/thirdpartyRequests/verifications/{ID}': + /thirdpartyRequests/verifications/{ID}: parameters: - $ref: '#/components/parameters/ID' - $ref: '#/components/parameters/Content-Type' @@ -417,18 +417,17 @@ paths: - sampled operationId: PutThirdpartyRequestsVerificationsById summary: PutThirdpartyRequestsVerificationsById - description: > + description: >- The HTTP request `PUT /thirdpartyRequests/verifications/{ID}` is used by - the Auth-Service to inform - - the DFSP of a successful result in validating the verification of a - Thirdparty Transaction Request. - + the Auth-Service to inform the DFSP of a successful result in validating + the verification of a Thirdparty Transaction Request. - If the validation fails, The Auth-Service MUST use `PUT - /thirdpartyRequests/verifications/{ID}/error` + If the validation fails, the auth-service will send back `PUT + /thirdpartyRequests/verifications/{ID}` with `authenticationResponse: + 'REJECTED'`. - instead. + In unplanned error cases the Auth-Service MUST use `PUT + /thirdpartyRequests/verifications/{ID}/error`. parameters: - $ref: '#/components/parameters/Content-Length' requestBody: @@ -460,7 +459,7 @@ paths: $ref: '#/components/responses/501' '503': $ref: '#/components/responses/503' - '/thirdpartyRequests/verifications/{ID}/error': + /thirdpartyRequests/verifications/{ID}/error: parameters: - $ref: '#/components/parameters/ID' - $ref: '#/components/parameters/Content-Type' @@ -650,7 +649,7 @@ components: required: true schema: type: string - description: 'The type of the party identifier. For example, `MSISDN`, `PERSONAL_ID`.' + description: The type of the party identifier. For example, `MSISDN`, `PERSONAL_ID`. responses: '200': description: OK @@ -767,7 +766,7 @@ components: ErrorCode: title: ErrorCode type: string - pattern: '^[1-9]\d{3}$' + pattern: ^[1-9]\d{3}$ description: >- The API data type ErrorCode is a JSON String of four characters, consisting of digits only. Negative numbers are not allowed. A leading @@ -856,7 +855,7 @@ components: be Bank Account Number or anything that may expose a User's private bank account information. - pattern: '^([0-9A-Za-z_~\-\.]+[0-9A-Za-z_~\-])$' + pattern: ^([0-9A-Za-z_~\-\.]+[0-9A-Za-z_~\-])$ minLength: 1 maxLength: 1023 ConsentScopeType: @@ -946,7 +945,7 @@ components: additionalProperties: false type: type: string - description: 'response type, we need only the type of public-key' + description: response type, we need only the type of public-key enum: - public-key required: @@ -1029,7 +1028,7 @@ components: type: string enum: - VERIFIED - description: 'The Credential is valid, and ready to be used by the PISP.' + description: The Credential is valid, and ready to be used by the PISP. payload: $ref: '#/components/schemas/FIDOPublicKeyCredentialAttestation' required: @@ -1105,7 +1104,7 @@ components: of the format is yyyy-MM-ddTHH:mm:ss.SSS[-HH:MM]. Examples are "2016-05-24T08:38:08.699-04:00", "2016-05-24T08:38:08.699Z" (where Z indicates Zulu time zone, same as UTC). - example: '2016-05-24T08:38:08.699-04:00' + example: 2016-05-24T08:38:08.699-04:00 ConsentsIDPatchResponseRevoked: title: ConsentsIDPatchResponseRevoked description: | @@ -1335,6 +1334,16 @@ components: $ref: '#/components/schemas/ExtensionList' required: - fspId + AuthenticationResponse: + title: AuthenticationResponse + type: string + enum: + - VERIFIED + - REJECTED + description: |- + Below are the allowed values for the enumeration AuthenticationResponse. + - VERIFIED - The challenge was correctly signed. + - REJECTED - The challenge was not correctly signed. ThirdpartyRequestsVerificationsIDPutResponse: title: ThirdpartyRequestsVerificationsIDPutResponse type: object @@ -1343,9 +1352,6 @@ components: request. properties: authenticationResponse: - type: string - enum: - - VERIFIED - description: The verification passed + $ref: '#/components/schemas/AuthenticationResponse' required: - authenticationResponse diff --git a/package-lock.json b/package-lock.json index 907ec2d5..b45f9d92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1994,9 +1994,9 @@ } }, "@mojaloop/api-snippets": { - "version": "12.6.6", - "resolved": "https://registry.npmjs.org/@mojaloop/api-snippets/-/api-snippets-12.6.6.tgz", - "integrity": "sha512-b8O6P5Z4iCFS1czNAzFOQMapxPS1RaAaqoF7KzxPX2p4IBD3etIlQAXfvo31/0mVObnJIJXCioVUdocDZ/Eqmw==", + "version": "12.6.8", + "resolved": "https://registry.npmjs.org/@mojaloop/api-snippets/-/api-snippets-12.6.8.tgz", + "integrity": "sha512-N12P0y2oDGtvDAKWKD/JSDCXM5Ftng20/NlW5c+313HY3wm2XTRDJNcXhVIo+DcmI7jVt5/4gGLOp3cpGauiBg==", "requires": { "commander": "^2.19.0", "jest-ts-auto-mock": "^2.0.0", @@ -2014,17 +2014,17 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, "hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", "requires": { "lru-cache": "^6.0.0" } }, "is-core-module": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", - "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", "requires": { "has": "^1.0.3" } @@ -2768,6 +2768,12 @@ "resolved": "https://registry.npmjs.org/@types/asn1js/-/asn1js-2.0.2.tgz", "integrity": "sha512-t4YHCgtD+ERvH0FyxvNlYwJ2ezhqw7t+Ygh4urQ7dJER8i185JPv6oIM3ey5YQmGN6Zp9EMbpohkjZi9t3UxwA==" }, + "@types/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-8GAYQ1jDRUQkSpHzJUqXwAkYFOxuWAOGLhIR4aPd/Y/yL12Q/9m7LsKpHKlfKdNE/362Hc9wPI1Yh6opDfxVJg==", + "dev": true + }, "@types/babel__core": { "version": "7.1.9", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.9.tgz", @@ -2811,6 +2817,15 @@ "integrity": "sha512-PH7bfkt1nu4pnlxz+Ws+wwJJF1HE12W3ia+Iace2JT7q56DLH3hbyjOJyNHJYRxk3PkKaC36fHfHKyeG1rMgCA==", "dev": true }, + "@types/btoa": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/btoa/-/btoa-1.2.3.tgz", + "integrity": "sha512-ANNCZICS/ofxhzUl8V1DniBJs+sFQ+Yg5am1ZwVEf/sxoKY/J2+h5Fuw3xUErlZ7eJLdgzukBjZwnsV6+/2Rmg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/bytebuffer": { "version": "5.0.42", "resolved": "https://registry.npmjs.org/@types/bytebuffer/-/bytebuffer-5.0.42.tgz", @@ -4580,12 +4595,12 @@ "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==" }, "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "requires": { "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "ieee754": "^1.2.1" } }, "buffer-equal-constant-time": { @@ -7735,9 +7750,9 @@ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, "follow-redirects": { - "version": "1.14.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz", - "integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==" + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" }, "for-in": { "version": "1.0.2", @@ -8290,9 +8305,9 @@ } }, "hapi-swagger": { - "version": "14.2.1", - "resolved": "https://registry.npmjs.org/hapi-swagger/-/hapi-swagger-14.2.1.tgz", - "integrity": "sha512-K1oN/88Jh4/Q6ZMKxrbQT6bYrFouA3PaE8Kh5F3loNyPm8dezAeGvx8vreeNWCzFLwqHNFjFbZcgBgKnYF1Dwg==", + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/hapi-swagger/-/hapi-swagger-14.2.5.tgz", + "integrity": "sha512-rIxwCT9i+R9E9Z5m9BT15rwYI58IOKTKu7NEx9+pHO5aVeJK703qW3PWk72D7x9MSAnhmlJoEyUiFAU+6zQJ9A==", "requires": { "@hapi/boom": "^9.1.0", "@hapi/hoek": "^9.0.2", @@ -8300,7 +8315,7 @@ "http-status": "^1.0.1", "json-schema-ref-parser": "^6.1.0", "swagger-parser": "4.0.2", - "swagger-ui-dist": "^3.47.1" + "swagger-ui-dist": "^4.5.0" }, "dependencies": { "commander": { @@ -14426,9 +14441,9 @@ }, "dependencies": { "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" } } }, @@ -14895,9 +14910,33 @@ "dev": true }, "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } }, "node-fetch-h2": { "version": "2.3.0", @@ -14908,9 +14947,9 @@ } }, "node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz", + "integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==" }, "node-gyp": { "version": "7.1.2", @@ -15034,19 +15073,19 @@ "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=" }, "node-jose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/node-jose/-/node-jose-2.0.0.tgz", - "integrity": "sha512-j8zoFze1gijl8+DK/dSXXqX7+o2lMYv1XS+ptnXgGV/eloQaqq1YjNtieepbKs9jBS4WTnMOqyKSaQuunJzx0A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/node-jose/-/node-jose-2.1.0.tgz", + "integrity": "sha512-Zmm8vFPJabphGBc5Wz1/LUMPS+1cynqw16RIhgVNQMEI2yEQrvl7Gx2EwN9GhP8tkm8f7SH53K2nIx8TeNTIdg==", "requires": { "base64url": "^3.0.1", - "buffer": "^5.5.0", + "buffer": "^6.0.3", "es6-promise": "^4.2.8", - "lodash": "^4.17.15", - "long": "^4.0.0", - "node-forge": "^0.10.0", - "pako": "^1.0.11", + "lodash": "^4.17.21", + "long": "^5.2.0", + "node-forge": "^1.2.1", + "pako": "^2.0.4", "process": "^0.11.10", - "uuid": "^3.3.3" + "uuid": "^8.3.2" }, "dependencies": { "es6-promise": { @@ -15055,14 +15094,9 @@ "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" }, "long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz", + "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==" } } }, @@ -16521,9 +16555,9 @@ } }, "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.0.4.tgz", + "integrity": "sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg==" }, "parent-module": { "version": "1.0.1", @@ -19276,9 +19310,9 @@ "integrity": "sha1-cAcEaNbSl3ylI3suUZyn0Gouo/0=" }, "swagger-ui-dist": { - "version": "3.51.1", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.51.1.tgz", - "integrity": "sha512-df2mEeVgnJp/FcXY3DRh3CsTfvHVTaO6g3FJP/kfwhxfOD1+YTXqBZrOIIsYTPtcRIFBkCAto0NFCxAV4XFRbw==" + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.5.2.tgz", + "integrity": "sha512-wV4w54eW9z+VKbYJBJfULfqO05otCbM9jwgRIkwRl9CrfTVKelDzyhhEvdUQkGUzro+Ir8TOZPiZgKIdIdolWQ==" }, "swagger2openapi": { "version": "6.2.3", @@ -19817,9 +19851,9 @@ } }, "ttypescript": { - "version": "1.5.12", - "resolved": "https://registry.npmjs.org/ttypescript/-/ttypescript-1.5.12.tgz", - "integrity": "sha512-1ojRyJvpnmgN9kIHmUnQPlEV1gq+VVsxVYjk/NfvMlHSmYxjK5hEvOOU2MQASrbekTUiUM7pR/nXeCc8bzvMOQ==", + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/ttypescript/-/ttypescript-1.5.13.tgz", + "integrity": "sha512-KT/RBfGGlVJFqEI8cVvI3nMsmYcFvPSZh8bU0qX+pAwbi7/ABmYkzn7l/K8skw0xmYjVCoyaV6WLsBQxdadybQ==", "requires": { "resolve": ">=1.9.0" } diff --git a/package.json b/package.json index d9001c61..c334455a 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,9 @@ "description": "Shared Authorization Service for the Third Party Transaction Verification", "main": "dist/index.js", "typings": "dist/index.d.ts", + "engines": { + "node": ">=12.0.0" + }, "files": [ "dist" ], @@ -79,6 +82,8 @@ "@commitlint/cli": "^12.1.4", "@commitlint/config-conventional": "^12.1.4", "@redocly/openapi-cli": "^1.0.0-beta.63", + "@types/atob": "^2.1.2", + "@types/btoa": "^1.2.3", "@types/convict": "^6.1.1", "@types/crypto-js": "^4.0.2", "@types/hapi": "^18.0.5", @@ -121,9 +126,9 @@ "sqlite3": "^5.0.2", "standard-version": "^9.3.0", "swagger-cli": "^4.0.4", + "ts-jest": "^27.0.5", "ts-node": "^10.0.0", - "tsconfig-paths": "^3.10.1", - "ts-jest": "^27.0.5" + "tsconfig-paths": "^3.10.1" }, "dependencies": { "@hapi/boom": "^9.1.3", @@ -131,7 +136,7 @@ "@hapi/hapi": "^20.1.5", "@hapi/inert": "^6.0.3", "@hapi/vision": "^6.1.0", - "@mojaloop/api-snippets": "^12.6.6", + "@mojaloop/api-snippets": "12.6.8", "@mojaloop/central-services-error-handling": "11.3.0", "@mojaloop/central-services-health": "^13.0.0", "@mojaloop/central-services-logger": "10.6.1", @@ -153,7 +158,7 @@ "dot-prop": "^6.0.1", "fido2-lib": "2.6.7", "hapi-openapi": "^3.0.0", - "hapi-swagger": "^14.2.1", + "hapi-swagger": "^14.2.5", "javascript-state-machine": "^3.1.0", "json-canonicalize": "^1.0.4", "knex": "^0.95.11", diff --git a/src/domain/stateMachine/registerConsent.model.ts b/src/domain/stateMachine/registerConsent.model.ts index 1be66b23..96f0bc43 100644 --- a/src/domain/stateMachine/registerConsent.model.ts +++ b/src/domain/stateMachine/registerConsent.model.ts @@ -51,7 +51,7 @@ import { AttestationResult, ExpectedAttestationResult, Fido2Lib } from 'fido2-li import str2ab from 'string-to-arraybuffer' import { createAndStoreConsent } from '~/domain/consents' -const atob = require('atob') +import atob from 'atob' export class RegisterConsentModel extends PersistentModel { protected config: RegisterConsentModelConfig @@ -121,12 +121,12 @@ export class RegisterConsentModel async onVerifyConsent (): Promise { const { consentsPostRequestAUTH, participantDFSPId } = this.data + const payload = (consentsPostRequestAUTH.credential.payload as tpAPI.Schemas.FIDOPublicKeyCredentialAttestation) try { - const challenge = deriveChallenge(consentsPostRequestAUTH) - const decodedJsonString = decodeBase64String(consentsPostRequestAUTH.credential.payload.response.clientDataJSON) + const decodedJsonString = decodeBase64String(payload.response.clientDataJSON) const parsedClientData = JSON.parse(decodedJsonString) - + const attestationExpectations: ExpectedAttestationResult = { challenge, // not sure what origin should be here @@ -137,32 +137,31 @@ export class RegisterConsentModel } const f2l = new Fido2Lib() - const clientAttestationResponse: AttestationResult = { + const clientAttestationResponse: AttestationResult = { // This is a little tricky here // we first need to convert from ascii (base64 representation) --> Binary // then to an ArrayBuffer to reconstruct the object // TODO: fix me! - id: str2ab(atob(consentsPostRequestAUTH.credential.payload.id)), + id: str2ab(atob(payload.id)), // id: str2ab(consentsPostRequestAUTH.credential.payload.id), response: { - clientDataJSON: consentsPostRequestAUTH.credential.payload.response.clientDataJSON, - attestationObject: consentsPostRequestAUTH.credential.payload.response.attestationObject + clientDataJSON: payload.response.clientDataJSON, + attestationObject: payload.response.attestationObject } } // if consentsPostRequestAUTH.credential.payload.id is in config.get('SKIP_VALIDATION_FOR_CREDENTIAL_IDS') // then skip this step, and make up a random public key if (this.config.demoSkipValidationForCredentialIds.length > 0 && - this.config.demoSkipValidationForCredentialIds.indexOf(consentsPostRequestAUTH.credential.payload.id) > -1) { - - this.logger.warn(`found demo credentialId: ${consentsPostRequestAUTH.credential.payload.id}. Skipping FIDO attestation validation step.`) - - this.data.credentialCounter = 0; + this.config.demoSkipValidationForCredentialIds.indexOf(payload.id) > -1) { + this.logger.warn(`found demo credentialId: ${payload.id}. Skipping FIDO attestation validation step.`) + + this.data.credentialCounter = 0 this.data.credentialPublicKey = 'demo public key.' return } - const attestationResult = await f2l.attestationResult( + const attestationResult = await f2l.attestationResult( clientAttestationResponse, attestationExpectations ) @@ -172,16 +171,17 @@ export class RegisterConsentModel } catch (error) { this.logger.push({ error }).error('start -> consentVerified') - let mojaloopError - // if error is planned and is a MojaloopApiErrorCode we send back that code - if ((error as Errors.MojaloopApiErrorCode).code) { - mojaloopError = reformatError(error, this.logger) - } else { - // if error is not planned send back a generalized error - mojaloopError = reformatError( - Errors.MojaloopApiErrorCodes.TP_ACCOUNT_LINKING_ERROR, - this.logger - ) + const mojaloopError = reformatError( + Errors.MojaloopApiErrorCodes.SERVER_ERROR, + this.logger + ) as unknown as fspiopAPI.Schemas.ErrorInformationObject + + mojaloopError.errorInformation.extensionList = { + extension: [ + { key: 'authServiceParticipant', value: this.config.authServiceParticipantFSPId }, + { key: 'transitionFailure', value: 'RegisterConsentModel: start -> consentVerified' }, + { key: 'rawError', value: JSON.stringify(error) } + ] } await this.thirdpartyRequests.putConsentsError( @@ -211,16 +211,17 @@ export class RegisterConsentModel } catch (error) { this.logger.push({ error }).error('consentVerified -> consentStoredAndVerified') - let mojaloopError - // if error is planned and is a MojaloopApiErrorCode we send back that code - if ((error as Errors.MojaloopApiErrorCode).code) { - mojaloopError = reformatError(error, this.logger) - } else { - // if error is not planned send back a generalized error - mojaloopError = reformatError( - Errors.MojaloopApiErrorCodes.TP_ACCOUNT_LINKING_ERROR, - this.logger - ) + const mojaloopError = reformatError( + Errors.MojaloopApiErrorCodes.SERVER_ERROR, + this.logger + ) as unknown as fspiopAPI.Schemas.ErrorInformationObject + + mojaloopError.errorInformation.extensionList = { + extension: [ + { key: 'authServiceParticipant', value: this.config.authServiceParticipantFSPId }, + { key: 'transitionFailure', value: 'RegisterConsentModel: consentVerified -> consentStoredAndVerified' }, + { key: 'rawError', value: JSON.stringify(error) } + ] } await this.thirdpartyRequests.putConsentsError( @@ -295,22 +296,28 @@ export class RegisterConsentModel }) .wait(this.config.requestProcessingTimeoutSeconds * 1000) } catch (error) { + // unplanned error - inform participant this.logger.push({ error }).error('consentStoredAndVerified -> registeredAsAuthoritativeSource') - // we send back an account linking error despite the actual error const mojaloopError = reformatError( - Errors.MojaloopApiErrorCodes.TP_ACCOUNT_LINKING_ERROR, + Errors.MojaloopApiErrorCodes.SERVER_ERROR, this.logger - ) + ) as unknown as fspiopAPI.Schemas.ErrorInformationObject + + mojaloopError.errorInformation.extensionList = { + extension: [ + { key: 'authServiceParticipant', value: this.config.authServiceParticipantFSPId }, + { key: 'transitionFailure', value: 'RegisterConsentModel: consentStoredAndVerified -> registeredAsAuthoritativeSource' }, + { key: 'rawError', value: JSON.stringify(error) } + ] + } - // if the flow fails to run for any reason notify the DFSP that the account - // linking process has failed await this.thirdpartyRequests.putConsentsError( consentsPostRequestAUTH.consentId, mojaloopError as unknown as fspiopAPI.Schemas.ErrorInformationObject, participantDFSPId ) - // throw the actual error + // throw error to stop state machine throw error } } @@ -321,7 +328,8 @@ export class RegisterConsentModel try { // copy credential and update status const verifiedCredential: tpAPI.Schemas.VerifiedCredential = { - ...consentsPostRequestAUTH.credential, + credentialType: 'FIDO', + payload: (consentsPostRequestAUTH.credential.payload as tpAPI.Schemas.FIDOPublicKeyCredentialAttestation), status: 'VERIFIED' } @@ -336,16 +344,23 @@ export class RegisterConsentModel participantDFSPId ) } catch (error) { + // unplanned error - inform participant this.logger.push({ error }).error('registeredAsAuthoritativeSource -> callbackSent') - // we send back an account linking error despite the actual error const mojaloopError = reformatError( - Errors.MojaloopApiErrorCodes.TP_ACCOUNT_LINKING_ERROR, + Errors.MojaloopApiErrorCodes.SERVER_ERROR, this.logger - ) - + ) as unknown as fspiopAPI.Schemas.ErrorInformationObject + + mojaloopError.errorInformation.extensionList = { + extension: [ + { key: 'authServiceParticipant', value: this.config.authServiceParticipantFSPId }, + { key: 'transitionFailure', value: 'RegisterConsentModel: registeredAsAuthoritativeSource -> callbackSent' }, + { key: 'rawError', value: JSON.stringify(error) } + ] + } await this.thirdpartyRequests.putConsentsError( consentsPostRequestAUTH.consentId, - mojaloopError as unknown as fspiopAPI.Schemas.ErrorInformationObject, + mojaloopError, participantDFSPId ) @@ -381,7 +396,7 @@ export class RegisterConsentModel this.logger.info('State machine in errored state') return } - } catch (err) { + } catch (err: any) { this.logger.info(`Error running RegisterConsentModel : ${inspect(err)}`) // as this function is recursive, we don't want to error the state machine multiple times diff --git a/src/domain/stateMachine/verifyTransaction.interface.ts b/src/domain/stateMachine/verifyTransaction.interface.ts index 3b3cac72..66567251 100644 --- a/src/domain/stateMachine/verifyTransaction.interface.ts +++ b/src/domain/stateMachine/verifyTransaction.interface.ts @@ -37,8 +37,8 @@ import { PubSub } from '~/shared/pub-sub' import { Consent } from '../consents' export interface VerifyTransactionStateMachine extends ControlledStateMachine { - retreiveConsent: Method - onRetreiveConsent: Method + retrieveConsent: Method + onRetrieveConsent: Method verifyTransaction: Method onVerifyTransaction: Method sendCallbackToDFSP: Method @@ -60,6 +60,7 @@ export interface VerifyTransactionData extends StateData { // initial POST /thirdpartyRequests/verifications request verificationRequest: tpAPI.Schemas.ThirdpartyRequestsVerificationsPostRequest + verificationResponse?: tpAPI.Schemas.ThirdpartyRequestsVerificationsIDPutResponse // // metadata related to the verification request (for now just the origin) // verificationRequestMetadata: { origin: string } diff --git a/src/domain/stateMachine/verifyTransaction.model.ts b/src/domain/stateMachine/verifyTransaction.model.ts index 9958c939..5d298edc 100644 --- a/src/domain/stateMachine/verifyTransaction.model.ts +++ b/src/domain/stateMachine/verifyTransaction.model.ts @@ -33,8 +33,7 @@ import inspect from '~/shared/inspect' import { reformatError } from '~/shared/api-error' import { VerifyTransactionModelConfig, VerifyTransactionData, VerifyTransactionStateMachine } from './verifyTransaction.interface' import { - thirdparty as tpAPI, - v1_1 as fspiopAPI, + v1_1 as fspiopAPI } from '@mojaloop/api-snippets' import * as ConsentDomain from '../consents' import { IncorrectConsentStatusError } from '../errors' @@ -42,27 +41,27 @@ import { InvalidDataError } from '~/shared/invalidDataError' import { AssertionResult, ExpectedAssertionResult, Fido2Lib } from 'fido2-lib' import FidoUtils from '~/shared/fido-utils' -const btoa = require('btoa') +import btoa from 'btoa' export class VerifyTransactionModel extends PersistentModel { protected config: VerifyTransactionModelConfig - constructor( + constructor ( data: VerifyTransactionData, config: VerifyTransactionModelConfig ) { const spec: StateMachineConfig = { init: 'start', transitions: [ - { name: 'retreiveConsent', from: 'start', to: 'consentRetreived' }, - { name: 'verifyTransaction', from: 'consentRetreived', to: 'transactionVerified' }, - { name: 'sendCallbackToDFSP', from: 'transactionVerified', to: 'callbackSent' }, + { name: 'retrieveConsent', from: 'start', to: 'consentRetrieved' }, + { name: 'verifyTransaction', from: 'consentRetrieved', to: 'transactionVerified' }, + { name: 'sendCallbackToDFSP', from: 'transactionVerified', to: 'callbackSent' } ], methods: { // specific transitions handlers methods - onRetreiveConsent: () => this.onRetreiveConsent(), + onRetrieveConsent: () => this.onRetrieveConsent(), onVerifyTransaction: () => this.onVerifyTransaction(), - onSendCallbackToDFSP: () => this.onSendCallbackToDFSP(), + onSendCallbackToDFSP: () => this.onSendCallbackToDFSP() } } super(data, config, spec) @@ -70,44 +69,45 @@ export class VerifyTransactionModel } // getters - get subscriber(): PubSub { + get subscriber (): PubSub { return this.config.subscriber } - get mojaloopRequests(): MojaloopRequests { + get mojaloopRequests (): MojaloopRequests { return this.config.mojaloopRequests } - get thirdpartyRequests(): ThirdpartyRequests { + get thirdpartyRequests (): ThirdpartyRequests { return this.config.thirdpartyRequests } // utility function to check if an error after a transition which // pub/subs for a response that can return a mojaloop error - async checkModelDataForErrorInformation(): Promise { + async checkModelDataForErrorInformation (): Promise { if (this.data.errorInformation) { await this.fsm.error(this.data.errorInformation) } } - async onRetreiveConsent(): Promise { + async onRetrieveConsent (): Promise { try { const consentId = this.data.verificationRequest.consentId const consent = await ConsentDomain.getConsent(consentId) this.data.consent = consent } catch (error) { - this.logger.push({ error }).error('start -> consentRetreived') - - let mojaloopError - // if error is planned and is a MojaloopApiErrorCode we send back that code - if ((error as Errors.MojaloopApiErrorCode).code) { - mojaloopError = reformatError(error, this.logger) - } else { - // if error is not planned send back a generalized error - mojaloopError = reformatError( - Errors.MojaloopApiErrorCodes.TP_AUTH_SERVICE_ERROR, - this.logger - ) + this.logger.push({ error }).error('start -> consentRetrieved') + + const mojaloopError = reformatError( + Errors.MojaloopApiErrorCodes.SERVER_ERROR, + this.logger + ) as unknown as fspiopAPI.Schemas.ErrorInformationObject + + mojaloopError.errorInformation.extensionList = { + extension: [ + { key: 'authServiceParticipant', value: this.config.authServiceParticipantFSPId }, + { key: 'transitionFailure', value: 'VerifyTransactionModel: start -> consentRetrieved' }, + { key: 'rawError', value: JSON.stringify(error) } + ] } await this.thirdpartyRequests.putThirdpartyRequestsVerificationsError( @@ -121,7 +121,9 @@ export class VerifyTransactionModel } } - async onVerifyTransaction(): Promise { + async onVerifyTransaction (): Promise { + const { verificationRequest, participantDFSPId } = this.data + try { InvalidDataError.throwIfInvalidProperty(this.data, 'consent') const f2l = new Fido2Lib() @@ -145,11 +147,11 @@ export class VerifyTransactionModel // on the client base64 encodes the challenge BEFORE signing it. challenge: btoa(request.challenge), origin, - factor: "either", + factor: 'either', publicKey: consent.credentialPayload, prevCounter: consent.credentialCounter, userHandle: request.signedPayload.response.userHandle || null - }; + } const assertionResult: AssertionResult = { // fido2lib requires an ArrayBuffer, not just any old Buffer! id: FidoUtils.stringToArrayBuffer(request.signedPayload.id), @@ -163,27 +165,43 @@ export class VerifyTransactionModel // TODO: for greater security, store the updated counter result // out of scope for now. - await f2l.assertionResult(assertionResult, assertionExpectations); // will throw on error + // the fido2lib throws an error if the challenge is not signed correctly + // the library doesn't have error types so we can't distinguish what + // exactly caused the error. this needs further investigation. + // for now we will assume that if it errors the challenge was signed + // incorrectly and inform the participant that the authentication was + // rejected + try { + await f2l.assertionResult(assertionResult, assertionExpectations) + this.data.verificationResponse = { + authenticationResponse: 'VERIFIED' + } + } catch (error) { + this.data.verificationResponse = { + authenticationResponse: 'REJECTED' + } + } } catch (error) { - this.logger.push({ error }).error('consentRetreived -> transactionVerified') - - let mojaloopError - // if error is planned and is a MojaloopApiErrorCode we send back that code - if ((error as Errors.MojaloopApiErrorCode).code) { - mojaloopError = reformatError(error, this.logger) - } else { - // if error is not planned send back a generalized error - mojaloopError = reformatError( - Errors.MojaloopApiErrorCodes.TP_FSP_TRANSACTION_AUTHORIZATION_NOT_VALID, - this.logger - ) + this.logger.push({ error }).error('consentRetrieved -> transactionVerified') + + const mojaloopError = reformatError( + Errors.MojaloopApiErrorCodes.SERVER_ERROR, + this.logger + ) as unknown as fspiopAPI.Schemas.ErrorInformationObject + + mojaloopError.errorInformation.extensionList = { + extension: [ + { key: 'authServiceParticipant', value: this.config.authServiceParticipantFSPId }, + { key: 'transitionFailure', value: 'VerifyTransactionModel: consentRetrieved -> transactionVerified' }, + { key: 'rawError', value: JSON.stringify(error) } + ] } await this.thirdpartyRequests.putThirdpartyRequestsVerificationsError( mojaloopError as unknown as fspiopAPI.Schemas.ErrorInformationObject, - this.data.verificationRequest.verificationRequestId, - this.data.participantDFSPId + verificationRequest.verificationRequestId, + participantDFSPId ) // throw error to stop state machine @@ -191,24 +209,27 @@ export class VerifyTransactionModel } } - async onSendCallbackToDFSP(): Promise { - const { verificationRequest, participantDFSPId } = this.data + async onSendCallbackToDFSP (): Promise { + const { verificationRequest, participantDFSPId, verificationResponse } = this.data try { - const response: tpAPI.Schemas.ThirdpartyRequestsVerificationsIDPutResponse = { - authenticationResponse: 'VERIFIED' - } - await this.thirdpartyRequests.putThirdpartyRequestsVerifications( - response, verificationRequest.verificationRequestId, participantDFSPId + verificationResponse!, verificationRequest.verificationRequestId, participantDFSPId ) } catch (error) { this.logger.push({ error }).error('onSendCallbackToDFSP -> callbackSent') - // // we send back an account linking error despite the actual error const mojaloopError = reformatError( - Errors.MojaloopApiErrorCodes.TP_FSP_TRANSACTION_REQUEST_NOT_VALID, + Errors.MojaloopApiErrorCodes.SERVER_ERROR, this.logger - ) + ) as unknown as fspiopAPI.Schemas.ErrorInformationObject + + mojaloopError.errorInformation.extensionList = { + extension: [ + { key: 'authServiceParticipant', value: this.config.authServiceParticipantFSPId }, + { key: 'transitionFailure', value: 'VerifyTransactionModel: onSendCallbackToDFSP -> callbackSent' }, + { key: 'rawError', value: JSON.stringify(error) } + ] + } await this.thirdpartyRequests.putThirdpartyRequestsVerificationsError( mojaloopError as unknown as fspiopAPI.Schemas.ErrorInformationObject, @@ -221,15 +242,15 @@ export class VerifyTransactionModel } } - async run(): Promise { + async run (): Promise { const data = this.data try { // run transitions based on incoming state switch (data.currentState) { case 'start': - await this.fsm.retreiveConsent() + await this.fsm.retrieveConsent() return this.run() - case 'consentRetreived': + case 'consentRetrieved': await this.fsm.verifyTransaction() return this.run() case 'transactionVerified': @@ -262,7 +283,7 @@ export class VerifyTransactionModel } } -export async function create( +export async function create ( data: VerifyTransactionData, config: VerifyTransactionModelConfig ): Promise { diff --git a/src/interface/api.yaml b/src/interface/api.yaml index 8b1ef99e..654a7c12 100644 --- a/src/interface/api.yaml +++ b/src/interface/api.yaml @@ -558,7 +558,7 @@ components: properties: key: $ref: '#/components/schemas/ExtensionKey' - value: + signedPayload: $ref: '#/components/schemas/ExtensionValue' required: - key diff --git a/src/interface/openapi.d.ts b/src/interface/openapi.d.ts index ed9183ac..0c22e736 100644 --- a/src/interface/openapi.d.ts +++ b/src/interface/openapi.d.ts @@ -169,7 +169,7 @@ export interface components { /** Data model for the complex type Extension. */ Extension: { key: components['schemas']['ExtensionKey']; - value: components['schemas']['ExtensionValue']; + signedPayload: components['schemas']['ExtensionValue']; }; /** Data model for the complex type ExtensionList. An optional list of extensions, specific to deployment. */ ExtensionList: { diff --git a/test/data/data.ts b/test/data/data.ts index e13703ef..48d9684b 100644 --- a/test/data/data.ts +++ b/test/data/data.ts @@ -213,7 +213,6 @@ export const completeConsentActive: ConsentModel = { originalCredential: JSON.stringify(credential) } - /* * Mock Scope Resources */ @@ -283,7 +282,6 @@ export const completeConsentActiveCredential: ConsentModel = { originalCredential: JSON.stringify(credential) } - /* Verifications request variations */ export const validVerificationRequest: tpAPI.Schemas.ThirdpartyRequestsVerificationsPostRequest = { verificationRequestId: '835a8444-8cdc-41ef-bf18-ca4916c2e005', diff --git a/test/integration/domain/consents.test.ts b/test/integration/domain/consents.test.ts index 794d7649..a69c4bd1 100644 --- a/test/integration/domain/consents.test.ts +++ b/test/integration/domain/consents.test.ts @@ -75,7 +75,7 @@ describe('server/domain/consents', (): void => { it('Should resolve successfully', async (): Promise => { // Arrange consentIdsToCleanup.push(consentId) - + // Act const response = await createAndStoreConsent( consentId, @@ -89,7 +89,7 @@ describe('server/domain/consents', (): void => { deriveChallenge(payload), 0 ) - + // Assert // We're mainly testing that nothing threw! expect(response).toBe(undefined) diff --git a/test/integration/model/consent.test.ts b/test/integration/model/consent.test.ts index e616184a..886e7875 100644 --- a/test/integration/model/consent.test.ts +++ b/test/integration/model/consent.test.ts @@ -31,8 +31,7 @@ import { Knex, knex } from 'knex' import Config from '~/shared/config' import { ConsentDB, ConsentModel } from '~/model/consent' import { ScopeModel } from '~/model/scope' -import { NotFoundError } from '~/model/errors' -import { RevokedConsentModificationError } from '../../../src/model/errors'; +import { NotFoundError, RevokedConsentModificationError } from '~/model/errors' /* * Mock Consent Resources @@ -45,7 +44,7 @@ const completeConsent: ConsentModel = { credentialChallenge: 'xyhdushsoa82w92mzs', credentialPayload: 'dwuduwd&e2idjoj0w', credentialCounter: 4, - originalCredential: JSON.stringify({ status:'PENDING', payload:{}, credentialType:'test'}), + originalCredential: JSON.stringify({ status: 'PENDING', payload: {}, credentialType: 'test' }) } const expectedCompleteConsent = { @@ -58,7 +57,7 @@ const expectedCompleteConsent = { credentialPayload: 'dwuduwd&e2idjoj0w', credentialCounter: 4, originalCredential: expect.any(String), - revokedAt: null, + revokedAt: null } /* * Consent Resource Model Integration Tests @@ -97,7 +96,7 @@ describe('src/model/consent', (): void => { expect(consents.length).toEqual(1) expect(consents[0]).toEqual(expectedCompleteConsent) - expect(JSON.parse(consents[0].originalCredential)).toEqual({ status:'PENDING', payload:{}, credentialType:'test'}) + expect(JSON.parse(consents[0].originalCredential)).toEqual({ status: 'PENDING', payload: {}, credentialType: 'test' }) } ) @@ -131,7 +130,7 @@ describe('src/model/consent', (): void => { credentialChallenge: 'xyhdushsoa82w92mzs', credentialPayload: 'dwuduwd&e2idjoj0w', credentialCounter: 4, - originalCredential: expect.any(String), + originalCredential: expect.any(String) } await expect(consentDB.insert(consentWithoutId)).rejects.toThrow() @@ -147,7 +146,7 @@ describe('src/model/consent', (): void => { expect(consent.createdAt).toEqual(expect.any(Date)) expect(consent).toEqual(expect.objectContaining(expectedCompleteConsent)) - expect(JSON.parse(consent.originalCredential)).toEqual({ status:'PENDING', payload:{}, credentialType:'test'}) + expect(JSON.parse(consent.originalCredential)).toEqual({ status: 'PENDING', payload: {}, credentialType: 'test' }) }) it('throws an error on retrieving non-existent consent', async (): Promise => { @@ -261,10 +260,10 @@ describe('src/model/consent', (): void => { credentialCounter: 4, originalCredential: expect.any(String), createdAt: expect.any(Date), - revokedAt: expect.any(Date), + revokedAt: expect.any(Date) }) - expect(JSON.parse(consentRevoked.originalCredential)).toEqual({ status:'PENDING', payload:{}, credentialType:'test'}) + expect(JSON.parse(consentRevoked.originalCredential)).toEqual({ status: 'PENDING', payload: {}, credentialType: 'test' }) }) it('throws an error on revoking non-existent consent', async (): Promise => { @@ -300,7 +299,7 @@ describe('src/model/consent', (): void => { revokedAt: expect.any(Date) }) - expect(JSON.parse(consents[0].originalCredential)).toEqual({ status:'PENDING', payload:{}, credentialType:'test'}) + expect(JSON.parse(consents[0].originalCredential)).toEqual({ status: 'PENDING', payload: {}, credentialType: 'test' }) await expect(consentDB.revoke(completeConsent.id)) .rejects.toThrowError(RevokedConsentModificationError) diff --git a/test/integration/model/scope.test.ts b/test/integration/model/scope.test.ts index 1140506a..6869aa41 100644 --- a/test/integration/model/scope.test.ts +++ b/test/integration/model/scope.test.ts @@ -45,7 +45,7 @@ const consents: ConsentModel[] = [ credentialChallenge: 'xyhdushsoa82w92mzs', credentialPayload: 'dwuduwd&e2idjoj0w', credentialCounter: 4, - originalCredential: JSON.stringify({ status: 'PENDING', payload: {}, credentialType: 'test'}), + originalCredential: JSON.stringify({ status: 'PENDING', payload: {}, credentialType: 'test' }) }, { id: '948', @@ -55,7 +55,7 @@ const consents: ConsentModel[] = [ credentialChallenge: 'xyhdushsoa82w92mzs', credentialPayload: 'dwuduwd&e2idjoj0w', credentialCounter: 4, - originalCredential: JSON.stringify({ status: 'PENDING', payload: {}, credentialType: 'test'}), + originalCredential: JSON.stringify({ status: 'PENDING', payload: {}, credentialType: 'test' }) } ] diff --git a/test/integration/server/handlers/consents.test.ts b/test/integration/server/handlers/consents.test.ts index b87fe382..3e9daf3b 100644 --- a/test/integration/server/handlers/consents.test.ts +++ b/test/integration/server/handlers/consents.test.ts @@ -36,7 +36,6 @@ import { thirdparty as tpAPI } from '@mojaloop/api-snippets' describe('POST /consents - AUTH case', (): void => { // hmm this isn't working!!! - it.skip('Should return 202 (Accepted) status code', async (): Promise => { // Endpoint const scenariosURI = 'http://localhost:4004/consents' @@ -153,19 +152,19 @@ describe('POST /consents - AUTH case', (): void => { // https://stackoverflow.com/questions/9267899/arraybuffer-to-base64-encoded-string // in nodejs we use only Buffer.from([...]).toString('base64') const payload: tpAPI.Schemas.ConsentsPostRequestAUTH = { - consentId: "46876aac-5db8-4353-bb3c-a6a905843ce7", - scopes: [{ "accountId": "dfspa.username.5678", "actions": ["accounts.transfer"] }], + consentId: '46876aac-5db8-4353-bb3c-a6a905843ce7', + scopes: [{ accountId: 'dfspa.username.5678', actions: ['accounts.transfer'] }], credential: { credentialType: 'FIDO', status: 'PENDING', payload: { - "id": "MNSJ9C29Mhz9mMyG3ZFSeP3yZCvEeMHPXc7WcHQTi1WNKAdRhQG5GhFC0KWCDuljDGjVsffx1rqzxRWpzj8J1A", - "rawId": "MNSJ9C29Mhz9mMyG3ZFSeP3yZCvEeMHPXc7WcHQTi1WNKAdRhQG5GhFC0KWCDuljDGjVsffx1rqzxRWpzj8J1A==", - "response": { - "attestationObject": "o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEcwRQIhAPKGWYeG6YuLXLb+hPrS4VsmyoWWOv6swonxn49V4a04AiAJig9PLZCUCcAO9B3aWKDMIXdlUnkyyofhDZK+KIlgOGN4NWOBWQLBMIICvTCCAaWgAwIBAgIECwXNUzANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowbjELMAkGA1UEBhMCU0UxEjAQBgNVBAoMCVl1YmljbyBBQjEiMCAGA1UECwwZQXV0aGVudGljYXRvciBBdHRlc3RhdGlvbjEnMCUGA1UEAwweWXViaWNvIFUyRiBFRSBTZXJpYWwgMTg0OTI5NjE5MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIRpvsbWJJcsKwRhffCrjqLSIEBR5sR7/9VXgfZdRvSsXaiUt7lns44WZIFuz6ii/j9f8fadcBUJyrkhY5ZH8WqNsMGowIgYJKwYBBAGCxAoCBBUxLjMuNi4xLjQuMS40MTQ4Mi4xLjEwEwYLKwYBBAGC5RwCAQEEBAMCBDAwIQYLKwYBBAGC5RwBAQQEEgQQFJogIY72QTOWuIH41bfx9TAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQA+/qPfPSrgclePfgTQ3VpLaNsBr+hjLhi04LhzQxiRGWwYS+vB1TOiPXeLsQQIwbmqQU51doVbCTaXGLNIr1zvbLAwhnLWH7i9m4ahCqaCzowtTvCQ7VBUGP5T1M4eYnoo83IDCVjQj/pZG8QYgOGOigztGoWAf5CWcUF6C0UyFbONwUcqJEl2QLToa/7E8VRjm4W46IAUljYkODVZASv8h3wLROx9p5TSBlSymtwdulxQe/DKbfNSvM3edA0up+EIJKLOOU+QTR2ZQV46fEW1/ih6m8vcaY6L3NW0eYpc7TXeijUJAgoUtya/vzmnRAecuY9bncoJt8PrvL2ir2kDaGF1dGhEYXRhWMRJlg3liA6MaHQ0Fw9kdmBbj+SuuaKGMseZXPO6gx2XY0EAAAAAAAAAAAAAAAAAAAAAAAAAAABAMNSJ9C29Mhz9mMyG3ZFSeP3yZCvEeMHPXc7WcHQTi1WNKAdRhQG5GhFC0KWCDuljDGjVsffx1rqzxRWpzj8J1KUBAgMmIAEhWCCnZrZ1rTXKmPhbMg/x8K22H5MkS0m5dIrsaQrI7o8COiJYIInSu3mbM7G3F/BrXI8wcOQLvPq8t5d8pdm8JoS++9Hk", - "clientDataJSON": "eyJjaGFsbGVuZ2UiOiJORGxqT1RjeFltWXdZVFExWm1Ka1pUa3pOek13Tm1SalpUazNZVFl6TURjM01HSmtZamMzWW1FellqWm1OemcwWkRJMU5HWTJPR0UwTm1Sa05EQmhNZyIsImNsaWVudEV4dGVuc2lvbnMiOnt9LCJoYXNoQWxnb3JpdGhtIjoiU0hBLTI1NiIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsInR5cGUiOiJ3ZWJhdXRobi5jcmVhdGUifQ==" + id: 'MNSJ9C29Mhz9mMyG3ZFSeP3yZCvEeMHPXc7WcHQTi1WNKAdRhQG5GhFC0KWCDuljDGjVsffx1rqzxRWpzj8J1A', + rawId: 'MNSJ9C29Mhz9mMyG3ZFSeP3yZCvEeMHPXc7WcHQTi1WNKAdRhQG5GhFC0KWCDuljDGjVsffx1rqzxRWpzj8J1A==', + response: { + attestationObject: 'o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEcwRQIhAPKGWYeG6YuLXLb+hPrS4VsmyoWWOv6swonxn49V4a04AiAJig9PLZCUCcAO9B3aWKDMIXdlUnkyyofhDZK+KIlgOGN4NWOBWQLBMIICvTCCAaWgAwIBAgIECwXNUzANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowbjELMAkGA1UEBhMCU0UxEjAQBgNVBAoMCVl1YmljbyBBQjEiMCAGA1UECwwZQXV0aGVudGljYXRvciBBdHRlc3RhdGlvbjEnMCUGA1UEAwweWXViaWNvIFUyRiBFRSBTZXJpYWwgMTg0OTI5NjE5MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIRpvsbWJJcsKwRhffCrjqLSIEBR5sR7/9VXgfZdRvSsXaiUt7lns44WZIFuz6ii/j9f8fadcBUJyrkhY5ZH8WqNsMGowIgYJKwYBBAGCxAoCBBUxLjMuNi4xLjQuMS40MTQ4Mi4xLjEwEwYLKwYBBAGC5RwCAQEEBAMCBDAwIQYLKwYBBAGC5RwBAQQEEgQQFJogIY72QTOWuIH41bfx9TAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQA+/qPfPSrgclePfgTQ3VpLaNsBr+hjLhi04LhzQxiRGWwYS+vB1TOiPXeLsQQIwbmqQU51doVbCTaXGLNIr1zvbLAwhnLWH7i9m4ahCqaCzowtTvCQ7VBUGP5T1M4eYnoo83IDCVjQj/pZG8QYgOGOigztGoWAf5CWcUF6C0UyFbONwUcqJEl2QLToa/7E8VRjm4W46IAUljYkODVZASv8h3wLROx9p5TSBlSymtwdulxQe/DKbfNSvM3edA0up+EIJKLOOU+QTR2ZQV46fEW1/ih6m8vcaY6L3NW0eYpc7TXeijUJAgoUtya/vzmnRAecuY9bncoJt8PrvL2ir2kDaGF1dGhEYXRhWMRJlg3liA6MaHQ0Fw9kdmBbj+SuuaKGMseZXPO6gx2XY0EAAAAAAAAAAAAAAAAAAAAAAAAAAABAMNSJ9C29Mhz9mMyG3ZFSeP3yZCvEeMHPXc7WcHQTi1WNKAdRhQG5GhFC0KWCDuljDGjVsffx1rqzxRWpzj8J1KUBAgMmIAEhWCCnZrZ1rTXKmPhbMg/x8K22H5MkS0m5dIrsaQrI7o8COiJYIInSu3mbM7G3F/BrXI8wcOQLvPq8t5d8pdm8JoS++9Hk', + clientDataJSON: 'eyJjaGFsbGVuZ2UiOiJORGxqT1RjeFltWXdZVFExWm1Ka1pUa3pOek13Tm1SalpUazNZVFl6TURjM01HSmtZamMzWW1FellqWm1OemcwWkRJMU5HWTJPR0UwTm1Sa05EQmhNZyIsImNsaWVudEV4dGVuc2lvbnMiOnt9LCJoYXNoQWxnb3JpdGhtIjoiU0hBLTI1NiIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsInR5cGUiOiJ3ZWJhdXRobi5jcmVhdGUifQ==' }, - "type": "public-key" + type: 'public-key' } } } diff --git a/test/integration/server/handlers/thirdpartyRequestsVerifications.test.ts b/test/integration/server/handlers/thirdpartyRequestsVerifications.test.ts index c3cf68ea..e049b4e6 100644 --- a/test/integration/server/handlers/thirdpartyRequestsVerifications.test.ts +++ b/test/integration/server/handlers/thirdpartyRequestsVerifications.test.ts @@ -38,8 +38,8 @@ describe('POST /thirdpartyRequests/verifications', () => { // Act const response = await axios.post(url, validVerificationRequest, { headers }) - + // Assert expect(response.status).toEqual(202) }) -}) \ No newline at end of file +}) diff --git a/test/integration/server/workflows/registerConsent.test.ts b/test/integration/server/workflows/registerConsent.test.ts index ffd376ff..89b1c091 100644 --- a/test/integration/server/workflows/registerConsent.test.ts +++ b/test/integration/server/workflows/registerConsent.test.ts @@ -46,7 +46,7 @@ const consentsPostRequestAUTH: tpAPI.Schemas.ConsentsPostRequestAUTH = { actions: [ 'accounts.transfer' ] - }, + } ], // todo: make note in api that we are converting all array buffers to base64 encoded strings credential: { @@ -126,13 +126,13 @@ const consentsPostRequestAUTH: tpAPI.Schemas.ConsentsPostRequestAUTH = { } } -// note to Kevin - I want to get the PR for main implementation and unit tests in +// note to Kevin - I want to get the PR for main implementation and unit tests in // now, so I'll cover the fixes to the integration tests and new integration tests // for tx in the next one! describe('Inbound POST /consents', (): void => { - const ttkRequestsHistoryUri = `http://localhost:5050/api/history/requests` + const ttkRequestsHistoryUri = 'http://localhost:5050/api/history/requests' - beforeEach(async(): Promise => { + beforeEach(async (): Promise => { // clear the request history in TTK between tests. await axios.delete(ttkRequestsHistoryUri, {}) }) @@ -170,7 +170,7 @@ describe('Inbound POST /consents', (): void => { // check that the auth-service has sent a POST /participants/{Type}/{ID} to the ALS (TTK) const requestsHistory: MLTestingToolkitRequest[] = (await axios.get(ttkRequestsHistoryUri, axiosConfig)).data - var postParticipantsTypeIdToALS = requestsHistory.filter(req => { + const postParticipantsTypeIdToALS = requestsHistory.filter(req => { return req.method === 'post' && req.path === '/participants/CONSENT/76059a0a-684f-4002-a880-b01159afe119' }) expect(postParticipantsTypeIdToALS.length).toEqual(1) @@ -188,7 +188,7 @@ describe('Inbound POST /consents', (): void => { const axiosConfig = { headers: { 'Content-Type': 'application/vnd.interoperability.participants+json;version=1.1', - 'Accept': 'application/vnd.interoperability.participants+json;version=1.1', + Accept: 'application/vnd.interoperability.participants+json;version=1.1', 'FSPIOP-Source': 'als', Date: 'Thu, 24 Jan 2019 10:23:12 GMT', 'FSPIOP-Destination': 'centralAuth' @@ -198,7 +198,7 @@ describe('Inbound POST /consents', (): void => { const putParticipantsTypeIdFromALS = { fspId: 'centralAuth' } - const putScenarioUri = `http://localhost:4004/participants/CONSENT/76059a0a-684f-4002-a880-b01159afe119` + const putScenarioUri = 'http://localhost:4004/participants/CONSENT/76059a0a-684f-4002-a880-b01159afe119' const responseToPutParticipantsTypeId = await axios.put(putScenarioUri, putParticipantsTypeIdFromALS, axiosConfig) expect(responseToPutParticipantsTypeId.status).toEqual(200) @@ -209,7 +209,7 @@ describe('Inbound POST /consents', (): void => { // check that the auth-service has sent a PUT /consents/{ID} to the DFSP (TTK) const requestsHistory: MLTestingToolkitRequest[] = (await axios.get(ttkRequestsHistoryUri, axiosConfig)).data - var putConsentsIdToDFSP = requestsHistory.filter(req => { + const putConsentsIdToDFSP = requestsHistory.filter(req => { return req.method === 'put' && req.path === '/consents/76059a0a-684f-4002-a880-b01159afe119' }) expect(putConsentsIdToDFSP.length).toEqual(1) diff --git a/test/integration/server/workflows/verifyTransaction.test.ts b/test/integration/server/workflows/verifyTransaction.test.ts index 0b1c38d7..fd211c1c 100644 --- a/test/integration/server/workflows/verifyTransaction.test.ts +++ b/test/integration/server/workflows/verifyTransaction.test.ts @@ -11,8 +11,8 @@ const validConsentId = 'be433b9e-9473-4b7d-bdd5-ac5b42463afb' const validConsentsPostRequestAuth: tpAPI.Schemas.ConsentsPostRequestAUTH = { consentId: validConsentId, scopes: [ - {actions: ['accounts.getBalance', 'accounts.transfer'], accountId: '412ddd18-07a0-490d-814d-27d0e9af9982'}, - {actions: ['accounts.getBalance'], accountId: '10e88508-e542-4630-be7f-bc0076029ea7'} + { actions: ['accounts.getBalance', 'accounts.transfer'], accountId: '412ddd18-07a0-490d-814d-27d0e9af9982' }, + { actions: ['accounts.getBalance'], accountId: '10e88508-e542-4630-be7f-bc0076029ea7' } ], credential: { credentialType: 'FIDO', @@ -86,18 +86,17 @@ export const invalidVerificationRequest: tpAPI.Schemas.ThirdpartyRequestsVerific const axiosConfig = { headers: { 'Content-Type': 'application/vnd.interoperability.participants+json;version=1.1', - 'Accept': 'application/vnd.interoperability.participants+json;version=1.1', + Accept: 'application/vnd.interoperability.participants+json;version=1.1', 'FSPIOP-Source': 'als', Date: 'Thu, 24 Jan 2019 10:23:12 GMT', 'FSPIOP-Destination': 'centralAuth' } } -const ttkRequestsHistoryUri = `http://localhost:5050/api/history/requests` - +const ttkRequestsHistoryUri = 'http://localhost:5050/api/history/requests' describe('POST /thirdpartyRequests/verifications', () => { jest.setTimeout(15000) - + beforeEach(async () => { // clear the request history in TTK between tests. await axios.delete(ttkRequestsHistoryUri, {}) @@ -124,13 +123,13 @@ describe('POST /thirdpartyRequests/verifications', () => { // Arrange const consentsURI = 'http://localhost:4004/consents' - + // register the consent - const response = await axios.post(consentsURI, validConsentsPostRequestAuth, {headers}) - + const response = await axios.post(consentsURI, validConsentsPostRequestAuth, { headers }) + // auth-service should return Accepted code expect(response.status).toEqual(202) - + // wait a bit for the auth-service to process the request // takes a bit since attestation takes a bit of time await new Promise(resolve => setTimeout(resolve, 2000)) @@ -138,17 +137,17 @@ describe('POST /thirdpartyRequests/verifications', () => { fspId: 'centralAuth' } const mockAlsParticipantsURI = `http://localhost:4004/participants/CONSENT/${validConsentId}` - + // mock the ALS callback to the auth-service const responseToPutParticipantsTypeId = await axios.put(mockAlsParticipantsURI, putParticipantsTypeIdFromALS, axiosConfig) expect(responseToPutParticipantsTypeId.status).toEqual(200) - // // we have a registered credential - now let's try verifying a transaction + // // we have a registered credential - now let's try verifying a transaction const verifyURI = 'http://localhost:4004/thirdpartyRequests/verifications' // Act const result = await axios.post(verifyURI, validVerificationRequest, { headers }) - + // Assert expect(result.status).toBe(202) @@ -199,7 +198,7 @@ describe('POST /thirdpartyRequests/verifications', () => { const responseToPutParticipantsTypeId = await axios.put(mockAlsParticipantsURI, putParticipantsTypeIdFromALS, axiosConfig) expect(responseToPutParticipantsTypeId.status).toEqual(200) - // // we have a registered credential - now let's try verifying a transaction + // // we have a registered credential - now let's try verifying a transaction const verifyURI = 'http://localhost:4004/thirdpartyRequests/verifications' // Act @@ -214,8 +213,7 @@ describe('POST /thirdpartyRequests/verifications', () => { // check that the auth-service has sent a PUT /thirdpartyRequests/verifications/{ID} to the DFSP (TTK) const requestsHistory: MLTestingToolkitRequest[] = (await axios.get(ttkRequestsHistoryUri, axiosConfig)).data const asyncCallback = requestsHistory.filter(req => { - return req.method === 'put' - && req.path === `/thirdpartyRequests/verifications/${invalidVerificationRequest.verificationRequestId}/error` + return req.method === 'put' && req.path === `/thirdpartyRequests/verifications/${validVerificationRequest.verificationRequestId}` }) expect(asyncCallback.length).toEqual(1) @@ -223,11 +221,8 @@ describe('POST /thirdpartyRequests/verifications', () => { // check the payload const asyncCallbackPayload = asyncCallback[0].body as tpAPI.Schemas.ThirdpartyRequestsVerificationsIDPutResponse expect(asyncCallbackPayload).toStrictEqual({ - "errorInformation": { - "errorCode": "7105", - "errorDescription": "Authorization received from PISP failed DFSP validation", - } + authenticationResponse: 'REJECTED' }) }) }) -}) \ No newline at end of file +}) diff --git a/test/step-definitions/template.step.ts b/test/step-definitions/template.step.ts index 3f478f07..c915f523 100644 --- a/test/step-definitions/template.step.ts +++ b/test/step-definitions/template.step.ts @@ -45,5 +45,4 @@ defineFeature(feature, (test): void => { expect(healthResponse.uptime).toBeGreaterThan(1.0) }) }) - }) diff --git a/test/unit/domain/consents.test.ts b/test/unit/domain/consents.test.ts index ff93157c..cd5eee0e 100644 --- a/test/unit/domain/consents.test.ts +++ b/test/unit/domain/consents.test.ts @@ -38,7 +38,6 @@ import { thirdparty as tpAPI } from '@mojaloop/api-snippets' import { ConsentModel } from '~/model/consent' import { NotFoundError } from '~/model/errors' - // Declare Mocks // jest.mock('~/model/db') const mockInsertConsentWithScopes = jest.spyOn(DB, 'insertConsentWithScopes') @@ -163,8 +162,6 @@ describe('server/domain/consents', (): void => { revokedAt: new Date('2021-01-01') } - - beforeEach(async (): Promise => { jest.clearAllMocks() }) @@ -178,7 +175,7 @@ describe('server/domain/consents', (): void => { it('stores the consent', async (): Promise => { // Arrange mockInsertConsentWithScopes.mockResolvedValueOnce() - + // Act await ConsentDomain.createAndStoreConsent( consentId, @@ -189,7 +186,7 @@ describe('server/domain/consents', (): void => { 'some-credential-challenge', 4 ) - + // Assert expect(mockInsertConsentWithScopes).toHaveBeenCalledWith(consentActiveFIDO, scopesWithoutIds) }) @@ -197,7 +194,7 @@ describe('server/domain/consents', (): void => { it('throw if there is an error in the db', async (): Promise => { // Arrange mockInsertConsentWithScopes.mockRejectedValueOnce(new Error('test error')) - + // Act try { await ConsentDomain.createAndStoreConsent( @@ -213,7 +210,7 @@ describe('server/domain/consents', (): void => { } catch (err: any) { // Assert expect(mockInsertConsentWithScopes).toHaveBeenCalledWith(consentActiveFIDO, scopesWithoutIds) - expect(err.message).toMatch(`Auth service database error for`) + expect(err.message).toMatch('Auth service database error for') } }) }) @@ -229,12 +226,12 @@ describe('server/domain/consents', (): void => { scopes: [ { accountId: 'as2342', - actions: [ 'accounts.getBalance', 'accounts.transfer'], + actions: ['accounts.getBalance', 'accounts.transfer'] }, { accountId: 'as22', - actions: [ 'accounts.getBalance'], - }, + actions: ['accounts.getBalance'] + } ], credential: credential, status: 'VERIFIED', @@ -243,10 +240,10 @@ describe('server/domain/consents', (): void => { createdAt: expect.objectContaining({}), revokedAt: undefined } - + // Act const result = await ConsentDomain.getConsent(consentId) - + // Assert expect(result).toStrictEqual(expected) // test dates separately for better guarantees @@ -264,24 +261,24 @@ describe('server/domain/consents', (): void => { scopes: [ { accountId: 'as2342', - actions: ['accounts.getBalance', 'accounts.transfer'], + actions: ['accounts.getBalance', 'accounts.transfer'] }, { accountId: 'as22', - actions: ['accounts.getBalance'], - }, + actions: ['accounts.getBalance'] + } ], credential: credential, status: 'REVOKED', credentialCounter: 4, credentialPayload: 'some-public-key', createdAt: expect.objectContaining({}), - revokedAt: expect.objectContaining({}), + revokedAt: expect.objectContaining({}) } - + // Act const result = await ConsentDomain.getConsent(consentId) - + // Assert expect(result).toStrictEqual(expected) expect(result.createdAt.toISOString()).toBe('2020-01-01T00:00:00.000Z') diff --git a/test/unit/domain/stateMachine/registerConsent.model.test.ts b/test/unit/domain/stateMachine/registerConsent.model.test.ts index 72506974..2f89d68f 100644 --- a/test/unit/domain/stateMachine/registerConsent.model.test.ts +++ b/test/unit/domain/stateMachine/registerConsent.model.test.ts @@ -50,7 +50,7 @@ import config from '~/shared/config' import axios from 'axios' import shouldNotBeExecuted from '../../shouldNotBeExecuted' import { createAndStoreConsent } from '~/domain/consents' -import * as challenge from '~/domain/challenge' +import * as challenge from '~/domain/challenge' import * as consents from '~/domain/consents' import { MojaloopApiErrorCode } from '~/shared/api-error' import { mockDeferredJobWithCallbackMessage } from '../../mockDeferredJob' @@ -79,7 +79,7 @@ const consentsPostRequestAUTH: tpAPI.Schemas.ConsentsPostRequestAUTH = { actions: [ 'accounts.transfer' ] - }, + } ], // todo: make note in api that we are converting all array buffers to base64 encoded strings credential: { @@ -160,7 +160,7 @@ const consentsPostRequestAUTH: tpAPI.Schemas.ConsentsPostRequestAUTH = { } const participantsTypeIDPutResponse: fspiopAPI.Schemas.ParticipantsTypeIDPutResponse = { - 'fspId': config.PARTICIPANT_ID + fspId: config.PARTICIPANT_ID } const genericErrorResponse: fspiopAPI.Schemas.ErrorInformationObject = { @@ -260,7 +260,7 @@ describe('RegisterConsentModel', () => { 'registerAuthoritativeSourceWithALS', 'sendConsentCallbackToDFSP', 'storeConsent', - 'verifyConsent', + 'verifyConsent' ]) } @@ -306,7 +306,6 @@ describe('RegisterConsentModel', () => { }) }) - describe('verifyConsent', () => { const registerConsentData: RegisterConsentData = { currentState: 'start', @@ -340,17 +339,17 @@ describe('RegisterConsentModel', () => { actions: [ 'accounts.transfer' ] - }, + } ], credential: { credentialType: 'FIDO', status: 'PENDING', payload: { - "id": "Pdm3TpHQxvmYMdNKcY3R6i8PHRcZqtvSSFssJp0OQawchMOYBnpPQ7E97CPy_caTxPNYVJL-E7cT_HBm4sIuNA", - "rawId": "Pdm3TpHQxvmYMdNKcY3R6i8PHRcZqtvSSFssJp0OQawchMOYBnpPQ7E97CPy_caTxPNYVJL-E7cT_HBm4sIuNA==", - "response": { - "attestationObject": "o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEgwRgIhAOrrUscl/GRHvjoAtJE6KbgQxUSj3vwp3Ztmh9nQEvuSAiEAgDjZEL8PKFvgJnX7JCk260lOeeht5Ffe/kmA9At17a9jeDVjgVkCwTCCAr0wggGloAMCAQICBAsFzVMwDQYJKoZIhvcNAQELBQAwLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEwIBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMG4xCzAJBgNVBAYTAlNFMRIwEAYDVQQKDAlZdWJpY28gQUIxIjAgBgNVBAsMGUF1dGhlbnRpY2F0b3IgQXR0ZXN0YXRpb24xJzAlBgNVBAMMHll1YmljbyBVMkYgRUUgU2VyaWFsIDE4NDkyOTYxOTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCEab7G1iSXLCsEYX3wq46i0iBAUebEe//VV4H2XUb0rF2olLe5Z7OOFmSBbs+oov4/X/H2nXAVCcq5IWOWR/FqjbDBqMCIGCSsGAQQBgsQKAgQVMS4zLjYuMS40LjEuNDE0ODIuMS4xMBMGCysGAQQBguUcAgEBBAQDAgQwMCEGCysGAQQBguUcAQEEBBIEEBSaICGO9kEzlriB+NW38fUwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAPv6j3z0q4HJXj34E0N1aS2jbAa/oYy4YtOC4c0MYkRlsGEvrwdUzoj13i7EECMG5qkFOdXaFWwk2lxizSK9c72ywMIZy1h+4vZuGoQqmgs6MLU7wkO1QVBj+U9TOHmJ6KPNyAwlY0I/6WRvEGIDhjooM7RqFgH+QlnFBegtFMhWzjcFHKiRJdkC06Gv+xPFUY5uFuOiAFJY2JDg1WQEr/Id8C0TsfaeU0gZUsprcHbpcUHvwym3zUrzN3nQNLqfhCCSizjlPkE0dmUFeOnxFtf4oepvL3GmOi9zVtHmKXO013oo1CQIKFLcmv785p0QHnLmPW53KCbfD67y9oq9pA2hhdXRoRGF0YVjExGzvgq0bVGR3WR0Aiwh1nsPm0uy085R0v+ppaZJdA7dBAAAAAAAAAAAAAAAAAAAAAAAAAAAAQD3Zt06R0Mb5mDHTSnGN0eovDx0XGarb0khbLCadDkGsHITDmAZ6T0OxPewj8v3Gk8TzWFSS/hO3E/xwZuLCLjSlAQIDJiABIVggiSfmVgOyesk2SDOaPhShPbnahfrl3Vs0iQUW6QF4IHUiWCDi6beycQU49cvsW32MNlAqXxGJ7uaXY06NOKGq1HraxQ==", - "clientDataJSON": "eyJjaGFsbGVuZ2UiOiJBcEZqVmZSVFF3NV9OUjRZNXBvVHo4a3RkM2dhNGpJNUx5NjJfZzk3b0ZrIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9kZW1vLnl1Ymljby5jb20iLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0=" + id: 'Pdm3TpHQxvmYMdNKcY3R6i8PHRcZqtvSSFssJp0OQawchMOYBnpPQ7E97CPy_caTxPNYVJL-E7cT_HBm4sIuNA', + rawId: 'Pdm3TpHQxvmYMdNKcY3R6i8PHRcZqtvSSFssJp0OQawchMOYBnpPQ7E97CPy_caTxPNYVJL-E7cT_HBm4sIuNA==', + response: { + attestationObject: 'o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEgwRgIhAOrrUscl/GRHvjoAtJE6KbgQxUSj3vwp3Ztmh9nQEvuSAiEAgDjZEL8PKFvgJnX7JCk260lOeeht5Ffe/kmA9At17a9jeDVjgVkCwTCCAr0wggGloAMCAQICBAsFzVMwDQYJKoZIhvcNAQELBQAwLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEwIBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMG4xCzAJBgNVBAYTAlNFMRIwEAYDVQQKDAlZdWJpY28gQUIxIjAgBgNVBAsMGUF1dGhlbnRpY2F0b3IgQXR0ZXN0YXRpb24xJzAlBgNVBAMMHll1YmljbyBVMkYgRUUgU2VyaWFsIDE4NDkyOTYxOTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCEab7G1iSXLCsEYX3wq46i0iBAUebEe//VV4H2XUb0rF2olLe5Z7OOFmSBbs+oov4/X/H2nXAVCcq5IWOWR/FqjbDBqMCIGCSsGAQQBgsQKAgQVMS4zLjYuMS40LjEuNDE0ODIuMS4xMBMGCysGAQQBguUcAgEBBAQDAgQwMCEGCysGAQQBguUcAQEEBBIEEBSaICGO9kEzlriB+NW38fUwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAPv6j3z0q4HJXj34E0N1aS2jbAa/oYy4YtOC4c0MYkRlsGEvrwdUzoj13i7EECMG5qkFOdXaFWwk2lxizSK9c72ywMIZy1h+4vZuGoQqmgs6MLU7wkO1QVBj+U9TOHmJ6KPNyAwlY0I/6WRvEGIDhjooM7RqFgH+QlnFBegtFMhWzjcFHKiRJdkC06Gv+xPFUY5uFuOiAFJY2JDg1WQEr/Id8C0TsfaeU0gZUsprcHbpcUHvwym3zUrzN3nQNLqfhCCSizjlPkE0dmUFeOnxFtf4oepvL3GmOi9zVtHmKXO013oo1CQIKFLcmv785p0QHnLmPW53KCbfD67y9oq9pA2hhdXRoRGF0YVjExGzvgq0bVGR3WR0Aiwh1nsPm0uy085R0v+ppaZJdA7dBAAAAAAAAAAAAAAAAAAAAAAAAAAAAQD3Zt06R0Mb5mDHTSnGN0eovDx0XGarb0khbLCadDkGsHITDmAZ6T0OxPewj8v3Gk8TzWFSS/hO3E/xwZuLCLjSlAQIDJiABIVggiSfmVgOyesk2SDOaPhShPbnahfrl3Vs0iQUW6QF4IHUiWCDi6beycQU49cvsW32MNlAqXxGJ7uaXY06NOKGq1HraxQ==', + clientDataJSON: 'eyJjaGFsbGVuZ2UiOiJBcEZqVmZSVFF3NV9OUjRZNXBvVHo4a3RkM2dhNGpJNUx5NjJfZzk3b0ZrIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9kZW1vLnl1Ymljby5jb20iLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0=' }, type: 'public-key' } @@ -392,8 +391,24 @@ describe('RegisterConsentModel', () => { '76059a0a-684f-4002-a880-b01159afe119', { errorInformation: { - errorCode: '7200', - errorDescription: 'Generic Thirdparty account linking error' + errorCode: '2000', + errorDescription: 'Generic server error', + extensionList: { + extension: [ + { + key: 'authServiceParticipant', + value: 'centralAuth' + }, + { + key: 'transitionFailure', + value: 'RegisterConsentModel: start -> consentVerified' + }, + { + key: 'rawError', + value: '{}' + } + ] + } } }, 'dfspA' @@ -424,8 +439,24 @@ describe('RegisterConsentModel', () => { '76059a0a-684f-4002-a880-b01159afe119', { errorInformation: { - errorCode: '7200', - errorDescription: 'Generic Thirdparty account linking error' + errorCode: '2000', + errorDescription: 'Generic server error', + extensionList: { + extension: [ + { + key: 'authServiceParticipant', + value: 'centralAuth' + }, + { + key: 'transitionFailure', + value: 'RegisterConsentModel: start -> consentVerified' + }, + { + key: 'rawError', + value: JSON.stringify(error) + } + ] + } } }, 'dfspA' @@ -442,10 +473,10 @@ describe('RegisterConsentModel', () => { const registerConsentSkipVerificationData: RegisterConsentData = { currentState: 'start', participantDFSPId: 'dfspA', - consentsPostRequestAUTH: consentsPostRequestSkipCredentialId, + consentsPostRequestAUTH: consentsPostRequestSkipCredentialId } const model = await create(registerConsentSkipVerificationData, modelConfig) - + // Act await model.fsm.verifyConsent() @@ -510,8 +541,24 @@ describe('RegisterConsentModel', () => { '76059a0a-684f-4002-a880-b01159afe119', { errorInformation: { - errorCode: '7200', - errorDescription: 'Generic Thirdparty account linking error' + errorCode: '2000', + errorDescription: 'Generic server error', + extensionList: { + extension: [ + { + key: 'authServiceParticipant', + value: 'centralAuth' + }, + { + key: 'transitionFailure', + value: 'RegisterConsentModel: consentVerified -> consentStoredAndVerified' + }, + { + key: 'rawError', + value: '{}' + } + ] + } } }, 'dfspA' @@ -546,7 +593,6 @@ describe('RegisterConsentModel', () => { ) mockDeferredJobWithCallbackMessage(waitOnParticipantResponseFromALSChannel, participantsTypeIDPutResponse) - await model.fsm.registerAuthoritativeSourceWithALS() await model.checkModelDataForErrorInformation() @@ -556,7 +602,7 @@ describe('RegisterConsentModel', () => { // check we made a call to the als expect(axios.post).toBeCalledWith( `http://${config.SHARED.ALS_ENDPOINT}/participants/CONSENT/${consentsPostRequestAUTH.consentId}`, - { fspId: "centralAuth"}, + { fspId: 'centralAuth' }, expect.any(Object) ) }) @@ -568,7 +614,6 @@ describe('RegisterConsentModel', () => { ) mockDeferredJobWithCallbackMessage(waitOnParticipantResponseFromALSChannel, genericALSErrorResponse) - const model = await create(registerConsentData, modelConfig) await model.fsm.registerAuthoritativeSourceWithALS() @@ -579,9 +624,9 @@ describe('RegisterConsentModel', () => { // check it sends an error back to DFSP expect(model.thirdpartyRequests.putConsentsError).toBeCalledWith( - "76059a0a-684f-4002-a880-b01159afe119", + '76059a0a-684f-4002-a880-b01159afe119', genericErrorResponse, - "dfspA" + 'dfspA' ) }) }) @@ -641,8 +686,24 @@ describe('RegisterConsentModel', () => { '76059a0a-684f-4002-a880-b01159afe119', { errorInformation: { - errorCode: '7200', - errorDescription: 'Generic Thirdparty account linking error' + errorCode: '2000', + errorDescription: 'Generic server error', + extensionList: { + extension: [ + { + key: 'authServiceParticipant', + value: 'centralAuth' + }, + { + key: 'transitionFailure', + value: 'RegisterConsentModel: registeredAsAuthoritativeSource -> callbackSent' + }, + { + key: 'rawError', + value: '{}' + } + ] + } } }, 'dfspA' @@ -667,7 +728,7 @@ describe('RegisterConsentModel', () => { mockDeferredJobWithCallbackMessage(waitOnParticipantResponseFromALSChannel, participantsTypeIDPutResponse) const model = await create(registerConsentData, modelConfig) - + await model.run() // check that the fsm was able complete the workflow expect(model.data.currentState).toEqual('callbackSent') diff --git a/test/unit/domain/stateMachine/verifyTransaction.model.test.ts b/test/unit/domain/stateMachine/verifyTransaction.model.test.ts index 89ba3376..c7720fbb 100644 --- a/test/unit/domain/stateMachine/verifyTransaction.model.test.ts +++ b/test/unit/domain/stateMachine/verifyTransaction.model.test.ts @@ -25,7 +25,6 @@ optionally within square brackets . -------------- ******/ - import { KVS } from '~/shared/kvs' import { Message, @@ -48,9 +47,8 @@ import { import * as ConsentDomain from '~/domain/consents' import shouldNotBeExecuted from 'test/unit/shouldNotBeExecuted' -const atob = require('atob') -const btoa = require('btoa') - +import atob from 'atob' +import btoa from 'btoa' // mock KVS default exported class jest.mock('~/shared/kvs') @@ -69,11 +67,11 @@ const credential: tpAPI.Schemas.VerifiedCredential = { credentialType: 'FIDO', status: 'VERIFIED', payload: { - "id": atob("Pdm3TpHQxvmYMdNKcY3R6i8PHRcZqtvSSFssJp0OQawchMOYBnpPQ7E97CPy_caTxPNYVJL-E7cT_HBm4sIuNA"), - "rawId": atob("Pdm3TpHQxvmYMdNKcY3R6i8PHRcZqtvSSFssJp0OQawchMOYBnpPQ7E97CPy_caTxPNYVJL-E7cT_HBm4sIuNA=="), - "response": { - "attestationObject": "o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEgwRgIhAOrrUscl/GRHvjoAtJE6KbgQxUSj3vwp3Ztmh9nQEvuSAiEAgDjZEL8PKFvgJnX7JCk260lOeeht5Ffe/kmA9At17a9jeDVjgVkCwTCCAr0wggGloAMCAQICBAsFzVMwDQYJKoZIhvcNAQELBQAwLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEwIBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMG4xCzAJBgNVBAYTAlNFMRIwEAYDVQQKDAlZdWJpY28gQUIxIjAgBgNVBAsMGUF1dGhlbnRpY2F0b3IgQXR0ZXN0YXRpb24xJzAlBgNVBAMMHll1YmljbyBVMkYgRUUgU2VyaWFsIDE4NDkyOTYxOTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCEab7G1iSXLCsEYX3wq46i0iBAUebEe//VV4H2XUb0rF2olLe5Z7OOFmSBbs+oov4/X/H2nXAVCcq5IWOWR/FqjbDBqMCIGCSsGAQQBgsQKAgQVMS4zLjYuMS40LjEuNDE0ODIuMS4xMBMGCysGAQQBguUcAgEBBAQDAgQwMCEGCysGAQQBguUcAQEEBBIEEBSaICGO9kEzlriB+NW38fUwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAPv6j3z0q4HJXj34E0N1aS2jbAa/oYy4YtOC4c0MYkRlsGEvrwdUzoj13i7EECMG5qkFOdXaFWwk2lxizSK9c72ywMIZy1h+4vZuGoQqmgs6MLU7wkO1QVBj+U9TOHmJ6KPNyAwlY0I/6WRvEGIDhjooM7RqFgH+QlnFBegtFMhWzjcFHKiRJdkC06Gv+xPFUY5uFuOiAFJY2JDg1WQEr/Id8C0TsfaeU0gZUsprcHbpcUHvwym3zUrzN3nQNLqfhCCSizjlPkE0dmUFeOnxFtf4oepvL3GmOi9zVtHmKXO013oo1CQIKFLcmv785p0QHnLmPW53KCbfD67y9oq9pA2hhdXRoRGF0YVjExGzvgq0bVGR3WR0Aiwh1nsPm0uy085R0v+ppaZJdA7dBAAAAAAAAAAAAAAAAAAAAAAAAAAAAQD3Zt06R0Mb5mDHTSnGN0eovDx0XGarb0khbLCadDkGsHITDmAZ6T0OxPewj8v3Gk8TzWFSS/hO3E/xwZuLCLjSlAQIDJiABIVggiSfmVgOyesk2SDOaPhShPbnahfrl3Vs0iQUW6QF4IHUiWCDi6beycQU49cvsW32MNlAqXxGJ7uaXY06NOKGq1HraxQ==", - "clientDataJSON": "eyJjaGFsbGVuZ2UiOiJBcEZqVmZSVFF3NV9OUjRZNXBvVHo4a3RkM2dhNGpJNUx5NjJfZzk3b0ZrIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9kZW1vLnl1Ymljby5jb20iLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0=" + id: atob('Pdm3TpHQxvmYMdNKcY3R6i8PHRcZqtvSSFssJp0OQawchMOYBnpPQ7E97CPy_caTxPNYVJL-E7cT_HBm4sIuNA'), + rawId: atob('Pdm3TpHQxvmYMdNKcY3R6i8PHRcZqtvSSFssJp0OQawchMOYBnpPQ7E97CPy_caTxPNYVJL-E7cT_HBm4sIuNA=='), + response: { + attestationObject: 'o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEgwRgIhAOrrUscl/GRHvjoAtJE6KbgQxUSj3vwp3Ztmh9nQEvuSAiEAgDjZEL8PKFvgJnX7JCk260lOeeht5Ffe/kmA9At17a9jeDVjgVkCwTCCAr0wggGloAMCAQICBAsFzVMwDQYJKoZIhvcNAQELBQAwLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEwIBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMG4xCzAJBgNVBAYTAlNFMRIwEAYDVQQKDAlZdWJpY28gQUIxIjAgBgNVBAsMGUF1dGhlbnRpY2F0b3IgQXR0ZXN0YXRpb24xJzAlBgNVBAMMHll1YmljbyBVMkYgRUUgU2VyaWFsIDE4NDkyOTYxOTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCEab7G1iSXLCsEYX3wq46i0iBAUebEe//VV4H2XUb0rF2olLe5Z7OOFmSBbs+oov4/X/H2nXAVCcq5IWOWR/FqjbDBqMCIGCSsGAQQBgsQKAgQVMS4zLjYuMS40LjEuNDE0ODIuMS4xMBMGCysGAQQBguUcAgEBBAQDAgQwMCEGCysGAQQBguUcAQEEBBIEEBSaICGO9kEzlriB+NW38fUwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAPv6j3z0q4HJXj34E0N1aS2jbAa/oYy4YtOC4c0MYkRlsGEvrwdUzoj13i7EECMG5qkFOdXaFWwk2lxizSK9c72ywMIZy1h+4vZuGoQqmgs6MLU7wkO1QVBj+U9TOHmJ6KPNyAwlY0I/6WRvEGIDhjooM7RqFgH+QlnFBegtFMhWzjcFHKiRJdkC06Gv+xPFUY5uFuOiAFJY2JDg1WQEr/Id8C0TsfaeU0gZUsprcHbpcUHvwym3zUrzN3nQNLqfhCCSizjlPkE0dmUFeOnxFtf4oepvL3GmOi9zVtHmKXO013oo1CQIKFLcmv785p0QHnLmPW53KCbfD67y9oq9pA2hhdXRoRGF0YVjExGzvgq0bVGR3WR0Aiwh1nsPm0uy085R0v+ppaZJdA7dBAAAAAAAAAAAAAAAAAAAAAAAAAAAAQD3Zt06R0Mb5mDHTSnGN0eovDx0XGarb0khbLCadDkGsHITDmAZ6T0OxPewj8v3Gk8TzWFSS/hO3E/xwZuLCLjSlAQIDJiABIVggiSfmVgOyesk2SDOaPhShPbnahfrl3Vs0iQUW6QF4IHUiWCDi6beycQU49cvsW32MNlAqXxGJ7uaXY06NOKGq1HraxQ==', + clientDataJSON: 'eyJjaGFsbGVuZ2UiOiJBcEZqVmZSVFF3NV9OUjRZNXBvVHo4a3RkM2dhNGpJNUx5NjJfZzk3b0ZrIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9kZW1vLnl1Ymljby5jb20iLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0=' }, type: 'public-key' } @@ -86,12 +84,12 @@ const validConsent: ConsentDomain.Consent = { scopes: [ { accountId: 'as2342', - actions: ['accounts.getBalance', 'accounts.transfer'], + actions: ['accounts.getBalance', 'accounts.transfer'] }, { accountId: 'as22', - actions: ['accounts.getBalance'], - }, + actions: ['accounts.getBalance'] + } ], credential: credential, status: 'VERIFIED', @@ -100,10 +98,9 @@ const validConsent: ConsentDomain.Consent = { 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEiSfmVgOyesk2SDOaPhShPbnahfrl\n' + '3Vs0iQUW6QF4IHXi6beycQU49cvsW32MNlAqXxGJ7uaXY06NOKGq1HraxQ==\n' + '-----END PUBLIC KEY-----\n', - createdAt: new Date('2021-01-01'), + createdAt: new Date('2021-01-01') } - // yubico example const verificationRequest: tpAPI.Schemas.ThirdpartyRequestsVerificationsPostRequest = { verificationRequestId: '835a8444-8cdc-41ef-bf18-ca4916c2e005', @@ -115,12 +112,12 @@ const verificationRequest: tpAPI.Schemas.ThirdpartyRequestsVerificationsPostRequ consentId: '8d34f91d-d078-4077-8263-2c0498dhbjr', signedPayloadType: 'FIDO', signedPayload: { - "id": atob("Pdm3TpHQxvmYMdNKcY3R6i8PHRcZqtvSSFssJp0OQawchMOYBnpPQ7E97CPy_caTxPNYVJL-E7cT_HBm4sIuNA"), - "rawId": atob("Pdm3TpHQxvmYMdNKcY3R6i8PHRcZqtvSSFssJp0OQawchMOYBnpPQ7E97CPy_caTxPNYVJL-E7cT_HBm4sIuNA"), - "response": { - "authenticatorData": "xGzvgq0bVGR3WR0Aiwh1nsPm0uy085R0v+ppaZJdA7cBAAAABA==", - "clientDataJSON": "eyJjaGFsbGVuZ2UiOiJxdUZZTkNUV3dmTTZWREttcnhUVDEyemJTT2hXSnlXZ2x6S29xRjBQak1VIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9kZW1vLnl1Ymljby5jb20iLCJ0eXBlIjoid2ViYXV0aG4uZ2V0In0=", - "signature": "MEUCIQCb/nwG57/d8lWXfbBA7HtgIf8wM6A1XJ+LgZlEnClJBAIgKV8FAGkE9B8UXenmp589uTPgkDCJh5jiNMs+Tx2GQG8=" + id: atob('Pdm3TpHQxvmYMdNKcY3R6i8PHRcZqtvSSFssJp0OQawchMOYBnpPQ7E97CPy_caTxPNYVJL-E7cT_HBm4sIuNA'), + rawId: atob('Pdm3TpHQxvmYMdNKcY3R6i8PHRcZqtvSSFssJp0OQawchMOYBnpPQ7E97CPy_caTxPNYVJL-E7cT_HBm4sIuNA'), + response: { + authenticatorData: 'xGzvgq0bVGR3WR0Aiwh1nsPm0uy085R0v+ppaZJdA7cBAAAABA==', + clientDataJSON: 'eyJjaGFsbGVuZ2UiOiJxdUZZTkNUV3dmTTZWREttcnhUVDEyemJTT2hXSnlXZ2x6S29xRjBQak1VIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9kZW1vLnl1Ymljby5jb20iLCJ0eXBlIjoid2ViYXV0aG4uZ2V0In0=', + signature: 'MEUCIQCb/nwG57/d8lWXfbBA7HtgIf8wM6A1XJ+LgZlEnClJBAIgKV8FAGkE9B8UXenmp589uTPgkDCJh5jiNMs+Tx2GQG8=' }, type: 'public-key' } @@ -149,11 +146,11 @@ describe('VerifyTransactionModel', () => { subscriber: new PubSub(connectionConfig), logger: connectionConfig.logger, mojaloopRequests: { - _put: jest.fn(() => Promise.resolve({ statusCode: 200 })), + _put: jest.fn(() => Promise.resolve({ statusCode: 200 })) } as unknown as MojaloopRequests, thirdpartyRequests: { putThirdpartyRequestsVerifications: jest.fn(() => Promise.resolve({ statusCode: 200 })), - putThirdpartyRequestsVerificationsError: jest.fn(() => Promise.resolve({ statusCode: 200 })), + putThirdpartyRequestsVerificationsError: jest.fn(() => Promise.resolve({ statusCode: 200 })) } as unknown as ThirdpartyRequests, authServiceParticipantFSPId: config.PARTICIPANT_ID, requestProcessingTimeoutSeconds: 3 @@ -178,7 +175,7 @@ describe('VerifyTransactionModel', () => { await modelConfig.subscriber.disconnect() }) - function checkModelLayout(VerifyTransactionModel: VerifyTransactionModel, optData?: VerifyTransactionData) { + function checkModelLayout (VerifyTransactionModel: VerifyTransactionModel, optData?: VerifyTransactionData) { expect(VerifyTransactionModel).toBeTruthy() expect(VerifyTransactionModel.data).toBeDefined() expect(VerifyTransactionModel.fsm.state).toEqual(optData?.currentState || 'start') @@ -190,18 +187,18 @@ describe('VerifyTransactionModel', () => { // check is fsm correctly constructed expect(typeof VerifyTransactionModel.fsm.init).toEqual('function') - expect(typeof VerifyTransactionModel.fsm.retreiveConsent).toEqual('function') + expect(typeof VerifyTransactionModel.fsm.retrieveConsent).toEqual('function') expect(typeof VerifyTransactionModel.fsm.verifyTransaction).toEqual('function') expect(typeof VerifyTransactionModel.fsm.sendCallbackToDFSP).toEqual('function') // check fsm notification handler - expect(typeof VerifyTransactionModel.onRetreiveConsent).toEqual('function') + expect(typeof VerifyTransactionModel.onRetrieveConsent).toEqual('function') expect(typeof VerifyTransactionModel.onVerifyTransaction).toEqual('function') expect(typeof VerifyTransactionModel.onSendCallbackToDFSP).toEqual('function') expect(sortedArray(VerifyTransactionModel.fsm.allStates())).toEqual([ 'callbackSent', - 'consentRetreived', + 'consentRetrieved', 'errored', 'none', 'start', @@ -210,9 +207,9 @@ describe('VerifyTransactionModel', () => { expect(sortedArray(VerifyTransactionModel.fsm.allTransitions())).toEqual([ 'error', 'init', - 'retreiveConsent', + 'retrieveConsent', 'sendCallbackToDFSP', - 'verifyTransaction', + 'verifyTransaction' ]) } @@ -221,30 +218,29 @@ describe('VerifyTransactionModel', () => { expect(typeof create).toEqual('function') }) - describe('onRetreiveConsent', () => { - const retreiveConsentData: VerifyTransactionData = { + describe('onRetrieveConsent', () => { + const retrieveConsentData: VerifyTransactionData = { currentState: 'start', participantDFSPId: 'dfspa', verificationRequest } it('should be well constructed', async () => { - const model = await create(retreiveConsentData, modelConfig) - checkModelLayout(model, retreiveConsentData) + const model = await create(retrieveConsentData, modelConfig) + checkModelLayout(model, retrieveConsentData) }) - it('fetches the consent from the database', async () => { // Arrange mockGetConsent.mockResolvedValueOnce(validConsent) - const model = await create(retreiveConsentData, modelConfig) + const model = await create(retrieveConsentData, modelConfig) // Act - await model.fsm.retreiveConsent() + await model.fsm.retrieveConsent() // Assert expect(mockGetConsent).toHaveBeenCalledTimes(1) - expect(model.data.currentState).toEqual('consentRetreived') + expect(model.data.currentState).toEqual('consentRetrieved') }) it('responds with an error if something went wrong fetching the consent', async () => { @@ -252,11 +248,11 @@ describe('VerifyTransactionModel', () => { mockGetConsent.mockImplementationOnce(() => { throw new Error('test error') }) - const model = await create(retreiveConsentData, modelConfig) + const model = await create(retrieveConsentData, modelConfig) // Act try { - await model.fsm.retreiveConsent() + await model.fsm.retrieveConsent() shouldNotBeExecuted() } catch (error: any) { // Assert @@ -269,7 +265,7 @@ describe('VerifyTransactionModel', () => { describe('onVerifyTransaction', () => { const verifyTransactionData: VerifyTransactionData = { - currentState: 'consentRetreived', + currentState: 'consentRetrieved', participantDFSPId: 'dfspa', verificationRequest, consent: validConsent @@ -296,15 +292,11 @@ describe('VerifyTransactionModel', () => { '-----END PUBLIC KEY-----' const model = await create(invalidTransactionData, modelConfig) - // Act - try { - await model.fsm.verifyTransaction() - shouldNotBeExecuted() - } catch (error: any) { - // Assert - expect(error.message).toEqual('signature validation failed') - expect(modelConfig.thirdpartyRequests.putThirdpartyRequestsVerificationsError).toHaveBeenCalledTimes(1) - } + await model.fsm.verifyTransaction() + + expect(model.data.verificationResponse).toEqual({ + authenticationResponse: 'REJECTED' + }) }) it('responds with an error if clientDataJSON cannot be parsed', async () => { @@ -314,20 +306,16 @@ describe('VerifyTransactionModel', () => { if (invalidTransactionData.verificationRequest.signedPayloadType !== 'FIDO') { throw new Error('test data error') } - invalidTransactionData.verificationRequest.signedPayload.response.clientDataJSON = + invalidTransactionData.verificationRequest.signedPayload.response.clientDataJSON = btoa('{"notChallenge":"quFYNCTWwfM6VDKmrxTT12zbSOhWJyWglzKoqF0PjMU","clientExtensions":{},"hashAlgorithm":"SHA-256","origin":"https://demo.yubico.com","type":"webauthn.get"}') - + const model = await create(invalidTransactionData, modelConfig) - // Act - try { - await model.fsm.verifyTransaction() - shouldNotBeExecuted() - } catch (error: any) { - // Assert - expect(error.message).toEqual('clientData challenge was not a string') - expect(modelConfig.thirdpartyRequests.putThirdpartyRequestsVerificationsError).toHaveBeenCalledTimes(1) - } + await model.fsm.verifyTransaction() + + expect(model.data.verificationResponse).toEqual({ + authenticationResponse: 'REJECTED' + }) }) it('responds with an error if the consent status is REVOKED', async () => { @@ -335,7 +323,7 @@ describe('VerifyTransactionModel', () => { const invalidTransactionData: VerifyTransactionData = JSON.parse(JSON.stringify(verifyTransactionData)) invalidTransactionData.consent!.status = 'REVOKED' invalidTransactionData.consent!.revokedAt = new Date('2020-01-1') - + const model = await create(invalidTransactionData, modelConfig) // Act @@ -359,7 +347,7 @@ describe('VerifyTransactionModel', () => { signedPayloadType: 'GENERIC', signedPayload: '12345678' } - + const model = await create(invalidTransactionData, modelConfig) // Act @@ -382,22 +370,29 @@ describe('VerifyTransactionModel', () => { consent: validConsent } - it('sends the callback to the DFSP', async () => { + it('sends the verificationResponse data callback to the DFSP', async () => { // Arrange const model = await create(sendCallbackData, modelConfig) - + model.data.verificationResponse = { + authenticationResponse: 'VERIFIED' + } + // Act await model.fsm.sendCallbackToDFSP() // Assert expect(model.data.currentState).toEqual('callbackSent') expect(modelConfig.thirdpartyRequests.putThirdpartyRequestsVerifications).toHaveBeenCalledTimes(1) + expect(modelConfig.thirdpartyRequests.putThirdpartyRequestsVerifications).toHaveBeenCalledWith( + model.data.verificationResponse, '835a8444-8cdc-41ef-bf18-ca4916c2e005', 'dfspa' + ) }) it('sends an error callback to the DFSP if the original request fails', async () => { // Arrange const model = await create(sendCallbackData, modelConfig) modelConfig.thirdpartyRequests.putThirdpartyRequestsVerifications + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - mocked function .mockRejectedValueOnce(new Error('Test Error')) @@ -405,7 +400,7 @@ describe('VerifyTransactionModel', () => { try { await model.fsm.sendCallbackToDFSP() shouldNotBeExecuted() - } catch (err: any) { + } catch (err: any) { // Assert expect(err.message).toBe('Test Error') expect(modelConfig.thirdpartyRequests.putThirdpartyRequestsVerifications).toHaveBeenCalledTimes(1) @@ -446,10 +441,10 @@ describe('VerifyTransactionModel', () => { // Arrange mockGetConsent.mockResolvedValueOnce(validConsent) const model = await create(registerConsentData, modelConfig) - + // Act await model.run() - + // Assert // check that the fsm was able complete the workflow expect(model.data.currentState).toEqual('callbackSent') diff --git a/test/unit/shared/fido-lib.test.ts b/test/unit/shared/fido-lib.test.ts index 6ea7d47e..19eb0a61 100644 --- a/test/unit/shared/fido-lib.test.ts +++ b/test/unit/shared/fido-lib.test.ts @@ -31,15 +31,7 @@ import { deriveChallenge } from '~/domain/challenge' import { decodeBase64String } from '~/domain/buffer' import FidoUtils from '~/shared/fido-utils' -function ab2str(buf: ArrayBuffer) { - var str = ""; - new Uint8Array(buf).forEach((ch) => { - str += String.fromCharCode(ch); - }); - return str; -} - -const btoa = require('btoa') +import btoa from 'btoa' /* Example attestation result @@ -172,8 +164,15 @@ const btoa = require('btoa') } } */ -const atob = require('atob') +import atob from 'atob' +function ab2str (buf: ArrayBuffer) { + let str = '' + new Uint8Array(buf).forEach((ch) => { + str += String.fromCharCode(ch) + }) + return str +} const consentsPostRequestAUTH = { headers: { @@ -195,7 +194,7 @@ const consentsPostRequestAUTH = { rawId: atob('vwWPva1iiTJIk/c7n9a49spEtJZBqrn4SECerci0b+Ue+6Jv9/DZo3rNX02Lq5PU4N5kGlkEPAkIoZ3499AzWQ=='), response: { clientDataJSON: 'eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiTXpnd056QTFZMkU1TlRaaFlUZzBOMlE0T1dVMFlUUTBOR1JoT1dKbFpXUmpOR1EzTlRZNU1XSTBNV0l3WldNeE9EVTJZalJoWW1Sa05EbGhORE0yTUEiLCJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjQyMTgxIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ==', - attestationObject: 'o2NmbXRmcGFja2VkZ2F0dFN0bXSjY2FsZyZjc2lnWEcwRQIhAJEFVHrzmq90fdBVy4nOPc48vtvJVAyQleGVcp+nQ8lUAiB67XFnGhC7q7WI3NdcrCdqnewSjCfhqEvO+sbWKC60c2N4NWOBWQLBMIICvTCCAaWgAwIBAgIECwXNUzANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowbjELMAkGA1UEBhMCU0UxEjAQBgNVBAoMCVl1YmljbyBBQjEiMCAGA1UECwwZQXV0aGVudGljYXRvciBBdHRlc3RhdGlvbjEnMCUGA1UEAwweWXViaWNvIFUyRiBFRSBTZXJpYWwgMTg0OTI5NjE5MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIRpvsbWJJcsKwRhffCrjqLSIEBR5sR7/9VXgfZdRvSsXaiUt7lns44WZIFuz6ii/j9f8fadcBUJyrkhY5ZH8WqNsMGowIgYJKwYBBAGCxAoCBBUxLjMuNi4xLjQuMS40MTQ4Mi4xLjEwEwYLKwYBBAGC5RwCAQEEBAMCBDAwIQYLKwYBBAGC5RwBAQQEEgQQFJogIY72QTOWuIH41bfx9TAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQA+/qPfPSrgclePfgTQ3VpLaNsBr+hjLhi04LhzQxiRGWwYS+vB1TOiPXeLsQQIwbmqQU51doVbCTaXGLNIr1zvbLAwhnLWH7i9m4ahCqaCzowtTvCQ7VBUGP5T1M4eYnoo83IDCVjQj/pZG8QYgOGOigztGoWAf5CWcUF6C0UyFbONwUcqJEl2QLToa/7E8VRjm4W46IAUljYkODVZASv8h3wLROx9p5TSBlSymtwdulxQe/DKbfNSvM3edA0up+EIJKLOOU+QTR2ZQV46fEW1/ih6m8vcaY6L3NW0eYpc7TXeijUJAgoUtya/vzmnRAecuY9bncoJt8PrvL2ir2kDaGF1dGhEYXRhWMRJlg3liA6MaHQ0Fw9kdmBbj+SuuaKGMseZXPO6gx2XY0EAAAABFJogIY72QTOWuIH41bfx9QBAvwWPva1iiTJIk/c7n9a49spEtJZBqrn4SECerci0b+Ue+6Jv9/DZo3rNX02Lq5PU4N5kGlkEPAkIoZ3499AzWaUBAgMmIAEhWCAITUwire20kCqzl0A3Fbpwx2cnSqwFfTgbA2b8+a/aUiJYIHRMWJlb4Lud02oWTdQ+fejwkVo17qD0KvrwwrZZxWIg', + attestationObject: 'o2NmbXRmcGFja2VkZ2F0dFN0bXSjY2FsZyZjc2lnWEcwRQIhAJEFVHrzmq90fdBVy4nOPc48vtvJVAyQleGVcp+nQ8lUAiB67XFnGhC7q7WI3NdcrCdqnewSjCfhqEvO+sbWKC60c2N4NWOBWQLBMIICvTCCAaWgAwIBAgIECwXNUzANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowbjELMAkGA1UEBhMCU0UxEjAQBgNVBAoMCVl1YmljbyBBQjEiMCAGA1UECwwZQXV0aGVudGljYXRvciBBdHRlc3RhdGlvbjEnMCUGA1UEAwweWXViaWNvIFUyRiBFRSBTZXJpYWwgMTg0OTI5NjE5MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIRpvsbWJJcsKwRhffCrjqLSIEBR5sR7/9VXgfZdRvSsXaiUt7lns44WZIFuz6ii/j9f8fadcBUJyrkhY5ZH8WqNsMGowIgYJKwYBBAGCxAoCBBUxLjMuNi4xLjQuMS40MTQ4Mi4xLjEwEwYLKwYBBAGC5RwCAQEEBAMCBDAwIQYLKwYBBAGC5RwBAQQEEgQQFJogIY72QTOWuIH41bfx9TAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQA+/qPfPSrgclePfgTQ3VpLaNsBr+hjLhi04LhzQxiRGWwYS+vB1TOiPXeLsQQIwbmqQU51doVbCTaXGLNIr1zvbLAwhnLWH7i9m4ahCqaCzowtTvCQ7VBUGP5T1M4eYnoo83IDCVjQj/pZG8QYgOGOigztGoWAf5CWcUF6C0UyFbONwUcqJEl2QLToa/7E8VRjm4W46IAUljYkODVZASv8h3wLROx9p5TSBlSymtwdulxQe/DKbfNSvM3edA0up+EIJKLOOU+QTR2ZQV46fEW1/ih6m8vcaY6L3NW0eYpc7TXeijUJAgoUtya/vzmnRAecuY9bncoJt8PrvL2ir2kDaGF1dGhEYXRhWMRJlg3liA6MaHQ0Fw9kdmBbj+SuuaKGMseZXPO6gx2XY0EAAAABFJogIY72QTOWuIH41bfx9QBAvwWPva1iiTJIk/c7n9a49spEtJZBqrn4SECerci0b+Ue+6Jv9/DZo3rNX02Lq5PU4N5kGlkEPAkIoZ3499AzWaUBAgMmIAEhWCAITUwire20kCqzl0A3Fbpwx2cnSqwFfTgbA2b8+a/aUiJYIHRMWJlb4Lud02oWTdQ+fejwkVo17qD0KvrwwrZZxWIg' }, type: 'public-key' } @@ -215,7 +214,7 @@ const verificationRequest: tpAPI.Schemas.ThirdpartyRequestsVerificationsPostRequ rawId: 'vwWPva1iiTJIk_c7n9a49spEtJZBqrn4SECerci0b-Ue-6Jv9_DZo3rNX02Lq5PU4N5kGlkEPAkIoZ3499AzWQ', response: { authenticatorData: Buffer.from([73, 150, 13, 229, 136, 14, 140, 104, 116, 52, 23, -15, 100, 118, 96, 91, 143, 228, 174, 185, 162, 134, 50, 199, 153, 92, 243, 186, 131, 29, 151, 99, 1, 0, 0, 0, 18]).toString('base64'), + 15, 100, 118, 96, 91, 143, 228, 174, 185, 162, 134, 50, 199, 153, 92, 243, 186, 131, 29, 151, 99, 1, 0, 0, 0, 18]).toString('base64'), clientDataJSON: Buffer.from([123, 34, 116, 121, 112, 101, 34, 58, 34, 119, 101, 98, 97, 117, 116, 104, 110, 46, 103, 101, 116, 34, 44, 34, 99, 104, 97, 108, 108, 101, 110, 103, 101, 34, 58, 34, 100, 87, 53, 112, 98, 88, 66, 115, 90, 87, 49, 108, 98, 110, 82, 108, 90, 68, 69, 121, 77, 119, 34, 44, 34, 111, 114, 105, 103, 105, 110, 34, 58, 34, 104, 116, 116, 112, 58, 47, 47, 108, 111, 99, 97, 108, 104, @@ -247,10 +246,10 @@ describe('fido-lib', (): void => { it('should decode the clientDataJSON', () => { // Arrange const expected = { - "type": "webauthn.create", - "challenge": "MzgwNzA1Y2E5NTZhYTg0N2Q4OWU0YTQ0NGRhOWJlZWRjNGQ3NTY5MWI0MWIwZWMxODU2YjRhYmRkNDlhNDM2MA", - "origin": "http://localhost:42181", - "crossOrigin": false, + type: 'webauthn.create', + challenge: 'MzgwNzA1Y2E5NTZhYTg0N2Q4OWU0YTQ0NGRhOWJlZWRjNGQ3NTY5MWI0MWIwZWMxODU2YjRhYmRkNDlhNDM2MA', + origin: 'http://localhost:42181', + crossOrigin: false } // Act @@ -268,8 +267,8 @@ describe('fido-lib', (): void => { const challenge = 'MzgwNzA1Y2E5NTZhYTg0N2Q4OWU0YTQ0NGRhOWJlZWRjNGQ3NTY5MWI0MWIwZWMxODU2YjRhYmRkNDlhNDM2MA==' const attestationExpectations: ExpectedAttestationResult = { challenge, - origin: "http://localhost:42181", - factor: "either" + origin: 'http://localhost:42181', + factor: 'either' } const f2l = new Fido2Lib() @@ -278,20 +277,21 @@ describe('fido-lib', (): void => { rawId: str2ab(consentsPostRequestAUTH.payload.credential.payload.rawId), response: { clientDataJSON: consentsPostRequestAUTH.payload.credential.payload.response.clientDataJSON, - attestationObject: consentsPostRequestAUTH.payload.credential.payload.response.attestationObject, + attestationObject: consentsPostRequestAUTH.payload.credential.payload.response.attestationObject } } + // eslint-disable-next-line no-useless-catch try { const result = await f2l.attestationResult( clientAttestationResponse, attestationExpectations ) console.log('credentialPublicKeyPem:', result.authnrData.get('credentialPublicKeyPem')) - + const credIdAB = result.authnrData.get('credId') const credId = btoa(ab2str(credIdAB)) console.log('credId:', credId) - } catch (error){ + } catch (error) { throw error } }) @@ -304,15 +304,15 @@ describe('fido-lib', (): void => { origin: 'http://localhost:42181', // fido2lib infers this from origin, so we don't need to set it // rpId: 'localhost', - factor: "either", + factor: 'either', // Get this from the log statement in the previous request publicKey: `-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAECE1MIq3ttJAqs5dANxW6cMdnJ0qs BX04GwNm/Pmv2lJ0TFiZW+C7ndNqFk3UPn3o8JFaNe6g9Cr68MK2WcViIA== -----END PUBLIC KEY-----`, prevCounter: 0, - userHandle: null, - }; + userHandle: null + } const authenticatorData = FidoUtils.stringToArrayBuffer(verificationRequest.signedPayload.response.authenticatorData) console.log('authenticatorData.length', authenticatorData.byteLength) const assertionResult: AssertionResult = { @@ -325,28 +325,27 @@ BX04GwNm/Pmv2lJ0TFiZW+C7ndNqFk3UPn3o8JFaNe6g9Cr68MK2WcViIA== userHandle: verificationRequest.signedPayload.response.userHandle } } - + // Act - await f2l.assertionResult(assertionResult, assertionExpectations); // will throw on error + await f2l.assertionResult(assertionResult, assertionExpectations) // will throw on error // Assert }) - describe('custom site based attestation and assertion', () => { - const consent = { "consentId": "46876aac-5db8-4353-bb3c-a6a905843ce7", "consentRequestId": "c51ec534-ee48-4575-b6a9-ead2955b8069", "scopes": [{ "accountId": "dfspa.username.5678", "actions": ["accounts.transfer"] }] } + const consent = { consentId: '46876aac-5db8-4353-bb3c-a6a905843ce7', consentRequestId: 'c51ec534-ee48-4575-b6a9-ead2955b8069', scopes: [{ accountId: 'dfspa.username.5678', actions: ['accounts.transfer'] }] } const challenge = deriveChallenge(consent as unknown as tpAPI.Schemas.ConsentsPostRequestAUTH) const credential: tpAPI.Schemas.VerifiedCredential = { credentialType: 'FIDO', status: 'VERIFIED', payload: { - "id": "iehRZTiFY7bRoBOKSAZDOgLkHKmLA3O90Aq1WZBlqjQjDmemdSjlFUvB2g6_U00NfDa_Wxyti0uVwPzragBrzw", - "rawId": "iehRZTiFY7bRoBOKSAZDOgLkHKmLA3O90Aq1WZBlqjQjDmemdSjlFUvB2g6/U00NfDa/Wxyti0uVwPzragBrzw==", - "response": { - "attestationObject": "o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEcwRQIgC8d5Y5Tfs4nNybpZT97j5ZVuTNFu1AWWwqpR8em4LJcCIQDDzayDA6lzgrbB3jDMM2/NI70TtZux2T3lIWMK8IGxr2N4NWOBWQLBMIICvTCCAaWgAwIBAgIECwXNUzANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowbjELMAkGA1UEBhMCU0UxEjAQBgNVBAoMCVl1YmljbyBBQjEiMCAGA1UECwwZQXV0aGVudGljYXRvciBBdHRlc3RhdGlvbjEnMCUGA1UEAwweWXViaWNvIFUyRiBFRSBTZXJpYWwgMTg0OTI5NjE5MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIRpvsbWJJcsKwRhffCrjqLSIEBR5sR7/9VXgfZdRvSsXaiUt7lns44WZIFuz6ii/j9f8fadcBUJyrkhY5ZH8WqNsMGowIgYJKwYBBAGCxAoCBBUxLjMuNi4xLjQuMS40MTQ4Mi4xLjEwEwYLKwYBBAGC5RwCAQEEBAMCBDAwIQYLKwYBBAGC5RwBAQQEEgQQFJogIY72QTOWuIH41bfx9TAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQA+/qPfPSrgclePfgTQ3VpLaNsBr+hjLhi04LhzQxiRGWwYS+vB1TOiPXeLsQQIwbmqQU51doVbCTaXGLNIr1zvbLAwhnLWH7i9m4ahCqaCzowtTvCQ7VBUGP5T1M4eYnoo83IDCVjQj/pZG8QYgOGOigztGoWAf5CWcUF6C0UyFbONwUcqJEl2QLToa/7E8VRjm4W46IAUljYkODVZASv8h3wLROx9p5TSBlSymtwdulxQe/DKbfNSvM3edA0up+EIJKLOOU+QTR2ZQV46fEW1/ih6m8vcaY6L3NW0eYpc7TXeijUJAgoUtya/vzmnRAecuY9bncoJt8PrvL2ir2kDaGF1dGhEYXRhWMTLX5kZhaUsGGUVJvPd6efRKHVvWMqnrf4u23AvlzDddEEAAAAAAAAAAAAAAAAAAAAAAAAAAABAiehRZTiFY7bRoBOKSAZDOgLkHKmLA3O90Aq1WZBlqjQjDmemdSjlFUvB2g6/U00NfDa/Wxyti0uVwPzragBrz6UBAgMmIAEhWCDAVRRKKW4qj4bWykF+8L4FI49plPv1i7yD+ef0ATwwlyJYIO7sxbQE+9J1LAY6lLMMh+jiSU0/Rf9j0MXiqC2/b7Cq", - "clientDataJSON": "eyJjaGFsbGVuZ2UiOiJORGxqT1RjeFltWXdZVFExWm1Ka1pUa3pOek13Tm1SalpUazNZVFl6TURjM01HSmtZamMzWW1FellqWm1OemcwWkRJMU5HWTJPR0UwTm1Sa05EQmhNZyIsImNsaWVudEV4dGVuc2lvbnMiOnt9LCJoYXNoQWxnb3JpdGhtIjoiU0hBLTI1NiIsIm9yaWdpbiI6Imh0dHBzOi8vc2FuZGJveC5tb2phbG9vcC5pbyIsInR5cGUiOiJ3ZWJhdXRobi5jcmVhdGUifQ==" + id: 'iehRZTiFY7bRoBOKSAZDOgLkHKmLA3O90Aq1WZBlqjQjDmemdSjlFUvB2g6_U00NfDa_Wxyti0uVwPzragBrzw', + rawId: 'iehRZTiFY7bRoBOKSAZDOgLkHKmLA3O90Aq1WZBlqjQjDmemdSjlFUvB2g6/U00NfDa/Wxyti0uVwPzragBrzw==', + response: { + attestationObject: 'o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEcwRQIgC8d5Y5Tfs4nNybpZT97j5ZVuTNFu1AWWwqpR8em4LJcCIQDDzayDA6lzgrbB3jDMM2/NI70TtZux2T3lIWMK8IGxr2N4NWOBWQLBMIICvTCCAaWgAwIBAgIECwXNUzANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowbjELMAkGA1UEBhMCU0UxEjAQBgNVBAoMCVl1YmljbyBBQjEiMCAGA1UECwwZQXV0aGVudGljYXRvciBBdHRlc3RhdGlvbjEnMCUGA1UEAwweWXViaWNvIFUyRiBFRSBTZXJpYWwgMTg0OTI5NjE5MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIRpvsbWJJcsKwRhffCrjqLSIEBR5sR7/9VXgfZdRvSsXaiUt7lns44WZIFuz6ii/j9f8fadcBUJyrkhY5ZH8WqNsMGowIgYJKwYBBAGCxAoCBBUxLjMuNi4xLjQuMS40MTQ4Mi4xLjEwEwYLKwYBBAGC5RwCAQEEBAMCBDAwIQYLKwYBBAGC5RwBAQQEEgQQFJogIY72QTOWuIH41bfx9TAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQA+/qPfPSrgclePfgTQ3VpLaNsBr+hjLhi04LhzQxiRGWwYS+vB1TOiPXeLsQQIwbmqQU51doVbCTaXGLNIr1zvbLAwhnLWH7i9m4ahCqaCzowtTvCQ7VBUGP5T1M4eYnoo83IDCVjQj/pZG8QYgOGOigztGoWAf5CWcUF6C0UyFbONwUcqJEl2QLToa/7E8VRjm4W46IAUljYkODVZASv8h3wLROx9p5TSBlSymtwdulxQe/DKbfNSvM3edA0up+EIJKLOOU+QTR2ZQV46fEW1/ih6m8vcaY6L3NW0eYpc7TXeijUJAgoUtya/vzmnRAecuY9bncoJt8PrvL2ir2kDaGF1dGhEYXRhWMTLX5kZhaUsGGUVJvPd6efRKHVvWMqnrf4u23AvlzDddEEAAAAAAAAAAAAAAAAAAAAAAAAAAABAiehRZTiFY7bRoBOKSAZDOgLkHKmLA3O90Aq1WZBlqjQjDmemdSjlFUvB2g6/U00NfDa/Wxyti0uVwPzragBrz6UBAgMmIAEhWCDAVRRKKW4qj4bWykF+8L4FI49plPv1i7yD+ef0ATwwlyJYIO7sxbQE+9J1LAY6lLMMh+jiSU0/Rf9j0MXiqC2/b7Cq', + clientDataJSON: 'eyJjaGFsbGVuZ2UiOiJORGxqT1RjeFltWXdZVFExWm1Ka1pUa3pOek13Tm1SalpUazNZVFl6TURjM01HSmtZamMzWW1FellqWm1OemcwWkRJMU5HWTJPR0UwTm1Sa05EQmhNZyIsImNsaWVudEV4dGVuc2lvbnMiOnt9LCJoYXNoQWxnb3JpdGhtIjoiU0hBLTI1NiIsIm9yaWdpbiI6Imh0dHBzOi8vc2FuZGJveC5tb2phbG9vcC5pbyIsInR5cGUiOiJ3ZWJhdXRobi5jcmVhdGUifQ==' }, - "type": "public-key" + type: 'public-key' } } @@ -357,14 +356,14 @@ BX04GwNm/Pmv2lJ0TFiZW+C7ndNqFk3UPn3o8JFaNe6g9Cr68MK2WcViIA== consentId: 'be433b9e-9473-4b7d-bdd5-ac5b42463afb', signedPayloadType: 'FIDO', signedPayload: { - "id": "iehRZTiFY7bRoBOKSAZDOgLkHKmLA3O90Aq1WZBlqjQjDmemdSjlFUvB2g6_U00NfDa_Wxyti0uVwPzragBrzw", - "rawId": "iehRZTiFY7bRoBOKSAZDOgLkHKmLA3O90Aq1WZBlqjQjDmemdSjlFUvB2g6/U00NfDa/Wxyti0uVwPzragBrzw==", - "response": { - "authenticatorData": "y1+ZGYWlLBhlFSbz3enn0Sh1b1jKp63+LttwL5cw3XQBAAAAAg==", - "clientDataJSON": "eyJjaGFsbGVuZ2UiOiJUMWRhYUZscVFYaGFWR04zV1dwVk5GbDZVbWhOZWxKdFQxZFJkMDU2UW0xYWFteHNXa1JHYVU1cVl6Sk9WMVpvVFhwQk1VNUhTVEZOVjFwcVdsUm9hbHBIUm1wT1JFVjVXa1JDYlU1dFRUSk5WMFpvVFZFIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9zYW5kYm94Lm1vamFsb29wLmlvIiwidHlwZSI6IndlYmF1dGhuLmdldCJ9", - "signature": "MEYCIQC3Igm0I4uFjJydEYIcDPn6Wq39fY0QyQdZu2pEwaaMoAIhAKb2B6XaVXKO+ORsUgP5Riw22rkvIhS6eb3KadyFfaos" + id: 'iehRZTiFY7bRoBOKSAZDOgLkHKmLA3O90Aq1WZBlqjQjDmemdSjlFUvB2g6_U00NfDa_Wxyti0uVwPzragBrzw', + rawId: 'iehRZTiFY7bRoBOKSAZDOgLkHKmLA3O90Aq1WZBlqjQjDmemdSjlFUvB2g6/U00NfDa/Wxyti0uVwPzragBrzw==', + response: { + authenticatorData: 'y1+ZGYWlLBhlFSbz3enn0Sh1b1jKp63+LttwL5cw3XQBAAAAAg==', + clientDataJSON: 'eyJjaGFsbGVuZ2UiOiJUMWRhYUZscVFYaGFWR04zV1dwVk5GbDZVbWhOZWxKdFQxZFJkMDU2UW0xYWFteHNXa1JHYVU1cVl6Sk9WMVpvVFhwQk1VNUhTVEZOVjFwcVdsUm9hbHBIUm1wT1JFVjVXa1JDYlU1dFRUSk5WMFpvVFZFIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9zYW5kYm94Lm1vamFsb29wLmlvIiwidHlwZSI6IndlYmF1dGhuLmdldCJ9', + signature: 'MEYCIQC3Igm0I4uFjJydEYIcDPn6Wq39fY0QyQdZu2pEwaaMoAIhAKb2B6XaVXKO+ORsUgP5Riw22rkvIhS6eb3KadyFfaos' }, - "type": "public-key" + type: 'public-key' } } @@ -372,8 +371,8 @@ BX04GwNm/Pmv2lJ0TFiZW+C7ndNqFk3UPn3o8JFaNe6g9Cr68MK2WcViIA== // Arrange const attestationExpectations: ExpectedAttestationResult = { challenge, - origin: "https://sandbox.mojaloop.io", - factor: "either" + origin: 'https://sandbox.mojaloop.io', + factor: 'either' } const f2l = new Fido2Lib() @@ -382,7 +381,7 @@ BX04GwNm/Pmv2lJ0TFiZW+C7ndNqFk3UPn3o8JFaNe6g9Cr68MK2WcViIA== rawId: str2ab(credential.payload.rawId), response: { clientDataJSON: credential.payload.response.clientDataJSON, - attestationObject: credential.payload.response.attestationObject, + attestationObject: credential.payload.response.attestationObject } } @@ -404,16 +403,16 @@ BX04GwNm/Pmv2lJ0TFiZW+C7ndNqFk3UPn3o8JFaNe6g9Cr68MK2WcViIA== // This must be base64 encoded, // as navigator.credentials.get base64 encodes the challenge challenge: btoa(customSiteVR.challenge), - origin: "https://sandbox.mojaloop.io", - factor: "either", + origin: 'https://sandbox.mojaloop.io', + factor: 'either', // Get this from the log statement in the previous request publicKey: `-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwFUUSiluKo+G1spBfvC+BSOPaZT7 9Yu8g/nn9AE8MJfu7MW0BPvSdSwGOpSzDIfo4klNP0X/Y9DF4qgtv2+wqg== -----END PUBLIC KEY-----`, prevCounter: 0, - userHandle: null, - }; + userHandle: null + } const authenticatorData = FidoUtils.stringToArrayBuffer(customSiteVR.signedPayload.response.authenticatorData) console.log('authenticatorData.length', authenticatorData.byteLength) const assertionResult: AssertionResult = { @@ -428,23 +427,22 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwFUUSiluKo+G1spBfvC+BSOPaZT7 } // Act - await f2l.assertionResult(assertionResult, assertionExpectations); // will throw on error + await f2l.assertionResult(assertionResult, assertionExpectations) // will throw on error // Assert }) }) - describe('yubikey site based attestation and assertion', () => { const credential: tpAPI.Schemas.VerifiedCredential = { credentialType: 'FIDO', status: 'VERIFIED', payload: { - "id": atob("Pdm3TpHQxvmYMdNKcY3R6i8PHRcZqtvSSFssJp0OQawchMOYBnpPQ7E97CPy_caTxPNYVJL-E7cT_HBm4sIuNA"), - "rawId": atob("Pdm3TpHQxvmYMdNKcY3R6i8PHRcZqtvSSFssJp0OQawchMOYBnpPQ7E97CPy_caTxPNYVJL-E7cT_HBm4sIuNA=="), - "response": { - "attestationObject": "o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEgwRgIhAOrrUscl/GRHvjoAtJE6KbgQxUSj3vwp3Ztmh9nQEvuSAiEAgDjZEL8PKFvgJnX7JCk260lOeeht5Ffe/kmA9At17a9jeDVjgVkCwTCCAr0wggGloAMCAQICBAsFzVMwDQYJKoZIhvcNAQELBQAwLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEwIBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMG4xCzAJBgNVBAYTAlNFMRIwEAYDVQQKDAlZdWJpY28gQUIxIjAgBgNVBAsMGUF1dGhlbnRpY2F0b3IgQXR0ZXN0YXRpb24xJzAlBgNVBAMMHll1YmljbyBVMkYgRUUgU2VyaWFsIDE4NDkyOTYxOTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCEab7G1iSXLCsEYX3wq46i0iBAUebEe//VV4H2XUb0rF2olLe5Z7OOFmSBbs+oov4/X/H2nXAVCcq5IWOWR/FqjbDBqMCIGCSsGAQQBgsQKAgQVMS4zLjYuMS40LjEuNDE0ODIuMS4xMBMGCysGAQQBguUcAgEBBAQDAgQwMCEGCysGAQQBguUcAQEEBBIEEBSaICGO9kEzlriB+NW38fUwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAPv6j3z0q4HJXj34E0N1aS2jbAa/oYy4YtOC4c0MYkRlsGEvrwdUzoj13i7EECMG5qkFOdXaFWwk2lxizSK9c72ywMIZy1h+4vZuGoQqmgs6MLU7wkO1QVBj+U9TOHmJ6KPNyAwlY0I/6WRvEGIDhjooM7RqFgH+QlnFBegtFMhWzjcFHKiRJdkC06Gv+xPFUY5uFuOiAFJY2JDg1WQEr/Id8C0TsfaeU0gZUsprcHbpcUHvwym3zUrzN3nQNLqfhCCSizjlPkE0dmUFeOnxFtf4oepvL3GmOi9zVtHmKXO013oo1CQIKFLcmv785p0QHnLmPW53KCbfD67y9oq9pA2hhdXRoRGF0YVjExGzvgq0bVGR3WR0Aiwh1nsPm0uy085R0v+ppaZJdA7dBAAAAAAAAAAAAAAAAAAAAAAAAAAAAQD3Zt06R0Mb5mDHTSnGN0eovDx0XGarb0khbLCadDkGsHITDmAZ6T0OxPewj8v3Gk8TzWFSS/hO3E/xwZuLCLjSlAQIDJiABIVggiSfmVgOyesk2SDOaPhShPbnahfrl3Vs0iQUW6QF4IHUiWCDi6beycQU49cvsW32MNlAqXxGJ7uaXY06NOKGq1HraxQ==", - "clientDataJSON": "eyJjaGFsbGVuZ2UiOiJBcEZqVmZSVFF3NV9OUjRZNXBvVHo4a3RkM2dhNGpJNUx5NjJfZzk3b0ZrIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9kZW1vLnl1Ymljby5jb20iLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0=" + id: atob('Pdm3TpHQxvmYMdNKcY3R6i8PHRcZqtvSSFssJp0OQawchMOYBnpPQ7E97CPy_caTxPNYVJL-E7cT_HBm4sIuNA'), + rawId: atob('Pdm3TpHQxvmYMdNKcY3R6i8PHRcZqtvSSFssJp0OQawchMOYBnpPQ7E97CPy_caTxPNYVJL-E7cT_HBm4sIuNA=='), + response: { + attestationObject: 'o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEgwRgIhAOrrUscl/GRHvjoAtJE6KbgQxUSj3vwp3Ztmh9nQEvuSAiEAgDjZEL8PKFvgJnX7JCk260lOeeht5Ffe/kmA9At17a9jeDVjgVkCwTCCAr0wggGloAMCAQICBAsFzVMwDQYJKoZIhvcNAQELBQAwLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEwIBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMG4xCzAJBgNVBAYTAlNFMRIwEAYDVQQKDAlZdWJpY28gQUIxIjAgBgNVBAsMGUF1dGhlbnRpY2F0b3IgQXR0ZXN0YXRpb24xJzAlBgNVBAMMHll1YmljbyBVMkYgRUUgU2VyaWFsIDE4NDkyOTYxOTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCEab7G1iSXLCsEYX3wq46i0iBAUebEe//VV4H2XUb0rF2olLe5Z7OOFmSBbs+oov4/X/H2nXAVCcq5IWOWR/FqjbDBqMCIGCSsGAQQBgsQKAgQVMS4zLjYuMS40LjEuNDE0ODIuMS4xMBMGCysGAQQBguUcAgEBBAQDAgQwMCEGCysGAQQBguUcAQEEBBIEEBSaICGO9kEzlriB+NW38fUwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAPv6j3z0q4HJXj34E0N1aS2jbAa/oYy4YtOC4c0MYkRlsGEvrwdUzoj13i7EECMG5qkFOdXaFWwk2lxizSK9c72ywMIZy1h+4vZuGoQqmgs6MLU7wkO1QVBj+U9TOHmJ6KPNyAwlY0I/6WRvEGIDhjooM7RqFgH+QlnFBegtFMhWzjcFHKiRJdkC06Gv+xPFUY5uFuOiAFJY2JDg1WQEr/Id8C0TsfaeU0gZUsprcHbpcUHvwym3zUrzN3nQNLqfhCCSizjlPkE0dmUFeOnxFtf4oepvL3GmOi9zVtHmKXO013oo1CQIKFLcmv785p0QHnLmPW53KCbfD67y9oq9pA2hhdXRoRGF0YVjExGzvgq0bVGR3WR0Aiwh1nsPm0uy085R0v+ppaZJdA7dBAAAAAAAAAAAAAAAAAAAAAAAAAAAAQD3Zt06R0Mb5mDHTSnGN0eovDx0XGarb0khbLCadDkGsHITDmAZ6T0OxPewj8v3Gk8TzWFSS/hO3E/xwZuLCLjSlAQIDJiABIVggiSfmVgOyesk2SDOaPhShPbnahfrl3Vs0iQUW6QF4IHUiWCDi6beycQU49cvsW32MNlAqXxGJ7uaXY06NOKGq1HraxQ==', + clientDataJSON: 'eyJjaGFsbGVuZ2UiOiJBcEZqVmZSVFF3NV9OUjRZNXBvVHo4a3RkM2dhNGpJNUx5NjJfZzk3b0ZrIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9kZW1vLnl1Ymljby5jb20iLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0=' }, // // front end demo // id: atob('vwWPva1iiTJIk_c7n9a49spEtJZBqrn4SECerci0b-Ue-6Jv9_DZo3rNX02Lq5PU4N5kGlkEPAkIoZ3499AzWQ'), @@ -465,12 +463,12 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwFUUSiluKo+G1spBfvC+BSOPaZT7 consentId: '8d34f91d-d078-4077-8263-2c0498dhbjr', signedPayloadType: 'FIDO', signedPayload: { - "id": atob("Pdm3TpHQxvmYMdNKcY3R6i8PHRcZqtvSSFssJp0OQawchMOYBnpPQ7E97CPy_caTxPNYVJL-E7cT_HBm4sIuNA"), - "rawId": atob("Pdm3TpHQxvmYMdNKcY3R6i8PHRcZqtvSSFssJp0OQawchMOYBnpPQ7E97CPy_caTxPNYVJL-E7cT_HBm4sIuNA"), - "response": { - "authenticatorData": "xGzvgq0bVGR3WR0Aiwh1nsPm0uy085R0v+ppaZJdA7cBAAAABA==", - "clientDataJSON": "eyJjaGFsbGVuZ2UiOiJxdUZZTkNUV3dmTTZWREttcnhUVDEyemJTT2hXSnlXZ2x6S29xRjBQak1VIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9kZW1vLnl1Ymljby5jb20iLCJ0eXBlIjoid2ViYXV0aG4uZ2V0In0=", - "signature": "MEUCIQCb/nwG57/d8lWXfbBA7HtgIf8wM6A1XJ+LgZlEnClJBAIgKV8FAGkE9B8UXenmp589uTPgkDCJh5jiNMs+Tx2GQG8=" + id: atob('Pdm3TpHQxvmYMdNKcY3R6i8PHRcZqtvSSFssJp0OQawchMOYBnpPQ7E97CPy_caTxPNYVJL-E7cT_HBm4sIuNA'), + rawId: atob('Pdm3TpHQxvmYMdNKcY3R6i8PHRcZqtvSSFssJp0OQawchMOYBnpPQ7E97CPy_caTxPNYVJL-E7cT_HBm4sIuNA'), + response: { + authenticatorData: 'xGzvgq0bVGR3WR0Aiwh1nsPm0uy085R0v+ppaZJdA7cBAAAABA==', + clientDataJSON: 'eyJjaGFsbGVuZ2UiOiJxdUZZTkNUV3dmTTZWREttcnhUVDEyemJTT2hXSnlXZ2x6S29xRjBQak1VIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9kZW1vLnl1Ymljby5jb20iLCJ0eXBlIjoid2ViYXV0aG4uZ2V0In0=', + signature: 'MEUCIQCb/nwG57/d8lWXfbBA7HtgIf8wM6A1XJ+LgZlEnClJBAIgKV8FAGkE9B8UXenmp589uTPgkDCJh5jiNMs+Tx2GQG8=' }, type: 'public-key' } @@ -483,8 +481,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwFUUSiluKo+G1spBfvC+BSOPaZT7 const challenge = 'ApFjVfRTQw5_NR4Y5poTz8ktd3ga4jI5Ly62_g97oFk' const attestationExpectations: ExpectedAttestationResult = { challenge, - origin: "https://demo.yubico.com", - factor: "either" + origin: 'https://demo.yubico.com', + factor: 'either' } const f2l = new Fido2Lib() @@ -493,10 +491,10 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwFUUSiluKo+G1spBfvC+BSOPaZT7 rawId: str2ab(credential.payload.rawId), response: { clientDataJSON: credential.payload.response.clientDataJSON, - attestationObject: credential.payload.response.attestationObject, + attestationObject: credential.payload.response.attestationObject } } - + // Act const result = await f2l.attestationResult( clientAttestationResponse, @@ -514,15 +512,15 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwFUUSiluKo+G1spBfvC+BSOPaZT7 const assertionExpectations: ExpectedAssertionResult = { challenge: verificationRequest.challenge, origin: 'https://demo.yubico.com', - factor: "either", + factor: 'either', // Get this from the log statement in the previous request publicKey: `-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEiSfmVgOyesk2SDOaPhShPbnahfrl 3Vs0iQUW6QF4IHXi6beycQU49cvsW32MNlAqXxGJ7uaXY06NOKGq1HraxQ== -----END PUBLIC KEY-----`, prevCounter: 0, - userHandle: null, - }; + userHandle: null + } const authenticatorData = FidoUtils.stringToArrayBuffer(verificationRequest.signedPayload.response.authenticatorData) console.log('authenticatorData.length', authenticatorData.byteLength) const assertionResult: AssertionResult = { @@ -537,7 +535,7 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEiSfmVgOyesk2SDOaPhShPbnahfrl } // Act - await f2l.assertionResult(assertionResult, assertionExpectations); // will throw on error + await f2l.assertionResult(assertionResult, assertionExpectations) // will throw on error // Assert }) diff --git a/test/unit/shared/kvs.test.ts b/test/unit/shared/kvs.test.ts index e00d430b..7028cec4 100644 --- a/test/unit/shared/kvs.test.ts +++ b/test/unit/shared/kvs.test.ts @@ -107,7 +107,7 @@ describe('KVS: Key Value Storage', () => { const setSpy = jest.spyOn(kvs.client, 'set').mockImplementationOnce(( _key: string, - _value: string, + _signedPayload: string, flag: string, _mode: string, _duration: number,