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 });