diff --git a/package-lock.json b/package-lock.json index e26aa36029686..6b6ab6e6e1976 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55,11 +55,14 @@ "@types/json-diff": "^0.5.1", "@types/jsonwebtoken": "^8.5.2", "@types/localtunnel": "^1.9.0", - "@types/lodash": "^4.14.182", "@types/lodash.camelcase": "^4.3.6", "@types/lodash.get": "^4.4.6", + "@types/lodash.intersection": "^4.4.7", "@types/lodash.merge": "^4.6.6", + "@types/lodash.omit": "^4.5.7", "@types/lodash.set": "^4.3.6", + "@types/lodash.split": "^4.4.7", + "@types/lodash.unset": "^4.5.7", "@types/lossless-json": "^1.0.0", "@types/luxon": "^2.0.9", "@types/mailparser": "^2.7.3", @@ -179,13 +182,15 @@ "jwks-rsa": "~1.12.1", "kafkajs": "^1.14.0", "localtunnel": "^2.0.0", - "lodash": "^4.17.21", "lodash.camelcase": "^4.3.0", "lodash.debounce": "^4.0.8", "lodash.get": "^4.4.2", + "lodash.intersection": "^4.4.0", "lodash.isequal": "^4.5.0", "lodash.merge": "^4.6.2", + "lodash.omit": "^4.5.0", "lodash.set": "^4.3.2", + "lodash.split": "^4.4.2", "lodash.unset": "^4.5.2", "lossless-json": "^1.0.4", "luxon": "^2.3.0", @@ -15035,6 +15040,14 @@ "@types/lodash": "*" } }, + "node_modules/@types/lodash.intersection": { + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/@types/lodash.intersection/-/lodash.intersection-4.4.7.tgz", + "integrity": "sha512-7ukD2s54bmRNNpiH9ApEErO4H6mB8+WmXFr/6RpP3e/n7h3UFhEJC7QwLcoWAqOrYCIRFMAAwDf3ambSsW8c5Q==", + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/lodash.merge": { "version": "4.6.7", "resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.7.tgz", @@ -15043,6 +15056,14 @@ "@types/lodash": "*" } }, + "node_modules/@types/lodash.omit": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/@types/lodash.omit/-/lodash.omit-4.5.7.tgz", + "integrity": "sha512-6q6cNg0tQ6oTWjSM+BcYMBhan54P/gLqBldG4AuXd3nKr0oeVekWNS4VrNEu3BhCSDXtGapi7zjhnna0s03KpA==", + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/lodash.set": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/@types/lodash.set/-/lodash.set-4.3.7.tgz", @@ -15051,6 +15072,22 @@ "@types/lodash": "*" } }, + "node_modules/@types/lodash.split": { + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/@types/lodash.split/-/lodash.split-4.4.7.tgz", + "integrity": "sha512-4L/89eW9ZFOkJscM/u0nRwWaN5jHK/esQ71FHuVgePAtBD9YRdYS4cM1HRyIxN3xAoRRrw1Ohf2HQjSk0zwuqA==", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/lodash.unset": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/@types/lodash.unset/-/lodash.unset-4.5.7.tgz", + "integrity": "sha512-/i371dATnLQ4tazwcX/n+rGk3M6RnMbA3lJKrKFjELicPExmZ1LcKtGfHBECuPS2TTl3yDuaFmWtmfACVuBBAQ==", + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/lossless-json": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/lossless-json/-/lossless-json-1.0.1.tgz", @@ -15228,9 +15265,9 @@ } }, "node_modules/@types/passport": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.9.tgz", - "integrity": "sha512-9+ilzUhmZQR4JP49GdC2O4UdDE3POPLwpmaTC/iLkW7l0TZCXOo1zsTnnlXPq6rP1UsUZPfbAV4IUdiwiXyC7g==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.8.tgz", + "integrity": "sha512-Gdcvis7+7G/Mobm+25AeFi+oe5teBhHzpbCOFWeN10Bj8tnoEE1L5lkraQjzmDEKkJQuM7xSJUGIFGl/giyRfQ==", "dependencies": { "@types/express": "*" } @@ -15532,9 +15569,9 @@ } }, "node_modules/@types/uglify-js": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.16.0.tgz", - "integrity": "sha512-0yeUr92L3r0GLRnBOvtYK1v2SjqMIqQDHMl7GLb+l2L8+6LSFWEEWEIgVsPdMn5ImLM8qzWT8xFPtQYpp8co0g==", + "version": "3.13.3", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.3.tgz", + "integrity": "sha512-9dmBYXt/rKxedUXfCvXSxyiPvpDXLkiRlv17DnqdhS+pRustL1967rI1jZVt1xysTO+xJGMoZzcy3cWC9+b6Tw==", "dependencies": { "source-map": "^0.6.1" } @@ -22872,20 +22909,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/bonjour": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", @@ -31022,20 +31045,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/express/node_modules/qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/ext": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", @@ -37013,9 +37022,9 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/iso-639-1": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/iso-639-1/-/iso-639-1-2.1.15.tgz", - "integrity": "sha512-7c7mBznZu2ktfvyT582E2msM+Udc1EjOyhVRE/0ZsjD9LBtWSm23h3PtiRh2a35XoUsTQQjJXaJzuLjXsOdFDg==", + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/iso-639-1/-/iso-639-1-2.1.14.tgz", + "integrity": "sha512-nekI+mmtSpYySPXIXJtWhv/s+06nAU9wQzq4QPu3YSEMmjnkOoippPY+MEdqDP0Pie8/LsOFEuPbUHslLanDag==", "engines": { "node": ">=6.0" } @@ -43086,6 +43095,11 @@ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" }, + "node_modules/lodash.intersection": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.intersection/-/lodash.intersection-4.4.0.tgz", + "integrity": "sha512-N+L0cCfnqMv6mxXtSPeKt+IavbOBBSiAEkKyLasZ8BVcP9YXQgxLO12oPR8OyURwKV8l5vJKiE1M8aS70heuMg==" + }, "node_modules/lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", @@ -43147,6 +43161,11 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "node_modules/lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==" + }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -43167,6 +43186,11 @@ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" }, + "node_modules/lodash.split": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.split/-/lodash.split-4.4.2.tgz", + "integrity": "sha512-kn1IDX0aHfg0FsnPIyxCHTamZXt3YK3aExRH1LW8YhzP6+sCldTm8+E4aIg+nSmM6R4eqdWGrXWtfYI961bwIw==" + }, "node_modules/lodash.template": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", @@ -43999,9 +44023,9 @@ "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" }, "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "engines": { "node": ">= 0.8" } @@ -54999,9 +55023,9 @@ } }, "node_modules/terser-webpack-plugin/node_modules/terser": { - "version": "5.14.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.1.tgz", - "integrity": "sha512-+ahUAE+iheqBTDxXhTisdA8hgvbEG1hHOQ9xmNjeUJSoi6DU/gMrKNcfZjHkyY6Alnuyc+ikYJaxxfHkT3+WuQ==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.0.tgz", + "integrity": "sha512-JC6qfIEkPBd9j1SMO3Pfn+A6w2kQV54tv+ABQLgZr7dA3k/DL/OBoYSWxzVpZev3J+bUHXfr55L8Mox7AaNo6g==", "dependencies": { "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", @@ -60520,9 +60544,9 @@ } }, "node_modules/xss": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.13.tgz", - "integrity": "sha512-clu7dxTm1e8Mo5fz3n/oW3UCXBfV89xZ72jM8yzo1vR/pIS0w3sgB3XV2H8Vm6zfGnHL0FzvLJPJEBhd86/z4Q==", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.12.tgz", + "integrity": "sha512-8pXgz5BUUfKMrb81tmcbvLNA97ab4d6HdoBHYF5XYHa8oarc2s64hF+oqI4FhBHVBWvEM1wHGy+vqt8kZhCaNw==", "dependencies": { "commander": "^2.20.3", "cssfilter": "0.0.10" @@ -72931,6 +72955,14 @@ "@types/lodash": "*" } }, + "@types/lodash.intersection": { + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/@types/lodash.intersection/-/lodash.intersection-4.4.7.tgz", + "integrity": "sha512-7ukD2s54bmRNNpiH9ApEErO4H6mB8+WmXFr/6RpP3e/n7h3UFhEJC7QwLcoWAqOrYCIRFMAAwDf3ambSsW8c5Q==", + "requires": { + "@types/lodash": "*" + } + }, "@types/lodash.merge": { "version": "4.6.7", "resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.7.tgz", @@ -72939,6 +72971,14 @@ "@types/lodash": "*" } }, + "@types/lodash.omit": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/@types/lodash.omit/-/lodash.omit-4.5.7.tgz", + "integrity": "sha512-6q6cNg0tQ6oTWjSM+BcYMBhan54P/gLqBldG4AuXd3nKr0oeVekWNS4VrNEu3BhCSDXtGapi7zjhnna0s03KpA==", + "requires": { + "@types/lodash": "*" + } + }, "@types/lodash.set": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/@types/lodash.set/-/lodash.set-4.3.7.tgz", @@ -72947,6 +72987,22 @@ "@types/lodash": "*" } }, + "@types/lodash.split": { + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/@types/lodash.split/-/lodash.split-4.4.7.tgz", + "integrity": "sha512-4L/89eW9ZFOkJscM/u0nRwWaN5jHK/esQ71FHuVgePAtBD9YRdYS4cM1HRyIxN3xAoRRrw1Ohf2HQjSk0zwuqA==", + "requires": { + "@types/lodash": "*" + } + }, + "@types/lodash.unset": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/@types/lodash.unset/-/lodash.unset-4.5.7.tgz", + "integrity": "sha512-/i371dATnLQ4tazwcX/n+rGk3M6RnMbA3lJKrKFjELicPExmZ1LcKtGfHBECuPS2TTl3yDuaFmWtmfACVuBBAQ==", + "requires": { + "@types/lodash": "*" + } + }, "@types/lossless-json": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/lossless-json/-/lossless-json-1.0.1.tgz", @@ -73120,9 +73176,9 @@ } }, "@types/passport": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.9.tgz", - "integrity": "sha512-9+ilzUhmZQR4JP49GdC2O4UdDE3POPLwpmaTC/iLkW7l0TZCXOo1zsTnnlXPq6rP1UsUZPfbAV4IUdiwiXyC7g==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.8.tgz", + "integrity": "sha512-Gdcvis7+7G/Mobm+25AeFi+oe5teBhHzpbCOFWeN10Bj8tnoEE1L5lkraQjzmDEKkJQuM7xSJUGIFGl/giyRfQ==", "requires": { "@types/express": "*" } @@ -73423,9 +73479,9 @@ } }, "@types/uglify-js": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.16.0.tgz", - "integrity": "sha512-0yeUr92L3r0GLRnBOvtYK1v2SjqMIqQDHMl7GLb+l2L8+6LSFWEEWEIgVsPdMn5ImLM8qzWT8xFPtQYpp8co0g==", + "version": "3.13.3", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.3.tgz", + "integrity": "sha512-9dmBYXt/rKxedUXfCvXSxyiPvpDXLkiRlv17DnqdhS+pRustL1967rI1jZVt1xysTO+xJGMoZzcy3cWC9+b6Tw==", "requires": { "source-map": "^0.6.1" } @@ -79200,14 +79256,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "requires": { - "side-channel": "^1.0.4" - } } } }, @@ -85492,14 +85540,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "requires": { - "side-channel": "^1.0.4" - } } } }, @@ -90156,9 +90196,9 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "iso-639-1": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/iso-639-1/-/iso-639-1-2.1.15.tgz", - "integrity": "sha512-7c7mBznZu2ktfvyT582E2msM+Udc1EjOyhVRE/0ZsjD9LBtWSm23h3PtiRh2a35XoUsTQQjJXaJzuLjXsOdFDg==" + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/iso-639-1/-/iso-639-1-2.1.14.tgz", + "integrity": "sha512-nekI+mmtSpYySPXIXJtWhv/s+06nAU9wQzq4QPu3YSEMmjnkOoippPY+MEdqDP0Pie8/LsOFEuPbUHslLanDag==" }, "isobject": { "version": "3.0.1", @@ -94926,6 +94966,11 @@ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" }, + "lodash.intersection": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.intersection/-/lodash.intersection-4.4.0.tgz", + "integrity": "sha512-N+L0cCfnqMv6mxXtSPeKt+IavbOBBSiAEkKyLasZ8BVcP9YXQgxLO12oPR8OyURwKV8l5vJKiE1M8aS70heuMg==" + }, "lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", @@ -94987,6 +95032,11 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==" + }, "lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -95007,6 +95057,11 @@ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" }, + "lodash.split": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.split/-/lodash.split-4.4.2.tgz", + "integrity": "sha512-kn1IDX0aHfg0FsnPIyxCHTamZXt3YK3aExRH1LW8YhzP6+sCldTm8+E4aIg+nSmM6R4eqdWGrXWtfYI961bwIw==" + }, "lodash.template": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", @@ -95710,9 +95765,8 @@ "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" }, "media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==" + "version": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" }, "memfs": { "version": "3.4.7", @@ -104428,9 +104482,9 @@ } }, "terser": { - "version": "5.14.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.1.tgz", - "integrity": "sha512-+ahUAE+iheqBTDxXhTisdA8hgvbEG1hHOQ9xmNjeUJSoi6DU/gMrKNcfZjHkyY6Alnuyc+ikYJaxxfHkT3+WuQ==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.0.tgz", + "integrity": "sha512-JC6qfIEkPBd9j1SMO3Pfn+A6w2kQV54tv+ABQLgZr7dA3k/DL/OBoYSWxzVpZev3J+bUHXfr55L8Mox7AaNo6g==", "requires": { "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", @@ -108643,9 +108697,9 @@ "integrity": "sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA==" }, "xss": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.13.tgz", - "integrity": "sha512-clu7dxTm1e8Mo5fz3n/oW3UCXBfV89xZ72jM8yzo1vR/pIS0w3sgB3XV2H8Vm6zfGnHL0FzvLJPJEBhd86/z4Q==", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.12.tgz", + "integrity": "sha512-8pXgz5BUUfKMrb81tmcbvLNA97ab4d6HdoBHYF5XYHa8oarc2s64hF+oqI4FhBHVBWvEM1wHGy+vqt8kZhCaNw==", "requires": { "commander": "^2.20.3", "cssfilter": "0.0.10" diff --git a/packages/cli/package.json b/packages/cli/package.json index 6833f64041761..df00615c0b360 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -70,7 +70,12 @@ "@types/express": "^4.17.6", "@types/jest": "^27.4.0", "@types/localtunnel": "^1.9.0", - "@types/lodash": "^4.14.182", + "@types/lodash.get": "^4.4.6", + "@types/lodash.merge": "^4.6.6", + "@types/lodash.omit": "^4.5.7", + "@types/lodash.set": "^4.3.6", + "@types/lodash.split": "^4.4.7", + "@types/lodash.unset": "^4.5.7", "@types/node": "^16.11.22", "@types/open": "^6.1.0", "@types/parseurl": "^1.3.1", @@ -100,6 +105,7 @@ "@rudderstack/rudder-sdk-node": "1.0.6", "@types/json-diff": "^0.5.1", "@types/jsonwebtoken": "^8.5.2", + "@types/lodash.intersection": "^4.4.7", "@types/shelljs": "^0.8.11", "@types/swagger-ui-express": "^4.1.3", "@types/yamljs": "^0.2.31", @@ -130,7 +136,13 @@ "jsonwebtoken": "^8.5.1", "jwks-rsa": "~1.12.1", "localtunnel": "^2.0.0", - "lodash": "^4.17.21", + "lodash.get": "^4.4.2", + "lodash.intersection": "^4.4.0", + "lodash.merge": "^4.6.2", + "lodash.omit": "^4.5.0", + "lodash.set": "^4.3.2", + "lodash.split": "^4.4.2", + "lodash.unset": "^4.5.2", "mysql2": "~2.3.0", "n8n-core": "~0.126.0", "n8n-editor-ui": "~0.152.0", diff --git a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts index 440a83b615a9a..494e7389bf5ad 100644 --- a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts +++ b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts @@ -1,5 +1,5 @@ import { FindManyOptions, In, UpdateResult } from 'typeorm'; -import { intersection } from 'lodash'; +import intersection from 'lodash.intersection'; import type { INode } from 'n8n-workflow'; import { Db } from '../../../..'; diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 435992cb455ab..ec677a713fbcf 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -159,6 +159,7 @@ import { ExecutionEntity } from './databases/entities/ExecutionEntity'; import { SharedWorkflow } from './databases/entities/SharedWorkflow'; import { AUTH_COOKIE_NAME, RESPONSE_ERROR_MESSAGES } from './constants'; import { credentialsController } from './api/credentials.api'; +import { oauth2CredentialController } from './api/oauth2Credential.api'; import { getInstanceBaseUrl, isEmailSetUp, @@ -1953,302 +1954,10 @@ class App { ); // ---------------------------------------- - // OAuth2-Credential/Auth + // OAuth2-Credential // ---------------------------------------- - // Authorize OAuth Data - this.app.get( - `/${this.restEndpoint}/oauth2-credential/auth`, - ResponseHelper.send(async (req: OAuthRequest.OAuth2Credential.Auth): Promise => { - const { id: credentialId } = req.query; - - if (!credentialId) { - throw new ResponseHelper.ResponseError( - 'Required credential ID is missing', - undefined, - 400, - ); - } - - const credential = await getCredentialForUser(credentialId, req.user); - - if (!credential) { - LoggerProxy.error('Failed to authorize OAuth2 due to lack of permissions', { - userId: req.user.id, - credentialId, - }); - throw new ResponseHelper.ResponseError( - RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL, - undefined, - 404, - ); - } - - let encryptionKey: string; - try { - encryptionKey = await UserSettings.getEncryptionKey(); - } catch (error) { - throw new ResponseHelper.ResponseError(error.message, undefined, 500); - } - - const mode: WorkflowExecuteMode = 'internal'; - const timezone = config.getEnv('generic.timezone'); - const credentialsHelper = new CredentialsHelper(encryptionKey); - const decryptedDataOriginal = await credentialsHelper.getDecrypted( - credential as INodeCredentialsDetails, - credential.type, - mode, - timezone, - true, - ); - - const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites( - decryptedDataOriginal, - credential.type, - mode, - timezone, - ); - - const token = new csrf(); - // Generate a CSRF prevention token and send it as a OAuth2 state stringma/ERR - const csrfSecret = token.secretSync(); - const state = { - token: token.create(csrfSecret), - cid: req.query.id, - }; - const stateEncodedStr = Buffer.from(JSON.stringify(state)).toString('base64'); - - const oAuthOptions: clientOAuth2.Options = { - clientId: _.get(oauthCredentials, 'clientId') as string, - clientSecret: _.get(oauthCredentials, 'clientSecret', '') as string, - accessTokenUri: _.get(oauthCredentials, 'accessTokenUrl', '') as string, - authorizationUri: _.get(oauthCredentials, 'authUrl', '') as string, - redirectUri: `${WebhookHelpers.getWebhookBaseUrl()}${ - this.restEndpoint - }/oauth2-credential/callback`, - scopes: _.split(_.get(oauthCredentials, 'scope', 'openid,') as string, ','), - state: stateEncodedStr, - }; - - await this.externalHooks.run('oauth2.authenticate', [oAuthOptions]); - - const oAuthObj = new clientOAuth2(oAuthOptions); - - // Encrypt the data - const credentials = new Credentials( - credential as INodeCredentialsDetails, - credential.type, - credential.nodesAccess, - ); - decryptedDataOriginal.csrfSecret = csrfSecret; - - credentials.setData(decryptedDataOriginal, encryptionKey); - const newCredentialsData = credentials.getDataToSave() as unknown as ICredentialsDb; - - // Add special database related data - newCredentialsData.updatedAt = this.getCurrentDate(); - - // Update the credentials in DB - await Db.collections.Credentials.update(req.query.id as string, newCredentialsData); - - const authQueryParameters = _.get(oauthCredentials, 'authQueryParameters', '') as string; - let returnUri = oAuthObj.code.getUri(); - - // if scope uses comma, change it as the library always return then with spaces - if ((_.get(oauthCredentials, 'scope') as string).includes(',')) { - const data = querystring.parse(returnUri.split('?')[1]); - data.scope = _.get(oauthCredentials, 'scope') as string; - returnUri = `${_.get(oauthCredentials, 'authUrl', '')}?${querystring.stringify(data)}`; - } - - if (authQueryParameters) { - returnUri += `&${authQueryParameters}`; - } - - LoggerProxy.verbose('OAuth2 authentication successful for new credential', { - userId: req.user.id, - credentialId, - }); - return returnUri; - }), - ); - - // ---------------------------------------- - // OAuth2-Credential/Callback - // ---------------------------------------- - - // Verify and store app code. Generate access tokens and store for respective credential. - this.app.get( - `/${this.restEndpoint}/oauth2-credential/callback`, - async (req: OAuthRequest.OAuth2Credential.Callback, res: express.Response) => { - try { - // realmId it's currently just use for the quickbook OAuth2 flow - const { code, state: stateEncoded } = req.query; - - if (!code || !stateEncoded) { - const errorResponse = new ResponseHelper.ResponseError( - `Insufficient parameters for OAuth2 callback. Received following query parameters: ${JSON.stringify( - req.query, - )}`, - undefined, - 503, - ); - return ResponseHelper.sendErrorResponse(res, errorResponse); - } - - let state; - try { - state = JSON.parse(Buffer.from(stateEncoded, 'base64').toString()); - } catch (error) { - const errorResponse = new ResponseHelper.ResponseError( - 'Invalid state format returned', - undefined, - 503, - ); - return ResponseHelper.sendErrorResponse(res, errorResponse); - } - - const credential = await getCredentialWithoutUser(state.cid); - - if (!credential) { - LoggerProxy.error('OAuth2 callback failed because of insufficient permissions', { - userId: req.user?.id, - credentialId: state.cid, - }); - const errorResponse = new ResponseHelper.ResponseError( - RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL, - undefined, - 404, - ); - return ResponseHelper.sendErrorResponse(res, errorResponse); - } - - let encryptionKey: string; - try { - encryptionKey = await UserSettings.getEncryptionKey(); - } catch (error) { - throw new ResponseHelper.ResponseError(error.message, undefined, 500); - } - - const mode: WorkflowExecuteMode = 'internal'; - const timezone = config.getEnv('generic.timezone'); - const credentialsHelper = new CredentialsHelper(encryptionKey); - const decryptedDataOriginal = await credentialsHelper.getDecrypted( - credential as INodeCredentialsDetails, - credential.type, - mode, - timezone, - true, - ); - const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites( - decryptedDataOriginal, - credential.type, - mode, - timezone, - ); - - const token = new csrf(); - if ( - decryptedDataOriginal.csrfSecret === undefined || - !token.verify(decryptedDataOriginal.csrfSecret as string, state.token) - ) { - LoggerProxy.debug('OAuth2 callback state is invalid', { - userId: req.user?.id, - credentialId: state.cid, - }); - const errorResponse = new ResponseHelper.ResponseError( - 'The OAuth2 callback state is invalid!', - undefined, - 404, - ); - return ResponseHelper.sendErrorResponse(res, errorResponse); - } - - let options = {}; - - const oAuth2Parameters = { - clientId: _.get(oauthCredentials, 'clientId') as string, - clientSecret: _.get(oauthCredentials, 'clientSecret', '') as string | undefined, - accessTokenUri: _.get(oauthCredentials, 'accessTokenUrl', '') as string, - authorizationUri: _.get(oauthCredentials, 'authUrl', '') as string, - redirectUri: `${WebhookHelpers.getWebhookBaseUrl()}${ - this.restEndpoint - }/oauth2-credential/callback`, - scopes: _.split(_.get(oauthCredentials, 'scope', 'openid,') as string, ','), - }; - - if ((_.get(oauthCredentials, 'authentication', 'header') as string) === 'body') { - options = { - body: { - client_id: _.get(oauthCredentials, 'clientId') as string, - client_secret: _.get(oauthCredentials, 'clientSecret', '') as string, - }, - }; - delete oAuth2Parameters.clientSecret; - } - - await this.externalHooks.run('oauth2.callback', [oAuth2Parameters]); - - const oAuthObj = new clientOAuth2(oAuth2Parameters); - - const queryParameters = req.originalUrl.split('?').splice(1, 1).join(''); - - const oauthToken = await oAuthObj.code.getToken( - `${oAuth2Parameters.redirectUri}?${queryParameters}`, - options, - ); - - if (Object.keys(req.query).length > 2) { - _.set(oauthToken.data, 'callbackQueryString', _.omit(req.query, 'state', 'code')); - } - - if (oauthToken === undefined) { - LoggerProxy.error('OAuth2 callback failed: unable to get access tokens', { - userId: req.user?.id, - credentialId: state.cid, - }); - const errorResponse = new ResponseHelper.ResponseError( - 'Unable to get access tokens!', - undefined, - 404, - ); - return ResponseHelper.sendErrorResponse(res, errorResponse); - } - - if (decryptedDataOriginal.oauthTokenData) { - // Only overwrite supplied data as some providers do for example just return the - // refresh_token on the very first request and not on subsequent ones. - Object.assign(decryptedDataOriginal.oauthTokenData, oauthToken.data); - } else { - // No data exists so simply set - decryptedDataOriginal.oauthTokenData = oauthToken.data; - } - - _.unset(decryptedDataOriginal, 'csrfSecret'); - - const credentials = new Credentials( - credential as INodeCredentialsDetails, - credential.type, - credential.nodesAccess, - ); - credentials.setData(decryptedDataOriginal, encryptionKey); - const newCredentialsData = credentials.getDataToSave() as unknown as ICredentialsDb; - // Add special database related data - newCredentialsData.updatedAt = this.getCurrentDate(); - // Save the credentials in DB - await Db.collections.Credentials.update(state.cid, newCredentialsData); - LoggerProxy.verbose('OAuth2 callback successful for new credential', { - userId: req.user?.id, - credentialId: state.cid, - }); - - res.sendFile(pathResolve(__dirname, '../../templates/oauth-callback.html')); - } catch (error) { - // Error response - return ResponseHelper.sendErrorResponse(res, error); - } - }, - ); + this.app.use(`/${this.restEndpoint}/oauth2-credential`, oauth2CredentialController); // ---------------------------------------- // Executions diff --git a/packages/cli/src/api/oauth2Credential.api.ts b/packages/cli/src/api/oauth2Credential.api.ts new file mode 100644 index 0000000000000..2db0b10d73c73 --- /dev/null +++ b/packages/cli/src/api/oauth2Credential.api.ts @@ -0,0 +1,344 @@ +/* eslint-disable import/no-cycle */ +import ClientOAuth2 from 'client-oauth2'; +import Csrf from 'csrf'; +import express from 'express'; +import get from 'lodash.get'; +import omit from 'lodash.omit'; +import set from 'lodash.set'; +import split from 'lodash.split'; +import unset from 'lodash.unset'; +import { Credentials, UserSettings } from 'n8n-core'; +import { + LoggerProxy, + WorkflowExecuteMode, + INodeCredentialsDetails, + ICredentialsEncrypted, + IDataObject, +} from 'n8n-workflow'; +import { resolve as pathResolve } from 'path'; +import querystring from 'querystring'; + +import { Db, ICredentialsDb, ResponseHelper, WebhookHelpers } from '..'; +import { RESPONSE_ERROR_MESSAGES } from '../constants'; +import { + CredentialsHelper, + getCredentialForUser, + getCredentialWithoutUser, +} from '../CredentialsHelper'; +import { getLogger } from '../Logger'; +import { OAuthRequest } from '../requests'; +import { externalHooks } from '../Server'; +import config from '../../config'; + +export const oauth2CredentialController = express.Router(); + +/** + * Initialize Logger if needed + */ +oauth2CredentialController.use((req, res, next) => { + try { + LoggerProxy.getInstance(); + } catch (error) { + LoggerProxy.init(getLogger()); + } + next(); +}); + +const restEndpoint = config.getEnv('endpoints.rest'); + +/** + * GET /oauth2-credential/auth + * + * Authorize OAuth Data + */ +oauth2CredentialController.get( + '/auth', + ResponseHelper.send(async (req: OAuthRequest.OAuth1Credential.Auth): Promise => { + const { id: credentialId } = req.query; + + if (!credentialId) { + throw new ResponseHelper.ResponseError('Required credential ID is missing', undefined, 400); + } + + const credential = await getCredentialForUser(credentialId, req.user); + + if (!credential) { + LoggerProxy.error('Failed to authorize OAuth2 due to lack of permissions', { + userId: req.user.id, + credentialId, + }); + throw new ResponseHelper.ResponseError(RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL, undefined, 404); + } + + let encryptionKey: string; + try { + encryptionKey = await UserSettings.getEncryptionKey(); + } catch (error) { + throw new ResponseHelper.ResponseError((error as Error).message, undefined, 500); + } + + const mode: WorkflowExecuteMode = 'internal'; + const timezone = config.getEnv('generic.timezone'); + const credentialsHelper = new CredentialsHelper(encryptionKey); + const decryptedDataOriginal = await credentialsHelper.getDecrypted( + credential as INodeCredentialsDetails, + (credential as unknown as ICredentialsEncrypted).type, + mode, + timezone, + true, + ); + + const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites( + decryptedDataOriginal, + (credential as unknown as ICredentialsEncrypted).type, + mode, + timezone, + ); + + const token = new Csrf(); + // Generate a CSRF prevention token and send it as a OAuth2 state stringma/ERR + const csrfSecret = token.secretSync(); + const state = { + token: token.create(csrfSecret), + cid: req.query.id, + }; + const stateEncodedStr = Buffer.from(JSON.stringify(state)).toString('base64'); + + const oAuthOptions: ClientOAuth2.Options = { + clientId: get(oauthCredentials, 'clientId') as string, + clientSecret: get(oauthCredentials, 'clientSecret', '') as string, + accessTokenUri: get(oauthCredentials, 'accessTokenUrl', '') as string, + authorizationUri: get(oauthCredentials, 'authUrl', '') as string, + redirectUri: `${WebhookHelpers.getWebhookBaseUrl()}${restEndpoint}/oauth2-credential/callback`, + scopes: split(get(oauthCredentials, 'scope', 'openid,') as string, ','), + state: stateEncodedStr, + }; + + await externalHooks.run('oauth2.authenticate', [oAuthOptions]); + + const oAuthObj = new ClientOAuth2(oAuthOptions); + + // Encrypt the data + const credentials = new Credentials( + credential as INodeCredentialsDetails, + (credential as unknown as ICredentialsEncrypted).type, + (credential as unknown as ICredentialsEncrypted).nodesAccess, + ); + decryptedDataOriginal.csrfSecret = csrfSecret; + + credentials.setData(decryptedDataOriginal, encryptionKey); + const newCredentialsData = credentials.getDataToSave() as unknown as ICredentialsDb; + + // Add special database related data + newCredentialsData.updatedAt = new Date(); + + // Update the credentials in DB + await Db.collections.Credentials.update(req.query.id, newCredentialsData); + + const authQueryParameters = get(oauthCredentials, 'authQueryParameters', '') as string; + let returnUri = oAuthObj.code.getUri(); + + // if scope uses comma, change it as the library always return then with spaces + if ((get(oauthCredentials, 'scope') as string).includes(',')) { + const data = querystring.parse(returnUri.split('?')[1]); + data.scope = get(oauthCredentials, 'scope') as string; + returnUri = `${get(oauthCredentials, 'authUrl', '') as string}?${querystring.stringify( + data, + )}`; + } + + if (authQueryParameters) { + returnUri += `&${authQueryParameters}`; + } + + LoggerProxy.verbose('OAuth2 authentication successful for new credential', { + userId: req.user.id, + credentialId, + }); + return returnUri; + }), +); + +/** + * GET /oauth2-credential/callback + * + * Verify and store app code. Generate access tokens and store for respective credential. + */ + +oauth2CredentialController.get( + '/callback', + async (req: OAuthRequest.OAuth2Credential.Callback, res: express.Response) => { + try { + // realmId it's currently just use for the quickbook OAuth2 flow + const { code, state: stateEncoded } = req.query; + + if (!code || !stateEncoded) { + const errorResponse = new ResponseHelper.ResponseError( + `Insufficient parameters for OAuth2 callback. Received following query parameters: ${JSON.stringify( + req.query, + )}`, + undefined, + 503, + ); + return ResponseHelper.sendErrorResponse(res, errorResponse); + } + + let state; + try { + state = JSON.parse(Buffer.from(stateEncoded, 'base64').toString()) as { + cid: string; + token: string; + }; + } catch (error) { + const errorResponse = new ResponseHelper.ResponseError( + 'Invalid state format returned', + undefined, + 503, + ); + return ResponseHelper.sendErrorResponse(res, errorResponse); + } + + const credential = await getCredentialWithoutUser(state.cid); + + if (!credential) { + LoggerProxy.error('OAuth2 callback failed because of insufficient permissions', { + userId: req.user?.id, + credentialId: state.cid, + }); + const errorResponse = new ResponseHelper.ResponseError( + RESPONSE_ERROR_MESSAGES.NO_CREDENTIAL, + undefined, + 404, + ); + return ResponseHelper.sendErrorResponse(res, errorResponse); + } + + let encryptionKey: string; + try { + encryptionKey = await UserSettings.getEncryptionKey(); + } catch (error) { + throw new ResponseHelper.ResponseError( + (error as IDataObject).message as string, + undefined, + 500, + ); + } + + const mode: WorkflowExecuteMode = 'internal'; + const timezone = config.getEnv('generic.timezone'); + const credentialsHelper = new CredentialsHelper(encryptionKey); + const decryptedDataOriginal = await credentialsHelper.getDecrypted( + credential as INodeCredentialsDetails, + (credential as unknown as ICredentialsEncrypted).type, + mode, + timezone, + true, + ); + const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites( + decryptedDataOriginal, + (credential as unknown as ICredentialsEncrypted).type, + mode, + timezone, + ); + + const token = new Csrf(); + if ( + decryptedDataOriginal.csrfSecret === undefined || + !token.verify(decryptedDataOriginal.csrfSecret as string, state.token) + ) { + LoggerProxy.debug('OAuth2 callback state is invalid', { + userId: req.user?.id, + credentialId: state.cid, + }); + const errorResponse = new ResponseHelper.ResponseError( + 'The OAuth2 callback state is invalid!', + undefined, + 404, + ); + return ResponseHelper.sendErrorResponse(res, errorResponse); + } + + let options = {}; + + const oAuth2Parameters = { + clientId: get(oauthCredentials, 'clientId') as string, + clientSecret: get(oauthCredentials, 'clientSecret', '') as string | undefined, + accessTokenUri: get(oauthCredentials, 'accessTokenUrl', '') as string, + authorizationUri: get(oauthCredentials, 'authUrl', '') as string, + redirectUri: `${WebhookHelpers.getWebhookBaseUrl()}${restEndpoint}/oauth2-credential/callback`, + scopes: split(get(oauthCredentials, 'scope', 'openid,') as string, ','), + }; + + if ((get(oauthCredentials, 'authentication', 'header') as string) === 'body') { + options = { + body: { + client_id: get(oauthCredentials, 'clientId') as string, + client_secret: get(oauthCredentials, 'clientSecret', '') as string, + }, + }; + delete oAuth2Parameters.clientSecret; + } + + await externalHooks.run('oauth2.callback', [oAuth2Parameters]); + + const oAuthObj = new ClientOAuth2(oAuth2Parameters); + + const queryParameters = req.originalUrl.split('?').splice(1, 1).join(''); + + const oauthToken = await oAuthObj.code.getToken( + `${oAuth2Parameters.redirectUri}?${queryParameters}`, + options, + ); + + if (Object.keys(req.query).length > 2) { + set(oauthToken.data, 'callbackQueryString', omit(req.query, 'state', 'code')); + } + + if (oauthToken === undefined) { + LoggerProxy.error('OAuth2 callback failed: unable to get access tokens', { + userId: req.user?.id, + credentialId: state.cid, + }); + const errorResponse = new ResponseHelper.ResponseError( + 'Unable to get access tokens!', + undefined, + 404, + ); + return ResponseHelper.sendErrorResponse(res, errorResponse); + } + + if (decryptedDataOriginal.oauthTokenData) { + // Only overwrite supplied data as some providers do for example just return the + // refresh_token on the very first request and not on subsequent ones. + Object.assign(decryptedDataOriginal.oauthTokenData, oauthToken.data); + } else { + // No data exists so simply set + decryptedDataOriginal.oauthTokenData = oauthToken.data; + } + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + unset(decryptedDataOriginal, 'csrfSecret'); + + const credentials = new Credentials( + credential as INodeCredentialsDetails, + (credential as unknown as ICredentialsEncrypted).type, + (credential as unknown as ICredentialsEncrypted).nodesAccess, + ); + credentials.setData(decryptedDataOriginal, encryptionKey); + const newCredentialsData = credentials.getDataToSave() as unknown as ICredentialsDb; + // Add special database related data + newCredentialsData.updatedAt = new Date(); + // Save the credentials in DB + await Db.collections.Credentials.update(state.cid, newCredentialsData); + LoggerProxy.verbose('OAuth2 callback successful for new credential', { + userId: req.user?.id, + credentialId: state.cid, + }); + + return res.sendFile(pathResolve(__dirname, '../../../templates/oauth-callback.html')); + } catch (error) { + // Error response + return ResponseHelper.sendErrorResponse(res, error); + } + }, +);