diff --git a/package-lock.json b/package-lock.json
index ef720728a1..10d30e6f47 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -47,7 +47,6 @@
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
"integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
- "dev": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.0",
"@jridgewell/trace-mapping": "^0.3.9"
@@ -794,7 +793,6 @@
"version": "7.23.5",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz",
"integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==",
- "dev": true,
"engines": {
"node": ">=6.9.0"
}
@@ -803,7 +801,6 @@
"version": "7.23.9",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz",
"integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==",
- "dev": true,
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.23.5",
@@ -870,7 +867,6 @@
"version": "7.23.6",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz",
"integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==",
- "dev": true,
"dependencies": {
"@babel/compat-data": "^7.23.5",
"@babel/helper-validator-option": "^7.23.5",
@@ -886,7 +882,6 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
- "dev": true,
"dependencies": {
"yallist": "^3.0.2"
}
@@ -894,8 +889,7 @@
"node_modules/@babel/helper-compilation-targets/node_modules/yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
- "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
- "dev": true
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
},
"node_modules/@babel/helper-create-class-features-plugin": {
"version": "7.23.10",
@@ -1011,7 +1005,6 @@
"version": "7.23.3",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz",
"integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==",
- "dev": true,
"dependencies": {
"@babel/helper-environment-visitor": "^7.22.20",
"@babel/helper-module-imports": "^7.22.15",
@@ -1084,7 +1077,6 @@
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz",
"integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==",
- "dev": true,
"dependencies": {
"@babel/types": "^7.22.5"
},
@@ -1135,7 +1127,6 @@
"version": "7.23.5",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz",
"integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==",
- "dev": true,
"engines": {
"node": ">=6.9.0"
}
@@ -1158,7 +1149,6 @@
"version": "7.23.9",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.9.tgz",
"integrity": "sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==",
- "dev": true,
"dependencies": {
"@babel/template": "^7.23.9",
"@babel/traverse": "^7.23.9",
@@ -4529,6 +4519,23 @@
"node": ">=12"
}
},
+ "node_modules/@n8n/vm2": {
+ "version": "3.9.23",
+ "resolved": "https://registry.npmjs.org/@n8n/vm2/-/vm2-3.9.23.tgz",
+ "integrity": "sha512-yu+It+L89uljQsCJ2e9cQaXzoXJe9bU69QQIoWUOcUw0u5Zon37DuB7bdNNsjKS1ZdFD+fBWCQpq/FkqHsSjXQ==",
+ "peer": true,
+ "dependencies": {
+ "acorn": "^8.7.0",
+ "acorn-walk": "^8.2.0"
+ },
+ "bin": {
+ "vm2": "bin/vm2"
+ },
+ "engines": {
+ "node": ">=18.10",
+ "pnpm": ">=8.6.12"
+ }
+ },
"node_modules/@next/env": {
"version": "12.3.3",
"resolved": "https://registry.npmjs.org/@next/env/-/env-12.3.3.tgz",
@@ -6309,6 +6316,17 @@
"node": ">= 0.6"
}
},
+ "node_modules/acorn": {
+ "version": "8.11.3",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
+ "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/acorn-import-assertions": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz",
@@ -6318,6 +6336,14 @@
"acorn": "^8"
}
},
+ "node_modules/acorn-walk": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
+ "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/agent-base": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz",
@@ -7401,7 +7427,6 @@
"version": "4.22.3",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.3.tgz",
"integrity": "sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -7522,6 +7547,11 @@
"node": ">=0.4.0"
}
},
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
+ },
"node_modules/buffer-fill": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
@@ -8428,8 +8458,7 @@
"node_modules/convert-source-map": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
- "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
- "dev": true
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="
},
"node_modules/cookie": {
"version": "0.6.0",
@@ -9438,6 +9467,14 @@
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg=="
},
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -9691,8 +9728,7 @@
"node_modules/electron-to-chromium": {
"version": "1.4.667",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.667.tgz",
- "integrity": "sha512-66L3pLlWhTNVUhnmSA5+qDM3fwnXsM6KAqE36e2w4KN0g6pkEtlT5bs41FQtQwVwKnfhNBXiWRLPs30HSxd7Kw==",
- "dev": true
+ "integrity": "sha512-66L3pLlWhTNVUhnmSA5+qDM3fwnXsM6KAqE36e2w4KN0g6pkEtlT5bs41FQtQwVwKnfhNBXiWRLPs30HSxd7Kw=="
},
"node_modules/electron-util": {
"version": "0.17.2",
@@ -10816,7 +10852,6 @@
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
- "dev": true,
"engines": {
"node": ">=6.9.0"
}
@@ -13661,6 +13696,41 @@
"node": ">=12.0.0"
}
},
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
+ "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
+ "dependencies": {
+ "jws": "^3.2.2",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/jsonwebtoken/node_modules/semver": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
+ "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/jsprim": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz",
@@ -13701,6 +13771,25 @@
"node": "*"
}
},
+ "node_modules/jwa": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+ "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "dependencies": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "dependencies": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/kew": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz",
@@ -13955,12 +14044,47 @@
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
"dev": true
},
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
+ },
+ "node_modules/lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
+ },
+ "node_modules/lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
+ },
+ "node_modules/lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
+ },
+ "node_modules/lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
+ },
"node_modules/lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
"integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
"dev": true
},
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
+ },
"node_modules/lodash.uniq": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
@@ -14694,8 +14818,7 @@
"node_modules/node-releases": {
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
- "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
- "dev": true
+ "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw=="
},
"node_modules/node-vault": {
"version": "0.10.2",
@@ -17947,7 +18070,6 @@
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "devOptional": true,
"bin": {
"semver": "bin/semver.js"
}
@@ -19204,18 +19326,6 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
- "node_modules/terser/node_modules/acorn": {
- "version": "8.11.3",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
- "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
- "dev": true,
- "bin": {
- "acorn": "bin/acorn"
- },
- "engines": {
- "node": ">=0.4.0"
- }
- },
"node_modules/terser/node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
@@ -19727,7 +19837,6 @@
"version": "1.0.13",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
"integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -20024,25 +20133,6 @@
"node": ">=6.0"
}
},
- "node_modules/vm2/node_modules/acorn": {
- "version": "8.11.3",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
- "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
- "bin": {
- "acorn": "bin/acorn"
- },
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/vm2/node_modules/acorn-walk": {
- "version": "8.3.2",
- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
- "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
- "engines": {
- "node": ">=0.4.0"
- }
- },
"node_modules/vscode-languageserver-types": {
"version": "3.17.5",
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
@@ -20242,18 +20332,6 @@
"node": ">=10.13.0"
}
},
- "node_modules/webpack/node_modules/acorn": {
- "version": "8.11.3",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
- "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
- "dev": true,
- "bin": {
- "acorn": "bin/acorn"
- },
- "engines": {
- "node": ">=0.4.0"
- }
- },
"node_modules/webpack/node_modules/schema-utils": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
@@ -21073,6 +21151,7 @@
"express-xml-bodyparser": "^0.3.0",
"http-proxy": "^1.18.1",
"js-yaml": "^4.1.0",
+ "jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21",
"multer": "^1.4.5-lts.1"
}
diff --git a/packages/bruno-app/src/components/RequestPane/Auth/AuthMode/index.js b/packages/bruno-app/src/components/RequestPane/Auth/AuthMode/index.js
index 8eb4fee90f..35cf3a47d8 100644
--- a/packages/bruno-app/src/components/RequestPane/Auth/AuthMode/index.js
+++ b/packages/bruno-app/src/components/RequestPane/Auth/AuthMode/index.js
@@ -71,6 +71,15 @@ const AuthMode = ({ item, collection }) => {
>
Digest Auth
+
{
+ dropdownTippyRef.current.hide();
+ onModeChange('oauth2');
+ }}
+ >
+ OAuth 2.0
+
{
diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/StyledWrapper.js
new file mode 100644
index 0000000000..e4d6a7d6c3
--- /dev/null
+++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/StyledWrapper.js
@@ -0,0 +1,15 @@
+import styled from 'styled-components';
+
+const Wrapper = styled.div`
+ label {
+ font-size: 0.8125rem;
+ }
+ .single-line-editor-wrapper {
+ padding: 0.15rem 0.4rem;
+ border-radius: 3px;
+ border: solid 1px ${(props) => props.theme.input.border};
+ background-color: ${(props) => props.theme.input.bg};
+ }
+`;
+
+export default Wrapper;
diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/index.js
new file mode 100644
index 0000000000..08ac8dab11
--- /dev/null
+++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/index.js
@@ -0,0 +1,65 @@
+import React from 'react';
+import get from 'lodash/get';
+import { useTheme } from 'providers/Theme';
+import { useDispatch } from 'react-redux';
+import SingleLineEditor from 'components/SingleLineEditor';
+import { updateAuth } from 'providers/ReduxStore/slices/collections';
+import { saveRequest, sendRequest } from 'providers/ReduxStore/slices/collections/actions';
+import StyledWrapper from './StyledWrapper';
+import { inputsConfig } from './inputsConfig';
+
+const OAuth2AuthorizationCode = ({ item, collection }) => {
+ const dispatch = useDispatch();
+ const { storedTheme } = useTheme();
+
+ const oAuth = item.draft ? get(item, 'draft.request.auth.oauth2', {}) : get(item, 'request.auth.oauth2', {});
+
+ const handleRun = async () => {
+ dispatch(sendRequest(item, collection.uid));
+ };
+
+ const handleSave = () => dispatch(saveRequest(item.uid, collection.uid));
+
+ const handleChange = (key, value) => {
+ dispatch(
+ updateAuth({
+ mode: 'oauth2',
+ collectionUid: collection.uid,
+ itemUid: item.uid,
+ content: {
+ grantType: 'authorization_code',
+ ...oAuth,
+ [key]: value
+ }
+ })
+ );
+ };
+
+ return (
+
+ {inputsConfig.map((input) => {
+ const { key, label } = input;
+ return (
+
+
+
+ handleChange(key, val)}
+ onRun={() => {}}
+ collection={collection}
+ />
+
+
+ );
+ })}
+
+
+ );
+};
+
+export default OAuth2AuthorizationCode;
diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/inputsConfig.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/inputsConfig.js
new file mode 100644
index 0000000000..a1f3f3d45a
--- /dev/null
+++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/inputsConfig.js
@@ -0,0 +1,28 @@
+const inputsConfig = [
+ {
+ key: 'callbackUrl',
+ label: 'Callback URL'
+ },
+ {
+ key: 'authorizationUrl',
+ label: 'Auth URL'
+ },
+ {
+ key: 'accessTokenUrl',
+ label: 'Access Token URL'
+ },
+ {
+ key: 'clientId',
+ label: 'Client ID'
+ },
+ {
+ key: 'clientSecret',
+ label: 'Client Secret'
+ },
+ {
+ key: 'scope',
+ label: 'Scope'
+ }
+];
+
+export { inputsConfig };
diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/ClientCredentials/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/ClientCredentials/StyledWrapper.js
new file mode 100644
index 0000000000..e4d6a7d6c3
--- /dev/null
+++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/ClientCredentials/StyledWrapper.js
@@ -0,0 +1,15 @@
+import styled from 'styled-components';
+
+const Wrapper = styled.div`
+ label {
+ font-size: 0.8125rem;
+ }
+ .single-line-editor-wrapper {
+ padding: 0.15rem 0.4rem;
+ border-radius: 3px;
+ border: solid 1px ${(props) => props.theme.input.border};
+ background-color: ${(props) => props.theme.input.bg};
+ }
+`;
+
+export default Wrapper;
diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/ClientCredentials/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/ClientCredentials/index.js
new file mode 100644
index 0000000000..b8c460cac7
--- /dev/null
+++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/ClientCredentials/index.js
@@ -0,0 +1,78 @@
+import React from 'react';
+import get from 'lodash/get';
+import { useTheme } from 'providers/Theme';
+import { useDispatch } from 'react-redux';
+import SingleLineEditor from 'components/SingleLineEditor';
+import { updateAuth } from 'providers/ReduxStore/slices/collections';
+import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
+import StyledWrapper from './StyledWrapper';
+
+const OAuth2ClientCredentials = ({ item, collection }) => {
+ const dispatch = useDispatch();
+ const { storedTheme } = useTheme();
+
+ const oAuth = item.draft ? get(item, 'draft.request.auth.oauth2', {}) : get(item, 'request.auth.oauth2', {});
+
+ const handleRun = () => dispatch(sendRequest(item, collection.uid));
+ const handleSave = () => dispatch(saveRequest(item.uid, collection.uid));
+
+ const handleClientIdChange = (clientId) => {
+ dispatch(
+ updateAuth({
+ mode: 'oauth2',
+ collectionUid: collection.uid,
+ itemUid: item.uid,
+ content: {
+ grantType: 'client_credentials',
+ clientId: clientId,
+ clientSecret: oAuth.clientSecret
+ }
+ })
+ );
+ };
+
+ const handleClientSecretChange = (clientSecret) => {
+ dispatch(
+ updateAuth({
+ mode: 'oauth2',
+ collectionUid: collection.uid,
+ itemUid: item.uid,
+ content: {
+ grantType: 'client_credentials',
+ clientId: oAuth.clientId,
+ clientSecret: clientSecret
+ }
+ })
+ );
+ };
+
+ return (
+
+
+
+ handleClientIdChange(val)}
+ onRun={handleRun}
+ collection={collection}
+ />
+
+
+
+
+ handleClientSecretChange(val)}
+ onRun={handleRun}
+ collection={collection}
+ />
+
+
+ );
+};
+
+export default OAuth2ClientCredentials;
diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/GrantTypeSelector/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/GrantTypeSelector/StyledWrapper.js
new file mode 100644
index 0000000000..4423a49c1d
--- /dev/null
+++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/GrantTypeSelector/StyledWrapper.js
@@ -0,0 +1,54 @@
+import styled from 'styled-components';
+
+const Wrapper = styled.div`
+ font-size: 0.8125rem;
+
+ .grant-type-mode-selector {
+ padding: 0.5rem 0px;
+ border-radius: 3px;
+ border: solid 1px ${(props) => props.theme.input.border};
+ background-color: ${(props) => props.theme.input.bg};
+
+ .dropdown {
+ width: 100%;
+
+ div[data-tippy-root] {
+ width: 100%;
+ }
+ .tippy-box {
+ width: 100%;
+ max-width: none !important;
+
+ .tippy-content: {
+ width: 100%;
+ max-width: none !important;
+ }
+ }
+ }
+
+ .grant-type-label {
+ width: 100%;
+ color: ${(props) => props.theme.colors.text.yellow};
+ justify-content: space-between;
+ padding: 0 0.5rem;
+ }
+
+ .dropdown-item {
+ padding: 0.2rem 0.6rem !important;
+ }
+
+ .label-item {
+ padding: 0.2rem 0.6rem !important;
+ }
+ }
+
+ .caret {
+ color: rgb(140, 140, 140);
+ fill: rgb(140 140 140);
+ }
+ label {
+ font-size: 0.8125rem;
+ }
+`;
+
+export default Wrapper;
diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/GrantTypeSelector/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/GrantTypeSelector/index.js
new file mode 100644
index 0000000000..06a9b5e318
--- /dev/null
+++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/GrantTypeSelector/index.js
@@ -0,0 +1,75 @@
+import React, { useRef, forwardRef } from 'react';
+import get from 'lodash/get';
+import Dropdown from 'components/Dropdown';
+import { useDispatch } from 'react-redux';
+import StyledWrapper from './StyledWrapper';
+import { IconCaretDown } from '@tabler/icons';
+import { updateAuth } from 'providers/ReduxStore/slices/collections';
+import { humanizeGrantType } from 'utils/collections';
+
+const GrantTypeSelector = ({ item, collection }) => {
+ const dispatch = useDispatch();
+ const dropdownTippyRef = useRef();
+ const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);
+
+ const oAuth = item.draft ? get(item, 'draft.request.auth.oauth2', {}) : get(item, 'request.auth.oauth2', {});
+
+ const Icon = forwardRef((props, ref) => {
+ return (
+
+ {humanizeGrantType(oAuth?.grantType)}
+
+ );
+ });
+
+ const onGrantTypeChange = (grantType) => {
+ dispatch(
+ updateAuth({
+ mode: 'oauth2',
+ collectionUid: collection.uid,
+ itemUid: item.uid,
+ content: {
+ grantType
+ }
+ })
+ );
+ };
+
+ return (
+
+
+
+
} placement="bottom-end">
+
{
+ dropdownTippyRef.current.hide();
+ onGrantTypeChange('password');
+ }}
+ >
+ Resource Owner Password Credentials
+
+
{
+ dropdownTippyRef.current.hide();
+ onGrantTypeChange('authorization_code');
+ }}
+ >
+ Authorization Code
+
+
{
+ dropdownTippyRef.current.hide();
+ onGrantTypeChange('client_credentials');
+ }}
+ >
+ Client Credentials
+
+
+
+
+ );
+};
+export default GrantTypeSelector;
diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Ropc/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Ropc/StyledWrapper.js
new file mode 100644
index 0000000000..e4d6a7d6c3
--- /dev/null
+++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Ropc/StyledWrapper.js
@@ -0,0 +1,15 @@
+import styled from 'styled-components';
+
+const Wrapper = styled.div`
+ label {
+ font-size: 0.8125rem;
+ }
+ .single-line-editor-wrapper {
+ padding: 0.15rem 0.4rem;
+ border-radius: 3px;
+ border: solid 1px ${(props) => props.theme.input.border};
+ background-color: ${(props) => props.theme.input.bg};
+ }
+`;
+
+export default Wrapper;
diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Ropc/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Ropc/index.js
new file mode 100644
index 0000000000..104ebdd77f
--- /dev/null
+++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Ropc/index.js
@@ -0,0 +1,78 @@
+import React from 'react';
+import get from 'lodash/get';
+import { useTheme } from 'providers/Theme';
+import { useDispatch } from 'react-redux';
+import SingleLineEditor from 'components/SingleLineEditor';
+import { updateAuth } from 'providers/ReduxStore/slices/collections';
+import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
+import StyledWrapper from './StyledWrapper';
+
+const OAuth2Ropc = ({ item, collection }) => {
+ const dispatch = useDispatch();
+ const { storedTheme } = useTheme();
+
+ const oAuth = item.draft ? get(item, 'draft.request.auth.oauth2', {}) : get(item, 'request.auth.oauth2', {});
+
+ const handleRun = () => dispatch(sendRequest(item, collection.uid));
+ const handleSave = () => dispatch(saveRequest(item.uid, collection.uid));
+
+ const handleUsernameChange = (username) => {
+ dispatch(
+ updateAuth({
+ mode: 'oauth2',
+ collectionUid: collection.uid,
+ itemUid: item.uid,
+ content: {
+ grantType: 'password',
+ username: username,
+ password: oAuth.password
+ }
+ })
+ );
+ };
+
+ const handlePasswordChange = (password) => {
+ dispatch(
+ updateAuth({
+ mode: 'oauth2',
+ collectionUid: collection.uid,
+ itemUid: item.uid,
+ content: {
+ grantType: 'password',
+ username: oAuth.username,
+ password: password
+ }
+ })
+ );
+ };
+
+ return (
+
+
+
+ handleUsernameChange(val)}
+ onRun={handleRun}
+ collection={collection}
+ />
+
+
+
+
+ handlePasswordChange(val)}
+ onRun={handleRun}
+ collection={collection}
+ />
+
+
+ );
+};
+
+export default OAuth2Ropc;
diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/StyledWrapper.js
new file mode 100644
index 0000000000..e4d6a7d6c3
--- /dev/null
+++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/StyledWrapper.js
@@ -0,0 +1,15 @@
+import styled from 'styled-components';
+
+const Wrapper = styled.div`
+ label {
+ font-size: 0.8125rem;
+ }
+ .single-line-editor-wrapper {
+ padding: 0.15rem 0.4rem;
+ border-radius: 3px;
+ border: solid 1px ${(props) => props.theme.input.border};
+ background-color: ${(props) => props.theme.input.bg};
+ }
+`;
+
+export default Wrapper;
diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/index.js
new file mode 100644
index 0000000000..1d51962a57
--- /dev/null
+++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/index.js
@@ -0,0 +1,37 @@
+import React from 'react';
+import get from 'lodash/get';
+import StyledWrapper from './StyledWrapper';
+import GrantTypeSelector from './GrantTypeSelector/index';
+import OAuth2Ropc from './Ropc/index';
+import OAuth2AuthorizationCode from './AuthorizationCode/index';
+import OAuth2ClientCredentials from './ClientCredentials/index';
+
+const grantTypeComponentMap = (grantType, item, collection) => {
+ switch (grantType) {
+ case 'password':
+ return
;
+ break;
+ case 'authorization_code':
+ return
;
+ break;
+ case 'client_credentials':
+ return
;
+ break;
+ default:
+ return
TBD
;
+ break;
+ }
+};
+
+const OAuth2 = ({ item, collection }) => {
+ const oAuth = item.draft ? get(item, 'draft.request.auth.oauth2', {}) : get(item, 'request.auth.oauth2', {});
+
+ return (
+
+
+ {grantTypeComponentMap(oAuth?.grantType, item, collection)}
+
+ );
+};
+
+export default OAuth2;
diff --git a/packages/bruno-app/src/components/RequestPane/Auth/index.js b/packages/bruno-app/src/components/RequestPane/Auth/index.js
index bd388737e9..04cc954f34 100644
--- a/packages/bruno-app/src/components/RequestPane/Auth/index.js
+++ b/packages/bruno-app/src/components/RequestPane/Auth/index.js
@@ -6,6 +6,7 @@ import BearerAuth from './BearerAuth';
import BasicAuth from './BasicAuth';
import DigestAuth from './DigestAuth';
import StyledWrapper from './StyledWrapper';
+import OAuth2 from './OAuth2/index';
const Auth = ({ item, collection }) => {
const authMode = item.draft ? get(item, 'draft.request.auth.mode') : get(item, 'request.auth.mode');
@@ -24,6 +25,9 @@ const Auth = ({ item, collection }) => {
case 'digest': {
return
;
}
+ case 'oauth2': {
+ return
;
+ }
}
};
diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
index a5f9100c00..41b284149f 100644
--- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
+++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
@@ -402,6 +402,10 @@ export const collectionsSlice = createSlice({
item.draft.request.auth.mode = 'digest';
item.draft.request.auth.digest = action.payload.content;
break;
+ case 'oauth2':
+ item.draft.request.auth.mode = 'oauth2';
+ item.draft.request.auth.oauth2 = action.payload.content;
+ break;
}
}
}
diff --git a/packages/bruno-app/src/utils/collections/index.js b/packages/bruno-app/src/utils/collections/index.js
index 216d74a4de..c18dbe7cef 100644
--- a/packages/bruno-app/src/utils/collections/index.js
+++ b/packages/bruno-app/src/utils/collections/index.js
@@ -505,6 +505,30 @@ export const humanizeRequestAuthMode = (mode) => {
label = 'Digest Auth';
break;
}
+ case 'oauth2': {
+ label = 'OAuth 2.0';
+ break;
+ }
+ }
+
+ return label;
+};
+
+export const humanizeGrantType = (mode) => {
+ let label = 'No Auth';
+ switch (mode) {
+ case 'password': {
+ label = 'Resource Owner Password Credentials';
+ break;
+ }
+ case 'authorization_code': {
+ label = 'Authorization Code';
+ break;
+ }
+ case 'client_credentials': {
+ label = 'Client Credentials';
+ break;
+ }
}
return label;
diff --git a/packages/bruno-electron/src/index.js b/packages/bruno-electron/src/index.js
index ef0e141dca..52afec0a8c 100644
--- a/packages/bruno-electron/src/index.js
+++ b/packages/bruno-electron/src/index.js
@@ -21,7 +21,8 @@ const contentSecurityPolicy = [
"script-src * 'unsafe-inline' 'unsafe-eval'",
"connect-src * 'unsafe-inline'",
"font-src 'self' https:",
- "form-action 'none'",
+ // this has been commented out to make oauth2 work
+ // "form-action 'none'",
"img-src 'self' blob: data: https:",
"style-src 'self' 'unsafe-inline' https:"
];
diff --git a/packages/bruno-electron/src/ipc/network/authorize-user-in-window.js b/packages/bruno-electron/src/ipc/network/authorize-user-in-window.js
new file mode 100644
index 0000000000..e4439f612a
--- /dev/null
+++ b/packages/bruno-electron/src/ipc/network/authorize-user-in-window.js
@@ -0,0 +1,61 @@
+const { BrowserWindow } = require('electron');
+
+const authorizeUserInWindow = ({ authorizeUrl, callbackUrl }) => {
+ return new Promise(async (resolve, reject) => {
+ let finalUrl = null;
+
+ const window = new BrowserWindow({
+ webPreferences: {
+ nodeIntegration: false
+ },
+ show: false
+ });
+ window.on('ready-to-show', window.show.bind(window));
+
+ function onWindowRedirect(url) {
+ // check if the url contains an authorization code
+ if (url.match(/(code=).*/)) {
+ finalUrl = url;
+ if (url && finalUrl.includes(callbackUrl)) {
+ window.close();
+ } else {
+ reject(new Error('Invalid Callback Url'));
+ }
+ }
+ }
+
+ window.on('close', () => {
+ if (finalUrl) {
+ try {
+ const callbackUrlWithCode = new URL(finalUrl);
+ const authorizationCode = callbackUrlWithCode.searchParams.get('code');
+
+ return resolve(authorizationCode);
+ } catch (error) {
+ return reject(error);
+ }
+ } else {
+ return reject(new Error('Authorization window closed'));
+ }
+ });
+
+ // wait for the window to navigate to the callback url
+ const didNavigateListener = (_, url) => {
+ onWindowRedirect(url);
+ };
+ window.webContents.on('did-navigate', didNavigateListener);
+ const willRedirectListener = (_, authorizeUrl) => {
+ onWindowRedirect(authorizeUrl);
+ };
+ window.webContents.on('will-redirect', willRedirectListener);
+
+ try {
+ await window.loadURL(authorizeUrl);
+ } catch (error) {
+ reject(error);
+ window.close();
+ }
+ });
+};
+
+module.exports = { authorizeUserInWindow };
diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js
index b068193c72..3446d52566 100644
--- a/packages/bruno-electron/src/ipc/network/index.js
+++ b/packages/bruno-electron/src/ipc/network/index.js
@@ -9,7 +9,7 @@ const Mustache = require('mustache');
const contentDispositionParser = require('content-disposition');
const mime = require('mime-types');
const { ipcMain } = require('electron');
-const { isUndefined, isNull, each, get, compact } = require('lodash');
+const { isUndefined, isNull, each, get, compact, cloneDeep } = require('lodash');
const { VarsRuntime, AssertRuntime, ScriptRuntime, TestRuntime } = require('@usebruno/js');
const prepareRequest = require('./prepare-request');
const prepareGqlIntrospectionRequest = require('./prepare-gql-introspection-request');
@@ -29,6 +29,7 @@ const { addDigestInterceptor } = require('./digestauth-helper');
const { shouldUseProxy, PatchedHttpsProxyAgent } = require('../../utils/proxy-util');
const { chooseFileToSave, writeBinaryFile } = require('../../utils/filesystem');
const { getCookieStringForUrl, addCookieToJar, getDomainsWithCookies } = require('../../utils/cookies');
+const { resolveOAuth2AuthorizationCodecessToken } = require('./oauth2-authorization-code-helper');
// override the default escape function to prevent escaping
Mustache.escape = function (value) {
@@ -189,6 +190,16 @@ const configureRequest = async (
const axiosInstance = makeAxiosInstance();
+ if (request.oauth2) {
+ if (request?.oauth2?.grantType == 'authorization_code') {
+ let requestCopy = cloneDeep(request);
+ interpolateVars(requestCopy, envVars, collectionVariables, processEnvVars);
+ const { data, url } = await resolveOAuth2AuthorizationCodecessToken(requestCopy);
+ request.data = data;
+ request.url = url;
+ }
+ }
+
if (request.awsv4config) {
request.awsv4config = await resolveAwsV4Credentials(request);
addAwsV4Interceptor(axiosInstance, request);
@@ -484,7 +495,6 @@ const registerNetworkIpc = (mainWindow) => {
setCookieHeaders = Array.isArray(response.headers['set-cookie'])
? response.headers['set-cookie']
: [response.headers['set-cookie']];
-
for (let setCookieHeader of setCookieHeaders) {
if (typeof setCookieHeader === 'string' && setCookieHeader.length) {
addCookieToJar(setCookieHeader, request.url);
@@ -495,6 +505,7 @@ const registerNetworkIpc = (mainWindow) => {
// send domain cookies to renderer
const domainsWithCookies = await getDomainsWithCookies();
+
mainWindow.webContents.send('main:cookies-update', safeParseJSON(safeStringifyJSON(domainsWithCookies)));
await runPostResponse(
diff --git a/packages/bruno-electron/src/ipc/network/interpolate-vars.js b/packages/bruno-electron/src/ipc/network/interpolate-vars.js
index 417f2df719..abf06bd862 100644
--- a/packages/bruno-electron/src/ipc/network/interpolate-vars.js
+++ b/packages/bruno-electron/src/ipc/network/interpolate-vars.js
@@ -98,17 +98,53 @@ const interpolateVars = (request, envVars = {}, collectionVariables = {}, proces
}
// todo: we have things happening in two places w.r.t basic auth
- // need to refactor this in the future
+ // need to refactor this in the future
// the request.auth (basic auth) object gets set inside the prepare-request.js file
if (request.auth) {
const username = _interpolate(request.auth.username) || '';
const password = _interpolate(request.auth.password) || '';
-
// use auth header based approach and delete the request.auth object
request.headers['authorization'] = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`;
delete request.auth;
}
+ if (request?.oauth2?.grantType) {
+ switch (request.oauth2.grantType) {
+ case 'password':
+ let username = _interpolate(request.oauth2.username) || '';
+ let password = _interpolate(request.oauth2.password) || '';
+ request.oauth2.username = username;
+ request.oauth2.password = password;
+ request.data = {
+ grant_type: 'password',
+ username,
+ password
+ };
+ break;
+ case 'authorization_code':
+ request.oauth2.callbackUrl = _interpolate(request.oauth2.callbackUrl) || '';
+ request.oauth2.authorizationUrl = _interpolate(request.oauth2.authorizationUrl) || '';
+ request.oauth2.accessTokenUrl = _interpolate(request.oauth2.accessTokenUrl) || '';
+ request.oauth2.clientId = _interpolate(request.oauth2.clientId) || '';
+ request.oauth2.clientSecret = _interpolate(request.oauth2.clientSecret) || '';
+ request.oauth2.scope = _interpolate(request.oauth2.scope) || '';
+ break;
+ case 'client_credentials':
+ let clientId = _interpolate(request.oauth2.clientId) || '';
+ let clientSecret = _interpolate(request.oauth2.clientSecret) || '';
+ request.oauth2.clientId = clientId;
+ request.oauth2.clientSecret = clientSecret;
+ request.data = {
+ grant_type: 'client_credentials',
+ client_id: clientId,
+ client_secret: clientSecret
+ };
+ break;
+ default:
+ break;
+ }
+ }
+
// interpolate vars for aws sigv4 auth
if (request.awsv4config) {
request.awsv4config.accessKeyId = _interpolate(request.awsv4config.accessKeyId) || '';
diff --git a/packages/bruno-electron/src/ipc/network/oauth2-authorization-code-helper.js b/packages/bruno-electron/src/ipc/network/oauth2-authorization-code-helper.js
new file mode 100644
index 0000000000..303af81709
--- /dev/null
+++ b/packages/bruno-electron/src/ipc/network/oauth2-authorization-code-helper.js
@@ -0,0 +1,41 @@
+const { get, cloneDeep } = require('lodash');
+const { authorizeUserInWindow } = require('./authorize-user-in-window');
+
+const resolveOAuth2AuthorizationCodecessToken = async (request) => {
+ let requestCopy = cloneDeep(request);
+ const authorization_code = await getOAuth2AuthorizationCode(requestCopy);
+ const oAuth = get(requestCopy, 'oauth2', {});
+ const { clientId, clientSecret, callbackUrl, scope } = oAuth;
+ const data = {
+ grant_type: 'authorization_code',
+ code: authorization_code,
+ redirect_uri: callbackUrl,
+ client_id: clientId,
+ client_secret: clientSecret,
+ scope: scope
+ };
+ const url = requestCopy?.oauth2?.accessTokenUrl;
+ return {
+ data,
+ url
+ };
+};
+
+const getOAuth2AuthorizationCode = (request) => {
+ return new Promise(async (resolve, reject) => {
+ const { oauth2 } = request;
+ const { callbackUrl, clientId, authorizationUrl, scope } = oauth2;
+ const authorizationUrlWithQueryParams = `${authorizationUrl}?client_id=${clientId}&redirect_uri=${callbackUrl}&response_type=code&scope=${scope}`;
+ try {
+ const code = await authorizeUserInWindow({ authorizeUrl: authorizationUrlWithQueryParams, callbackUrl });
+ resolve(code);
+ } catch (err) {
+ reject(err);
+ }
+ });
+};
+
+module.exports = {
+ resolveOAuth2AuthorizationCodecessToken,
+ getOAuth2AuthorizationCode
+};
diff --git a/packages/bruno-electron/src/ipc/network/prepare-request.js b/packages/bruno-electron/src/ipc/network/prepare-request.js
index d793ad9386..f7317b59bc 100644
--- a/packages/bruno-electron/src/ipc/network/prepare-request.js
+++ b/packages/bruno-electron/src/ipc/network/prepare-request.js
@@ -62,6 +62,36 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
password: get(collectionAuth, 'digest.password')
};
break;
+ case 'oauth2':
+ const grantType = get(collectionAuth, 'auth.oauth2.grantType');
+ switch (grantType) {
+ case 'password':
+ axiosRequest.oauth2 = {
+ grantType: grantType,
+ username: get(collectionAuth, 'auth.oauth2.username'),
+ password: get(collectionAuth, 'auth.oauth2.password')
+ };
+ break;
+ case 'authorization_code':
+ axiosRequest.oauth2 = {
+ grantType: grantType,
+ callbackUrl: get(collectionAuth, 'auth.oauth2.callbackUrl'),
+ authorizationUrl: get(collectionAuth, 'auth.oauth2.authorizationUrl'),
+ accessTokenUrl: get(collectionAuth, 'auth.oauth2.accessTokenUrl'),
+ clientId: get(collectionAuth, 'auth.oauth2.clientId'),
+ clientSecret: get(collectionAuth, 'auth.oauth2.clientSecret'),
+ scope: get(collectionAuth, 'auth.oauth2.scope')
+ };
+ break;
+ case 'client_credentials':
+ axiosRequest.oauth2 = {
+ grantType: grantType,
+ clientId: get(collectionAuth, 'auth.oauth2.clientId'),
+ clientSecret: get(collectionAuth, 'auth.oauth2.clientSecret')
+ };
+ break;
+ }
+ break;
}
}
@@ -91,6 +121,37 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
username: get(request, 'auth.digest.username'),
password: get(request, 'auth.digest.password')
};
+ break;
+ case 'oauth2':
+ const grantType = get(request, 'auth.oauth2.grantType');
+ switch (grantType) {
+ case 'password':
+ axiosRequest.oauth2 = {
+ grantType: grantType,
+ username: get(request, 'auth.oauth2.username'),
+ password: get(request, 'auth.oauth2.password')
+ };
+ break;
+ case 'authorization_code':
+ axiosRequest.oauth2 = {
+ grantType: grantType,
+ callbackUrl: get(request, 'auth.oauth2.callbackUrl'),
+ authorizationUrl: get(request, 'auth.oauth2.authorizationUrl'),
+ accessTokenUrl: get(request, 'auth.oauth2.accessTokenUrl'),
+ clientId: get(request, 'auth.oauth2.clientId'),
+ clientSecret: get(request, 'auth.oauth2.clientSecret'),
+ scope: get(request, 'auth.oauth2.scope')
+ };
+ break;
+ case 'client_credentials':
+ axiosRequest.oauth2 = {
+ grantType: grantType,
+ clientId: get(request, 'auth.oauth2.clientId'),
+ clientSecret: get(request, 'auth.oauth2.clientSecret')
+ };
+ break;
+ }
+ break;
}
}
diff --git a/packages/bruno-lang/v2/src/bruToJson.js b/packages/bruno-lang/v2/src/bruToJson.js
index 3c2a6c8b08..71c3e9e6c8 100644
--- a/packages/bruno-lang/v2/src/bruToJson.js
+++ b/packages/bruno-lang/v2/src/bruToJson.js
@@ -23,7 +23,7 @@ const { outdentString } = require('../../v1/src/utils');
*/
const grammar = ohm.grammar(`Bru {
BruFile = (meta | http | query | headers | auths | bodies | varsandassert | script | tests | docs)*
- auths = authawsv4 | authbasic | authbearer | authdigest
+ auths = authawsv4 | authbasic | authbearer | authdigest | authOAuth2
bodies = bodyjson | bodytext | bodyxml | bodysparql | bodygraphql | bodygraphqlvars | bodyforms | body
bodyforms = bodyformurlencoded | bodymultipart
@@ -80,6 +80,7 @@ const grammar = ohm.grammar(`Bru {
authbasic = "auth:basic" dictionary
authbearer = "auth:bearer" dictionary
authdigest = "auth:digest" dictionary
+ authOAuth2 = "auth:oauth2" dictionary
body = "body" st* "{" nl* textblock tagend
bodyjson = "body:json" st* "{" nl* textblock tagend
@@ -380,6 +381,46 @@ const sem = grammar.createSemantics().addAttribute('ast', {
}
};
},
+ authOAuth2(_1, dictionary) {
+ const auth = mapPairListToKeyValPairs(dictionary.ast, false);
+ const grantTypeKey = _.find(auth, { name: 'grant_type' });
+ const usernameKey = _.find(auth, { name: 'username' });
+ const passwordKey = _.find(auth, { name: 'password' });
+ const callbackUrlKey = _.find(auth, { name: 'callback_url' });
+ const authorizationUrlKey = _.find(auth, { name: 'authorization_url' });
+ const accessTokenUrlKey = _.find(auth, { name: 'access_token_url' });
+ const clientIdKey = _.find(auth, { name: 'client_id' });
+ const clientSecretKey = _.find(auth, { name: 'client_secret' });
+ const scopeKey = _.find(auth, { name: 'scope' });
+ return {
+ auth: {
+ oauth2:
+ grantTypeKey?.value && grantTypeKey?.value == 'password'
+ ? {
+ grantType: grantTypeKey ? grantTypeKey.value : '',
+ username: usernameKey ? usernameKey.value : '',
+ password: passwordKey ? passwordKey.value : ''
+ }
+ : grantTypeKey?.value && grantTypeKey?.value == 'authorization_code'
+ ? {
+ grantType: grantTypeKey ? grantTypeKey.value : '',
+ callbackUrl: callbackUrlKey ? callbackUrlKey.value : '',
+ authorizationUrl: authorizationUrlKey ? authorizationUrlKey.value : '',
+ accessTokenUrl: accessTokenUrlKey ? accessTokenUrlKey.value : '',
+ clientId: clientIdKey ? clientIdKey.value : '',
+ clientSecret: clientSecretKey ? clientSecretKey.value : '',
+ scope: scopeKey ? scopeKey.value : ''
+ }
+ : grantTypeKey?.value && grantTypeKey?.value == 'client_credentials'
+ ? {
+ grantType: grantTypeKey ? grantTypeKey.value : '',
+ clientId: clientIdKey ? clientIdKey.value : '',
+ clientSecret: clientSecretKey ? clientSecretKey.value : ''
+ }
+ : {}
+ }
+ };
+ },
bodyformurlencoded(_1, dictionary) {
return {
body: {
diff --git a/packages/bruno-lang/v2/src/jsonToBru.js b/packages/bruno-lang/v2/src/jsonToBru.js
index 961a270e40..17b5514491 100644
--- a/packages/bruno-lang/v2/src/jsonToBru.js
+++ b/packages/bruno-lang/v2/src/jsonToBru.js
@@ -126,6 +126,42 @@ ${indentString(`password: ${auth.digest.password}`)}
`;
}
+ if (auth && auth.oauth2) {
+ switch (auth?.oauth2?.grantType) {
+ case 'password':
+ bru += `auth:oauth2 {
+${indentString(`grant_type: password`)}
+${indentString(`username: ${auth.oauth2.username}`)}
+${indentString(`password: ${auth.oauth2.password}`)}
+}
+
+`;
+ break;
+ case 'authorization_code':
+ bru += `auth:oauth2 {
+${indentString(`grant_type: authorization_code`)}
+${indentString(`callback_url: ${auth.oauth2.callbackUrl}`)}
+${indentString(`authorization_url: ${auth.oauth2.authorizationUrl}`)}
+${indentString(`access_token_url: ${auth.oauth2.accessTokenUrl}`)}
+${indentString(`client_id: ${auth.oauth2.clientId}`)}
+${indentString(`client_secret: ${auth.oauth2.clientSecret}`)}
+${indentString(`scope: ${auth.oauth2.scope}`)}
+}
+
+`;
+ break;
+ case 'client_credentials':
+ bru += `auth:oauth2 {
+${indentString(`grant_type: client_credentials`)}
+${indentString(`client_id: ${auth.oauth2.clientId}`)}
+${indentString(`client_secret: ${auth.oauth2.clientSecret}`)}
+}
+
+`;
+ break;
+ }
+ }
+
if (body && body.json && body.json.length) {
bru += `body:json {
${indentString(body.json)}
diff --git a/packages/bruno-lang/v2/tests/fixtures/request.bru b/packages/bruno-lang/v2/tests/fixtures/request.bru
index 21b20477bb..20f55e07b4 100644
--- a/packages/bruno-lang/v2/tests/fixtures/request.bru
+++ b/packages/bruno-lang/v2/tests/fixtures/request.bru
@@ -45,6 +45,15 @@ auth:digest {
password: secret
}
+auth:oauth2 {
+ grantType: authorization_code
+ client_id: client_id_1
+ client_secret: client_secret_1
+ auth_url: http://localhost:8080/api/auth/oauth2/ac/authorize
+ callback_url: http://localhost:8080/api/auth/oauth2/ac/callback
+ access_token_url: http://localhost:8080/api/auth/oauth2/ac/token
+}
+
body:json {
{
"hello": "world"
diff --git a/packages/bruno-lang/v2/tests/fixtures/request.json b/packages/bruno-lang/v2/tests/fixtures/request.json
index 778da60b28..edfb6fbb8f 100644
--- a/packages/bruno-lang/v2/tests/fixtures/request.json
+++ b/packages/bruno-lang/v2/tests/fixtures/request.json
@@ -63,6 +63,14 @@
"digest": {
"username": "john",
"password": "secret"
+ },
+ "oauth2": {
+ "grantType": "authorization_code",
+ "client_id": "client_id_1",
+ "client_secret": "client_secret_1",
+ "auth_url": "http://localhost:8080/api/auth/oauth2/ac/authorize",
+ "callback_url": "http://localhost:8080/api/auth/oauth2/ac/callback",
+ "access_token_url": "http://localhost:8080/api/auth/oauth2/ac/token"
}
},
"body": {
diff --git a/packages/bruno-schema/src/collections/index.js b/packages/bruno-schema/src/collections/index.js
index 5f7dc12372..3640a47bd1 100644
--- a/packages/bruno-schema/src/collections/index.js
+++ b/packages/bruno-schema/src/collections/index.js
@@ -119,12 +119,61 @@ const authDigestSchema = Yup.object({
.noUnknown(true)
.strict();
+const oauth2Schema = Yup.object({
+ grantType: Yup.string()
+ .oneOf(['client_credentials', 'password', 'authorization_code'])
+ .required('grantType is required'),
+ username: Yup.string().when('grantType', {
+ is: (val) => ['client_credentials', 'password'].includes(val),
+ then: Yup.string().nullable(),
+ otherwise: Yup.string().nullable().strip()
+ }),
+ password: Yup.string().when('grantType', {
+ is: (val) => ['client_credentials', 'password'].includes(val),
+ then: Yup.string().nullable(),
+ otherwise: Yup.string().nullable().strip()
+ }),
+ callbackUrl: Yup.string().when('grantType', {
+ is: (val) => ['authorization_code'].includes(val),
+ then: Yup.string().nullable(),
+ otherwise: Yup.string().nullable().strip()
+ }),
+ authorizationUrl: Yup.string().when('grantType', {
+ is: (val) => ['authorization_code'].includes(val),
+ then: Yup.string().nullable(),
+ otherwise: Yup.string().nullable().strip()
+ }),
+ accessTokenUrl: Yup.string().when('grantType', {
+ is: (val) => ['authorization_code'].includes(val),
+ then: Yup.string().nullable(),
+ otherwise: Yup.string().nullable().strip()
+ }),
+ clientId: Yup.string().when('grantType', {
+ is: (val) => ['authorization_code', 'client_credentials'].includes(val),
+ then: Yup.string().nullable(),
+ otherwise: Yup.string().nullable().strip()
+ }),
+ clientSecret: Yup.string().when('grantType', {
+ is: (val) => ['authorization_code', 'client_credentials'].includes(val),
+ then: Yup.string().nullable(),
+ otherwise: Yup.string().nullable().strip()
+ }),
+ scope: Yup.string().when('grantType', {
+ is: (val) => ['authorization_code'].includes(val),
+ then: Yup.string().nullable(),
+ otherwise: Yup.string().nullable().strip()
+ })
+})
+ .noUnknown(true)
+ .strict();
+
const authSchema = Yup.object({
- mode: Yup.string().oneOf(['none', 'awsv4', 'basic', 'bearer', 'digest']).required('mode is required'),
+ mode: Yup.string().oneOf(['none', 'awsv4', 'basic', 'bearer', 'digest', 'oauth2']).required('mode is required'),
awsv4: authAwsV4Schema.nullable(),
basic: authBasicSchema.nullable(),
bearer: authBearerSchema.nullable(),
- digest: authDigestSchema.nullable()
+ digest: authDigestSchema.nullable(),
+ oauth2: oauth2Schema.nullable()
})
.noUnknown(true)
.strict();
diff --git a/packages/bruno-tests/collection/auth/basic/via auth/Basic Auth 200.bru b/packages/bruno-tests/collection/auth/basic/via auth/Basic Auth 200.bru
index de5612b1f5..32fdf59d1e 100644
--- a/packages/bruno-tests/collection/auth/basic/via auth/Basic Auth 200.bru
+++ b/packages/bruno-tests/collection/auth/basic/via auth/Basic Auth 200.bru
@@ -15,6 +15,10 @@ auth:basic {
password: {{basic_auth_password}}
}
+body:json {
+ {"x":1}
+}
+
assert {
res.status: 200
res.body.message: Authentication successful
diff --git a/packages/bruno-tests/collection/auth/basic/via script/Basic Auth 200.bru b/packages/bruno-tests/collection/auth/basic/via script/Basic Auth 200.bru
index 04d12f2265..1afe97b6cc 100644
--- a/packages/bruno-tests/collection/auth/basic/via script/Basic Auth 200.bru
+++ b/packages/bruno-tests/collection/auth/basic/via script/Basic Auth 200.bru
@@ -10,6 +10,10 @@ post {
auth: none
}
+body:json {
+ {"x":1}
+}
+
assert {
res.status: eq 200
res.body.message: Authentication successful
diff --git a/packages/bruno-tests/collection/auth/oauth2/ac/github token with authorize.bru b/packages/bruno-tests/collection/auth/oauth2/ac/github token with authorize.bru
new file mode 100644
index 0000000000..c18d2c6ed2
--- /dev/null
+++ b/packages/bruno-tests/collection/auth/oauth2/ac/github token with authorize.bru
@@ -0,0 +1,25 @@
+meta {
+ name: github token with authorize
+ type: http
+ seq: 1
+}
+
+post {
+ url: github.com
+ body: none
+ auth: oauth2
+}
+
+auth:oauth2 {
+ grant_type: authorization_code
+ callback_url: {{callback_url}}
+ authorization_url: {{github_authorize_url}}
+ access_token_url: {{github_access_token_url}}
+ client_id: {{github_client_id}}
+ client_secret: {{github_client_secret}}
+ scope: repo,gist
+}
+
+script:post-response {
+ bru.setEnvVar('github_access_token',res.body.split('access_token=')[1]?.split('&scope')[0]);
+}
diff --git a/packages/bruno-tests/collection/auth/oauth2/ac/google token with authorize.bru b/packages/bruno-tests/collection/auth/oauth2/ac/google token with authorize.bru
new file mode 100644
index 0000000000..93ea7975e6
--- /dev/null
+++ b/packages/bruno-tests/collection/auth/oauth2/ac/google token with authorize.bru
@@ -0,0 +1,25 @@
+meta {
+ name: google token with authorize
+ type: http
+ seq: 4
+}
+
+post {
+ url:
+ body: none
+ auth: oauth2
+}
+
+auth:oauth2 {
+ grant_type: authorization_code
+ callback_url: {{callback_url}}
+ authorization_url: {{google_auth_url}}
+ access_token_url: {{google_access_token_url}}
+ client_id: {{google_client_id}}
+ client_secret: {{google_client_secret}}
+ scope: {{google_scope}}
+}
+
+script:post-response {
+ bru.setEnvVar('ac_access_token', res.body.access_token);
+}
diff --git a/packages/bruno-tests/collection/auth/oauth2/ac/resource.bru b/packages/bruno-tests/collection/auth/oauth2/ac/resource.bru
new file mode 100644
index 0000000000..ead30ec6bf
--- /dev/null
+++ b/packages/bruno-tests/collection/auth/oauth2/ac/resource.bru
@@ -0,0 +1,27 @@
+meta {
+ name: resource
+ type: http
+ seq: 3
+}
+
+post {
+ url: {{host}}/api/auth/oauth2/ac/resource?token={{ac_access_token}}
+ body: json
+ auth: none
+}
+
+query {
+ token: {{ac_access_token}}
+}
+
+auth:bearer {
+ token:
+}
+
+body:json {
+ {
+ "code": "eb30dbf783b65bec4539ee1dcb068606",
+ "client_id": "{{client_id}}",
+ "client_secret": "{{client_secret}}"
+ }
+}
diff --git a/packages/bruno-tests/collection/auth/oauth2/ac/token with authorize.bru b/packages/bruno-tests/collection/auth/oauth2/ac/token with authorize.bru
new file mode 100644
index 0000000000..e42fd7c776
--- /dev/null
+++ b/packages/bruno-tests/collection/auth/oauth2/ac/token with authorize.bru
@@ -0,0 +1,25 @@
+meta {
+ name: token with authorize
+ type: http
+ seq: 4
+}
+
+post {
+ url:
+ body: none
+ auth: oauth2
+}
+
+auth:oauth2 {
+ grant_type: authorization_code
+ callback_url: {{callback_url}}
+ authorization_url: {{auth_url}}
+ access_token_url: {{access_token_url}}
+ client_id: {{client_id}}
+ client_secret: {{client_secret}}
+ scope:
+}
+
+script:post-response {
+ bru.setEnvVar('ac_access_token', res.body.access_token);
+}
diff --git a/packages/bruno-tests/collection/auth/oauth2/cc/resource.bru b/packages/bruno-tests/collection/auth/oauth2/cc/resource.bru
new file mode 100644
index 0000000000..c4a1ce3992
--- /dev/null
+++ b/packages/bruno-tests/collection/auth/oauth2/cc/resource.bru
@@ -0,0 +1,15 @@
+meta {
+ name: resource
+ type: http
+ seq: 2
+}
+
+get {
+ url: {{host}}/api/auth/oauth2/cc/resource?token={{cc_access_token}}
+ body: none
+ auth: none
+}
+
+query {
+ token: {{cc_access_token}}
+}
diff --git a/packages/bruno-tests/collection/auth/oauth2/cc/token.bru b/packages/bruno-tests/collection/auth/oauth2/cc/token.bru
new file mode 100644
index 0000000000..13987b2ebe
--- /dev/null
+++ b/packages/bruno-tests/collection/auth/oauth2/cc/token.bru
@@ -0,0 +1,21 @@
+meta {
+ name: token
+ type: http
+ seq: 1
+}
+
+post {
+ url: {{host}}/api/auth/oauth2/cc/token
+ body: none
+ auth: oauth2
+}
+
+auth:oauth2 {
+ grant_type: client_credentials
+ client_id: {{client_id}}
+ client_secret: {{client_secret}}
+}
+
+script:post-response {
+ bru.setEnvVar('cc_access_token', res.body.access_token);
+}
diff --git a/packages/bruno-tests/collection/auth/oauth2/ropc/resource.bru b/packages/bruno-tests/collection/auth/oauth2/ropc/resource.bru
new file mode 100644
index 0000000000..1395250ee8
--- /dev/null
+++ b/packages/bruno-tests/collection/auth/oauth2/ropc/resource.bru
@@ -0,0 +1,15 @@
+meta {
+ name: resource
+ type: http
+ seq: 2
+}
+
+post {
+ url: {{host}}/api/auth/oauth2/ropc/resource
+ body: none
+ auth: bearer
+}
+
+auth:bearer {
+ token: {{ropc_access_token}}
+}
diff --git a/packages/bruno-tests/collection/auth/oauth2/ropc/token.bru b/packages/bruno-tests/collection/auth/oauth2/ropc/token.bru
new file mode 100644
index 0000000000..495655ab36
--- /dev/null
+++ b/packages/bruno-tests/collection/auth/oauth2/ropc/token.bru
@@ -0,0 +1,21 @@
+meta {
+ name: token
+ type: http
+ seq: 1
+}
+
+post {
+ url: {{host}}/api/auth/oauth2/ropc/token
+ body: none
+ auth: oauth2
+}
+
+auth:oauth2 {
+ grant_type: password
+ username: {{ropc_username}}
+ password: {{ropc_password}}
+}
+
+script:post-response {
+ bru.setEnvVar('ropc_access_token', res.body.access_token);
+}
diff --git a/packages/bruno-tests/collection/environments/Local.bru b/packages/bruno-tests/collection/environments/Local.bru
index 26d3a65752..d4c08d9c8b 100644
--- a/packages/bruno-tests/collection/environments/Local.bru
+++ b/packages/bruno-tests/collection/environments/Local.bru
@@ -1,8 +1,22 @@
vars {
- host: http://localhost:80
+ host: http://localhost:8080
bearer_auth_token: your_secret_token
basic_auth_password: della
- env.var1: envVar1
- env-var2: envVar2
- bark: {{process.env.PROC_ENV_VAR}}
+ client_id: client_id_1
+ client_secret: client_secret_1
+ auth_url: http://localhost:8080/api/auth/oauth2/ac/authorize
+ callback_url: http://localhost:8080/api/auth/oauth2/ac/callback
+ access_token_url: http://localhost:8080/api/auth/oauth2/ac/token
+ ropc_username: foo
+ ropc_password: bar
+ github_client_secret: 4634189fc1f44fd2d6052178f960446dfd9aa0d1
+ github_client_id: 8a75a075912b5e979786
+ github_authorize_url: https://github.com/login/oauth/authorize
+ github_access_token_url: https://github.com/login/oauth/access_token
+ google_auth_url: https://accounts.google.com/o/oauth2/auth
+ google_access_token_url: https://accounts.google.com/o/oauth2/token
+ google_client_id: 156878810073-1insgtpe5t45amrs0mjfa69ld2n5kkbt.apps.googleusercontent.com
+ google_client_secret: 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
+ google_scope: https://www.googleapis.com/auth/userinfo.email
+ github_authorization_code: 13285dba60810ab20981
}
diff --git a/packages/bruno-tests/collection/ping.bru b/packages/bruno-tests/collection/ping.bru
index eea7bae3af..b09936a9cc 100644
--- a/packages/bruno-tests/collection/ping.bru
+++ b/packages/bruno-tests/collection/ping.bru
@@ -29,6 +29,21 @@ assert {
~res.body: eq {{pong}}
}
+script:pre-request {
+ const project = 'foo'
+ const roles = ('abc, xyz'.split(",")).map(element => {
+ return element.trim();
+ });
+
+ var project_roles = [];
+
+ for (let i in roles) {
+ project_roles.push("\""+project+"_"+roles[i]+"\"");
+ }
+
+ bru.setEnvVar("roles", 1);
+}
+
tests {
test("should ping pong", function() {
const data = res.getBody();
diff --git a/packages/bruno-tests/collection/query.bru b/packages/bruno-tests/collection/query.bru
new file mode 100644
index 0000000000..cf0d58bb6e
--- /dev/null
+++ b/packages/bruno-tests/collection/query.bru
@@ -0,0 +1,78 @@
+meta {
+ name: query
+ type: http
+ seq: 2
+}
+
+get {
+ url: {{host}}/query?foo={{pong}}
+ body: none
+ auth: none
+}
+
+query {
+ foo: {{pong}}
+}
+
+auth:awsv4 {
+ accessKeyId: a
+ secretAccessKey: b
+ sessionToken: c
+ service: d
+ region: e
+ profileName: f
+}
+
+vars:pre-request {
+ m4: true
+ pong: pong
+}
+
+assert {
+ res.status: eq 200
+ ~res.body: eq {{pong}}
+}
+
+script:pre-request {
+ const project = 'foo'
+ const roles = ('abc, xyz'.split(",")).map(element => {
+ return element.trim();
+ });
+
+ var project_roles = [];
+
+ for (let i in roles) {
+ project_roles.push("\""+project+"_"+roles[i]+"\"");
+ }
+
+ bru.setVar("pong", JSON.stringify([1,2,3,4]));
+}
+
+tests {
+ test("should ping pong", function() {
+ const data = res.getBody();
+ expect(data).to.equal(bru.getVar("pong"));
+ });
+}
+
+docs {
+ # API Documentation
+
+ ## Introduction
+
+ Welcome to the API documentation for [Your API Name]. This document provides instructions on how to make requests to the API and covers available authentication methods.
+
+ ## Authentication
+
+ Before making requests to the API, you need to authenticate your application. [Your API Name] supports the following authentication methods:
+
+ ### API Key
+
+ To use API key authentication, include your API key in the request headers as follows:
+
+ ```http
+ GET /api/endpoint
+ Host: api.example.com
+ Authorization: Bearer YOUR_API_KEY
+
+}
diff --git a/packages/bruno-tests/package.json b/packages/bruno-tests/package.json
index 0135eeb616..84ede3d622 100644
--- a/packages/bruno-tests/package.json
+++ b/packages/bruno-tests/package.json
@@ -27,6 +27,7 @@
"express-xml-bodyparser": "^0.3.0",
"http-proxy": "^1.18.1",
"js-yaml": "^4.1.0",
+ "jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21",
"multer": "^1.4.5-lts.1"
}
diff --git a/packages/bruno-tests/src/auth/index.js b/packages/bruno-tests/src/auth/index.js
index 84d93798e2..0b5dc7f630 100644
--- a/packages/bruno-tests/src/auth/index.js
+++ b/packages/bruno-tests/src/auth/index.js
@@ -4,7 +4,13 @@ const router = express.Router();
const authBearer = require('./bearer');
const authBasic = require('./basic');
const authCookie = require('./cookie');
+const authOAuth2Ropc = require('./oauth2/ropc');
+const authOAuth2AuthorizationCode = require('./oauth2/ac');
+const authOAuth2Cc = require('./oauth2/cc');
+router.use('/oauth2/ropc', authOAuth2Ropc);
+router.use('/oauth2/ac', authOAuth2AuthorizationCode);
+router.use('/oauth2/cc', authOAuth2Cc);
router.use('/bearer', authBearer);
router.use('/basic', authBasic);
router.use('/cookie', authCookie);
diff --git a/packages/bruno-tests/src/auth/oauth2/ac.js b/packages/bruno-tests/src/auth/oauth2/ac.js
new file mode 100644
index 0000000000..840c2a7788
--- /dev/null
+++ b/packages/bruno-tests/src/auth/oauth2/ac.js
@@ -0,0 +1,141 @@
+const express = require('express');
+const router = express.Router();
+const crypto = require('crypto');
+const clients = [
+ {
+ client_id: 'client_id_1',
+ client_secret: 'client_secret_1',
+ redirect_uri: 'http://localhost:3001/callback'
+ }
+];
+
+const authCodes = [];
+
+const tokens = [];
+
+function generateUniqueString() {
+ return crypto.randomBytes(16).toString('hex');
+}
+
+router.get('/authorize', (req, res) => {
+ const { response_type, client_id, redirect_uri } = req.query;
+ if (response_type !== 'code') {
+ return res.status(401).json({ error: 'Invalid Response type, expected "code"' });
+ }
+
+ const client = clients.find((c) => c.client_id === client_id);
+
+ if (!client) {
+ return res.status(401).json({ error: 'Invalid client' });
+ }
+
+ if (!redirect_uri) {
+ return res.status(401).json({ error: 'Invalid redirect URI' });
+ }
+
+ const authorization_code = generateUniqueString();
+ authCodes.push({
+ authCode: authorization_code,
+ client_id,
+ redirect_uri
+ });
+
+ const redirectUrl = `${redirect_uri}?code=${authorization_code}`;
+
+ const _res = `
+
+
+
+
+
+
+ `;
+
+ res.send(_res);
+});
+
+// Handle the authorization callback
+router.get('/callback', (req, res) => {
+ const { code } = req.query;
+
+ // Check if the authCode is valid.
+ const storedAuthCode = authCodes.find((t) => t.authCode === code);
+
+ if (!storedAuthCode) {
+ return res.status(401).json({ error: 'Invalid Authorization Code' });
+ }
+
+ return res.json({ message: 'Authorization successful', storedAuthCode });
+});
+
+router.post('/token', (req, res) => {
+ let grant_type, code, redirect_uri, client_id, client_secret;
+ if (req?.body?.grant_type) {
+ grant_type = req?.body?.grant_type;
+ code = req?.body?.code;
+ redirect_uri = req?.body?.redirect_uri;
+ client_id = req?.body?.client_id;
+ client_secret = req?.body?.client_secret;
+ }
+ if (req?.headers?.grant_type) {
+ grant_type = req?.headers?.grant_type;
+ code = req?.headers?.code;
+ redirect_uri = req?.headers?.redirect_uri;
+ client_id = req?.headers?.client_id;
+ client_secret = req?.headers?.client_secret;
+ }
+
+ if (grant_type !== 'authorization_code') {
+ return res.status(401).json({ error: 'Invalid Grant Type' });
+ }
+
+ // const client = clients.find((c) => c.client_id === client_id && c.client_secret === client_secret);
+ // if (!client) {
+ // return res.status(401).json({ error: 'Invalid client credentials' });
+ // }
+
+ const storedAuthCode = authCodes.find((t) => t.authCode === code);
+
+ if (!storedAuthCode) {
+ return res.status(401).json({ error: 'Invalid Authorization Code' });
+ }
+
+ const accessToken = generateUniqueString();
+ tokens.push({
+ accessToken: accessToken,
+ client_id
+ });
+
+ res.json({ access_token: accessToken });
+});
+
+router.post('/resource', (req, res) => {
+ try {
+ const { token } = req.query;
+ const storedToken = tokens.find((t) => t.accessToken === token);
+ if (!storedToken) {
+ return res.status(401).json({ error: 'Invalid Access Token' });
+ }
+ return res.json({ resource: { name: 'foo', email: 'foo@bar.com' } });
+ } catch (err) {
+ return res.status(401).json({ error: 'Corrupt Access Token' });
+ }
+});
+
+module.exports = router;
diff --git a/packages/bruno-tests/src/auth/oauth2/cc.js b/packages/bruno-tests/src/auth/oauth2/cc.js
new file mode 100644
index 0000000000..dcaee30270
--- /dev/null
+++ b/packages/bruno-tests/src/auth/oauth2/cc.js
@@ -0,0 +1,62 @@
+const express = require('express');
+const router = express.Router();
+const crypto = require('crypto');
+const clients = [
+ {
+ client_id: 'client_id_1',
+ client_secret: 'client_secret_1'
+ }
+];
+
+const tokens = [];
+
+function generateUniqueString() {
+ return crypto.randomBytes(16).toString('hex');
+}
+
+router.post('/token', (req, res) => {
+ let grant_type, client_id, client_secret;
+ if (req?.body?.grant_type) {
+ grant_type = req?.body?.grant_type;
+ client_id = req?.body?.client_id;
+ client_secret = req?.body?.client_secret;
+ } else if (req?.headers?.grant_type) {
+ grant_type = req?.headers?.grant_type;
+ client_id = req?.headers?.client_id;
+ client_secret = req?.headers?.client_secret;
+ }
+
+ if (grant_type !== 'client_credentials') {
+ return res.status(401).json({ error: 'Invalid Grant Type, expected "client_credentials"' });
+ }
+
+ const client = clients.find((c) => c.client_id == client_id && c.client_secret == client_secret);
+
+ if (!client) {
+ return res.status(401).json({ error: 'Invalid client' });
+ }
+
+ const token = generateUniqueString();
+ tokens.push({
+ token,
+ client_id,
+ client_secret
+ });
+
+ return res.json({ message: 'Authenticated successfully', access_token: token });
+});
+
+router.get('/resource', (req, res) => {
+ try {
+ const { token } = req.query;
+ const storedToken = tokens.find((t) => t.token === token);
+ if (!storedToken) {
+ return res.status(401).json({ error: 'Invalid Access Token' });
+ }
+ return res.json({ resource: { name: 'foo', email: 'foo@bar.com' } });
+ } catch (err) {
+ return res.status(401).json({ error: 'Corrupt Access Token' });
+ }
+});
+
+module.exports = router;
diff --git a/packages/bruno-tests/src/auth/oauth2/ropc.js b/packages/bruno-tests/src/auth/oauth2/ropc.js
new file mode 100644
index 0000000000..84bb979a75
--- /dev/null
+++ b/packages/bruno-tests/src/auth/oauth2/ropc.js
@@ -0,0 +1,59 @@
+const express = require('express');
+const router = express.Router();
+const jwt = require('jsonwebtoken');
+
+const users = [
+ {
+ username: 'foo',
+ password: 'bar'
+ }
+];
+
+// P
+// {
+// grant_type: 'password',
+// username: 'foo',
+// password: 'bar'
+// }
+
+// I
+// {
+// grant_type: 'password',
+// username: 'foo',
+// password: 'bar',
+// client_id: 'client_id_1',
+// client_secret: 'client_secret_1'
+// }
+router.post('/token', (req, res) => {
+ const { grant_type, username, password, client_id, client_secret } = req.body;
+
+ if (grant_type !== 'password') {
+ return res.status(401).json({ error: 'Invalid Grant Type' });
+ }
+
+ const user = users.find((u) => u.username == username && u.password == password);
+
+ if (!user) {
+ return res.status(401).json({ error: 'Invalid user credentials' });
+ }
+ var token = jwt.sign({ username, password }, 'bruno');
+ return res.json({ message: 'Authorization successful', access_token: token });
+});
+
+router.post('/resource', (req, res) => {
+ try {
+ const tokenString = req.header('Authorization');
+ const token = tokenString.split(' ')[1];
+ var decodedJwt = jwt.verify(token, 'bruno');
+ const { username, password } = decodedJwt;
+ const user = users.find((u) => u.username === username && u.password === password);
+ if (!user) {
+ return res.status(401).json({ error: 'Invalid token' });
+ }
+ return res.json({ resource: { name: 'foo', email: 'foo@bar.com' } });
+ } catch (err) {
+ return res.status(401).json({ error: 'Corrupt token' });
+ }
+});
+
+module.exports = router;
diff --git a/packages/bruno-tests/src/index.js b/packages/bruno-tests/src/index.js
index 286cfdad67..9ba6e31702 100644
--- a/packages/bruno-tests/src/index.js
+++ b/packages/bruno-tests/src/index.js
@@ -5,7 +5,7 @@ const cors = require('cors');
const multer = require('multer');
const app = new express();
-const port = process.env.PORT || 80;
+const port = process.env.PORT || 8080;
const upload = multer();
app.use(cors());