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 e4fa31cd5..fa300c296 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 @@ -22,9 +22,9 @@ const OAuth2AuthorizationCode = ({ save, item = {}, request, handleRun, updateAu const oAuth = get(request, 'auth.oauth2', {}); - const { callbackUrl, authorizationUrl, accessTokenUrl, clientId, clientSecret, scope, state, pkce, credentialsId, tokenPlacement, tokenPrefix, tokenQueryParamKey, reuseToken } = oAuth; + const { callbackUrl, authorizationUrl, accessTokenUrl, clientId, clientSecret, scope, credentialsPlacement, state, pkce, credentialsId, tokenPlacement, tokenPrefix, tokenQueryKey, reuseToken } = oAuth; - const Icon = forwardRef((props, ref) => { + const TokenPlacementIcon = forwardRef((props, ref) => { return (
{tokenPlacement == 'url' ? 'URL' : 'Headers'} @@ -33,6 +33,16 @@ const OAuth2AuthorizationCode = ({ save, item = {}, request, handleRun, updateAu ); }); + const CredentialsPlacementIcon = forwardRef((props, ref) => { + return ( +
+ {credentialsPlacement == 'body' ? 'Request Body' : 'Basic Auth Header'} + +
+ ); + }); + + const handleFetchOauth2Credentials = async () => { let requestCopy = cloneDeep(request); requestCopy.oauth2 = requestCopy?.auth.oauth2; @@ -41,11 +51,12 @@ const OAuth2AuthorizationCode = ({ save, item = {}, request, handleRun, updateAu try { await dispatch(fetchOauth2Credentials({ request: requestCopy, collection })); toggleFetchingToken(false); + toast.success('token fetched successfully!'); } catch(error) { - console.error('could not fetch the token!'); console.error(error); toggleFetchingToken(false); + toast.error('An error occured while fetching token!'); } } @@ -67,10 +78,11 @@ const OAuth2AuthorizationCode = ({ save, item = {}, request, handleRun, updateAu state, scope, pkce, + credentialsPlacement, credentialsId, tokenPlacement, tokenPrefix, - tokenQueryParamKey, + tokenQueryKey, reuseToken, [key]: value } @@ -93,10 +105,11 @@ const OAuth2AuthorizationCode = ({ save, item = {}, request, handleRun, updateAu clientSecret, state, scope, + credentialsPlacement, credentialsId, tokenPlacement, tokenPrefix, - tokenQueryParamKey, + tokenQueryKey, reuseToken, pkce: !Boolean(oAuth?.['pkce']) } @@ -146,6 +159,31 @@ const OAuth2AuthorizationCode = ({ save, item = {}, request, handleRun, updateAu
); })} +
+ +
+ } placement="bottom-end"> +
{ + dropdownTippyRef.current.hide(); + handleChange('credentialsPlacement', 'body'); + }} + > + Request Body +
+
{ + dropdownTippyRef.current.hide(); + handleChange('credentialsPlacement', 'basic_auth_header'); + }} + > + Basic Auth Header +
+
+
+
- } placement="bottom-end"> + } placement="bottom-end">
{ @@ -222,10 +260,10 @@ const OAuth2AuthorizationCode = ({ save, item = {}, request, handleRun, updateAu
handleChange('tokenQueryParamKey', val)} + onChange={(val) => handleChange('tokenQueryKey', val)} onRun={handleRun} collection={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 50a8ace73..59ace4a9e 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 @@ -22,7 +22,7 @@ const OAuth2ClientCredentials = ({ save, item = {}, request, handleRun, updateAu const oAuth = get(request, 'auth.oauth2', {}); - const { accessTokenUrl, clientId, clientSecret, scope, credentialsId, tokenPlacement, tokenPrefix, tokenQueryParamKey, reuseToken } = oAuth; + const { accessTokenUrl, clientId, clientSecret, scope, credentialsPlacement, credentialsId, tokenPlacement, tokenPrefix, tokenQueryKey, reuseToken } = oAuth; const handleFetchOauth2Credentials = async () => { let requestCopy = cloneDeep(request); @@ -32,24 +32,34 @@ const OAuth2ClientCredentials = ({ save, item = {}, request, handleRun, updateAu try { await dispatch(fetchOauth2Credentials({ request: requestCopy, collection })); toggleFetchingToken(false); + toast.success('Token fetched successfully!'); } catch (error) { console.error('could not fetch the token!'); console.error(error); toggleFetchingToken(false); + toast.error('An error occured while fetching token!'); } } const handleSave = () => { save(); }; - const Icon = forwardRef((props, ref) => { + const TokenPlacementIcon = forwardRef((props, ref) => { return (
- {tokenPlacement == 'url' ? 'URL' : 'Headers'} + {tokenPlacement == 'url' ? 'URL' : 'Headers'}
); }); + const CredentialsPlacementIcon = forwardRef((props, ref) => { + return ( +
+ {credentialsPlacement == 'body' ? 'Request Body' : 'Basic Auth Header'} + +
+ ); + }); const handleChange = (key, value) => { dispatch( @@ -63,10 +73,11 @@ const OAuth2ClientCredentials = ({ save, item = {}, request, handleRun, updateAu clientId, clientSecret, scope, + credentialsPlacement, credentialsId, tokenPlacement, tokenPrefix, - tokenQueryParamKey, + tokenQueryKey, reuseToken, [key]: value } @@ -116,6 +127,31 @@ const OAuth2ClientCredentials = ({ save, item = {}, request, handleRun, updateAu
); })} +
+ +
+ } placement="bottom-end"> +
{ + dropdownTippyRef.current.hide(); + handleChange('credentialsPlacement', 'body'); + }} + > + Request Body +
+
{ + dropdownTippyRef.current.hide(); + handleChange('credentialsPlacement', 'basic_auth_header'); + }} + > + Basic Auth Header +
+
+
+
@@ -141,7 +177,7 @@ const OAuth2ClientCredentials = ({ save, item = {}, request, handleRun, updateAu
- } placement="bottom-end"> + } placement="bottom-end">
{ @@ -183,10 +219,10 @@ const OAuth2ClientCredentials = ({ save, item = {}, request, handleRun, updateAu
handleChange('tokenQueryParamKey', val)} + onChange={(val) => handleChange('tokenQueryKey', val)} onRun={handleRun} collection={collection} /> diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/GrantTypeSelector/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/GrantTypeSelector/index.js index 998914958..7928fa790 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/GrantTypeSelector/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/GrantTypeSelector/index.js @@ -6,13 +6,16 @@ import StyledWrapper from './StyledWrapper'; import { IconCaretDown, IconKey } from '@tabler/icons'; import { humanizeGrantType } from 'utils/collections'; import { useEffect } from 'react'; +import { useState } from 'react'; const GrantTypeSelector = ({ item = {}, request, updateAuth, collection }) => { const dispatch = useDispatch(); const dropdownTippyRef = useRef(); - const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref); - const oAuth = get(request, 'auth.oauth2', {}); + const [valuesCache, setValuesCache] = useState({ + ...oAuth + }); + const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref); const Icon = forwardRef((props, ref) => { return ( @@ -23,13 +26,19 @@ const GrantTypeSelector = ({ item = {}, request, updateAuth, collection }) => { }); const onGrantTypeChange = (grantType) => { + let updatedValues = { + ...valuesCache, + ...oAuth, + grantType + }; + setValuesCache(updatedValues); dispatch( updateAuth({ mode: 'oauth2', collectionUid: collection.uid, itemUid: item.uid, content: { - ...(defaultValues?.[grantType] || {}) + ...updatedValues } }) ); @@ -52,10 +61,11 @@ const GrantTypeSelector = ({ item = {}, request, updateAuth, collection }) => { clientId: '', clientSecret: '', scope: '', + credentialsPlacement: 'body', credentialsId: 'credentials', tokenPlacement: 'header', tokenPrefix: 'Bearer', - tokenQueryParamKey: 'access_token', + tokenQueryKey: 'access_token', reuseToken: false } }) @@ -106,47 +116,4 @@ const GrantTypeSelector = ({ item = {}, request, updateAuth, collection }) => { ); }; -export default GrantTypeSelector; - -const defaultValues = { - 'authorization_code': { - grantType: 'authorization_code', - accessTokenUrl: '', - username: '', - password: '', - clientId: '', - clientSecret: '', - scope: '', - credentialsId: 'credentials', - tokenPlacement: 'header', - tokenPrefix: 'Bearer', - tokenQueryParamKey: 'access_token', - reuseToken: false - }, - 'client_credentials': { - grantType: 'client_credentials', - accessTokenUrl: '', - clientId: '', - clientSecret: '', - scope: '', - credentialsId: 'credentials', - tokenPlacement: 'header', - tokenPrefix: 'Bearer', - tokenQueryParamKey: 'access_token', - reuseToken: false - }, - 'password': { - grantType: 'password', - accessTokenUrl: '', - username: '', - password: '', - clientId: '', - clientSecret: '', - scope: '', - credentialsId: 'credentials', - tokenPlacement: 'header', - tokenPrefix: 'Bearer', - tokenQueryParamKey: 'access_token', - reuseToken: false - } -} \ No newline at end of file +export default GrantTypeSelector; \ No newline at end of file diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Oauth2TokenViewer/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Oauth2TokenViewer/index.js index 84552d313..00be40f88 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Oauth2TokenViewer/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Oauth2TokenViewer/index.js @@ -37,12 +37,15 @@ const TokenSection = ({ title, token }) => { className="flex items-center justify-between px-3 py-2 bg-gray-50 dark:bg-gray-800 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-750 transition-colors" onClick={() => setIsExpanded(!isExpanded)} > -
+
{isExpanded ? : } +

{title}

+ {decodedToken?.exp && } +
{isExpanded && ( @@ -86,62 +89,48 @@ const TokenSection = ({ title, token }) => { }; const formatExpiryTime = (seconds) => { - if (seconds < 60) { - return `${seconds}s`; - } else if (seconds < 3600) { - const mins = Math.floor(seconds / 60); - const secs = seconds % 60; - return `${mins}m ${secs}s`; - } else { - const hours = Math.floor(seconds / 3600); - const mins = Math.floor((seconds % 3600) / 60); - return `${hours}h ${mins}m`; - } + if (seconds < 60) return `${seconds}s`; + if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${seconds % 60}s`; + return `${Math.floor(seconds / 3600)}h ${Math.floor((seconds % 3600) / 60)}m`; }; -const ExpiryTimer = ({ initialExpiresIn }) => { - const [timeLeft, setTimeLeft] = useState(initialExpiresIn); +const ExpiryTimer = ({ expiresIn }) => { + if (!expiresIn) return null; + + const [timeLeft, setTimeLeft] = useState(() => Math.max(0, Math.floor((expiresIn - Date.now() / 1000)))); useEffect(() => { const timer = setInterval(() => { - setTimeLeft(prev => { - if (prev <= 0) { - clearInterval(timer); - return 0; - } - return prev - 1; - }); + setTimeLeft((prev) => (prev > 0 ? prev - 1 : 0)); }, 1000); return () => clearInterval(timer); }, []); return ( -
- Expires in {formatExpiryTime(timeLeft)} +
+ {timeLeft > 0 ? `Expires in ${formatExpiryTime(timeLeft)}` : `Expired` }
); }; + const Oauth2TokenViewer = ({ collection, item, url, credentialsId, handleRun }) => { const { uid: collectionUid } = collection; const interpolatedUrl = interpolateStringUsingCollectionAndItem({ collection, item, string: url }); const credentialsData = find(collection?.oauth2Credentials, creds => creds?.url == interpolatedUrl && creds?.collectionUid == collectionUid && creds?.credentialsId == credentialsId); - const creds = credentialsData?.credentials; + const creds = credentialsData?.credentials || {}; return ( - {creds ? ( + {Object.keys(creds)?.length ? (
-
-

Token

- {creds?.expires_in && } -
- 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 8d9709c45..a8810dc5e 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 @@ -22,7 +22,7 @@ const OAuth2PasswordCredentials = ({ save, item = {}, request, handleRun, update const oAuth = get(request, 'auth.oauth2', {}); - const { accessTokenUrl, username, password, clientId, clientSecret, scope, credentialsId, tokenPlacement, tokenPrefix, tokenQueryParamKey, reuseToken } = oAuth; + const { accessTokenUrl, username, password, clientId, clientSecret, scope, credentialsPlacement, credentialsId, tokenPlacement, tokenPrefix, tokenQueryKey, reuseToken } = oAuth; const handleFetchOauth2Credentials = async () => { let requestCopy = cloneDeep(request); @@ -32,20 +32,30 @@ const OAuth2PasswordCredentials = ({ save, item = {}, request, handleRun, update try { await dispatch(fetchOauth2Credentials({ request: requestCopy, collection })); toggleFetchingToken(false); + toast.success('Token fetched successfully!'); } catch (error) { - console.error('could not fetch the token!'); console.error(error); toggleFetchingToken(false); + toast.error('An error occured while fetching token!'); } } const handleSave = () => { save(); } - const Icon = forwardRef((props, ref) => { + const TokenPlacementIcon = forwardRef((props, ref) => { return (
- {tokenPlacement == 'url' ? 'URL' : 'Headers'} + {tokenPlacement == 'url' ? 'URL' : 'Headers'} + +
+ ); + }); + + const CredentialsPlacementIcon = forwardRef((props, ref) => { + return ( +
+ {credentialsPlacement == 'body' ? 'Request Body' : 'Basic Auth Header'}
); @@ -65,10 +75,11 @@ const OAuth2PasswordCredentials = ({ save, item = {}, request, handleRun, update clientId, clientSecret, scope, + credentialsPlacement, credentialsId, tokenPlacement, tokenPrefix, - tokenQueryParamKey, + tokenQueryKey, reuseToken, [key]: value } @@ -118,6 +129,31 @@ const OAuth2PasswordCredentials = ({ save, item = {}, request, handleRun, update
); })} +
+ +
+ } placement="bottom-end"> +
{ + dropdownTippyRef.current.hide(); + handleChange('credentialsPlacement', 'body'); + }} + > + Request Body +
+
{ + dropdownTippyRef.current.hide(); + handleChange('credentialsPlacement', 'basic_auth_header'); + }} + > + Basic Auth Header +
+
+
+
@@ -143,7 +179,7 @@ const OAuth2PasswordCredentials = ({ save, item = {}, request, handleRun, update
- } placement="bottom-end"> + } placement="bottom-end">
{ @@ -185,10 +221,10 @@ const OAuth2PasswordCredentials = ({ save, item = {}, request, handleRun, update
handleChange('tokenQueryParamKey', val)} + onChange={(val) => handleChange('tokenQueryKey', val)} onRun={handleRun} collection={collection} /> diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js index 57f1a0e13..6b98bf68d 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -1225,7 +1225,7 @@ export const clearOauth2Cache = (payload) => async (dispatch, getState) => { const { collectionUid, url, credentialsId } = payload; return new Promise((resolve, reject) => { const { ipcRenderer } = window; - ipcRenderer.invoke('clear-oauth2-cache', collectionUid, url, credentialsId).then(resolve => { + ipcRenderer.invoke('clear-oauth2-cache', collectionUid, url, credentialsId).then(() => { dispatch(collectionClearOauth2CredentialsByUrl({ collectionUid, url, credentialsId })); resolve(); }).catch(reject); diff --git a/packages/bruno-app/src/utils/collections/index.js b/packages/bruno-app/src/utils/collections/index.js index b1182b6e6..64c346ae6 100644 --- a/packages/bruno-app/src/utils/collections/index.js +++ b/packages/bruno-app/src/utils/collections/index.js @@ -361,10 +361,11 @@ export const transformCollectionToSaveToExportAsFile = (collection, options = {} clientId: get(si.request, 'auth.oauth2.clientId', ''), clientSecret: get(si.request, 'auth.oauth2.clientSecret', ''), scope: get(si.request, 'auth.oauth2.scope', ''), + credentialsPlacement: get(si.request, 'auth.oauth2.credentialsPlacement', 'body'), credentialsId: get(si.request, 'auth.oauth2.credentialsId', 'credentials'), tokenPlacement: get(si.request, 'auth.oauth2.tokenPlacement', 'header'), tokenPrefix: get(si.request, 'auth.oauth2.tokenPrefix', 'Bearer'), - tokenQueryParamKey: get(si.request, 'auth.oauth2.tokenQueryParamKey', ''), + tokenQueryKey: get(si.request, 'auth.oauth2.tokenQueryKey', ''), reuseToken: get(si.request, 'auth.oauth2.reuseToken', false) }; break; @@ -377,11 +378,12 @@ export const transformCollectionToSaveToExportAsFile = (collection, options = {} clientId: get(si.request, 'auth.oauth2.clientId', ''), clientSecret: get(si.request, 'auth.oauth2.clientSecret', ''), scope: get(si.request, 'auth.oauth2.scope', ''), + credentialsPlacement: get(si.request, 'auth.oauth2.credentialsPlacement', 'body'), pkce: get(si.request, 'auth.oauth2.pkce', false), credentialsId: get(si.request, 'auth.oauth2.credentialsId', 'credentials'), tokenPlacement: get(si.request, 'auth.oauth2.tokenPlacement', 'header'), tokenPrefix: get(si.request, 'auth.oauth2.tokenPrefix', 'Bearer'), - tokenQueryParamKey: get(si.request, 'auth.oauth2.tokenQueryParamKey', ''), + tokenQueryKey: get(si.request, 'auth.oauth2.tokenQueryKey', ''), reuseToken: get(si.request, 'auth.oauth2.reuseToken', false) }; break; @@ -392,10 +394,11 @@ export const transformCollectionToSaveToExportAsFile = (collection, options = {} clientId: get(si.request, 'auth.oauth2.clientId', ''), clientSecret: get(si.request, 'auth.oauth2.clientSecret', ''), scope: get(si.request, 'auth.oauth2.scope', ''), + credentialsPlacement: get(si.request, 'auth.oauth2.credentialsPlacement', 'body'), credentialsId: get(si.request, 'auth.oauth2.credentialsId', 'credentials'), tokenPlacement: get(si.request, 'auth.oauth2.tokenPlacement', 'header'), tokenPrefix: get(si.request, 'auth.oauth2.tokenPrefix', 'Bearer'), - tokenQueryParamKey: get(si.request, 'auth.oauth2.tokenQueryParamKey', ''), + tokenQueryKey: get(si.request, 'auth.oauth2.tokenQueryKey', ''), reuseToken: get(si.request, 'auth.oauth2.reuseToken', false) }; break; @@ -1045,7 +1048,7 @@ export const getFormattedCollectionOauth2Credentials = ({ oauth2Credentials = [] let credentialsVariables = {}; oauth2Credentials.forEach(({ credentialsId, credentials }) => { Object.entries(credentials).forEach(([key, value]) => { - credentialsVariables[`$auth.${credentialsId}.${key}`] = value; + credentialsVariables[`$oauth2.${credentialsId}.${key}`] = value; }); }); return credentialsVariables; diff --git a/packages/bruno-cli/src/runner/interpolate-vars.js b/packages/bruno-cli/src/runner/interpolate-vars.js index 88cee00cb..514ed850e 100644 --- a/packages/bruno-cli/src/runner/interpolate-vars.js +++ b/packages/bruno-cli/src/runner/interpolate-vars.js @@ -147,13 +147,13 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc // todo: we have things happening in two places w.r.t basic auth // need to refactor this in the future // the request.auth (basic auth) object gets set inside the prepare-request.js file - if (request.auth) { - const username = _interpolate(request.auth.username) || ''; - const password = _interpolate(request.auth.password) || ''; + if (request.basicAuth) { + const username = _interpolate(request.basicAuth.username) || ''; + const password = _interpolate(request.basicAuth.password) || ''; // use auth header based approach and delete the request.auth object request.headers['Authorization'] = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`; - delete request.auth; + delete request.basicAuth; } if (request.awsv4config) { @@ -165,12 +165,14 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc request.awsv4config.profileName = _interpolate(request.awsv4config.profileName) || ''; } - // interpolate vars for ntlmConfig auth - if (request.ntlmConfig) { - request.ntlmConfig.username = _interpolate(request.ntlmConfig.username) || ''; - request.ntlmConfig.password = _interpolate(request.ntlmConfig.password) || ''; - request.ntlmConfig.domain = _interpolate(request.ntlmConfig.domain) || ''; - } + // interpolate vars for ntlmConfig auth + if (request.ntlmConfig) { + request.ntlmConfig.username = _interpolate(request.ntlmConfig.username) || ''; + request.ntlmConfig.password = _interpolate(request.ntlmConfig.password) || ''; + request.ntlmConfig.domain = _interpolate(request.ntlmConfig.domain) || ''; + } + + if(request?.auth) delete request.auth; if (request) return request; }; diff --git a/packages/bruno-cli/src/runner/prepare-request.js b/packages/bruno-cli/src/runner/prepare-request.js index 11963bd29..b9efac616 100644 --- a/packages/bruno-cli/src/runner/prepare-request.js +++ b/packages/bruno-cli/src/runner/prepare-request.js @@ -38,7 +38,7 @@ const prepareRequest = (item = {}, collection = {}) => { const collectionAuth = get(collection, 'root.request.auth'); if (collectionAuth && request.auth?.mode === 'inherit') { if (collectionAuth.mode === 'basic') { - axiosRequest.auth = { + axiosRequest.basicAuth = { username: get(collectionAuth, 'basic.username'), password: get(collectionAuth, 'basic.password') }; @@ -69,7 +69,7 @@ const prepareRequest = (item = {}, collection = {}) => { if (request.auth && request.auth.mode !== 'inherit') { if (request.auth.mode === 'basic') { - axiosRequest.auth = { + axiosRequest.basicAuth = { username: get(request, 'auth.basic.username'), password: get(request, 'auth.basic.password') }; diff --git a/packages/bruno-electron/src/ipc/network/interpolate-vars.js b/packages/bruno-electron/src/ipc/network/interpolate-vars.js index 06d18abd0..c7b374261 100644 --- a/packages/bruno-electron/src/ipc/network/interpolate-vars.js +++ b/packages/bruno-electron/src/ipc/network/interpolate-vars.js @@ -149,12 +149,12 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc // todo: we have things happening in two places w.r.t basic auth // need to refactor this in the future // the request.auth (basic auth) object gets set inside the prepare-request.js file - if (request.auth) { - const username = _interpolate(request.auth.username) || ''; - const password = _interpolate(request.auth.password) || ''; + if (request.basicAuth) { + const username = _interpolate(request.basicAuth.username) || ''; + const password = _interpolate(request.basicAuth.password) || ''; // use auth header based approach and delete the request.auth object request.headers['Authorization'] = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`; - delete request.auth; + delete request.basicAuth; } if (request?.oauth2?.grantType) { @@ -167,10 +167,11 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc request.oauth2.clientId = _interpolate(request.oauth2.clientId) || ''; request.oauth2.clientSecret = _interpolate(request.oauth2.clientSecret) || ''; request.oauth2.scope = _interpolate(request.oauth2.scope) || ''; + request.oauth2.credentialsPlacement = _interpolate(request.oauth2.credentialsPlacement) || ''; request.oauth2.credentialsId = _interpolate(request.oauth2.credentialsId) || ''; request.oauth2.tokenPlacement = _interpolate(request.oauth2.tokenPlacement) || ''; request.oauth2.tokenPrefix = _interpolate(request.oauth2.tokenPrefix) || ''; - request.oauth2.tokenQueryParamKey = _interpolate(request.oauth2.tokenQueryParamKey) || ''; + request.oauth2.tokenQueryKey = _interpolate(request.oauth2.tokenQueryKey) || ''; request.oauth2.reuseToken = _interpolate(request.oauth2.reuseToken) || false; break; case 'authorization_code': @@ -182,10 +183,11 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc request.oauth2.scope = _interpolate(request.oauth2.scope) || ''; request.oauth2.state = _interpolate(request.oauth2.state) || ''; request.oauth2.pkce = _interpolate(request.oauth2.pkce) || false; + request.oauth2.credentialsPlacement = _interpolate(request.oauth2.credentialsPlacement) || ''; request.oauth2.credentialsId = _interpolate(request.oauth2.credentialsId) || ''; request.oauth2.tokenPlacement = _interpolate(request.oauth2.tokenPlacement) || ''; request.oauth2.tokenPrefix = _interpolate(request.oauth2.tokenPrefix) || ''; - request.oauth2.tokenQueryParamKey = _interpolate(request.oauth2.tokenQueryParamKey) || ''; + request.oauth2.tokenQueryKey = _interpolate(request.oauth2.tokenQueryKey) || ''; request.oauth2.reuseToken = _interpolate(request.oauth2.reuseToken) || false; break; case 'client_credentials': @@ -193,10 +195,11 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc request.oauth2.clientId = _interpolate(request.oauth2.clientId) || ''; request.oauth2.clientSecret = _interpolate(request.oauth2.clientSecret) || ''; request.oauth2.scope = _interpolate(request.oauth2.scope) || ''; + request.oauth2.credentialsPlacement = _interpolate(request.oauth2.credentialsPlacement) || ''; request.oauth2.credentialsId = _interpolate(request.oauth2.credentialsId) || ''; request.oauth2.tokenPlacement = _interpolate(request.oauth2.tokenPlacement) || ''; request.oauth2.tokenPrefix = _interpolate(request.oauth2.tokenPrefix) || ''; - request.oauth2.tokenQueryParamKey = _interpolate(request.oauth2.tokenQueryParamKey) || ''; + request.oauth2.tokenQueryKey = _interpolate(request.oauth2.tokenQueryKey) || ''; request.oauth2.reuseToken = _interpolate(request.oauth2.reuseToken) || false; break; default: @@ -234,6 +237,8 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc request.ntlmConfig.domain = _interpolate(request.ntlmConfig.domain) || ''; } + if(request?.auth) delete request.auth; + return request; }; diff --git a/packages/bruno-electron/src/utils/collection.js b/packages/bruno-electron/src/utils/collection.js index 7d0a2f69b..29a50629c 100644 --- a/packages/bruno-electron/src/utils/collection.js +++ b/packages/bruno-electron/src/utils/collection.js @@ -294,7 +294,7 @@ const getFormattedCollectionOauth2Credentials = ({ oauth2Credentials = [] }) => let credentialsVariables = {}; oauth2Credentials.forEach(({ credentialsId, credentials }) => { Object.entries(credentials).forEach(([key, value]) => { - credentialsVariables[`$auth.${credentialsId}.${key}`] = value; + credentialsVariables[`$oauth2.${credentialsId}.${key}`] = value; }); }); return credentialsVariables; diff --git a/packages/bruno-electron/src/utils/oauth2.js b/packages/bruno-electron/src/utils/oauth2.js index 0b60ba4e7..bf5fdaf92 100644 --- a/packages/bruno-electron/src/utils/oauth2.js +++ b/packages/bruno-electron/src/utils/oauth2.js @@ -3,7 +3,7 @@ const crypto = require('crypto'); const { authorizeUserInWindow } = require('../ipc/network/authorize-user-in-window'); const Oauth2Store = require('../store/oauth2'); const { makeAxiosInstance } = require('./axios-instance'); -const { safeParseJSON } = require('./common'); +const { safeParseJSON, safeStringifyJSON } = require('./common'); const oauth2Store = new Oauth2Store(); @@ -31,38 +31,46 @@ const getOAuth2TokenUsingAuthorizationCode = async ({ request, collectionUid, fo let requestCopy = cloneDeep(request); const oAuth = get(requestCopy, 'oauth2', {}); - const { clientId, clientSecret, callbackUrl, scope, pkce, authorizationUrl, credentialsId, reuseToken } = oAuth; + const { clientId, clientSecret, callbackUrl, scope, pkce, credentialsPlacement, authorizationUrl, credentialsId, reuseToken } = oAuth; const url = requestCopy?.oauth2?.accessTokenUrl; if ((reuseToken || ALWAYS_REUSE_ACCESS_TOKEN____UNLESS_FETCHED_MANUALLY) && !forceFetch) { const credentials = getStoredOauth2Credentials({ collectionUid, url, credentialsId }) || {}; return { collectionUid, url, credentials, credentialsId }; } - const { authorizationCode } = await getOAuth2AuthorizationCode(requestCopy, codeChallenge, collectionUid); - const data = { - grant_type: 'authorization_code', - code: authorizationCode, - redirect_uri: callbackUrl, - client_id: clientId, - client_secret: clientSecret - }; - if (pkce) { - data['code_verifier'] = codeVerifier; - } requestCopy.method = 'POST'; requestCopy.headers['content-type'] = 'application/x-www-form-urlencoded'; requestCopy.headers['Accept'] = 'application/json'; - requestCopy.data = data; + if (credentialsPlacement == "basic_auth_header") { + requestCopy.headers['Authorization'] = `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`; + } + else { + const data = { + grant_type: 'authorization_code', + code: authorizationCode, + redirect_uri: callbackUrl, + client_id: clientId, + client_secret: clientSecret + }; + if (pkce) { + data['code_verifier'] = codeVerifier; + } + requestCopy.data = data; + } requestCopy.url = url; - - const axiosInstance = makeAxiosInstance(); - const response = await axiosInstance(requestCopy); - const responseData = Buffer.isBuffer(response.data) ? response.data?.toString() : response.data; - const parsedResponseData = safeParseJSON(responseData); - persistOauth2Credentials({ collectionUid, url, credentials: parsedResponseData, credentialsId }); - return { collectionUid, url, credentials: parsedResponseData, credentialsId }; + try { + const axiosInstance = makeAxiosInstance(); + const response = await axiosInstance(requestCopy); + const responseData = Buffer.isBuffer(response.data) ? response.data?.toString() : response.data; + const parsedResponseData = safeParseJSON(responseData); + persistOauth2Credentials({ collectionUid, url, credentials: parsedResponseData, credentialsId }); + return { collectionUid, url, credentials: parsedResponseData, credentialsId }; + } + catch (error) { + return Promise.reject(safeStringifyJSON(error?.response?.data)); + } }; const getOAuth2AuthorizationCode = (request, codeChallenge, collectionUid) => { @@ -105,15 +113,7 @@ const getOAuth2AuthorizationCode = (request, codeChallenge, collectionUid) => { const getOAuth2TokenUsingClientCredentials = async ({ request, collectionUid, forceFetch = false }) => { let requestCopy = cloneDeep(request); const oAuth = get(requestCopy, 'oauth2', {}); - const { clientId, clientSecret, scope, credentialsId, reuseToken } = oAuth; - const data = { - grant_type: 'client_credentials', - client_id: clientId, - client_secret: clientSecret - }; - if (scope) { - data.scope = scope; - } + const { clientId, clientSecret, scope, credentialsPlacement, credentialsId, reuseToken } = oAuth; const url = requestCopy?.oauth2?.accessTokenUrl; @@ -125,16 +125,34 @@ const getOAuth2TokenUsingClientCredentials = async ({ request, collectionUid, fo requestCopy.method = 'POST'; requestCopy.headers['content-type'] = 'application/x-www-form-urlencoded'; requestCopy.headers['Accept'] = 'application/json'; - requestCopy.data = data; + if (credentialsPlacement == "basic_auth_header") { + requestCopy.headers['Authorization'] = `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`; + } + else { + const data = { + grant_type: 'client_credentials', + client_id: clientId, + client_secret: clientSecret + }; + if (scope) { + data.scope = scope; + } + requestCopy.data = data; + } requestCopy.url = url; const axiosInstance = makeAxiosInstance(); - const response = await axiosInstance(requestCopy); - const responseData = Buffer.isBuffer(response.data) ? response.data?.toString() : response.data; - const parsedResponseData = safeParseJSON(responseData); - persistOauth2Credentials({ collectionUid, url, credentials: parsedResponseData, credentialsId }); - return { collectionUid, url, credentials: parsedResponseData, credentialsId }; + try { + const response = await axiosInstance(requestCopy); + const responseData = Buffer.isBuffer(response.data) ? response.data?.toString() : response.data; + const parsedResponseData = safeParseJSON(responseData); + persistOauth2Credentials({ collectionUid, url, credentials: parsedResponseData, credentialsId }); + return { collectionUid, url, credentials: parsedResponseData, credentialsId }; + } + catch (error) { + return Promise.reject(safeStringifyJSON(error?.response?.data)); + } }; // PASSWORD CREDENTIALS @@ -142,17 +160,7 @@ const getOAuth2TokenUsingClientCredentials = async ({ request, collectionUid, fo const getOAuth2TokenUsingPasswordCredentials = async ({ request, collectionUid, forceFetch = false }) => { let requestCopy = cloneDeep(request); const oAuth = get(requestCopy, 'oauth2', {}); - const { username, password, clientId, clientSecret, scope, credentialsId, reuseToken } = oAuth; - const data = { - grant_type: 'password', - username, - password, - client_id: clientId, - client_secret: clientSecret - }; - if (scope) { - data.scope = scope; - } + const { username, password, clientId, clientSecret, scope, credentialsPlacement, credentialsId, reuseToken } = oAuth; const url = requestCopy?.oauth2?.accessTokenUrl; if ((reuseToken || ALWAYS_REUSE_ACCESS_TOKEN____UNLESS_FETCHED_MANUALLY) && !forceFetch) { @@ -163,15 +171,35 @@ const getOAuth2TokenUsingPasswordCredentials = async ({ request, collectionUid, requestCopy.method = 'POST'; requestCopy.headers['content-type'] = 'application/x-www-form-urlencoded'; requestCopy.headers['Accept'] = 'application/json'; - requestCopy.data = data; + if (credentialsPlacement == "basic_auth_header") { + requestCopy.headers['Authorization'] = `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`; + } + else { + const data = { + grant_type: 'password', + username, + password, + client_id: clientId, + client_secret: clientSecret + }; + if (scope) { + data.scope = scope; + } + requestCopy.data = data; + } requestCopy.url = url; - const axiosInstance = makeAxiosInstance(); - const response = await axiosInstance(requestCopy); - const responseData = Buffer.isBuffer(response.data) ? response.data?.toString() : response.data; - const parsedResponseData = safeParseJSON(responseData); - persistOauth2Credentials({ collectionUid, url, credentials: parsedResponseData, credentialsId }); - return { collectionUid, url, credentials: parsedResponseData, credentialsId }; + try { + const axiosInstance = makeAxiosInstance(); + const response = await axiosInstance(requestCopy); + const responseData = Buffer.isBuffer(response.data) ? response.data?.toString() : response.data; + const parsedResponseData = safeParseJSON(responseData); + persistOauth2Credentials({ collectionUid, url, credentials: parsedResponseData, credentialsId }); + return { collectionUid, url, credentials: parsedResponseData, credentialsId }; + } + catch (error) { + return Promise.reject(safeStringifyJSON(error?.response?.data)); + } }; const refreshOauth2Token = async (request, collectionUid) => { @@ -207,9 +235,9 @@ const refreshOauth2Token = async (request, collectionUid) => { persistOauth2Credentials({ collectionUid, url, credentials: parsedResponseData, credentialsId }); return { collectionUid, url, credentials: parsedResponseData, credentialsId }; } - catch(error) { + catch (error) { clearOauth2Credentials({ collectionUid, url, credentialsId }); - return Promise.reject(error); + return Promise.reject(safeStringifyJSON(error?.response?.data)); } } } diff --git a/packages/bruno-electron/src/utils/request.js b/packages/bruno-electron/src/utils/request.js index d155876a7..e3aef5ad6 100644 --- a/packages/bruno-electron/src/utils/request.js +++ b/packages/bruno-electron/src/utils/request.js @@ -38,7 +38,7 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => { }; break; case 'basic': - axiosRequest.auth = { + axiosRequest.basicAuth = { username: get(collectionAuth, 'basic.username'), password: get(collectionAuth, 'basic.password') }; @@ -97,10 +97,11 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => { clientId: get(collectionAuth, 'oauth2.clientId'), clientSecret: get(collectionAuth, 'oauth2.clientSecret'), scope: get(collectionAuth, 'oauth2.scope'), + credentialsPlacement: get(collectionAuth, 'oauth2.credentialsPlacement'), credentialsId: get(collectionAuth, 'oauth2.credentialsId'), tokenPlacement: get(collectionAuth, 'oauth2.tokenPlacement'), tokenPrefix: get(collectionAuth, 'oauth2.tokenPrefix'), - tokenQueryParamKey: get(collectionAuth, 'oauth2.tokenQueryParamKey'), + tokenQueryKey: get(collectionAuth, 'oauth2.tokenQueryKey'), reuseToken: get(collectionAuth, 'oauth2.reuseToken') }; break; @@ -115,10 +116,11 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => { scope: get(collectionAuth, 'oauth2.scope'), state: get(collectionAuth, 'oauth2.state'), pkce: get(collectionAuth, 'oauth2.pkce'), + credentialsPlacement: get(collectionAuth, 'oauth2.credentialsPlacement'), credentialsId: get(collectionAuth, 'oauth2.credentialsId'), tokenPlacement: get(collectionAuth, 'oauth2.tokenPlacement'), tokenPrefix: get(collectionAuth, 'oauth2.tokenPrefix'), - tokenQueryParamKey: get(collectionAuth, 'oauth2.tokenQueryParamKey'), + tokenQueryKey: get(collectionAuth, 'oauth2.tokenQueryKey'), reuseToken: get(collectionAuth, 'oauth2.reuseToken') }; break; @@ -129,10 +131,11 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => { clientId: get(collectionAuth, 'oauth2.clientId'), clientSecret: get(collectionAuth, 'oauth2.clientSecret'), scope: get(collectionAuth, 'oauth2.scope'), + credentialsPlacement: get(collectionAuth, 'oauth2.credentialsPlacement'), credentialsId: get(collectionAuth, 'oauth2.credentialsId'), tokenPlacement: get(collectionAuth, 'oauth2.tokenPlacement'), tokenPrefix: get(collectionAuth, 'oauth2.tokenPrefix'), - tokenQueryParamKey: get(collectionAuth, 'oauth2.tokenQueryParamKey'), + tokenQueryKey: get(collectionAuth, 'oauth2.tokenQueryKey'), reuseToken: get(collectionAuth, 'oauth2.reuseToken') }; break; @@ -154,7 +157,7 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => { }; break; case 'basic': - axiosRequest.auth = { + axiosRequest.basicAuth = { username: get(request, 'auth.basic.username'), password: get(request, 'auth.basic.password') }; @@ -186,10 +189,11 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => { clientId: get(request, 'auth.oauth2.clientId'), clientSecret: get(request, 'auth.oauth2.clientSecret'), scope: get(request, 'auth.oauth2.scope'), + credentialsPlacement: get(request, 'auth.oauth2.credentialsPlacement'), credentialsId: get(request, 'auth.oauth2.credentialsId'), tokenPlacement: get(request, 'auth.oauth2.tokenPlacement'), tokenPrefix: get(request, 'auth.oauth2.tokenPrefix'), - tokenQueryParamKey: get(request, 'auth.oauth2.tokenQueryParamKey'), + tokenQueryKey: get(request, 'auth.oauth2.tokenQueryKey'), reuseToken: get(request, 'auth.oauth2.reuseToken') }; break; @@ -204,10 +208,11 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => { scope: get(request, 'auth.oauth2.scope'), state: get(request, 'auth.oauth2.state'), pkce: get(request, 'auth.oauth2.pkce'), + credentialsPlacement: get(request, 'auth.oauth2.credentialsPlacement'), credentialsId: get(request, 'auth.oauth2.credentialsId'), tokenPlacement: get(request, 'auth.oauth2.tokenPlacement'), tokenPrefix: get(request, 'auth.oauth2.tokenPrefix'), - tokenQueryParamKey: get(request, 'auth.oauth2.tokenQueryParamKey'), + tokenQueryKey: get(request, 'auth.oauth2.tokenQueryKey'), reuseToken: get(request, 'auth.oauth2.reuseToken') }; break; @@ -218,10 +223,11 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => { clientId: get(request, 'auth.oauth2.clientId'), clientSecret: get(request, 'auth.oauth2.clientSecret'), scope: get(request, 'auth.oauth2.scope'), + credentialsPlacement: get(request, 'auth.oauth2.credentialsPlacement'), credentialsId: get(request, 'auth.oauth2.credentialsId'), tokenPlacement: get(request, 'auth.oauth2.tokenPlacement'), tokenPrefix: get(request, 'auth.oauth2.tokenPrefix'), - tokenQueryParamKey: get(request, 'auth.oauth2.tokenQueryParamKey'), + tokenQueryKey: get(request, 'auth.oauth2.tokenQueryKey'), reuseToken: get(request, 'auth.oauth2.reuseToken') }; break; @@ -607,7 +613,7 @@ const configureRequest = async ( if (request.oauth2) { let requestCopy = cloneDeep(request); - const { oauth2: { grantType, tokenPlacement, tokenPrefix, tokenQueryParamKey } = {} } = requestCopy || {}; + const { oauth2: { grantType, tokenPlacement, tokenPrefix, tokenQueryKey } = {} } = requestCopy || {}; let credentials, credentialsId; switch (grantType) { case 'authorization_code': @@ -620,7 +626,7 @@ const configureRequest = async ( else { try { const url = new URL(request.url); - url?.searchParams?.set(tokenQueryParamKey, credentials?.access_token); + url?.searchParams?.set(tokenQueryKey, credentials?.access_token); request.url = url?.toString(); } catch(error) {} @@ -636,7 +642,7 @@ const configureRequest = async ( else { try { const url = new URL(request.url); - url?.searchParams?.set(tokenQueryParamKey, credentials?.access_token); + url?.searchParams?.set(tokenQueryKey, credentials?.access_token); request.url = url?.toString(); } catch(error) {} @@ -652,7 +658,7 @@ const configureRequest = async ( else { try { const url = new URL(request.url); - url?.searchParams?.set(tokenQueryParamKey, credentials?.access_token); + url?.searchParams?.set(tokenQueryKey, credentials?.access_token); request.url = url?.toString(); } catch(error) {} diff --git a/packages/bruno-lang/v2/src/bruToJson.js b/packages/bruno-lang/v2/src/bruToJson.js index 3d29789e3..366859626 100644 --- a/packages/bruno-lang/v2/src/bruToJson.js +++ b/packages/bruno-lang/v2/src/bruToJson.js @@ -484,11 +484,12 @@ const sem = grammar.createSemantics().addAttribute('ast', { const scopeKey = _.find(auth, { name: 'scope' }); const stateKey = _.find(auth, { name: 'state' }); const pkceKey = _.find(auth, { name: 'pkce' }); - const credentialsIdKey = _.find(auth, { name: 'credentialsId' }); - const tokenPlacementKey = _.find(auth, { name: 'tokenPlacement' }); - const tokenPrefixKey = _.find(auth, { name: 'tokenPrefix' }); - const tokenQueryParamKeyKey = _.find(auth, { name: 'tokenQueryParamKey' }); - const reuseTokenKey = _.find(auth, { name: 'reuseToken' }); + const credentialsPlacementKey = _.find(auth, { name: 'credentials_placement' }); + const credentialsIdKey = _.find(auth, { name: 'credentials_id' }); + const tokenPlacementKey = _.find(auth, { name: 'token_placement' }); + const tokenPrefixKey = _.find(auth, { name: 'token_prefix' }); + const tokenQueryKeyKey = _.find(auth, { name: 'token_query_key' }); + const reuseTokenKey = _.find(auth, { name: 'reuse_token' }); return { auth: { oauth2: @@ -501,10 +502,11 @@ const sem = grammar.createSemantics().addAttribute('ast', { clientId: clientIdKey ? clientIdKey.value : '', clientSecret: clientSecretKey ? clientSecretKey.value : '', scope: scopeKey ? scopeKey.value : '', + credentialsPlacement: credentialsPlacementKey?.value ? credentialsPlacementKey.value : 'body', credentialsId: credentialsIdKey?.value ? credentialsIdKey.value : 'credentials', tokenPlacement: tokenPlacementKey?.value ? tokenPlacementKey.value : 'header', tokenPrefix: tokenPrefixKey?.value ? tokenPrefixKey.value : 'Bearer', - tokenQueryParamKey: tokenQueryParamKeyKey?.value ? tokenQueryParamKeyKey.value : 'access_token', + tokenQueryKey: tokenQueryKeyKey?.value ? tokenQueryKeyKey.value : 'access_token', reuseToken: reuseTokenKey?.value ? JSON.parse(reuseTokenKey?.value || false) : false } : grantTypeKey?.value && grantTypeKey?.value == 'authorization_code' @@ -518,10 +520,11 @@ const sem = grammar.createSemantics().addAttribute('ast', { scope: scopeKey ? scopeKey.value : '', state: stateKey ? stateKey.value : '', pkce: pkceKey ? JSON.parse(pkceKey?.value || false) : false, + credentialsPlacement: credentialsPlacementKey?.value ? credentialsPlacementKey.value : 'body', credentialsId: credentialsIdKey?.value ? credentialsIdKey.value : 'credentials', tokenPlacement: tokenPlacementKey?.value ? tokenPlacementKey.value : 'header', tokenPrefix: tokenPrefixKey?.value ? tokenPrefixKey.value : 'Bearer', - tokenQueryParamKey: tokenQueryParamKeyKey?.value ? tokenQueryParamKeyKey.value : 'access_token', + tokenQueryKey: tokenQueryKeyKey?.value ? tokenQueryKeyKey.value : 'access_token', reuseToken: reuseTokenKey?.value ? JSON.parse(reuseTokenKey?.value || false) : false } : grantTypeKey?.value && grantTypeKey?.value == 'client_credentials' @@ -531,10 +534,11 @@ const sem = grammar.createSemantics().addAttribute('ast', { clientId: clientIdKey ? clientIdKey.value : '', clientSecret: clientSecretKey ? clientSecretKey.value : '', scope: scopeKey ? scopeKey.value : '', + credentialsPlacement: credentialsPlacementKey?.value ? credentialsPlacementKey.value : 'body', credentialsId: credentialsIdKey?.value ? credentialsIdKey.value : 'credentials', tokenPlacement: tokenPlacementKey?.value ? tokenPlacementKey.value : 'header', tokenPrefix: tokenPrefixKey?.value ? tokenPrefixKey.value : 'Bearer', - tokenQueryParamKey: tokenQueryParamKeyKey?.value ? tokenQueryParamKeyKey.value : 'access_token', + tokenQueryKey: tokenQueryKeyKey?.value ? tokenQueryKeyKey.value : 'access_token', reuseToken: reuseTokenKey?.value ? JSON.parse(reuseTokenKey?.value || false) : false } : {} diff --git a/packages/bruno-lang/v2/src/collectionBruToJson.js b/packages/bruno-lang/v2/src/collectionBruToJson.js index f9f428e66..4d18f8e36 100644 --- a/packages/bruno-lang/v2/src/collectionBruToJson.js +++ b/packages/bruno-lang/v2/src/collectionBruToJson.js @@ -279,10 +279,11 @@ const sem = grammar.createSemantics().addAttribute('ast', { const scopeKey = _.find(auth, { name: 'scope' }); const stateKey = _.find(auth, { name: 'state' }); const pkceKey = _.find(auth, { name: 'pkce' }); - const credentialsIdKey = _.find(auth, { name: 'credentialsId' }); - const tokenPlacementKey = _.find(auth, { name: 'tokenPlacement' }); - const tokenPrefixKey = _.find(auth, { name: 'tokenPrefix' }); - const tokenQueryParamKeyKey = _.find(auth, { name: 'tokenQueryParamKey' }); + const credentialsPlacementKey = _.find(auth, { name: 'credentials_placement' }); + const credentialsIdKey = _.find(auth, { name: 'credentials_id' }); + const tokenPlacementKey = _.find(auth, { name: 'token_placement' }); + const tokenPrefixKey = _.find(auth, { name: 'token_prefix' }); + const tokenQueryKeyKey = _.find(auth, { name: 'token_query_key' }); const reuseTokenKey = _.find(auth, { name: 'reuseToken' }); return { auth: { @@ -296,10 +297,11 @@ const sem = grammar.createSemantics().addAttribute('ast', { clientId: clientIdKey ? clientIdKey.value : '', clientSecret: clientSecretKey ? clientSecretKey.value : '', scope: scopeKey ? scopeKey.value : '', + credentialsPlacement: credentialsPlacementKey?.value ? credentialsPlacementKey.value : 'body', credentialsId: credentialsIdKey?.value ? credentialsIdKey.value : 'credentials', tokenPlacement: tokenPlacementKey?.value ? tokenPlacementKey.value : 'header', tokenPrefix: tokenPrefixKey?.value ? tokenPrefixKey.value : 'Bearer', - tokenQueryParamKey: tokenQueryParamKeyKey?.value ? tokenQueryParamKeyKey.value : 'access_token', + tokenQueryKey: tokenQueryKeyKey?.value ? tokenQueryKeyKey.value : 'access_token', reuseToken: reuseTokenKey?.value ? JSON.parse(reuseTokenKey?.value || false) : false } : grantTypeKey?.value && grantTypeKey?.value == 'authorization_code' @@ -313,10 +315,11 @@ const sem = grammar.createSemantics().addAttribute('ast', { scope: scopeKey ? scopeKey.value : '', state: stateKey ? stateKey.value : '', pkce: pkceKey ? JSON.parse(pkceKey?.value || false) : false, + credentialsPlacement: credentialsPlacementKey?.value ? credentialsPlacementKey.value : 'body', credentialsId: credentialsIdKey?.value ? credentialsIdKey.value : 'credentials', tokenPlacement: tokenPlacementKey?.value ? tokenPlacementKey.value : 'header', tokenPrefix: tokenPrefixKey?.value ? tokenPrefixKey.value : 'Bearer', - tokenQueryParamKey: tokenQueryParamKeyKey?.value ? tokenQueryParamKeyKey.value : 'access_token', + tokenQueryKey: tokenQueryKeyKey?.value ? tokenQueryKeyKey.value : 'access_token', reuseToken: reuseTokenKey?.value ? JSON.parse(reuseTokenKey?.value || false) : false } : grantTypeKey?.value && grantTypeKey?.value == 'client_credentials' @@ -326,10 +329,11 @@ const sem = grammar.createSemantics().addAttribute('ast', { clientId: clientIdKey ? clientIdKey.value : '', clientSecret: clientSecretKey ? clientSecretKey.value : '', scope: scopeKey ? scopeKey.value : '', + credentialsPlacement: credentialsPlacementKey?.value ? credentialsPlacementKey.value : 'body', credentialsId: credentialsIdKey?.value ? credentialsIdKey.value : 'credentials', tokenPlacement: tokenPlacementKey?.value ? tokenPlacementKey.value : 'header', tokenPrefix: tokenPrefixKey?.value ? tokenPrefixKey.value : 'Bearer', - tokenQueryParamKey: tokenQueryParamKeyKey?.value ? tokenQueryParamKeyKey.value : 'access_token', + tokenQueryKey: tokenQueryKeyKey?.value ? tokenQueryKeyKey.value : 'access_token', reuseToken: reuseTokenKey?.value ? JSON.parse(reuseTokenKey?.value || false) : false } : {} diff --git a/packages/bruno-lang/v2/src/jsonToBru.js b/packages/bruno-lang/v2/src/jsonToBru.js index 6144199e9..a417ba46b 100644 --- a/packages/bruno-lang/v2/src/jsonToBru.js +++ b/packages/bruno-lang/v2/src/jsonToBru.js @@ -188,11 +188,14 @@ ${indentString(`password: ${auth?.oauth2?.password || ''}`)} ${indentString(`client_id: ${auth?.oauth2?.clientId || ''}`)} ${indentString(`client_secret: ${auth?.oauth2?.clientSecret || ''}`)} ${indentString(`scope: ${auth?.oauth2?.scope || ''}`)} -${indentString(`credentialsId: ${auth?.oauth2?.credentialsId || ''}`)} -${indentString(`tokenPlacement: ${auth?.oauth2?.tokenPlacement || ''}`)} -${indentString(`tokenPrefix: ${auth?.oauth2?.tokenPrefix || ''}`)} -${indentString(`tokenQueryParamKey: ${auth?.oauth2?.tokenQueryParamKey || ''}`)} -${indentString(`reuseToken: ${auth?.oauth2?.reuseToken || ''}`)} +${indentString(`credentials_placement: ${auth?.oauth2?.credentialsPlacement || ''}`)} +${indentString(`credentials_id: ${auth?.oauth2?.credentialsId || ''}`)} +${indentString(`token_placement: ${auth?.oauth2?.tokenPlacement || ''}`)}${ + auth?.oauth2?.tokenPlacement == 'header' ? '\n' + indentString(`token_prefix: ${auth?.oauth2?.tokenPrefix || ''}`) : '' +}${ + auth?.oauth2?.tokenPlacement !== 'header' ? '\n' + indentString(`token_query_key: ${auth?.oauth2?.tokenQueryKey || ''}`) : '' +} +${indentString(`reuse_token: ${auth?.oauth2?.reuseToken || ''}`)} } `; @@ -208,11 +211,14 @@ ${indentString(`client_secret: ${auth?.oauth2?.clientSecret || ''}`)} ${indentString(`scope: ${auth?.oauth2?.scope || ''}`)} ${indentString(`state: ${auth?.oauth2?.state || ''}`)} ${indentString(`pkce: ${(auth?.oauth2?.pkce || false).toString()}`)} -${indentString(`credentialsId: ${auth?.oauth2?.credentialsId || ''}`)} -${indentString(`tokenPlacement: ${auth?.oauth2?.tokenPlacement || ''}`)} -${indentString(`tokenPrefix: ${auth?.oauth2?.tokenPrefix || ''}`)} -${indentString(`tokenQueryParamKey: ${auth?.oauth2?.tokenQueryParamKey || ''}`)} -${indentString(`reuseToken: ${auth?.oauth2?.reuseToken || ''}`)} +${indentString(`credentials_placement: ${auth?.oauth2?.credentialsPlacement || ''}`)} +${indentString(`credentials_id: ${auth?.oauth2?.credentialsId || ''}`)} +${indentString(`token_placement: ${auth?.oauth2?.tokenPlacement || ''}`)}${ + auth?.oauth2?.tokenPlacement == 'header' ? '\n' + indentString(`token_prefix: ${auth?.oauth2?.tokenPrefix || ''}`) : '' +}${ + auth?.oauth2?.tokenPlacement !== 'header' ? '\n' + indentString(`token_query_key: ${auth?.oauth2?.tokenQueryKey || ''}`) : '' +} +${indentString(`reuse_token: ${auth?.oauth2?.reuseToken || ''}`)} } `; @@ -224,11 +230,14 @@ ${indentString(`access_token_url: ${auth?.oauth2?.accessTokenUrl || ''}`)} ${indentString(`client_id: ${auth?.oauth2?.clientId || ''}`)} ${indentString(`client_secret: ${auth?.oauth2?.clientSecret || ''}`)} ${indentString(`scope: ${auth?.oauth2?.scope || ''}`)} -${indentString(`credentialsId: ${auth?.oauth2?.credentialsId || ''}`)} -${indentString(`tokenPlacement: ${auth?.oauth2?.tokenPlacement || ''}`)} -${indentString(`tokenPrefix: ${auth?.oauth2?.tokenPrefix || ''}`)} -${indentString(`tokenQueryParamKey: ${auth?.oauth2?.tokenQueryParamKey || ''}`)} -${indentString(`reuseToken: ${auth?.oauth2?.reuseToken || ''}`)} +${indentString(`credentials_placement: ${auth?.oauth2?.credentialsPlacement || ''}`)} +${indentString(`credentials_id: ${auth?.oauth2?.credentialsId || ''}`)} +${indentString(`token_placement: ${auth?.oauth2?.tokenPlacement || ''}`)}${ + auth?.oauth2?.tokenPlacement == 'header' ? '\n' + indentString(`token_prefix: ${auth?.oauth2?.tokenPrefix || ''}`) : '' +}${ + auth?.oauth2?.tokenPlacement !== 'header' ? '\n' + indentString(`token_query_key: ${auth?.oauth2?.tokenQueryKey || ''}`) : '' +} +${indentString(`reuse_token: ${auth?.oauth2?.reuseToken || ''}`)} } `; diff --git a/packages/bruno-lang/v2/src/jsonToCollectionBru.js b/packages/bruno-lang/v2/src/jsonToCollectionBru.js index 600d08109..9fcd2452e 100644 --- a/packages/bruno-lang/v2/src/jsonToCollectionBru.js +++ b/packages/bruno-lang/v2/src/jsonToCollectionBru.js @@ -154,11 +154,14 @@ ${indentString(`password: ${auth?.oauth2?.password || ''}`)} ${indentString(`client_id: ${auth?.oauth2?.clientId || ''}`)} ${indentString(`client_secret: ${auth?.oauth2?.clientSecret || ''}`)} ${indentString(`scope: ${auth?.oauth2?.scope || ''}`)} -${indentString(`credentialsId: ${auth?.oauth2?.credentialsId || ''}`)} -${indentString(`tokenPlacement: ${auth?.oauth2?.tokenPlacement || ''}`)} -${indentString(`tokenPrefix: ${auth?.oauth2?.tokenPrefix || ''}`)} -${indentString(`tokenQueryParamKey: ${auth?.oauth2?.tokenQueryParamKey || ''}`)} -${indentString(`reuseToken: ${auth?.oauth2?.reuseToken || ''}`)} +${indentString(`credentials_placement: ${auth?.oauth2?.credentialsPlacement || ''}`)} +${indentString(`credentials_id: ${auth?.oauth2?.credentialsId || ''}`)} +${indentString(`token_placement: ${auth?.oauth2?.tokenPlacement || ''}`)}${ + auth?.oauth2?.tokenPlacement == 'header' ? '\n' + indentString(`token_prefix: ${auth?.oauth2?.tokenPrefix || ''}`) : '' +}${ + auth?.oauth2?.tokenPlacement !== 'header' ? '\n' + indentString(`token_query_key: ${auth?.oauth2?.tokenQueryKey || ''}`) : '' +} +${indentString(`reuse_token: ${auth?.oauth2?.reuseToken || ''}`)} } `; @@ -174,11 +177,14 @@ ${indentString(`client_secret: ${auth?.oauth2?.clientSecret || ''}`)} ${indentString(`scope: ${auth?.oauth2?.scope || ''}`)} ${indentString(`state: ${auth?.oauth2?.state || ''}`)} ${indentString(`pkce: ${(auth?.oauth2?.pkce || false).toString()}`)} -${indentString(`credentialsId: ${auth?.oauth2?.credentialsId || ''}`)} -${indentString(`tokenPlacement: ${auth?.oauth2?.tokenPlacement || ''}`)} -${indentString(`tokenPrefix: ${auth?.oauth2?.tokenPrefix || ''}`)} -${indentString(`tokenQueryParamKey: ${auth?.oauth2?.tokenQueryParamKey || ''}`)} -${indentString(`reuseToken: ${auth?.oauth2?.reuseToken || ''}`)} +${indentString(`credentials_placement: ${auth?.oauth2?.credentialsPlacement || ''}`)} +${indentString(`credentials_id: ${auth?.oauth2?.credentialsId || ''}`)} +${indentString(`token_placement: ${auth?.oauth2?.tokenPlacement || ''}`)}${ + auth?.oauth2?.tokenPlacement == 'header' ? '\n' + indentString(`token_prefix: ${auth?.oauth2?.tokenPrefix || ''}`) : '' +}${ + auth?.oauth2?.tokenPlacement !== 'header' ? '\n' + indentString(`token_query_key: ${auth?.oauth2?.tokenQueryKey || ''}`) : '' +} +${indentString(`reuse_token: ${auth?.oauth2?.reuseToken || ''}`)} } `; @@ -190,11 +196,14 @@ ${indentString(`access_token_url: ${auth?.oauth2?.accessTokenUrl || ''}`)} ${indentString(`client_id: ${auth?.oauth2?.clientId || ''}`)} ${indentString(`client_secret: ${auth?.oauth2?.clientSecret || ''}`)} ${indentString(`scope: ${auth?.oauth2?.scope || ''}`)} -${indentString(`credentialsId: ${auth?.oauth2?.credentialsId || ''}`)} -${indentString(`tokenPlacement: ${auth?.oauth2?.tokenPlacement || ''}`)} -${indentString(`tokenPrefix: ${auth?.oauth2?.tokenPrefix || ''}`)} -${indentString(`tokenQueryParamKey: ${auth?.oauth2?.tokenQueryParamKey || ''}`)} -${indentString(`reuseToken: ${auth?.oauth2?.reuseToken || ''}`)} +${indentString(`credentials_placement: ${auth?.oauth2?.credentialsPlacement || ''}`)} +${indentString(`credentials_id: ${auth?.oauth2?.credentialsId || ''}`)} +${indentString(`token_placement: ${auth?.oauth2?.tokenPlacement || ''}`)}${ + auth?.oauth2?.tokenPlacement == 'header' ? '\n' + indentString(`token_prefix: ${auth?.oauth2?.tokenPrefix || ''}`) : '' +}${ + auth?.oauth2?.tokenPlacement !== 'header' ? '\n' + indentString(`token_query_key: ${auth?.oauth2?.tokenQueryKey || ''}`) : '' +} +${indentString(`reuse_token: ${auth?.oauth2?.reuseToken || ''}`)} } `; diff --git a/packages/bruno-schema/src/collections/index.js b/packages/bruno-schema/src/collections/index.js index d4b52fc29..025059565 100644 --- a/packages/bruno-schema/src/collections/index.js +++ b/packages/bruno-schema/src/collections/index.js @@ -200,6 +200,11 @@ const oauth2Schema = Yup.object({ then: Yup.boolean().default(false), otherwise: Yup.boolean() }), + credentialsPlacement: Yup.string().when('grantType', { + is: (val) => ['client_credentials', 'password', 'authorization_code'].includes(val), + then: Yup.string().nullable(), + otherwise: Yup.string().nullable().strip() + }), credentialsId: Yup.string().when('grantType', { is: (val) => ['client_credentials', 'password', 'authorization_code'].includes(val), then: Yup.string().nullable(), @@ -216,7 +221,7 @@ const oauth2Schema = Yup.object({ then: Yup.string().nullable(), otherwise: Yup.string().nullable().strip() }), - tokenQueryParamKey: Yup.string().when(['grantType', 'tokenPlacement'], { + tokenQueryKey: Yup.string().when(['grantType', 'tokenPlacement'], { is: (grantType, tokenPlacement) => ['client_credentials', 'password', 'authorization_code'].includes(grantType) && tokenPlacement === 'url', then: Yup.string().nullable(), diff --git a/packages/bruno-tests/keycloak-authorization_code/collection.bru b/packages/bruno-tests/keycloak-authorization_code/collection.bru index b4bc199d8..066e30bfe 100644 --- a/packages/bruno-tests/keycloak-authorization_code/collection.bru +++ b/packages/bruno-tests/keycloak-authorization_code/collection.bru @@ -12,9 +12,9 @@ auth:oauth2 { scope: openid state: pkce: true - credentialsId: keycloak - tokenPlacement: header - tokenPrefix: Bearer - tokenQueryParamKey: access_token - reuseToken: + credentials_placement: body + credentials_id: credentials + token_placement: header + token_prefix: Bearer + reuse_token: } diff --git a/packages/bruno-tests/keycloak-authorization_code/user_info_custom.bru b/packages/bruno-tests/keycloak-authorization_code/user_info_custom.bru index 3ecf55350..58cadf9cf 100644 --- a/packages/bruno-tests/keycloak-authorization_code/user_info_custom.bru +++ b/packages/bruno-tests/keycloak-authorization_code/user_info_custom.bru @@ -11,5 +11,5 @@ get { } auth:bearer { - token: {{$auth.keycloak.access_token}} + token: {{$oauth2.keycloak.access_token}} } diff --git a/packages/bruno-tests/keycloak-authorization_code/user_info_request-auth.bru b/packages/bruno-tests/keycloak-authorization_code/user_info_request-auth.bru index b4b44fda6..b1bd2b37f 100644 --- a/packages/bruno-tests/keycloak-authorization_code/user_info_request-auth.bru +++ b/packages/bruno-tests/keycloak-authorization_code/user_info_request-auth.bru @@ -20,9 +20,9 @@ auth:oauth2 { scope: openid state: pkce: true - credentialsId: keycloak_request - tokenPlacement: header - tokenPrefix: Bearer - tokenQueryParamKey: token - reuseToken: + credentials_placement: body + credentials_id: credentials + token_placement: header + token_prefix: Bearer + reuse_token: } diff --git a/packages/bruno-tests/keycloak-client-credentials/bruno.json b/packages/bruno-tests/keycloak-client-credentials/bruno.json new file mode 100644 index 000000000..783e37474 --- /dev/null +++ b/packages/bruno-tests/keycloak-client-credentials/bruno.json @@ -0,0 +1,9 @@ +{ + "version": "1", + "name": "keycloak-client-credentials", + "type": "collection", + "ignore": [ + "node_modules", + ".git" + ] +} \ No newline at end of file diff --git a/packages/bruno-tests/keycloak-client-credentials/collection.bru b/packages/bruno-tests/keycloak-client-credentials/collection.bru new file mode 100644 index 000000000..16b0b9f86 --- /dev/null +++ b/packages/bruno-tests/keycloak-client-credentials/collection.bru @@ -0,0 +1,20 @@ +auth { + mode: oauth2 +} + +auth:oauth2 { + grant_type: authorization_code + callback_url: {{key-host}}/realms/bruno/account + authorization_url: {{key-host}}/realms/bruno/protocol/openid-connect/auth + access_token_url: {{key-host}}/realms/bruno/protocol/openid-connect/token + client_id: account + client_secret: Lh3NkRikMZpO12rwSBwVimde9v89B5Rw + scope: openid + state: + pkce: true + tokenId: keycloak + tokenPlacement: header + tokenPrefix: Bearer + tokenQueryKey: access_token + reuseToken: +} diff --git a/packages/bruno-tests/keycloak-client-credentials/environments/oauth2.bru b/packages/bruno-tests/keycloak-client-credentials/environments/oauth2.bru new file mode 100644 index 000000000..b9a0c5468 --- /dev/null +++ b/packages/bruno-tests/keycloak-client-credentials/environments/oauth2.bru @@ -0,0 +1,22 @@ +vars { + host: http://localhost:8080 + bearer_auth_token: your_secret_token + basic_auth_password: della + client_id: client_id_1 + client_secret: client_secret_1 + password_credentials_access_token_url: http://localhost:8081/api/auth/oauth2/password_credentials/token + password_credentials_username: foo + password_credentials_password: bar + password_credentials_scope: + authorization_code_authorize_url: http://localhost:8081/api/auth/oauth2/authorization_code/authorize + authorization_code_callback_url: http://localhost:8081/api/auth/oauth2/authorization_code/callback + authorization_code_access_token_url: http://localhost:8081/api/auth/oauth2/authorization_code/token + authorization_code_access_token: null + client_credentials_access_token_url: http://localhost:8081/api/auth/oauth2/client_credentials/token + client_credentials_client_id: client_id_1 + client_credentials_client_secret: client_secret_1 + client_credentials_scope: admin + client_credentials_access_token: 870132a2ed28a3c94d34f868e6514720 + key-host: http://localhost:8080 + key-host-1: http://localhost:8082 +} diff --git a/packages/bruno-tests/keycloak-client-credentials/user_info_coll-auth.bru b/packages/bruno-tests/keycloak-client-credentials/user_info_coll-auth.bru new file mode 100644 index 000000000..ec838c9fa --- /dev/null +++ b/packages/bruno-tests/keycloak-client-credentials/user_info_coll-auth.bru @@ -0,0 +1,11 @@ +meta { + name: user_info_coll-auth + type: http + seq: 1 +} + +get { + url: {{key-host}}/realms/bruno/protocol/openid-connect/userinfo + body: none + auth: inherit +} diff --git a/packages/bruno-tests/keycloak-client-credentials/user_info_custom.bru b/packages/bruno-tests/keycloak-client-credentials/user_info_custom.bru new file mode 100644 index 000000000..58cadf9cf --- /dev/null +++ b/packages/bruno-tests/keycloak-client-credentials/user_info_custom.bru @@ -0,0 +1,15 @@ +meta { + name: user_info_custom + type: http + seq: 2 +} + +get { + url: {{key-host}}/realms/bruno/protocol/openid-connect/userinfo + body: none + auth: bearer +} + +auth:bearer { + token: {{$oauth2.keycloak.access_token}} +} diff --git a/packages/bruno-tests/keycloak-client-credentials/user_info_request-auth.bru b/packages/bruno-tests/keycloak-client-credentials/user_info_request-auth.bru new file mode 100644 index 000000000..5cb20e622 --- /dev/null +++ b/packages/bruno-tests/keycloak-client-credentials/user_info_request-auth.bru @@ -0,0 +1,24 @@ +meta { + name: user_info_request-auth + type: http + seq: 3 +} + +get { + url: {{key-host}}/realms/bruno/protocol/openid-connect/userinfo + body: none + auth: oauth2 +} + +auth:oauth2 { + grant_type: client_credentials + access_token_url: {{key-host}}/realms/bruno/protocol/openid-connect/token + client_id: account + client_secret: Lh3NkRikMZpO12rwSBwVimde9v89B5Rw + scope: openid + credentials_placement: body + credentials_id: credentials + token_placement: header + token_prefix: Bearer + reuse_token: +}