From 5f9c21d00f0e6bb76bc62e5d0335966610dcfa78 Mon Sep 17 00:00:00 2001 From: Nikolai2038 Date: Wed, 22 May 2024 22:38:16 +0300 Subject: [PATCH 001/587] Update linux installation instructions via apt - Add instructions to install gpg; - Use "gpg --list-keys" to let gpg create ".gnupg" directory with correct rights; - Use "arch=amd64" - see commit 6c8c87fe28fca64cdd8a4c9b6159054577a387f7. --- docs/readme/readme_ar.md | 9 ++++----- docs/readme/readme_bn.md | 9 ++++----- docs/readme/readme_cn.md | 9 ++++----- docs/readme/readme_de.md | 9 ++++----- docs/readme/readme_es.md | 9 ++++----- docs/readme/readme_fr.md | 9 ++++----- docs/readme/readme_it.md | 9 ++++----- docs/readme/readme_ja.md | 9 ++++----- docs/readme/readme_kr.md | 9 ++++----- docs/readme/readme_pl.md | 9 ++++----- docs/readme/readme_pt_br.md | 9 ++++----- docs/readme/readme_ro.md | 9 ++++----- docs/readme/readme_tr.md | 9 ++++----- docs/readme/readme_zhtw.md | 9 ++++----- readme.md | 7 +++---- 15 files changed, 59 insertions(+), 74 deletions(-) diff --git a/docs/readme/readme_ar.md b/docs/readme/readme_ar.md index 614d60a14..7f0eb8004 100644 --- a/docs/readme/readme_ar.md +++ b/docs/readme/readme_ar.md @@ -57,12 +57,11 @@ flatpak install com.usebruno.Bruno # على نظام Linux عبر Apt sudo mkdir -p /etc/apt/keyrings +sudo apt update && sudo apt install gpg +sudo gpg --list-keys sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266 - -echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list - -sudo apt update -sudo apt +echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list +sudo apt update && sudo apt install bruno ``` ### التشغيل عبر منصات متعددة 🖥️ diff --git a/docs/readme/readme_bn.md b/docs/readme/readme_bn.md index d353201f0..588dc3b24 100644 --- a/docs/readme/readme_bn.md +++ b/docs/readme/readme_bn.md @@ -42,12 +42,11 @@ snap install bruno # Apt এর মাধ্যমে লিনাক্সে sudo mkdir -p /etc/apt/keyrings +sudo apt update && sudo apt install gpg +sudo gpg --list-keys sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266 - -echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list - -sudo apt update -sudo apt install bruno +echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list +sudo apt update && sudo apt install bruno ``` ### একাধিক প্ল্যাটফর্মে চালান 🖥️ diff --git a/docs/readme/readme_cn.md b/docs/readme/readme_cn.md index 196ee2ff9..5fa138a16 100644 --- a/docs/readme/readme_cn.md +++ b/docs/readme/readme_cn.md @@ -46,12 +46,11 @@ snap install bruno # 在 Linux 上用 Apt 安装 sudo mkdir -p /etc/apt/keyrings +sudo apt update && sudo apt install gpg +sudo gpg --list-keys sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266 - -echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list - -sudo apt update -sudo apt install bruno +echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list +sudo apt update && sudo apt install bruno ``` ### 在 Mac 上通过 Homebrew 安装 🖥️ diff --git a/docs/readme/readme_de.md b/docs/readme/readme_de.md index ac1a75fe6..17658fe92 100644 --- a/docs/readme/readme_de.md +++ b/docs/readme/readme_de.md @@ -61,12 +61,11 @@ flatpak install com.usebruno.Bruno # Auf Linux via Apt sudo mkdir -p /etc/apt/keyrings +sudo apt update && sudo apt install gpg +sudo gpg --list-keys sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266 - -echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list - -sudo apt update -sudo apt install bruno +echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list +sudo apt update && sudo apt install bruno ``` ### Einsatz auf verschiedensten Plattformen 🖥️ diff --git a/docs/readme/readme_es.md b/docs/readme/readme_es.md index b1efdafa3..3c790c763 100644 --- a/docs/readme/readme_es.md +++ b/docs/readme/readme_es.md @@ -58,12 +58,11 @@ flatpak install com.usebruno.Bruno # En Linux con Apt sudo mkdir -p /etc/apt/keyrings +sudo apt update && sudo apt install gpg +sudo gpg --list-keys sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266 - -echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list - -sudo apt update -sudo apt install bruno +echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list +sudo apt update && sudo apt install bruno ``` ### Ejecútalo en múltiples plataformas 🖥️ diff --git a/docs/readme/readme_fr.md b/docs/readme/readme_fr.md index 54a735c76..5efeced51 100644 --- a/docs/readme/readme_fr.md +++ b/docs/readme/readme_fr.md @@ -46,12 +46,11 @@ snap install bruno # Linux via Apt sudo mkdir -p /etc/apt/keyrings +sudo apt update && sudo apt install gpg +sudo gpg --list-keys sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266 - -echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list - -sudo apt update -sudo apt install bruno +echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list +sudo apt update && sudo apt install bruno ``` ### Fonctionne sur de multiples plateformes 🖥️ diff --git a/docs/readme/readme_it.md b/docs/readme/readme_it.md index ff29804aa..2122cd1e0 100644 --- a/docs/readme/readme_it.md +++ b/docs/readme/readme_it.md @@ -40,12 +40,11 @@ snap install bruno # Su Linux tramite Apt sudo mkdir -p /etc/apt/keyrings +sudo apt update && sudo apt install gpg +sudo gpg --list-keys sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266 - -echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list - -sudo apt update -sudo apt install bruno +echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list +sudo apt update && sudo apt install bruno ``` ### Funziona su diverse piattaforme 🖥️ diff --git a/docs/readme/readme_ja.md b/docs/readme/readme_ja.md index bd8571969..b5d278bb9 100644 --- a/docs/readme/readme_ja.md +++ b/docs/readme/readme_ja.md @@ -61,12 +61,11 @@ flatpak install com.usebruno.Bruno # LinuxでAptを使ってインストール sudo mkdir -p /etc/apt/keyrings +sudo apt update && sudo apt install gpg +sudo gpg --list-keys sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266 - -echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list - -sudo apt update -sudo apt install bruno +echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list +sudo apt update && sudo apt install bruno ``` ### マルチプラットフォームでの実行に対応 🖥️ diff --git a/docs/readme/readme_kr.md b/docs/readme/readme_kr.md index 451e11ec0..2fd5f0f5b 100644 --- a/docs/readme/readme_kr.md +++ b/docs/readme/readme_kr.md @@ -40,12 +40,11 @@ snap install bruno # On Linux via Apt sudo mkdir -p /etc/apt/keyrings +sudo apt update && sudo apt install gpg +sudo gpg --list-keys sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266 - -echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list - -sudo apt update -sudo apt install bruno +echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list +sudo apt update && sudo apt install bruno ``` ### 여러 플랫폼에서 실행하세요. 🖥️ diff --git a/docs/readme/readme_pl.md b/docs/readme/readme_pl.md index e8462f655..fd434a39a 100644 --- a/docs/readme/readme_pl.md +++ b/docs/readme/readme_pl.md @@ -46,12 +46,11 @@ snap install bruno # On Linux via Apt sudo mkdir -p /etc/apt/keyrings +sudo apt update && sudo apt install gpg +sudo gpg --list-keys sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266 - -echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list - -sudo apt update -sudo apt install bruno +echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list +sudo apt update && sudo apt install bruno ``` ### Uruchom na wielu platformach 🖥️ diff --git a/docs/readme/readme_pt_br.md b/docs/readme/readme_pt_br.md index 84bf2b2b8..4fd7b42d3 100644 --- a/docs/readme/readme_pt_br.md +++ b/docs/readme/readme_pt_br.md @@ -57,12 +57,11 @@ flatpak install com.usebruno.Bruno # No Linux via Apt sudo mkdir -p /etc/apt/keyrings +sudo apt update && sudo apt install gpg +sudo gpg --list-keys sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266 - -echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list - -sudo apt update -sudo apt install bruno +echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list +sudo apt update && sudo apt install bruno ``` ### Execute em várias plataformas 🖥️ diff --git a/docs/readme/readme_ro.md b/docs/readme/readme_ro.md index de160f70d..53eb44d84 100644 --- a/docs/readme/readme_ro.md +++ b/docs/readme/readme_ro.md @@ -42,12 +42,11 @@ snap install bruno # Pe Linux cu Apt sudo mkdir -p /etc/apt/keyrings +sudo apt update && sudo apt install gpg +sudo gpg --list-keys sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266 - -echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list - -sudo apt update -sudo apt install bruno +echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list +sudo apt update && sudo apt install bruno ``` ### Utilizați pe mai multe platforme 🖥️ diff --git a/docs/readme/readme_tr.md b/docs/readme/readme_tr.md index 7c4e2747e..96b9a7a0a 100644 --- a/docs/readme/readme_tr.md +++ b/docs/readme/readme_tr.md @@ -46,12 +46,11 @@ snap install bruno # Apt aracılığıyla Linux'ta sudo mkdir -p /etc/apt/keyrings +sudo apt update && sudo apt install gpg +sudo gpg --list-keys sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266 - -echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list - -sudo apt update -sudo apt install bruno +echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list +sudo apt update && sudo apt install bruno ``` ### Birden fazla platformda çalıştırın 🖥️ diff --git a/docs/readme/readme_zhtw.md b/docs/readme/readme_zhtw.md index 222c75b97..6d08f5854 100644 --- a/docs/readme/readme_zhtw.md +++ b/docs/readme/readme_zhtw.md @@ -46,12 +46,11 @@ snap install bruno # 在 Linux 上使用 Apt 安裝 sudo mkdir -p /etc/apt/keyrings +sudo apt update && sudo apt install gpg +sudo gpg --list-keys sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266 - -echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list - -sudo apt update -sudo apt install bruno +echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list +sudo apt update && sudo apt install bruno ``` ### 跨多個平台運行 🖥️ diff --git a/readme.md b/readme.md index aae5d2f17..3e6248bdc 100644 --- a/readme.md +++ b/readme.md @@ -61,12 +61,11 @@ flatpak install com.usebruno.Bruno # On Linux via Apt sudo mkdir -p /etc/apt/keyrings +sudo apt update && sudo apt install gpg +sudo gpg --list-keys sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266 - echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list - -sudo apt update -sudo apt install bruno +sudo apt update && sudo apt install bruno ``` ### Run across multiple platforms 🖥️ From f97273342678105594ec1c140203c5a97fda7afc Mon Sep 17 00:00:00 2001 From: lzl0304 Date: Thu, 29 Aug 2024 10:30:38 +0800 Subject: [PATCH 002/587] bugfix/chokidar disables globbing --- packages/bruno-electron/src/app/watcher.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/bruno-electron/src/app/watcher.js b/packages/bruno-electron/src/app/watcher.js index 589cd29d8..13aeac15b 100644 --- a/packages/bruno-electron/src/app/watcher.js +++ b/packages/bruno-electron/src/app/watcher.js @@ -453,7 +453,8 @@ class Watcher { stabilityThreshold: 80, pollInterval: 10 }, - depth: 20 + depth: 20, + disableGlobbing: true }); let startedNewWatcher = false; From 22a9502976bab035ab421e81c1b2ec812ec7574a Mon Sep 17 00:00:00 2001 From: Mateusz Pietryga Date: Thu, 11 Apr 2024 08:12:46 +0200 Subject: [PATCH 003/587] fix: OAuth2 - auth is successful but token endpoint is returned instead of api endpoint (#1999) Setting oauth2 authorization no longer equals overwriting user-specified data in a request. The pre-requests made to obtain oauth2 access_token are now separated from actual API request. --- .../bruno-electron/src/ipc/network/index.js | 43 +++++------- .../src/ipc/network/oauth2-helper.js | 67 ++++++++++++------- 2 files changed, 56 insertions(+), 54 deletions(-) diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js index b0dfbfb68..6a438ece4 100644 --- a/packages/bruno-electron/src/ipc/network/index.js +++ b/packages/bruno-electron/src/ipc/network/index.js @@ -31,9 +31,9 @@ const { shouldUseProxy, PatchedHttpsProxyAgent } = require('../../utils/proxy-ut const { chooseFileToSave, writeBinaryFile, writeFile } = require('../../utils/filesystem'); const { getCookieStringForUrl, addCookieToJar, getDomainsWithCookies } = require('../../utils/cookies'); const { - resolveOAuth2AuthorizationCodeAccessToken, - transformClientCredentialsRequest, - transformPasswordCredentialsRequest + oauth2AuthorizeWithAuthorizationCode, + oauth2AuthorizeWithClientCredentials, + oauth2AuthorizeWithPasswordCredentials } = require('./oauth2-helper'); const Oauth2Store = require('../../store/oauth2'); const iconv = require('iconv-lite'); @@ -267,36 +267,23 @@ const configureRequest = async ( if (request.oauth2) { let requestCopy = cloneDeep(request); + interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars); + let accessToken; switch (request?.oauth2?.grantType) { - case 'authorization_code': - interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars); - const { data: authorizationCodeData, url: authorizationCodeAccessTokenUrl } = - await resolveOAuth2AuthorizationCodeAccessToken(requestCopy, collectionUid); - request.method = 'POST'; - request.headers['content-type'] = 'application/x-www-form-urlencoded'; - request.data = authorizationCodeData; - request.url = authorizationCodeAccessTokenUrl; + case 'authorization_code': { + ({ accessToken } = await oauth2AuthorizeWithAuthorizationCode(requestCopy, collectionUid)); break; - case 'client_credentials': - interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars); - const { data: clientCredentialsData, url: clientCredentialsAccessTokenUrl } = - await transformClientCredentialsRequest(requestCopy); - request.method = 'POST'; - request.headers['content-type'] = 'application/x-www-form-urlencoded'; - request.data = clientCredentialsData; - request.url = clientCredentialsAccessTokenUrl; + } + case 'client_credentials': { + ({ accessToken } = await oauth2AuthorizeWithClientCredentials(requestCopy, collectionUid)); break; - case 'password': - interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars); - const { data: passwordData, url: passwordAccessTokenUrl } = await transformPasswordCredentialsRequest( - requestCopy - ); - request.method = 'POST'; - request.headers['content-type'] = 'application/x-www-form-urlencoded'; - request.data = passwordData; - request.url = passwordAccessTokenUrl; + } + case 'password': { + ({ accessToken } = await oauth2AuthorizeWithPasswordCredentials(requestCopy, collectionUid)); break; + } } + request.headers['Authorization'] = `Bearer ${accessToken}`; } if (request.awsv4config) { diff --git a/packages/bruno-electron/src/ipc/network/oauth2-helper.js b/packages/bruno-electron/src/ipc/network/oauth2-helper.js index 144542418..3c8489fa3 100644 --- a/packages/bruno-electron/src/ipc/network/oauth2-helper.js +++ b/packages/bruno-electron/src/ipc/network/oauth2-helper.js @@ -2,6 +2,7 @@ const { get, cloneDeep } = require('lodash'); const crypto = require('crypto'); const { authorizeUserInWindow } = require('./authorize-user-in-window'); const Oauth2Store = require('../../store/oauth2'); +const { makeAxiosInstance } = require('./axios-instance'); const generateCodeVerifier = () => { return crypto.randomBytes(22).toString('hex'); @@ -16,14 +17,14 @@ const generateCodeChallenge = (codeVerifier) => { // AUTHORIZATION CODE -const resolveOAuth2AuthorizationCodeAccessToken = async (request, collectionUid) => { +const oauth2AuthorizeWithAuthorizationCode = async (request, collectionUid) => { let codeVerifier = generateCodeVerifier(); let codeChallenge = generateCodeChallenge(codeVerifier); let requestCopy = cloneDeep(request); const { authorizationCode } = await getOAuth2AuthorizationCode(requestCopy, codeChallenge, collectionUid); const oAuth = get(requestCopy, 'oauth2', {}); - const { clientId, clientSecret, callbackUrl, scope, pkce } = oAuth; + const { clientId, clientSecret, callbackUrl, pkce } = oAuth; const data = { grant_type: 'authorization_code', code: authorizationCode, @@ -36,10 +37,16 @@ const resolveOAuth2AuthorizationCodeAccessToken = async (request, collectionUid) } const url = requestCopy?.oauth2?.accessTokenUrl; - return { - data, - url - }; + + request.method = 'POST'; + request.headers['content-type'] = 'application/x-www-form-urlencoded'; + request.data = data; + request.url = url; + + const axiosInstance = makeAxiosInstance(); + let response = await axiosInstance(request); + let accessToken = JSON.parse(response.data).access_token; + return { accessToken }; }; const getOAuth2AuthorizationCode = (request, codeChallenge, collectionUid) => { @@ -79,7 +86,7 @@ const getOAuth2AuthorizationCode = (request, codeChallenge, collectionUid) => { // CLIENT CREDENTIALS -const transformClientCredentialsRequest = async (request) => { +const oauth2AuthorizeWithClientCredentials = async (request) => { let requestCopy = cloneDeep(request); const oAuth = get(requestCopy, 'oauth2', {}); const { clientId, clientSecret, scope } = oAuth; @@ -91,18 +98,23 @@ const transformClientCredentialsRequest = async (request) => { if (scope) { data.scope = scope; } - const url = requestCopy?.oauth2?.accessTokenUrl; - return { - data, - url - }; + + request.method = 'POST'; + request.headers['content-type'] = 'application/x-www-form-urlencoded'; + request.data = data; + request.url = requestCopy?.oauth2?.accessTokenUrl; + + const axiosInstance = makeAxiosInstance(); + let response = await axiosInstance(request); + let accessToken = JSON.parse(response.data).access_token; + + return { accessToken }; }; // PASSWORD CREDENTIALS -const transformPasswordCredentialsRequest = async (request) => { - let requestCopy = cloneDeep(request); - const oAuth = get(requestCopy, 'oauth2', {}); +const oauth2AuthorizeWithPasswordCredentials = async (request) => { + const oAuth = get(request, 'oauth2', {}); const { username, password, clientId, clientSecret, scope } = oAuth; const data = { grant_type: 'password', @@ -114,16 +126,19 @@ const transformPasswordCredentialsRequest = async (request) => { if (scope) { data.scope = scope; } - const url = requestCopy?.oauth2?.accessTokenUrl; - return { - data, - url - }; -}; -module.exports = { - resolveOAuth2AuthorizationCodeAccessToken, - getOAuth2AuthorizationCode, - transformClientCredentialsRequest, - transformPasswordCredentialsRequest + request.method = 'POST'; + request.headers['content-type'] = 'application/x-www-form-urlencoded'; + request.data = data; + request.url = request?.oauth2?.accessTokenUrl; + + const axiosInstance = makeAxiosInstance(); + let response = await axiosInstance(request); + let accessToken = JSON.parse(response.data).access_token; + return { accessToken }; +}; +module.exports = { + oauth2AuthorizeWithAuthorizationCode, + oauth2AuthorizeWithClientCredentials, + oauth2AuthorizeWithPasswordCredentials }; From 63252d3ee2ea4e3e61d19dff7c05073536d98cc8 Mon Sep 17 00:00:00 2001 From: Mateusz Pietryga Date: Thu, 11 Apr 2024 08:12:46 +0200 Subject: [PATCH 004/587] feat: OAuth2 - Store authorization information Results of oauth2 authorization flow (i.e. access_token but also refresh_token, id_token, scope or any other information returned from token request) are stored in a collection specific cache. It is persisted in the file system, and will be automatically reused when executing requests until the cache is purged (using Clear Cache button available in all related views). --- .../Auth/OAuth2/ClientCredentials/index.js | 23 ++++++- .../Auth/OAuth2/PasswordCredentials/index.js | 23 ++++++- .../Auth/OAuth2/ClientCredentials/index.js | 23 ++++++- .../Auth/OAuth2/PasswordCredentials/index.js | 23 ++++++- .../bruno-electron/src/ipc/network/index.js | 10 +-- .../src/ipc/network/interpolate-vars.js | 14 ----- .../src/ipc/network/oauth2-helper.js | 61 ++++++++++++++----- packages/bruno-electron/src/store/oauth2.js | 1 + 8 files changed, 132 insertions(+), 46 deletions(-) diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/ClientCredentials/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/ClientCredentials/index.js index d69122b48..59a9bdeec 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/ClientCredentials/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/ClientCredentials/index.js @@ -7,6 +7,8 @@ import { saveCollectionRoot, sendCollectionOauth2Request } from 'providers/Redux import StyledWrapper from './StyledWrapper'; import { inputsConfig } from './inputsConfig'; import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections/index'; +import { clearOauth2Cache } from 'utils/network'; +import toast from 'react-hot-toast'; const OAuth2ClientCredentials = ({ collection }) => { const dispatch = useDispatch(); @@ -39,6 +41,16 @@ const OAuth2ClientCredentials = ({ collection }) => { ); }; + const handleClearCache = (e) => { + clearOauth2Cache(collection?.uid) + .then(() => { + toast.success('cleared cache successfully'); + }) + .catch((err) => { + toast.error(err.message); + }); + }; + return ( {inputsConfig.map((input) => { @@ -60,9 +72,14 @@ const OAuth2ClientCredentials = ({ collection }) => { ); })} - +
+ + +
); }; diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/PasswordCredentials/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/PasswordCredentials/index.js index d2d9eed1f..b07ceb72a 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/PasswordCredentials/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/PasswordCredentials/index.js @@ -7,6 +7,8 @@ import { saveCollectionRoot, sendCollectionOauth2Request } from 'providers/Redux import StyledWrapper from './StyledWrapper'; import { inputsConfig } from './inputsConfig'; import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections/index'; +import { clearOauth2Cache } from 'utils/network'; +import toast from 'react-hot-toast'; const OAuth2AuthorizationCode = ({ item, collection }) => { const dispatch = useDispatch(); @@ -41,6 +43,16 @@ const OAuth2AuthorizationCode = ({ item, collection }) => { ); }; + const handleClearCache = (e) => { + clearOauth2Cache(collection?.uid) + .then(() => { + toast.success('cleared cache successfully'); + }) + .catch((err) => { + toast.error(err.message); + }); + }; + return ( {inputsConfig.map((input) => { @@ -62,9 +74,14 @@ const OAuth2AuthorizationCode = ({ item, collection }) => { ); })} - +
+ + +
); }; 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 index a43c8f0ad..9c9f1553d 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/ClientCredentials/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/ClientCredentials/index.js @@ -7,6 +7,8 @@ import { updateAuth } from 'providers/ReduxStore/slices/collections'; import { saveRequest, sendRequest } from 'providers/ReduxStore/slices/collections/actions'; import StyledWrapper from './StyledWrapper'; import { inputsConfig } from './inputsConfig'; +import { clearOauth2Cache } from 'utils/network'; +import toast from 'react-hot-toast'; const OAuth2ClientCredentials = ({ item, collection }) => { const dispatch = useDispatch(); @@ -40,6 +42,16 @@ const OAuth2ClientCredentials = ({ item, collection }) => { ); }; + const handleClearCache = (e) => { + clearOauth2Cache(collection?.uid) + .then(() => { + toast.success('cleared cache successfully'); + }) + .catch((err) => { + toast.error(err.message); + }); + }; + return ( {inputsConfig.map((input) => { @@ -62,9 +74,14 @@ const OAuth2ClientCredentials = ({ item, collection }) => { ); })} - +
+ + +
); }; diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/PasswordCredentials/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/PasswordCredentials/index.js index 4ec8c1faa..543a17164 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/PasswordCredentials/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/PasswordCredentials/index.js @@ -7,6 +7,8 @@ import { updateAuth } from 'providers/ReduxStore/slices/collections'; import { saveRequest, sendRequest } from 'providers/ReduxStore/slices/collections/actions'; import StyledWrapper from './StyledWrapper'; import { inputsConfig } from './inputsConfig'; +import { clearOauth2Cache } from 'utils/network'; +import toast from 'react-hot-toast'; const OAuth2AuthorizationCode = ({ item, collection }) => { const dispatch = useDispatch(); @@ -42,6 +44,16 @@ const OAuth2AuthorizationCode = ({ item, collection }) => { ); }; + const handleClearCache = (e) => { + clearOauth2Cache(collection?.uid) + .then(() => { + toast.success('cleared cache successfully'); + }) + .catch((err) => { + toast.error(err.message); + }); + }; + return ( {inputsConfig.map((input) => { @@ -64,9 +76,14 @@ const OAuth2AuthorizationCode = ({ item, collection }) => { ); })} - +
+ + +
); }; diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js index 6a438ece4..407aeef39 100644 --- a/packages/bruno-electron/src/ipc/network/index.js +++ b/packages/bruno-electron/src/ipc/network/index.js @@ -268,22 +268,22 @@ const configureRequest = async ( if (request.oauth2) { let requestCopy = cloneDeep(request); interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars); - let accessToken; + let credentials; switch (request?.oauth2?.grantType) { case 'authorization_code': { - ({ accessToken } = await oauth2AuthorizeWithAuthorizationCode(requestCopy, collectionUid)); + ({ credentials } = await oauth2AuthorizeWithAuthorizationCode(requestCopy, collectionUid)); break; } case 'client_credentials': { - ({ accessToken } = await oauth2AuthorizeWithClientCredentials(requestCopy, collectionUid)); + ({ credentials } = await oauth2AuthorizeWithClientCredentials(requestCopy, collectionUid)); break; } case 'password': { - ({ accessToken } = await oauth2AuthorizeWithPasswordCredentials(requestCopy, collectionUid)); + ({ credentials } = await oauth2AuthorizeWithPasswordCredentials(requestCopy, collectionUid)); break; } } - request.headers['Authorization'] = `Bearer ${accessToken}`; + request.headers['Authorization'] = `Bearer ${credentials.access_token}`; } if (request.awsv4config) { diff --git a/packages/bruno-electron/src/ipc/network/interpolate-vars.js b/packages/bruno-electron/src/ipc/network/interpolate-vars.js index 90b072658..e8ec60e25 100644 --- a/packages/bruno-electron/src/ipc/network/interpolate-vars.js +++ b/packages/bruno-electron/src/ipc/network/interpolate-vars.js @@ -160,14 +160,6 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc request.oauth2.clientId = clientId; request.oauth2.clientSecret = clientSecret; request.oauth2.scope = scope; - request.data = { - grant_type: 'password', - username, - password, - client_id: clientId, - client_secret: clientSecret, - scope - }; break; case 'authorization_code': request.oauth2.callbackUrl = _interpolate(request.oauth2.callbackUrl) || ''; @@ -187,12 +179,6 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc request.oauth2.clientId = clientId; request.oauth2.clientSecret = clientSecret; request.oauth2.scope = scope; - request.data = { - grant_type: 'client_credentials', - client_id: clientId, - client_secret: clientSecret, - scope - }; break; default: break; diff --git a/packages/bruno-electron/src/ipc/network/oauth2-helper.js b/packages/bruno-electron/src/ipc/network/oauth2-helper.js index 3c8489fa3..4a447675e 100644 --- a/packages/bruno-electron/src/ipc/network/oauth2-helper.js +++ b/packages/bruno-electron/src/ipc/network/oauth2-helper.js @@ -4,6 +4,8 @@ const { authorizeUserInWindow } = require('./authorize-user-in-window'); const Oauth2Store = require('../../store/oauth2'); const { makeAxiosInstance } = require('./axios-instance'); +const oauth2Store = new Oauth2Store(); + const generateCodeVerifier = () => { return crypto.randomBytes(22).toString('hex'); }; @@ -15,9 +17,27 @@ const generateCodeChallenge = (codeVerifier) => { return base64Hash.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); }; +const getPersistedOauth2Credentials = (collectionUid) => { + const collectionOauthStore = oauth2Store.getOauth2DataOfCollection(collectionUid); + const cachedCredentials = collectionOauthStore.credentials; + return { cachedCredentials }; +}; + +const persistOauth2Credentials = (credentials, collectionUid) => { + const collectionOauthStore = oauth2Store.getOauth2DataOfCollection(collectionUid); + collectionOauthStore.credentials = credentials; + oauth2Store.updateOauth2DataOfCollection(collectionUid, collectionOauthStore); +}; + // AUTHORIZATION CODE const oauth2AuthorizeWithAuthorizationCode = async (request, collectionUid) => { + const { cachedCredentials } = getPersistedOauth2Credentials(collectionUid); + if (cachedCredentials?.access_token) { + console.log('Reusing Stored access token'); + return { credentials: cachedCredentials }; + } + let codeVerifier = generateCodeVerifier(); let codeChallenge = generateCodeChallenge(codeVerifier); @@ -36,17 +56,16 @@ const oauth2AuthorizeWithAuthorizationCode = async (request, collectionUid) => { data['code_verifier'] = codeVerifier; } - const url = requestCopy?.oauth2?.accessTokenUrl; - request.method = 'POST'; request.headers['content-type'] = 'application/x-www-form-urlencoded'; request.data = data; - request.url = url; + request.url = request?.oauth2?.accessTokenUrl; const axiosInstance = makeAxiosInstance(); - let response = await axiosInstance(request); - let accessToken = JSON.parse(response.data).access_token; - return { accessToken }; + const response = await axiosInstance(request); + const credentials = JSON.parse(response.data); + persistOauth2Credentials(credentials, collectionUid); + return { credentials }; }; const getOAuth2AuthorizationCode = (request, codeChallenge, collectionUid) => { @@ -71,7 +90,6 @@ const getOAuth2AuthorizationCode = (request, codeChallenge, collectionUid) => { authorizationUrlWithQueryParams.searchParams.append('state', state); } try { - const oauth2Store = new Oauth2Store(); const { authorizationCode } = await authorizeUserInWindow({ authorizeUrl: authorizationUrlWithQueryParams.toString(), callbackUrl, @@ -86,7 +104,13 @@ const getOAuth2AuthorizationCode = (request, codeChallenge, collectionUid) => { // CLIENT CREDENTIALS -const oauth2AuthorizeWithClientCredentials = async (request) => { +const oauth2AuthorizeWithClientCredentials = async (request, collectionUid) => { + const { cachedCredentials } = getPersistedOauth2Credentials(collectionUid); + if (cachedCredentials?.access_token) { + console.log('Reusing Stored access token'); + return { credentials: cachedCredentials }; + } + let requestCopy = cloneDeep(request); const oAuth = get(requestCopy, 'oauth2', {}); const { clientId, clientSecret, scope } = oAuth; @@ -102,18 +126,24 @@ const oauth2AuthorizeWithClientCredentials = async (request) => { request.method = 'POST'; request.headers['content-type'] = 'application/x-www-form-urlencoded'; request.data = data; - request.url = requestCopy?.oauth2?.accessTokenUrl; + request.url = request?.oauth2?.accessTokenUrl; const axiosInstance = makeAxiosInstance(); let response = await axiosInstance(request); - let accessToken = JSON.parse(response.data).access_token; - - return { accessToken }; + let credentials = JSON.parse(response.data); + persistOauth2Credentials(credentials, collectionUid); + return { credentials }; }; // PASSWORD CREDENTIALS -const oauth2AuthorizeWithPasswordCredentials = async (request) => { +const oauth2AuthorizeWithPasswordCredentials = async (request, collectionUid) => { + const { cachedCredentials } = getPersistedOauth2Credentials(collectionUid); + if (cachedCredentials?.access_token) { + console.log('Reusing Stored access token'); + return { credentials: cachedCredentials }; + } + const oAuth = get(request, 'oauth2', {}); const { username, password, clientId, clientSecret, scope } = oAuth; const data = { @@ -134,8 +164,9 @@ const oauth2AuthorizeWithPasswordCredentials = async (request) => { const axiosInstance = makeAxiosInstance(); let response = await axiosInstance(request); - let accessToken = JSON.parse(response.data).access_token; - return { accessToken }; + let credentials = JSON.parse(response.data); + persistOauth2Credentials(credentials, collectionUid); + return { credentials }; }; module.exports = { oauth2AuthorizeWithAuthorizationCode, diff --git a/packages/bruno-electron/src/store/oauth2.js b/packages/bruno-electron/src/store/oauth2.js index b0a2255b5..b24c560aa 100644 --- a/packages/bruno-electron/src/store/oauth2.js +++ b/packages/bruno-electron/src/store/oauth2.js @@ -85,6 +85,7 @@ class Oauth2Store { let oauth2DataForCollection = this.getOauth2DataOfCollection(collectionUid); delete oauth2DataForCollection.sessionId; + delete oauth2DataForCollection.credentials; let updatedOauth2Data = oauth2Data.filter((d) => d.collectionUid !== collectionUid); updatedOauth2Data.push({ ...oauth2DataForCollection }); From 4afcd442166af55cbc1b6dd530b0821a4d06b2ae Mon Sep 17 00:00:00 2001 From: Mateusz Pietryga Date: Sat, 13 Apr 2024 23:57:32 +0200 Subject: [PATCH 005/587] feat: OAuth2 - Include resolved authorization details in req object to be usable by scripts The new variable 'credentials' is now available in 'req' object. It is added automatically during request preparation if oauth2 method is used and is value is either evaluated or retrieved from collection oauth2 cache. --- .../bruno-electron/src/ipc/network/index.js | 1 + packages/bruno-js/src/bruno-request.js | 18 ++++++++++++------ .../src/sandbox/quickjs/shims/bruno-request.js | 2 ++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js index 407aeef39..001e56be5 100644 --- a/packages/bruno-electron/src/ipc/network/index.js +++ b/packages/bruno-electron/src/ipc/network/index.js @@ -283,6 +283,7 @@ const configureRequest = async ( break; } } + request.credentials = credentials; request.headers['Authorization'] = `Bearer ${credentials.access_token}`; } diff --git a/packages/bruno-js/src/bruno-request.js b/packages/bruno-js/src/bruno-request.js index b0d22b6ac..d716727fb 100644 --- a/packages/bruno-js/src/bruno-request.js +++ b/packages/bruno-js/src/bruno-request.js @@ -6,7 +6,8 @@ class BrunoRequest { * - req.headers * - req.timeout * - req.body - * + * - req.credentials + * * Above shorthands are useful for accessing the request properties directly in the scripts * It must be noted that the user cannot set these properties directly. * They should use the respective setter methods to set these properties. @@ -17,13 +18,14 @@ class BrunoRequest { this.method = req.method; this.headers = req.headers; this.timeout = req.timeout; + this.credentials = req.credentials; /** * We automatically parse the JSON body if the content type is JSON * This is to make it easier for the user to access the body directly - * + * * It must be noted that the request data is always a string and is what gets sent over the network - * If the user wants to access the raw data, they can use getBody({raw: true}) method + * If the user wants to access the raw data, they can use getBody({raw: true}) method */ const isJson = this.hasJSONContentType(this.req.headers); if (isJson) { @@ -84,6 +86,10 @@ class BrunoRequest { this.req.headers[name] = value; } + getCredentials() { + return this.credentials; + } + hasJSONContentType(headers) { const contentType = headers?.['Content-Type'] || headers?.['content-type'] || ''; return contentType.includes('json'); @@ -91,7 +97,7 @@ class BrunoRequest { /** * Get the body of the request - * + * * We automatically parse and return the JSON body if the content type is JSON * If the user wants the raw body, they can pass the raw option as true */ @@ -115,7 +121,7 @@ class BrunoRequest { * Otherwise * - We set the request data as the data itself * - We set the body property as the data itself - * + * * If the user wants to override this behavior, they can pass the raw option as true */ setBody(data, options = {}) { @@ -168,7 +174,7 @@ class BrunoRequest { __isObject(obj) { return obj !== null && typeof obj === 'object'; } - + disableParsingResponseJson() { this.req.__brunoDisableParsingResponseJson = true; diff --git a/packages/bruno-js/src/sandbox/quickjs/shims/bruno-request.js b/packages/bruno-js/src/sandbox/quickjs/shims/bruno-request.js index 1edfaaadb..0990270ac 100644 --- a/packages/bruno-js/src/sandbox/quickjs/shims/bruno-request.js +++ b/packages/bruno-js/src/sandbox/quickjs/shims/bruno-request.js @@ -8,12 +8,14 @@ const addBrunoRequestShimToContext = (vm, req) => { const headers = marshallToVm(req.getHeaders(), vm); const body = marshallToVm(req.getBody(), vm); const timeout = marshallToVm(req.getTimeout(), vm); + const credentials = marshallToVm(req.getCredentials(), vm); vm.setProp(reqObject, 'url', url); vm.setProp(reqObject, 'method', method); vm.setProp(reqObject, 'headers', headers); vm.setProp(reqObject, 'body', body); vm.setProp(reqObject, 'timeout', timeout); + vm.setProp(reqObject, 'credentials', credentials); url.dispose(); method.dispose(); From d982e35a17f450b1616f7ce65d3591c38221eee6 Mon Sep 17 00:00:00 2001 From: Mateusz Pietryga Date: Sat, 13 Apr 2024 23:59:36 +0200 Subject: [PATCH 006/587] feat: OAuth2 - Do not make axios request when executing collection level Get Access Token action The actual the authorization request is now part of request preparation, and its response is returned for post-request script processing. --- .../bruno-electron/src/ipc/network/index.js | 37 +++++++------- .../src/ipc/network/oauth2-helper.js | 12 ++--- .../ipc/network/prepare-collection-request.js | 49 ------------------- .../src/ipc/network/prepare-request.js | 16 +++--- 4 files changed, 32 insertions(+), 82 deletions(-) delete mode 100644 packages/bruno-electron/src/ipc/network/prepare-collection-request.js diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js index 001e56be5..bca74c334 100644 --- a/packages/bruno-electron/src/ipc/network/index.js +++ b/packages/bruno-electron/src/ipc/network/index.js @@ -12,7 +12,6 @@ const { ipcMain } = require('electron'); const { isUndefined, isNull, each, get, compact, cloneDeep, forOwn, extend } = require('lodash'); const { VarsRuntime, AssertRuntime, ScriptRuntime, TestRuntime } = require('@usebruno/js'); const prepareRequest = require('./prepare-request'); -const prepareCollectionRequest = require('./prepare-collection-request'); const prepareGqlIntrospectionRequest = require('./prepare-gql-introspection-request'); const { cancelTokens, saveCancelToken, deleteCancelToken } = require('../../utils/cancel-token'); const { uuid } = require('../../utils/common'); @@ -268,22 +267,23 @@ const configureRequest = async ( if (request.oauth2) { let requestCopy = cloneDeep(request); interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars); - let credentials; + let credentials, response; switch (request?.oauth2?.grantType) { case 'authorization_code': { - ({ credentials } = await oauth2AuthorizeWithAuthorizationCode(requestCopy, collectionUid)); + ({ credentials, response } = await oauth2AuthorizeWithAuthorizationCode(requestCopy, collectionUid)); break; } case 'client_credentials': { - ({ credentials } = await oauth2AuthorizeWithClientCredentials(requestCopy, collectionUid)); + ({ credentials, response } = await oauth2AuthorizeWithClientCredentials(requestCopy, collectionUid)); break; } case 'password': { - ({ credentials } = await oauth2AuthorizeWithPasswordCredentials(requestCopy, collectionUid)); + ({ credentials, response } = await oauth2AuthorizeWithPasswordCredentials(requestCopy, collectionUid)); break; } } request.credentials = credentials; + request.authRequestResponse = response; request.headers['Authorization'] = `Bearer ${credentials.access_token}`; } @@ -704,7 +704,11 @@ const registerNetworkIpc = (mainWindow) => { const collectionRoot = get(collection, 'root', {}); const _request = collectionRoot?.request; - const request = prepareCollectionRequest(_request, collectionRoot, collectionPath); + const request = prepareRequest(_request, collectionRoot, collectionPath); + + // Script from this collection-level pseudo-request should be erased as it duplicates the collection script + delete request.script; + const envVars = getEnvVars(environment); const processEnvVars = getProcessEnvVars(collectionUid); const brunoConfig = getBrunoConfig(collectionUid); @@ -724,7 +728,7 @@ const registerNetworkIpc = (mainWindow) => { ); interpolateVars(request, envVars, collection.runtimeVariables, processEnvVars); - const axiosInstance = await configureRequest( + await configureRequest( collection.uid, request, envVars, @@ -733,19 +737,13 @@ const registerNetworkIpc = (mainWindow) => { collectionPath ); - try { - response = await axiosInstance(request); - } catch (error) { - if (error?.response) { - response = error.response; - } else { - return Promise.reject(error); - } + const response = request.authRequestResponse; + // When credentials are loaded from cache, authRequestResponse has no data + if (response.data) { + const { data } = parseDataFromResponse(response, request.__brunoDisableParsingResponseJson); + response.data = data; } - const { data } = parseDataFromResponse(response, request.__brunoDisableParsingResponseJson); - response.data = data; - await runPostResponse( request, response, @@ -763,7 +761,8 @@ const registerNetworkIpc = (mainWindow) => { status: response.status, statusText: response.statusText, headers: response.headers, - data: response.data + data: response.data, + credentials: request.credentials }; } catch (error) { return Promise.reject(error); diff --git a/packages/bruno-electron/src/ipc/network/oauth2-helper.js b/packages/bruno-electron/src/ipc/network/oauth2-helper.js index 4a447675e..cdff9627d 100644 --- a/packages/bruno-electron/src/ipc/network/oauth2-helper.js +++ b/packages/bruno-electron/src/ipc/network/oauth2-helper.js @@ -35,7 +35,7 @@ const oauth2AuthorizeWithAuthorizationCode = async (request, collectionUid) => { const { cachedCredentials } = getPersistedOauth2Credentials(collectionUid); if (cachedCredentials?.access_token) { console.log('Reusing Stored access token'); - return { credentials: cachedCredentials }; + return { credentials: cachedCredentials, response: {} }; } let codeVerifier = generateCodeVerifier(); @@ -65,7 +65,7 @@ const oauth2AuthorizeWithAuthorizationCode = async (request, collectionUid) => { const response = await axiosInstance(request); const credentials = JSON.parse(response.data); persistOauth2Credentials(credentials, collectionUid); - return { credentials }; + return { credentials, response }; }; const getOAuth2AuthorizationCode = (request, codeChallenge, collectionUid) => { @@ -108,7 +108,7 @@ const oauth2AuthorizeWithClientCredentials = async (request, collectionUid) => { const { cachedCredentials } = getPersistedOauth2Credentials(collectionUid); if (cachedCredentials?.access_token) { console.log('Reusing Stored access token'); - return { credentials: cachedCredentials }; + return { credentials: cachedCredentials, response: {} }; } let requestCopy = cloneDeep(request); @@ -132,7 +132,7 @@ const oauth2AuthorizeWithClientCredentials = async (request, collectionUid) => { let response = await axiosInstance(request); let credentials = JSON.parse(response.data); persistOauth2Credentials(credentials, collectionUid); - return { credentials }; + return { credentials, response }; }; // PASSWORD CREDENTIALS @@ -141,7 +141,7 @@ const oauth2AuthorizeWithPasswordCredentials = async (request, collectionUid) => const { cachedCredentials } = getPersistedOauth2Credentials(collectionUid); if (cachedCredentials?.access_token) { console.log('Reusing Stored access token'); - return { credentials: cachedCredentials }; + return { credentials: cachedCredentials, response: {} }; } const oAuth = get(request, 'oauth2', {}); @@ -166,7 +166,7 @@ const oauth2AuthorizeWithPasswordCredentials = async (request, collectionUid) => let response = await axiosInstance(request); let credentials = JSON.parse(response.data); persistOauth2Credentials(credentials, collectionUid); - return { credentials }; + return { credentials, response }; }; module.exports = { oauth2AuthorizeWithAuthorizationCode, diff --git a/packages/bruno-electron/src/ipc/network/prepare-collection-request.js b/packages/bruno-electron/src/ipc/network/prepare-collection-request.js deleted file mode 100644 index 5fd630594..000000000 --- a/packages/bruno-electron/src/ipc/network/prepare-collection-request.js +++ /dev/null @@ -1,49 +0,0 @@ -const { get, each } = require('lodash'); -const { setAuthHeaders } = require('./prepare-request'); - -const prepareCollectionRequest = (request, collectionRoot) => { - const headers = {}; - let contentTypeDefined = false; - let url = request.url; - - // collection headers - each(get(collectionRoot, 'request.headers', []), (h) => { - if (h.enabled) { - headers[h.name] = h.value; - if (h.name.toLowerCase() === 'content-type') { - contentTypeDefined = true; - } - } - }); - - each(request.headers, (h) => { - if (h.enabled) { - headers[h.name] = h.value; - if (h.name.toLowerCase() === 'content-type') { - contentTypeDefined = true; - } - } - }); - - let axiosRequest = { - mode: request?.body?.mode, - method: request.method, - url, - headers, - responseType: 'arraybuffer' - }; - - axiosRequest = setAuthHeaders(axiosRequest, request, collectionRoot); - - if (request.script) { - axiosRequest.script = request.script; - } - - axiosRequest.vars = request.vars; - - axiosRequest.method = 'POST'; - - return axiosRequest; -}; - -module.exports = prepareCollectionRequest; diff --git a/packages/bruno-electron/src/ipc/network/prepare-request.js b/packages/bruno-electron/src/ipc/network/prepare-request.js index 0bac42af9..73acf2885 100644 --- a/packages/bruno-electron/src/ipc/network/prepare-request.js +++ b/packages/bruno-electron/src/ipc/network/prepare-request.js @@ -383,7 +383,7 @@ const prepareRequest = (item, collection) => { }); let axiosRequest = { - mode: request.body.mode, + mode: request?.body?.mode, method: request.method, url, headers, @@ -393,7 +393,7 @@ const prepareRequest = (item, collection) => { axiosRequest = setAuthHeaders(axiosRequest, request, collectionRoot); - if (request.body.mode === 'json') { + if (request.body?.mode === 'json') { if (!contentTypeDefined) { axiosRequest.headers['content-type'] = 'application/json'; } @@ -404,28 +404,28 @@ const prepareRequest = (item, collection) => { } } - if (request.body.mode === 'text') { + if (request.body?.mode === 'text') { if (!contentTypeDefined) { axiosRequest.headers['content-type'] = 'text/plain'; } axiosRequest.data = request.body.text; } - if (request.body.mode === 'xml') { + if (request.body?.mode === 'xml') { if (!contentTypeDefined) { axiosRequest.headers['content-type'] = 'text/xml'; } axiosRequest.data = request.body.xml; } - if (request.body.mode === 'sparql') { + if (request.body?.mode === 'sparql') { if (!contentTypeDefined) { axiosRequest.headers['content-type'] = 'application/sparql-query'; } axiosRequest.data = request.body.sparql; } - if (request.body.mode === 'formUrlEncoded') { + if (request.body?.mode === 'formUrlEncoded') { if (!contentTypeDefined) { axiosRequest.headers['content-type'] = 'application/x-www-form-urlencoded'; } @@ -433,7 +433,7 @@ const prepareRequest = (item, collection) => { axiosRequest.data = buildFormUrlEncodedPayload(enabledParams); } - if (request.body.mode === 'multipartForm') { + if (request.body?.mode === 'multipartForm') { axiosRequest.headers['content-type'] = 'multipart/form-data'; const params = {}; const enabledParams = filter(request.body.multipartForm, (p) => p.enabled); @@ -441,7 +441,7 @@ const prepareRequest = (item, collection) => { axiosRequest.data = params; } - if (request.body.mode === 'graphql') { + if (request.body?.mode === 'graphql') { const graphqlQuery = { query: get(request, 'body.graphql.query'), // https://github.com/usebruno/bruno/issues/884 - we must only parse the variables after the variable interpolation From 2064cc88abf4ac9e296f519bede540b3a1fd82c3 Mon Sep 17 00:00:00 2001 From: Mateusz Pietryga Date: Sun, 5 May 2024 23:47:52 +0200 Subject: [PATCH 007/587] feat: OAuth2 - automatically handle Bearer token type only According to RFC6749 Section 7.1, The client MUST NOT use an access token if it does not understand the token type. At this point bruno only understands 'bearer' token_type. --- packages/bruno-electron/src/ipc/network/index.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js index bca74c334..be81fa01c 100644 --- a/packages/bruno-electron/src/ipc/network/index.js +++ b/packages/bruno-electron/src/ipc/network/index.js @@ -284,7 +284,13 @@ const configureRequest = async ( } request.credentials = credentials; request.authRequestResponse = response; - request.headers['Authorization'] = `Bearer ${credentials.access_token}`; + + // Bruno can handle bearer token type automatically. + // Other - more exotic token types are not touched + // Users are free to use pre-request script and operate on req.credentials.access_token variable + if (credentials?.token_type.toLowerCase() === 'bearer') { + request.headers['Authorization'] = `Bearer ${credentials.access_token}`; + } } if (request.awsv4config) { From dd9cb21f8c0f86df4f8c529ce1b6d4d04c39f7bb Mon Sep 17 00:00:00 2001 From: Mateusz Pietryga Date: Sun, 5 May 2024 21:40:26 +0200 Subject: [PATCH 008/587] feat: OAuth2 - UI for OAuth2 Credentials independent of the Request Output pane fix: typo - rename OAuth2PasswordCredentials component fix: typo - Use the same name for AuthMode - OAuth 2.0 in collection and request level --- .../CollectionSettings/Auth/AuthMode/index.js | 2 +- .../Auth/OAuth2/AuthorizationCode/index.js | 21 ----- .../Auth/OAuth2/ClientCredentials/index.js | 20 ----- .../Auth/OAuth2/PasswordCredentials/index.js | 26 +----- .../CollectionSettings/Auth/OAuth2/index.js | 2 + .../Auth/OAuth2/AuthorizationCode/index.js | 20 ----- .../Auth/OAuth2/ClientCredentials/index.js | 20 ----- .../CredentialsPreview/StyledWrapper.js | 17 ++++ .../Auth/OAuth2/CredentialsPreview/index.js | 80 +++++++++++++++++++ .../Auth/OAuth2/PasswordCredentials/index.js | 26 +----- .../RequestPane/Auth/OAuth2/index.js | 2 + packages/bruno-app/src/utils/network/index.js | 7 ++ .../bruno-electron/src/ipc/network/index.js | 11 +++ 13 files changed, 126 insertions(+), 128 deletions(-) create mode 100644 packages/bruno-app/src/components/RequestPane/Auth/OAuth2/CredentialsPreview/StyledWrapper.js create mode 100644 packages/bruno-app/src/components/RequestPane/Auth/OAuth2/CredentialsPreview/index.js diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/AuthMode/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/AuthMode/index.js index 7dabb4c71..f7cebda40 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/AuthMode/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/AuthMode/index.js @@ -86,7 +86,7 @@ const AuthMode = ({ collection }) => { onModeChange('oauth2'); }} > - Oauth2 + OAuth 2.0
{ const dispatch = useDispatch(); @@ -64,17 +62,6 @@ const OAuth2AuthorizationCode = ({ collection }) => { }) ); }; - - const handleClearCache = (e) => { - clearOauth2Cache(collection?.uid) - .then(() => { - toast.success('cleared cache successfully'); - }) - .catch((err) => { - toast.error(err.message); - }); - }; - return ( {inputsConfig.map((input) => { @@ -105,14 +92,6 @@ const OAuth2AuthorizationCode = ({ collection }) => { onChange={handlePKCEToggle} />
-
- - -
); }; diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/ClientCredentials/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/ClientCredentials/index.js index 59a9bdeec..856e9373e 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/ClientCredentials/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/ClientCredentials/index.js @@ -7,8 +7,6 @@ import { saveCollectionRoot, sendCollectionOauth2Request } from 'providers/Redux import StyledWrapper from './StyledWrapper'; import { inputsConfig } from './inputsConfig'; import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections/index'; -import { clearOauth2Cache } from 'utils/network'; -import toast from 'react-hot-toast'; const OAuth2ClientCredentials = ({ collection }) => { const dispatch = useDispatch(); @@ -41,16 +39,6 @@ const OAuth2ClientCredentials = ({ collection }) => { ); }; - const handleClearCache = (e) => { - clearOauth2Cache(collection?.uid) - .then(() => { - toast.success('cleared cache successfully'); - }) - .catch((err) => { - toast.error(err.message); - }); - }; - return ( {inputsConfig.map((input) => { @@ -72,14 +60,6 @@ const OAuth2ClientCredentials = ({ collection }) => { ); })} -
- - -
); }; diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/PasswordCredentials/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/PasswordCredentials/index.js index b07ceb72a..068f0070c 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/PasswordCredentials/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/PasswordCredentials/index.js @@ -6,11 +6,9 @@ import SingleLineEditor from 'components/SingleLineEditor'; import { saveCollectionRoot, sendCollectionOauth2Request } from 'providers/ReduxStore/slices/collections/actions'; import StyledWrapper from './StyledWrapper'; import { inputsConfig } from './inputsConfig'; -import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections/index'; -import { clearOauth2Cache } from 'utils/network'; -import toast from 'react-hot-toast'; +import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections'; -const OAuth2AuthorizationCode = ({ item, collection }) => { +const OAuth2PasswordCredentials = ({ collection }) => { const dispatch = useDispatch(); const { storedTheme } = useTheme(); @@ -43,16 +41,6 @@ const OAuth2AuthorizationCode = ({ item, collection }) => { ); }; - const handleClearCache = (e) => { - clearOauth2Cache(collection?.uid) - .then(() => { - toast.success('cleared cache successfully'); - }) - .catch((err) => { - toast.error(err.message); - }); - }; - return ( {inputsConfig.map((input) => { @@ -74,16 +62,8 @@ const OAuth2AuthorizationCode = ({ item, collection }) => { ); })} -
- - -
); }; -export default OAuth2AuthorizationCode; +export default OAuth2PasswordCredentials; diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/index.js index 1aa674ab9..e9d511168 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/index.js @@ -5,6 +5,7 @@ import GrantTypeSelector from './GrantTypeSelector/index'; import OAuth2PasswordCredentials from './PasswordCredentials/index'; import OAuth2AuthorizationCode from './AuthorizationCode/index'; import OAuth2ClientCredentials from './ClientCredentials/index'; +import CredentialsPreview from 'components/RequestPane/Auth/OAuth2/CredentialsPreview'; const grantTypeComponentMap = (grantType, collection) => { switch (grantType) { @@ -30,6 +31,7 @@ const OAuth2 = ({ collection }) => { {grantTypeComponentMap(oAuth?.grantType, collection)} + ); }; 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 index 2bb5dcc35..0265ddbe4 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/index.js @@ -7,8 +7,6 @@ import { updateAuth } from 'providers/ReduxStore/slices/collections'; import { saveRequest, sendRequest } from 'providers/ReduxStore/slices/collections/actions'; import StyledWrapper from './StyledWrapper'; import { inputsConfig } from './inputsConfig'; -import { clearOauth2Cache } from 'utils/network/index'; -import toast from 'react-hot-toast'; const OAuth2AuthorizationCode = ({ item, collection }) => { const dispatch = useDispatch(); @@ -67,16 +65,6 @@ const OAuth2AuthorizationCode = ({ item, collection }) => { ); }; - const handleClearCache = (e) => { - clearOauth2Cache(collection?.uid) - .then(() => { - toast.success('cleared cache successfully'); - }) - .catch((err) => { - toast.error(err.message); - }); - }; - return ( {inputsConfig.map((input) => { @@ -108,14 +96,6 @@ const OAuth2AuthorizationCode = ({ item, collection }) => { onChange={handlePKCEToggle} /> -
- - -
); }; 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 index 9c9f1553d..1bbee2253 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/ClientCredentials/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/ClientCredentials/index.js @@ -7,8 +7,6 @@ import { updateAuth } from 'providers/ReduxStore/slices/collections'; import { saveRequest, sendRequest } from 'providers/ReduxStore/slices/collections/actions'; import StyledWrapper from './StyledWrapper'; import { inputsConfig } from './inputsConfig'; -import { clearOauth2Cache } from 'utils/network'; -import toast from 'react-hot-toast'; const OAuth2ClientCredentials = ({ item, collection }) => { const dispatch = useDispatch(); @@ -42,16 +40,6 @@ const OAuth2ClientCredentials = ({ item, collection }) => { ); }; - const handleClearCache = (e) => { - clearOauth2Cache(collection?.uid) - .then(() => { - toast.success('cleared cache successfully'); - }) - .catch((err) => { - toast.error(err.message); - }); - }; - return ( {inputsConfig.map((input) => { @@ -74,14 +62,6 @@ const OAuth2ClientCredentials = ({ item, collection }) => { ); })} -
- - -
); }; diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/CredentialsPreview/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/CredentialsPreview/StyledWrapper.js new file mode 100644 index 000000000..a1f84cfe6 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/CredentialsPreview/StyledWrapper.js @@ -0,0 +1,17 @@ +import styled from 'styled-components'; + +const Wrapper = styled.div` + label { + display: block; + font-size: 0.8125rem; + } + + textarea { + height: fit-content; + max-width: 400px; + 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/CredentialsPreview/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/CredentialsPreview/index.js new file mode 100644 index 000000000..d7415fe25 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/CredentialsPreview/index.js @@ -0,0 +1,80 @@ +import React, { useEffect, useState } from 'react'; +import { clearOauth2Cache, readOauth2CachedCredentials } from 'utils/network'; +import { sendCollectionOauth2Request, sendRequest } from 'providers/ReduxStore/slices/collections/actions'; +import toast from 'react-hot-toast'; +import { useDispatch } from 'react-redux'; +import StyledWrapper from './StyledWrapper'; + +const CredentialsPreview = ({ item, collection }) => { + const oauth2CredentialsAreaRef = React.createRef(); + const [oauth2Credentials, setOauth2Credentials] = useState({}); + + const dispatch = useDispatch(); + useEffect(() => { + oauth2CredentialsAreaRef.current.value = oauth2Credentials; + readOauth2CachedCredentials(collection.uid).then((credentials) => setOauth2Credentials(credentials)); + }, [oauth2CredentialsAreaRef]); + + const handleRun = async () => { + if (item) { + dispatch(sendRequest(item, collection.uid)); + } else { + dispatch(sendCollectionOauth2Request(collection.uid)); + } + }; + + const handleClearCache = (e) => { + clearOauth2Cache(collection?.uid) + .then(() => { + readOauth2CachedCredentials(collection.uid).then((credentials) => { + setOauth2Credentials(credentials); + toast.success('Cleared cache successfully'); + }); + }) + .catch((err) => { + toast.error(err.message); + }); + }; + + const sortedFields = () => { + const tokens = {}; + const extras = {}; + Object.entries(oauth2Credentials).forEach(([key, value]) => { + if (key.endsWith('_token')) { + tokens[key] = value; + } else { + extras[key] = value; + } + }); + return { ...tokens, ...extras }; + }; + + return ( + +
+ {Object.entries(oauth2Credentials).length > 0 ? ( + <> + +
+ Cached OAuth2 Credentials + {Object.entries(sortedFields()).map(([field, value]) => ( +
+ + + {formik.touched.curlCommand && formik.errors.curlCommand ? ( +
{formik.errors.curlCommand}
+ ) : null} +
+ )} +
+
+ } placement="bottom-start"> +
{ - dropdownTippyRef.current.hide(); - curlRequestTypeChange('graphql-request'); + key="show-filesystem-name" + onClick={(e) => { + advancedDropdownTippyRef.current.hide(); + toggleShowFilesystemName(!showFilesystemName); }} > - GraphQL + {showFilesystemName ? 'Hide Filesystem Name' : 'Show Filesystem Name'}
- - {formik.touched.curlCommand && formik.errors.curlCommand ? ( -
{formik.errors.curlCommand}
- ) : null} +
+ + + + + + +
- )} - - - + + + + ); }; From 1009d42f92d301b5eedd22135dbeca7bdc25b72f Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Tue, 18 Mar 2025 00:48:51 +0530 Subject: [PATCH 147/587] chore: fix caret color --- .../Sidebar/NewRequest/StyledWrapper.js | 92 ++++++++++--------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/packages/bruno-app/src/components/Sidebar/NewRequest/StyledWrapper.js b/packages/bruno-app/src/components/Sidebar/NewRequest/StyledWrapper.js index 872ba2877..338fb2e60 100644 --- a/packages/bruno-app/src/components/Sidebar/NewRequest/StyledWrapper.js +++ b/packages/bruno-app/src/components/Sidebar/NewRequest/StyledWrapper.js @@ -1,45 +1,53 @@ import styled from 'styled-components'; - const StyledWrapper = styled.div` - div.method-selector-container { - border: solid 1px ${(props) => props.theme.modal.input.border}; - border-right: none; - background-color: ${(props) => props.theme.modal.input.bg}; - border-top-left-radius: 3px; - border-bottom-left-radius: 3px; - .method-selector { - min-width: 80px; - } - } - div.method-selector-container, - div.input-container { - background-color: ${(props) => props.theme.modal.input.bg}; - height: 2.3rem; - } - div.input-container { - border: solid 1px ${(props) => props.theme.modal.input.border}; - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; - input { - background-color: ${(props) => props.theme.modal.input.bg}; - outline: none; - box-shadow: none; - &:focus { - outline: none !important; - box-shadow: none !important; - } - } - } - textarea.curl-command { - min-height: 150px; - } - .dropdown { - width: fit-content; - - .dropdown-item { - padding: 0.2rem 0.6rem !important; - } - } - `; +const StyledWrapper = styled.div` + div.method-selector-container { + border: solid 1px ${(props) => props.theme.modal.input.border}; + border-right: none; + background-color: ${(props) => props.theme.modal.input.bg}; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + .method-selector { + min-width: 80px; + } + } + div.method-selector-container, + div.input-container { + background-color: ${(props) => props.theme.modal.input.bg}; + height: 2.3rem; + } + div.input-container { + border: solid 1px ${(props) => props.theme.modal.input.border}; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + input { + background-color: ${(props) => props.theme.modal.input.bg}; + outline: none; + box-shadow: none; + &:focus { + outline: none !important; + box-shadow: none !important; + } + } + } - export default StyledWrapper; \ No newline at end of file + textarea.curl-command { + min-height: 150px; + } + .dropdown { + width: fit-content; + + .dropdown-item { + padding: 0.2rem 0.6rem !important; + } + } + + .advanced-options { + .caret { + color: ${(props) => props.theme.textLink}; + fill: ${(props) => props.theme.textLink}; + } + } +`; + +export default StyledWrapper; \ No newline at end of file From 039c157f33c18038d1fc6ad6fac5c4aa0e3d20cf Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Tue, 18 Mar 2025 19:46:31 +0530 Subject: [PATCH 148/587] feat: improved item info ux --- .../CloneCollectionItem/index.js | 2 +- .../CollectionItemInfo/index.js | 64 ++++++++++++------- .../RenameCollectionItem/index.js | 2 +- .../src/components/Sidebar/NewFolder/index.js | 2 +- .../components/Sidebar/NewRequest/index.js | 2 +- 5 files changed, 45 insertions(+), 27 deletions(-) diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index.js index 6edaf33b9..9194e8a64 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index.js @@ -73,7 +73,7 @@ const CloneCollectionItem = ({ collection, item, onClose }) => { className="btn-advanced" type="button" > - Advanced + Options
diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CollectionItemInfo/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CollectionItemInfo/index.js index d2aef1e21..ca46d0d79 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CollectionItemInfo/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CollectionItemInfo/index.js @@ -1,11 +1,10 @@ import React from 'react'; import Modal from 'components/Modal'; -import path from 'utils/common/path'; +import Help from 'components/Help'; + +const CollectionItemInfo = ({ item, onClose }) => { + const { name, filename, type } = item; -const CollectionItemInfo = ({ collection, item, onClose }) => { - const { pathname: collectionPathname } = collection; - const { name, filename, pathname, type } = item; - const relativePathname = path.relative(collectionPathname, pathname); return ( { hideCancel={true} hideFooter={true} > -
- - - - - - - - - - - - - - - -
Name :{name}
{type=='folder' ? 'Folder Name' : 'File Name'} :{filename}
Pathname :{relativePathname}
-
+
+ + + + + + + + + + + +
+ {type=='folder' ? 'Folder Name' : 'Request Name'} + + :{name} +
+ {type == 'folder' ? 'Folder Name' : 'File Name'} + (on filesystem) + {type == 'folder' ? ( + +

+ The name of the folder on your filesystem. +

+
+ ) : ( + +

+ Bruno saves each request as a file in your collection's folder. +

+
+ )} +
+ : + {filename} +
+
); }; diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js index 0b0350017..705c45c79 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js @@ -93,7 +93,7 @@ const RenameCollectionItem = ({ collection, item, onClose }) => { className="btn-advanced" type="button" > - Advanced + Options diff --git a/packages/bruno-app/src/components/Sidebar/NewFolder/index.js b/packages/bruno-app/src/components/Sidebar/NewFolder/index.js index 4ff681a86..c0b39b727 100644 --- a/packages/bruno-app/src/components/Sidebar/NewFolder/index.js +++ b/packages/bruno-app/src/components/Sidebar/NewFolder/index.js @@ -74,7 +74,7 @@ const NewFolder = ({ collection, item, onClose }) => { className="btn-advanced" type="button" > - Advanced + Options diff --git a/packages/bruno-app/src/components/Sidebar/NewRequest/index.js b/packages/bruno-app/src/components/Sidebar/NewRequest/index.js index f3ef32cba..61fdcd22a 100644 --- a/packages/bruno-app/src/components/Sidebar/NewRequest/index.js +++ b/packages/bruno-app/src/components/Sidebar/NewRequest/index.js @@ -243,7 +243,7 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => { className="btn-advanced" type="button" > - Advanced + Options From a7cf24278eed5f39487adb7b292e908797e9995b Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Tue, 18 Mar 2025 21:15:13 +0530 Subject: [PATCH 149/587] style: update font-family for improved typography consistency --- packages/bruno-app/src/styles/globals.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bruno-app/src/styles/globals.css b/packages/bruno-app/src/styles/globals.css index f81023479..ef2de6bbe 100644 --- a/packages/bruno-app/src/styles/globals.css +++ b/packages/bruno-app/src/styles/globals.css @@ -145,7 +145,7 @@ body { font-kerning: none; text-rendering: optimizeSpeed; letter-spacing: normal; - font-family: Inter, sans-serif !important; + font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important; overflow-x: hidden; } From 98bd99766561217987aa2f250d8bab31d6ec1836 Mon Sep 17 00:00:00 2001 From: lohxt1 Date: Tue, 18 Mar 2025 21:18:41 +0530 Subject: [PATCH 150/587] chore: fix font not loading issue, fix about menu item, fix padding for preferences modal --- .../src/components/Preferences/index.js | 2 +- .../bruno-electron/src/app/menu-template.js | 201 +++++++++++++++++- packages/bruno-electron/src/index.js | 2 +- scripts/build-electron.sh | 2 + 4 files changed, 196 insertions(+), 11 deletions(-) diff --git a/packages/bruno-app/src/components/Preferences/index.js b/packages/bruno-app/src/components/Preferences/index.js index 3635ca5a9..2319d4c78 100644 --- a/packages/bruno-app/src/components/Preferences/index.js +++ b/packages/bruno-app/src/components/Preferences/index.js @@ -46,7 +46,7 @@ const Preferences = ({ onClose }) => { return ( -
+
setTab('general')}> General diff --git a/packages/bruno-electron/src/app/menu-template.js b/packages/bruno-electron/src/app/menu-template.js index e662336ae..a25feaf57 100644 --- a/packages/bruno-electron/src/app/menu-template.js +++ b/packages/bruno-electron/src/app/menu-template.js @@ -1,7 +1,188 @@ const { ipcMain } = require('electron'); const os = require('os'); -const openAboutWindow = require('about-window').default; const { join } = require('path'); +const { BrowserWindow } = require('electron'); +const { version } = require('../../package.json'); + +const htmlContent = ` + + + + + + About Bruno + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bruno ${version}

+

Opensource API Client for Exploring and Testing APIs

+ +
+ All rights reserved ©2025 Bruno Software Inc +
+ + +`; + const template = [ { @@ -77,14 +258,16 @@ const template = [ submenu: [ { label: 'About Bruno', - click: () => - openAboutWindow({ - product_name: 'Bruno', - icon_path: join(__dirname, '../about/256x256.png'), - css_path: join(__dirname, '../about/about.css'), - homepage: 'https://www.usebruno.com/', - package_json_dir: join(__dirname, '../..') - }) + click: () => { + const aboutWindow = new BrowserWindow({ + width: 500, + height: 400, + webPreferences: { + nodeIntegration: true, + }, + }); + aboutWindow.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(htmlContent)}`); + } }, { label: 'Documentation', click: () => ipcMain.emit('main:open-docs') } ] diff --git a/packages/bruno-electron/src/index.js b/packages/bruno-electron/src/index.js index 522df6c68..6ef906ec2 100644 --- a/packages/bruno-electron/src/index.js +++ b/packages/bruno-electron/src/index.js @@ -31,7 +31,7 @@ const lastOpenedCollections = new LastOpenedCollections(); const contentSecurityPolicy = [ "default-src 'self'", "connect-src 'self' https://*.posthog.com", - "font-src 'self' https:", + "font-src 'self' https: data:;", "frame-src data:", // this has been commented out to make oauth2 work // "form-action 'none'", diff --git a/scripts/build-electron.sh b/scripts/build-electron.sh index 42c19a2d8..8ca74608c 100755 --- a/scripts/build-electron.sh +++ b/scripts/build-electron.sh @@ -16,6 +16,8 @@ cp -r packages/bruno-app/dist/* packages/bruno-electron/web # Change paths in next sed -i'' -e 's@/static/@static/@g' packages/bruno-electron/web/**.html +sed -i'' -e 's@/static/font@../../static/font@g' packages/bruno-electron/web/static/css/**.**.css + # Remove sourcemaps find packages/bruno-electron/web -name '*.map' -type f -delete From ccd4a14da60f8e4d215ae4057fb9537608d539ab Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Tue, 18 Mar 2025 21:43:28 +0530 Subject: [PATCH 151/587] feat: refactored about menu + added static path updates for win build --- .../bruno-electron/src/app/about-bruno.js | 176 ++++++++++++++++ .../bruno-electron/src/app/menu-template.js | 188 +----------------- scripts/build-electron.js | 19 +- scripts/build-electron.sh | 3 +- 4 files changed, 199 insertions(+), 187 deletions(-) create mode 100644 packages/bruno-electron/src/app/about-bruno.js diff --git a/packages/bruno-electron/src/app/about-bruno.js b/packages/bruno-electron/src/app/about-bruno.js new file mode 100644 index 000000000..484a062be --- /dev/null +++ b/packages/bruno-electron/src/app/about-bruno.js @@ -0,0 +1,176 @@ +module.exports = function aboutBruno({version}) { + return ` + + + + + + About Bruno + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bruno ${version}

+
+ ©2025 Bruno Software Inc +
+ + + `; +}; \ No newline at end of file diff --git a/packages/bruno-electron/src/app/menu-template.js b/packages/bruno-electron/src/app/menu-template.js index a25feaf57..f2b4c82aa 100644 --- a/packages/bruno-electron/src/app/menu-template.js +++ b/packages/bruno-electron/src/app/menu-template.js @@ -1,188 +1,8 @@ const { ipcMain } = require('electron'); const os = require('os'); -const { join } = require('path'); const { BrowserWindow } = require('electron'); const { version } = require('../../package.json'); - -const htmlContent = ` - - - - - - About Bruno - - - - - - - - - - - - - - - - - - - - - - - - - - -

Bruno ${version}

-

Opensource API Client for Exploring and Testing APIs

- -
- All rights reserved ©2025 Bruno Software Inc -
- - -`; - +const aboutBruno = require('./about-bruno'); const template = [ { @@ -260,13 +80,13 @@ const template = [ label: 'About Bruno', click: () => { const aboutWindow = new BrowserWindow({ - width: 500, - height: 400, + width: 350, + height: 250, webPreferences: { nodeIntegration: true, }, }); - aboutWindow.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(htmlContent)}`); + aboutWindow.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(aboutBruno({version}))}`); } }, { label: 'Documentation', click: () => ipcMain.emit('main:open-docs') } diff --git a/scripts/build-electron.js b/scripts/build-electron.js index ab44dcbdf..6d778ffdf 100644 --- a/scripts/build-electron.js +++ b/scripts/build-electron.js @@ -2,6 +2,7 @@ const os = require('os'); const fs = require('fs-extra'); const util = require('util'); const spawn = util.promisify(require('child_process').spawn); +const path = require('path'); async function deleteFileIfExists(filePath) { try { @@ -80,7 +81,7 @@ async function main() { // Copy build await copyFolderIfExists('packages/bruno-app/dist', 'packages/bruno-electron/web'); - // Change paths in next + // Update static paths const files = await fs.readdir('packages/bruno-electron/web'); for (const file of files) { if (file.endsWith('.html')) { @@ -90,6 +91,22 @@ async function main() { } } + // update font load paths + const cssDir = path.join('packages/bruno-electron/web/static/css'); + try { + const cssFiles = await fs.readdir(cssDir); + for (const file of cssFiles) { + if (file.endsWith('.css')) { + const filePath = path.join(cssDir, file); + let content = await fs.readFile(filePath, 'utf8'); + content = content.replace(/\/static\/font/g, '../../static/font'); + await fs.writeFile(filePath, content); + } + } + } catch (error) { + console.error(`Error updating font paths: ${error}`); + } + // Remove sourcemaps await removeSourceMapFiles('packages/bruno-electron/web'); diff --git a/scripts/build-electron.sh b/scripts/build-electron.sh index 8ca74608c..7f3887c70 100755 --- a/scripts/build-electron.sh +++ b/scripts/build-electron.sh @@ -13,9 +13,8 @@ mkdir packages/bruno-electron/web cp -r packages/bruno-app/dist/* packages/bruno-electron/web -# Change paths in next +# Update static paths sed -i'' -e 's@/static/@static/@g' packages/bruno-electron/web/**.html - sed -i'' -e 's@/static/font@../../static/font@g' packages/bruno-electron/web/static/css/**.**.css # Remove sourcemaps From 2e4014863f6a3695e8f2162060aca86179fbea22 Mon Sep 17 00:00:00 2001 From: lohxt1 Date: Thu, 20 Mar 2025 17:06:47 +0530 Subject: [PATCH 152/587] fix the validations for oauth2 json_to_bru --- packages/bruno-lang/v2/src/jsonToBru.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/bruno-lang/v2/src/jsonToBru.js b/packages/bruno-lang/v2/src/jsonToBru.js index 547fe380f..1a6d0fbfd 100644 --- a/packages/bruno-lang/v2/src/jsonToBru.js +++ b/packages/bruno-lang/v2/src/jsonToBru.js @@ -196,8 +196,8 @@ ${indentString(`token_placement: ${auth?.oauth2?.tokenPlacement || ''}`)}${ }${ auth?.oauth2?.tokenPlacement !== 'header' ? '\n' + indentString(`token_query_key: ${auth?.oauth2?.tokenQueryKey || ''}`) : '' } -${indentString(`auto_fetch_token: ${auth?.oauth2?.autoFetchToken?.toString?.()}`)} -${indentString(`auto_refresh_token: ${auth?.oauth2?.autoRefreshToken?.toString?.()}`)} +${indentString(`auto_fetch_token: ${(auth?.oauth2?.autoFetchToken|| false).toString()}`)} +${indentString(`auto_refresh_token: ${(auth?.oauth2?.autoRefreshToken || false).toString()}`)} } `; @@ -221,8 +221,8 @@ ${indentString(`token_placement: ${auth?.oauth2?.tokenPlacement || ''}`)}${ }${ auth?.oauth2?.tokenPlacement !== 'header' ? '\n' + indentString(`token_query_key: ${auth?.oauth2?.tokenQueryKey || ''}`) : '' } -${indentString(`auto_fetch_token: ${auth?.oauth2?.autoFetchToken?.toString?.()}`)} -${indentString(`auto_refresh_token: ${auth?.oauth2?.autoRefreshToken?.toString?.()}`)} +${indentString(`auto_fetch_token: ${(auth?.oauth2?.autoFetchToken|| false).toString()}`)} +${indentString(`auto_refresh_token: ${(auth?.oauth2?.autoRefreshToken || false).toString()}`)} } `; @@ -242,8 +242,8 @@ ${indentString(`token_placement: ${auth?.oauth2?.tokenPlacement || ''}`)}${ }${ auth?.oauth2?.tokenPlacement !== 'header' ? '\n' + indentString(`token_query_key: ${auth?.oauth2?.tokenQueryKey || ''}`) : '' } -${indentString(`auto_fetch_token: ${auth?.oauth2?.autoFetchToken.toString?.()}`)} -${indentString(`auto_refresh_token: ${auth?.oauth2?.autoRefreshToken.toString?.()}`)} +${indentString(`auto_fetch_token: ${(auth?.oauth2?.autoFetchToken|| false).toString()}`)} +${indentString(`auto_refresh_token: ${(auth?.oauth2?.autoRefreshToken || false).toString()}`)} } `; From 5a98da203102c3af4b622aa0abad21cbb4e8e186 Mon Sep 17 00:00:00 2001 From: lohxt1 Date: Thu, 20 Mar 2025 19:27:14 +0530 Subject: [PATCH 153/587] oauth2 fixes --- .../CollectionSettings/Auth/OAuth2/index.js | 4 +- .../components/FolderSettings/Auth/index.js | 4 +- .../Auth/OAuth2/AuthorizationCode/index.js | 30 ++++--- .../Auth/OAuth2/ClientCredentials/index.js | 22 +++-- .../CredentialsPreview/StyledWrapper.js | 17 ---- .../Auth/OAuth2/CredentialsPreview/index.js | 80 ------------------- .../Auth/OAuth2/Oauth2TokenViewer/index.js | 11 ++- .../Auth/OAuth2/PasswordCredentials/index.js | 22 +++-- .../RequestPane/Auth/OAuth2/index.js | 4 +- .../ResponsePane/QueryResult/index.js | 4 +- .../ReduxStore/slices/collections/index.js | 2 - .../bruno-app/src/utils/collections/index.js | 13 +-- .../src/utils/importers/postman-collection.js | 6 +- packages/bruno-app/src/utils/network/index.js | 7 -- packages/bruno-electron/src/index.js | 11 +++ .../src/ipc/network/axios-instance.js | 18 ++++- .../bruno-electron/src/ipc/network/index.js | 48 +++++------ .../src/ipc/network/interpolate-vars.js | 6 +- .../src/ipc/network/prepare-request.js | 12 +-- packages/bruno-electron/src/store/oauth2.js | 17 ++-- packages/bruno-electron/src/utils/oauth2.js | 4 +- .../bruno-electron/src/utils/proxy-util.js | 2 +- packages/bruno-lang/v2/src/bruToJson.js | 8 +- .../bruno-lang/v2/src/collectionBruToJson.js | 8 +- packages/bruno-lang/v2/src/jsonToBru.js | 6 +- .../bruno-lang/v2/src/jsonToCollectionBru.js | 6 +- .../bruno-lang/v2/tests/fixtures/request.bru | 2 +- .../bruno-lang/v2/tests/fixtures/request.json | 2 +- .../bruno-schema/src/collections/index.js | 2 +- .../user_info_request-auth.bru | 2 +- 30 files changed, 162 insertions(+), 218 deletions(-) delete mode 100644 packages/bruno-app/src/components/RequestPane/Auth/OAuth2/CredentialsPreview/StyledWrapper.js delete mode 100644 packages/bruno-app/src/components/RequestPane/Auth/OAuth2/CredentialsPreview/index.js diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/index.js index e29fe7fc0..474e44717 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/index.js @@ -9,7 +9,7 @@ import OAuth2PasswordCredentials from 'components/RequestPane/Auth/OAuth2/Passwo import OAuth2ClientCredentials from 'components/RequestPane/Auth/OAuth2/ClientCredentials/index'; import GrantTypeSelector from 'components/RequestPane/Auth/OAuth2/GrantTypeSelector/index'; -const grantTypeComponentMap = (collection) => { +const GrantTypeComponentMap = ({collection }) => { const dispatch = useDispatch(); const save = () => { @@ -41,7 +41,7 @@ const OAuth2 = ({ collection }) => { return ( - {grantTypeComponentMap(collection)} + ); }; diff --git a/packages/bruno-app/src/components/FolderSettings/Auth/index.js b/packages/bruno-app/src/components/FolderSettings/Auth/index.js index 4ef704cd7..e465d174d 100644 --- a/packages/bruno-app/src/components/FolderSettings/Auth/index.js +++ b/packages/bruno-app/src/components/FolderSettings/Auth/index.js @@ -10,7 +10,7 @@ import OAuth2ClientCredentials from 'components/RequestPane/Auth/OAuth2/ClientCr import GrantTypeSelector from 'components/RequestPane/Auth/OAuth2/GrantTypeSelector/index'; import AuthMode from '../AuthMode'; -const grantTypeComponentMap = (collection, folder) => { +const GrantTypeComponentMap = ({ collection, folder }) => { const dispatch = useDispatch(); const save = () => { @@ -52,7 +52,7 @@ const Auth = ({ collection, folder }) => { collection={collection} folder={folder} /> - {grantTypeComponentMap(collection, folder)} + ); } 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 index bae6e4a0d..40f74c713 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/index.js @@ -1,4 +1,4 @@ -import React, { useRef, forwardRef, useState, useEffect } from 'react'; +import React, { useRef, forwardRef, useState, useEffect, useMemo } from 'react'; import get from 'lodash/get'; import { useTheme } from 'providers/Theme'; import { useDispatch } from 'react-redux'; @@ -11,7 +11,9 @@ import { inputsConfig } from './inputsConfig'; import toast from 'react-hot-toast'; import Oauth2TokenViewer from '../Oauth2TokenViewer/index'; import { cloneDeep, find } from 'lodash'; -import { interpolateStringUsingCollectionAndItem } from 'utils/collections/index'; +import { getAllVariables } from 'utils/collections/index'; +import brunoCommon from '@usebruno/common'; +const { interpolate } = brunoCommon; const OAuth2AuthorizationCode = ({ save, item = {}, request, handleRun, updateAuth, collection, folder }) => { const dispatch = useDispatch(); @@ -37,13 +39,20 @@ const OAuth2AuthorizationCode = ({ save, item = {}, request, handleRun, updateAu tokenPlacement, tokenHeaderPrefix, tokenQueryKey, - refreshUrl, + refreshTokenUrl, autoRefreshToken, autoFetchToken } = oAuth; - const refreshUrlAvailable = refreshUrl?.trim() !== ''; - const isAutoRefreshDisabled = !refreshUrlAvailable; + const { uid: collectionUid } = collection; + + const interpolatedAccessTokenUrl = useMemo(() => { + const variables = getAllVariables(collection, item); + return interpolate(accessTokenUrl, variables); + }, [collection, item, accessTokenUrl]); + + const refreshTokenUrlAvailable = refreshTokenUrl?.trim() !== ''; + const isAutoRefreshDisabled = !refreshTokenUrlAvailable; const TokenPlacementIcon = forwardRef((props, ref) => { @@ -127,7 +136,7 @@ const OAuth2AuthorizationCode = ({ save, item = {}, request, handleRun, updateAu tokenPlacement, tokenHeaderPrefix, tokenQueryKey, - refreshUrl, + refreshTokenUrl, autoRefreshToken, autoFetchToken, [key]: value, @@ -164,7 +173,6 @@ const OAuth2AuthorizationCode = ({ save, item = {}, request, handleRun, updateAu }; const handleClearCache = (e) => { - const interpolatedAccessTokenUrl = interpolateStringUsingCollectionAndItem({ collection, item, string: accessTokenUrl }); dispatch(clearOauth2Cache({ collectionUid: collection?.uid, url: interpolatedAccessTokenUrl, credentialsId })) .then(() => { toast.success('cleared cache successfully'); @@ -174,9 +182,7 @@ const OAuth2AuthorizationCode = ({ save, item = {}, request, handleRun, updateAu }); }; - const { uid: collectionUid } = collection; - const interpolatedUrl = interpolateStringUsingCollectionAndItem({ collection, item, string: accessTokenUrl }); - const credentialsData = find(collection?.oauth2Credentials, creds => creds?.url == interpolatedUrl && creds?.collectionUid == collectionUid && creds?.credentialsId == credentialsId); + const credentialsData = find(collection?.oauth2Credentials, creds => creds?.url == interpolatedAccessTokenUrl && creds?.collectionUid == collectionUid && creds?.credentialsId == credentialsId); const creds = credentialsData?.credentials || {}; useEffect(() => { @@ -339,10 +345,10 @@ const OAuth2AuthorizationCode = ({ save, item = {}, request, handleRun, updateAu
handleChange("refreshUrl", val)} + onChange={(val) => handleChange("refreshTokenUrl", val)} collection={collection} item={item} /> 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 index 431ea2839..16df7ca7c 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/ClientCredentials/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/ClientCredentials/index.js @@ -11,7 +11,9 @@ import Dropdown from 'components/Dropdown'; import Oauth2TokenViewer from '../Oauth2TokenViewer/index'; import toast from 'react-hot-toast'; import { cloneDeep } from 'lodash'; -import { interpolateStringUsingCollectionAndItem } from 'utils/collections/index'; +import { getAllVariables } from 'utils/collections/index'; +import brunoCommon from '@usebruno/common'; +const { interpolate } = brunoCommon; const OAuth2ClientCredentials = ({ save, item = {}, request, handleRun, updateAuth, collection }) => { const dispatch = useDispatch(); @@ -33,13 +35,18 @@ const OAuth2ClientCredentials = ({ save, item = {}, request, handleRun, updateAu tokenPlacement, tokenHeaderPrefix, tokenQueryKey, - refreshUrl, + refreshTokenUrl, autoRefreshToken, autoFetchToken } = oAuth; - const refreshUrlAvailable = refreshUrl?.trim() !== ''; - const isAutoRefreshDisabled = !refreshUrlAvailable; + const interpolatedAccessTokenUrl = useMemo(() => { + const variables = getAllVariables(collection, item); + return interpolate(accessTokenUrl, variables); + }, [collection, item, accessTokenUrl]); + + const refreshTokenUrlAvailable = refreshTokenUrl?.trim() !== ''; + const isAutoRefreshDisabled = !refreshTokenUrlAvailable; const handleFetchOauth2Credentials = async () => { let requestCopy = cloneDeep(request); @@ -113,7 +120,7 @@ const OAuth2ClientCredentials = ({ save, item = {}, request, handleRun, updateAu tokenPlacement, tokenHeaderPrefix, tokenQueryKey, - refreshUrl, + refreshTokenUrl, autoRefreshToken, autoFetchToken, [key]: value @@ -123,7 +130,6 @@ const OAuth2ClientCredentials = ({ save, item = {}, request, handleRun, updateAu }; const handleClearCache = (e) => { - const interpolatedAccessTokenUrl = interpolateStringUsingCollectionAndItem({ collection, item, string: accessTokenUrl }); dispatch(clearOauth2Cache({ collectionUid: collection?.uid, url: interpolatedAccessTokenUrl, credentialsId })) .then(() => { toast.success('cleared cache successfully'); @@ -279,10 +285,10 @@ const OAuth2ClientCredentials = ({ save, item = {}, request, handleRun, updateAu
handleChange("refreshUrl", val)} + onChange={(val) => handleChange("refreshTokenUrl", val)} collection={collection} item={item} /> diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/CredentialsPreview/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/CredentialsPreview/StyledWrapper.js deleted file mode 100644 index a1f84cfe6..000000000 --- a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/CredentialsPreview/StyledWrapper.js +++ /dev/null @@ -1,17 +0,0 @@ -import styled from 'styled-components'; - -const Wrapper = styled.div` - label { - display: block; - font-size: 0.8125rem; - } - - textarea { - height: fit-content; - max-width: 400px; - 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/CredentialsPreview/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/CredentialsPreview/index.js deleted file mode 100644 index 7734affd4..000000000 --- a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/CredentialsPreview/index.js +++ /dev/null @@ -1,80 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { readOauth2CachedCredentials } from 'utils/network'; -import { sendCollectionOauth2Request, sendRequest, clearOauth2Cache } from 'providers/ReduxStore/slices/collections/actions'; -import toast from 'react-hot-toast'; -import { useDispatch } from 'react-redux'; -import StyledWrapper from './StyledWrapper'; - -const CredentialsPreview = ({ item, collection }) => { - const oauth2CredentialsAreaRef = React.createRef(); - const [oauth2Credentials, setOauth2Credentials] = useState({}); - - const dispatch = useDispatch(); - useEffect(() => { - oauth2CredentialsAreaRef.current.value = oauth2Credentials; - readOauth2CachedCredentials(collection.uid).then((credentials) => setOauth2Credentials(credentials)); - }, [oauth2CredentialsAreaRef]); - - const handleRun = async () => { - if (item) { - dispatch(sendRequest(item, collection.uid)); - } else { - dispatch(sendCollectionOauth2Request(collection.uid)); - } - }; - - const handleClearCache = (e) => { - dispatch(clearOauth2Cache({ collectionUid: collection?.uid, url: '' })) - .then(() => { - readOauth2CachedCredentials(collection.uid).then((credentials) => { - setOauth2Credentials(credentials); - toast.success('Cleared cache successfully'); - }); - }) - .catch((err) => { - toast.error(err.message); - }); - }; - - const sortedFields = () => { - const tokens = {}; - const extras = {}; - Object.entries(oauth2Credentials).forEach(([key, value]) => { - if (key.endsWith('_token')) { - tokens[key] = value; - } else { - extras[key] = value; - } - }); - return { ...tokens, ...extras }; - }; - - return ( - -
- {Object.entries(oauth2Credentials).length > 0 ? ( - <> - -
- Cached OAuth2 Credentials - {Object.entries(sortedFields()).map(([field, value]) => ( -
- -