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 d69122b48..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
@@ -60,9 +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 d2d9eed1f..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,9 +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 { updateCollectionAuth } from 'providers/ReduxStore/slices/collections';
-const OAuth2AuthorizationCode = ({ item, collection }) => {
+const OAuth2PasswordCredentials = ({ collection }) => {
const dispatch = useDispatch();
const { storedTheme } = useTheme();
@@ -62,11 +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 a43c8f0ad..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
@@ -62,9 +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]) => (
+
+
+
+
+ ))}
+
+ >
+ ) : (
+
+ )}
+
+
+ );
+};
+
+export default CredentialsPreview;
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..6911c6457 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
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useEffect, useState } from 'react';
import get from 'lodash/get';
import { useTheme } from 'providers/Theme';
import { useDispatch } from 'react-redux';
@@ -8,7 +8,7 @@ import { saveRequest, sendRequest } from 'providers/ReduxStore/slices/collection
import StyledWrapper from './StyledWrapper';
import { inputsConfig } from './inputsConfig';
-const OAuth2AuthorizationCode = ({ item, collection }) => {
+const OAuth2PasswordCredentials = ({ item, collection }) => {
const dispatch = useDispatch();
const { storedTheme } = useTheme();
@@ -64,11 +64,8 @@ const OAuth2AuthorizationCode = ({ item, collection }) => {
);
})}
-
);
};
-export default OAuth2AuthorizationCode;
+export default OAuth2PasswordCredentials;
diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/index.js
index 3965c8d3e..24ec0c8e1 100644
--- a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/index.js
+++ b/packages/bruno-app/src/components/RequestPane/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 './CredentialsPreview';
const grantTypeComponentMap = (grantType, item, collection) => {
switch (grantType) {
@@ -30,6 +31,7 @@ const OAuth2 = ({ item, collection }) => {
{grantTypeComponentMap(oAuth?.grantType, item, collection)}
+
);
};
diff --git a/packages/bruno-app/src/components/RequestPane/Auth/index.js b/packages/bruno-app/src/components/RequestPane/Auth/index.js
index 1515e5224..d48856a0c 100644
--- a/packages/bruno-app/src/components/RequestPane/Auth/index.js
+++ b/packages/bruno-app/src/components/RequestPane/Auth/index.js
@@ -8,8 +8,9 @@ import DigestAuth from './DigestAuth';
import WsseAuth from './WsseAuth';
import ApiKeyAuth from './ApiKeyAuth';
import StyledWrapper from './StyledWrapper';
-import { humanizeRequestAuthMode } from 'utils/collections/index';
+import { humanizeRequestAuthMode } from 'utils/collections';
import OAuth2 from './OAuth2/index';
+import CredentialsPreview from './OAuth2/CredentialsPreview';
const Auth = ({ item, collection }) => {
const authMode = item.draft ? get(item, 'draft.request.auth.mode') : get(item, 'request.auth.mode');
@@ -42,24 +43,13 @@ const Auth = ({ item, collection }) => {
}
case 'inherit': {
return (
-
- {collectionAuth?.mode === 'oauth2' ? (
-
-
-
Collection level auth is:
-
{humanizeRequestAuthMode(collectionAuth?.mode)}
-
-
- Note: You need to use scripting to set the access token in the request headers.
-
-
- ) : (
- <>
-
Auth inherited from the Collection:
-
{humanizeRequestAuthMode(collectionAuth?.mode)}
- >
- )}
-
+ <>
+
+
Auth inherited from the Collection:
+
{humanizeRequestAuthMode(collectionAuth?.mode)}
+
+ {collectionAuth?.mode === 'oauth2' && }
+ >
);
}
}
diff --git a/packages/bruno-app/src/utils/network/index.js b/packages/bruno-app/src/utils/network/index.js
index 18a2b8a1c..463e82c7c 100644
--- a/packages/bruno-app/src/utils/network/index.js
+++ b/packages/bruno-app/src/utils/network/index.js
@@ -50,6 +50,13 @@ export const clearOauth2Cache = async (uid) => {
});
};
+export const readOauth2CachedCredentials = async (uid) => {
+ return new Promise((resolve, reject) => {
+ const { ipcRenderer } = window;
+ ipcRenderer.invoke('read-oauth2-cached-credentials', uid).then(resolve).catch(reject);
+ });
+};
+
export const fetchGqlSchema = async (endpoint, environment, request, collection) => {
return new Promise((resolve, reject) => {
const { ipcRenderer } = window;
diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js
index 782878c59..7ca0f7aed 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');
@@ -31,9 +30,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');
@@ -276,35 +275,30 @@ const configureRequest = async (
if (request.oauth2) {
let requestCopy = cloneDeep(request);
+ interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars);
+ let credentials, response;
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': {
+ ({ credentials, response } = 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': {
+ ({ credentials, response } = 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': {
+ ({ credentials, response } = await oauth2AuthorizeWithPasswordCredentials(requestCopy, collectionUid));
break;
+ }
+ }
+ request.credentials = credentials;
+ request.authRequestResponse = response;
+
+ // 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}`;
}
}
@@ -781,7 +775,7 @@ const registerNetworkIpc = (mainWindow) => {
);
interpolateVars(request, envVars, collection.runtimeVariables, processEnvVars);
- const axiosInstance = await configureRequest(
+ await configureRequest(
collection.uid,
request,
envVars,
@@ -790,19 +784,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,
@@ -820,7 +808,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);
@@ -839,6 +828,17 @@ const registerNetworkIpc = (mainWindow) => {
});
});
+ ipcMain.handle('read-oauth2-cached-credentials', async (event, uid) => {
+ return new Promise((resolve, reject) => {
+ try {
+ const oauth2Store = new Oauth2Store();
+ return resolve(oauth2Store.getOauth2DataOfCollection(uid).credentials ?? {});
+ } catch (err) {
+ reject(new Error('Could not read cached oauth2 credentials'));
+ }
+ });
+ });
+
ipcMain.handle('cancel-http-request', async (event, cancelTokenUid) => {
return new Promise((resolve, reject) => {
if (cancelTokenUid && cancelTokens[cancelTokenUid]) {
diff --git a/packages/bruno-electron/src/ipc/network/interpolate-vars.js b/packages/bruno-electron/src/ipc/network/interpolate-vars.js
index d9833f59a..9478abf49 100644
--- a/packages/bruno-electron/src/ipc/network/interpolate-vars.js
+++ b/packages/bruno-electron/src/ipc/network/interpolate-vars.js
@@ -170,14 +170,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) || '';
@@ -197,12 +189,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 144542418..cdff9627d 100644
--- a/packages/bruno-electron/src/ipc/network/oauth2-helper.js
+++ b/packages/bruno-electron/src/ipc/network/oauth2-helper.js
@@ -2,6 +2,9 @@ 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 oauth2Store = new Oauth2Store();
const generateCodeVerifier = () => {
return crypto.randomBytes(22).toString('hex');
@@ -14,16 +17,34 @@ 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 resolveOAuth2AuthorizationCodeAccessToken = async (request, collectionUid) => {
+const oauth2AuthorizeWithAuthorizationCode = async (request, collectionUid) => {
+ const { cachedCredentials } = getPersistedOauth2Credentials(collectionUid);
+ if (cachedCredentials?.access_token) {
+ console.log('Reusing Stored access token');
+ return { credentials: cachedCredentials, response: {} };
+ }
+
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,
@@ -35,11 +56,16 @@ const resolveOAuth2AuthorizationCodeAccessToken = async (request, collectionUid)
data['code_verifier'] = codeVerifier;
}
- 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 = request?.oauth2?.accessTokenUrl;
+
+ const axiosInstance = makeAxiosInstance();
+ const response = await axiosInstance(request);
+ const credentials = JSON.parse(response.data);
+ persistOauth2Credentials(credentials, collectionUid);
+ return { credentials, response };
};
const getOAuth2AuthorizationCode = (request, codeChallenge, collectionUid) => {
@@ -64,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,
@@ -79,7 +104,13 @@ const getOAuth2AuthorizationCode = (request, codeChallenge, collectionUid) => {
// CLIENT CREDENTIALS
-const transformClientCredentialsRequest = async (request) => {
+const oauth2AuthorizeWithClientCredentials = async (request, collectionUid) => {
+ const { cachedCredentials } = getPersistedOauth2Credentials(collectionUid);
+ if (cachedCredentials?.access_token) {
+ console.log('Reusing Stored access token');
+ return { credentials: cachedCredentials, response: {} };
+ }
+
let requestCopy = cloneDeep(request);
const oAuth = get(requestCopy, 'oauth2', {});
const { clientId, clientSecret, scope } = oAuth;
@@ -91,18 +122,29 @@ 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 = request?.oauth2?.accessTokenUrl;
+
+ const axiosInstance = makeAxiosInstance();
+ let response = await axiosInstance(request);
+ let credentials = JSON.parse(response.data);
+ persistOauth2Credentials(credentials, collectionUid);
+ return { credentials, response };
};
// PASSWORD CREDENTIALS
-const transformPasswordCredentialsRequest = async (request) => {
- let requestCopy = cloneDeep(request);
- const oAuth = get(requestCopy, 'oauth2', {});
+const oauth2AuthorizeWithPasswordCredentials = async (request, collectionUid) => {
+ const { cachedCredentials } = getPersistedOauth2Credentials(collectionUid);
+ if (cachedCredentials?.access_token) {
+ console.log('Reusing Stored access token');
+ return { credentials: cachedCredentials, response: {} };
+ }
+
+ const oAuth = get(request, 'oauth2', {});
const { username, password, clientId, clientSecret, scope } = oAuth;
const data = {
grant_type: 'password',
@@ -114,16 +156,20 @@ 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 credentials = JSON.parse(response.data);
+ persistOauth2Credentials(credentials, collectionUid);
+ return { credentials, response };
+};
+module.exports = {
+ oauth2AuthorizeWithAuthorizationCode,
+ oauth2AuthorizeWithClientCredentials,
+ oauth2AuthorizeWithPasswordCredentials
};
diff --git a/packages/bruno-electron/src/ipc/network/prepare-request.js b/packages/bruno-electron/src/ipc/network/prepare-request.js
index 69825bc4e..6b27d32d3 100644
--- a/packages/bruno-electron/src/ipc/network/prepare-request.js
+++ b/packages/bruno-electron/src/ipc/network/prepare-request.js
@@ -59,6 +59,9 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
axiosRequest.apiKeyAuthValueForQueryParams = apiKeyAuth;
}
break;
+ case 'oauth2':
+ request.auth = collectionAuth;
+ break;
}
}
@@ -194,7 +197,7 @@ const prepareRequest = (item, collection) => {
});
let axiosRequest = {
- mode: request.body.mode,
+ mode: request?.body?.mode,
method: request.method,
url,
headers,
@@ -204,7 +207,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';
}
@@ -215,28 +218,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'] = 'application/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';
}
@@ -252,7 +255,7 @@ const prepareRequest = (item, collection) => {
axiosRequest.data = enabledParams;
}
- 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
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 });
diff --git a/packages/bruno-js/src/bruno-request.js b/packages/bruno-js/src/bruno-request.js
index 32e40c19e..c0c1ad3c3 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 e3f364fe7..00d93ce3d 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();