Merge pull request #4446 from lohxt1/pr_4416

fix(oauth2): improve additional parameters handling and ui updates
This commit is contained in:
lohit 2025-04-06 19:05:45 +05:30 committed by GitHub
commit 84ef5b1044
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 66 additions and 54 deletions

View File

@ -11,7 +11,6 @@ import Table from "components/Table/index";
const AdditionalParams = ({ item = {}, request, updateAuth, collection }) => { const AdditionalParams = ({ item = {}, request, updateAuth, collection }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const { storedTheme } = useTheme(); const { storedTheme } = useTheme();
const [activeTab, setActiveTab] = useState('authorization');
const oAuth = get(request, 'auth.oauth2', {}); const oAuth = get(request, 'auth.oauth2', {});
const { const {
@ -19,6 +18,8 @@ const AdditionalParams = ({ item = {}, request, updateAuth, collection }) => {
additionalParameters = {} additionalParameters = {}
} = oAuth; } = oAuth;
const [activeTab, setActiveTab] = useState(grantType == 'authorization_code' ? 'authorization' : 'token');
const isEmptyParam = (param) => { const isEmptyParam = (param) => {
return !param.name.trim() && !param.value.trim(); return !param.name.trim() && !param.value.trim();
}; };
@ -28,8 +29,8 @@ const AdditionalParams = ({ item = {}, request, updateAuth, collection }) => {
return tabParams.some(isEmptyParam); return tabParams.some(isEmptyParam);
}; };
const updateAdditionalParams = ({ updatedAdditionalParams }) => { const updateAdditionalParameters = ({ updatedAdditionalParameters }) => {
const filteredParams = cloneDeep(updatedAdditionalParams); const filteredParams = cloneDeep(updatedAdditionalParameters);
Object.keys(filteredParams).forEach(paramType => { Object.keys(filteredParams).forEach(paramType => {
if (filteredParams[paramType]?.length) { if (filteredParams[paramType]?.length) {
@ -60,14 +61,14 @@ const AdditionalParams = ({ item = {}, request, updateAuth, collection }) => {
} }
const handleUpdateAdditionalParam = ({ paramType, key, paramIndex, value }) => { const handleUpdateAdditionalParam = ({ paramType, key, paramIndex, value }) => {
const updatedAdditionalParams = cloneDeep(additionalParameters); const updatedAdditionalParameters = cloneDeep(additionalParameters);
if (!updatedAdditionalParams[paramType]) { if (!updatedAdditionalParameters[paramType]) {
updatedAdditionalParams[paramType] = []; updatedAdditionalParameters[paramType] = [];
} }
if (!updatedAdditionalParams[paramType][paramIndex]) { if (!updatedAdditionalParameters[paramType][paramIndex]) {
updatedAdditionalParams[paramType][paramIndex] = { updatedAdditionalParameters[paramType][paramIndex] = {
name: '', name: '',
value: '', value: '',
sendIn: 'headers', sendIn: 'headers',
@ -75,25 +76,25 @@ const AdditionalParams = ({ item = {}, request, updateAuth, collection }) => {
}; };
} }
updatedAdditionalParams[paramType][paramIndex][key] = value; updatedAdditionalParameters[paramType][paramIndex][key] = value;
// Only filter when updating a parameter // Only filter when updating a parameter
updateAdditionalParams({ updatedAdditionalParams }); updateAdditionalParameters({ updatedAdditionalParameters });
} }
const handleDeleteAdditionalParam = ({ paramType, paramIndex }) => { const handleDeleteAdditionalParam = ({ paramType, paramIndex }) => {
const updatedAdditionalParams = cloneDeep(additionalParameters); const updatedAdditionalParameters = cloneDeep(additionalParameters);
if (updatedAdditionalParams[paramType]?.length) { if (updatedAdditionalParameters[paramType]?.length) {
updatedAdditionalParams[paramType] = updatedAdditionalParams[paramType].filter((_, index) => index !== paramIndex); updatedAdditionalParameters[paramType] = updatedAdditionalParameters[paramType].filter((_, index) => index !== paramIndex);
// If the array is now empty, ensure we're not sending empty arrays // If the array is now empty, ensure we're not sending empty arrays
if (updatedAdditionalParams[paramType].length === 0) { if (updatedAdditionalParameters[paramType].length === 0) {
delete updatedAdditionalParams[paramType]; delete updatedAdditionalParameters[paramType];
} }
} }
updateAdditionalParams({ updatedAdditionalParams }); updateAdditionalParameters({ updatedAdditionalParameters });
} }
const handleAddNewAdditionalParam = () => { const handleAddNewAdditionalParam = () => {
@ -149,7 +150,7 @@ const AdditionalParams = ({ item = {}, request, updateAuth, collection }) => {
</div> </div>
<div className="tabs flex w-full gap-2 my-2"> <div className="tabs flex w-full gap-2 my-2">
<div className={`tab ${activeTab == 'authorization' ? 'active': ''}`} onClick={e => setActiveTab('authorization')}>Authorization</div> {grantType == 'authorization_code' && <div className={`tab ${activeTab == 'authorization' ? 'active': ''}`} onClick={e => setActiveTab('authorization')}>Authorization</div>}
<div className={`tab ${activeTab == 'token' ? 'active': ''}`} onClick={e => setActiveTab('token')}>Token</div> <div className={`tab ${activeTab == 'token' ? 'active': ''}`} onClick={e => setActiveTab('token')}>Token</div>
<div className={`tab ${activeTab == 'refresh' ? 'active': ''}`} onClick={e => setActiveTab('refresh')}>Refresh</div> <div className={`tab ${activeTab == 'refresh' ? 'active': ''}`} onClick={e => setActiveTab('refresh')}>Refresh</div>
</div> </div>
@ -279,12 +280,10 @@ const sendInOptionsMap = {
'refresh': ['headers', 'queryparams', 'body'] 'refresh': ['headers', 'queryparams', 'body']
}, },
'password': { 'password': {
'authorization': ['headers', 'queryparams'],
'token': ['headers', 'queryparams', 'body'], 'token': ['headers', 'queryparams', 'body'],
'refresh': ['headers', 'queryparams', 'body'] 'refresh': ['headers', 'queryparams', 'body']
}, },
'client_credentials': { 'client_credentials': {
'authorization': ['headers', 'queryparams'],
'token': ['headers', 'queryparams', 'body'], 'token': ['headers', 'queryparams', 'body'],
'refresh': ['headers', 'queryparams', 'body'] 'refresh': ['headers', 'queryparams', 'body']
} }

View File

@ -26,7 +26,7 @@ const Request = ({ collection, request, item, width }) => {
<div> <div>
{/* Method and URL */} {/* Method and URL */}
<div className="mb-1 flex gap-2"> <div className="mb-1 flex gap-2">
<pre className="whitespace-pre-wrap">{url}</pre> <pre className="whitespace-pre-wrap" title={url}>{url}</pre>
</div> </div>
{/* Headers */} {/* Headers */}

View File

@ -87,7 +87,8 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
tokenHeaderPrefix: get(collectionAuth, 'oauth2.tokenHeaderPrefix'), tokenHeaderPrefix: get(collectionAuth, 'oauth2.tokenHeaderPrefix'),
tokenQueryKey: get(collectionAuth, 'oauth2.tokenQueryKey'), tokenQueryKey: get(collectionAuth, 'oauth2.tokenQueryKey'),
autoFetchToken: get(collectionAuth, 'oauth2.autoFetchToken'), autoFetchToken: get(collectionAuth, 'oauth2.autoFetchToken'),
autoRefreshToken: get(collectionAuth, 'oauth2.autoRefreshToken') autoRefreshToken: get(collectionAuth, 'oauth2.autoRefreshToken'),
additionalParameters: get(collectionAuth, 'auth.oauth2.additionalParameters', { authorization: [], token: [], refresh: [] })
}; };
break; break;
case 'authorization_code': case 'authorization_code':
@ -108,7 +109,8 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
tokenHeaderPrefix: get(collectionAuth, 'oauth2.tokenHeaderPrefix'), tokenHeaderPrefix: get(collectionAuth, 'oauth2.tokenHeaderPrefix'),
tokenQueryKey: get(collectionAuth, 'oauth2.tokenQueryKey'), tokenQueryKey: get(collectionAuth, 'oauth2.tokenQueryKey'),
autoFetchToken: get(collectionAuth, 'oauth2.autoFetchToken'), autoFetchToken: get(collectionAuth, 'oauth2.autoFetchToken'),
autoRefreshToken: get(collectionAuth, 'oauth2.autoRefreshToken') autoRefreshToken: get(collectionAuth, 'oauth2.autoRefreshToken'),
additionalParameters: get(collectionAuth, 'auth.oauth2.additionalParameters', { authorization: [], token: [], refresh: [] })
}; };
break; break;
case 'client_credentials': case 'client_credentials':
@ -125,7 +127,8 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
tokenHeaderPrefix: get(collectionAuth, 'oauth2.tokenHeaderPrefix'), tokenHeaderPrefix: get(collectionAuth, 'oauth2.tokenHeaderPrefix'),
tokenQueryKey: get(collectionAuth, 'oauth2.tokenQueryKey'), tokenQueryKey: get(collectionAuth, 'oauth2.tokenQueryKey'),
autoFetchToken: get(collectionAuth, 'oauth2.autoFetchToken'), autoFetchToken: get(collectionAuth, 'oauth2.autoFetchToken'),
autoRefreshToken: get(collectionAuth, 'oauth2.autoRefreshToken') autoRefreshToken: get(collectionAuth, 'oauth2.autoRefreshToken'),
additionalParameters: get(collectionAuth, 'auth.oauth2.additionalParameters', { authorization: [], token: [], refresh: [] })
}; };
break; break;
} }
@ -185,7 +188,8 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
tokenHeaderPrefix: get(request, 'auth.oauth2.tokenHeaderPrefix'), tokenHeaderPrefix: get(request, 'auth.oauth2.tokenHeaderPrefix'),
tokenQueryKey: get(request, 'auth.oauth2.tokenQueryKey'), tokenQueryKey: get(request, 'auth.oauth2.tokenQueryKey'),
autoFetchToken: get(request, 'auth.oauth2.autoFetchToken'), autoFetchToken: get(request, 'auth.oauth2.autoFetchToken'),
autoRefreshToken: get(request, 'auth.oauth2.autoRefreshToken') autoRefreshToken: get(request, 'auth.oauth2.autoRefreshToken'),
additionalParameters: get(request, 'auth.oauth2.additionalParameters', { authorization: [], token: [], refresh: [] })
}; };
break; break;
case 'authorization_code': case 'authorization_code':
@ -206,7 +210,8 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
tokenHeaderPrefix: get(request, 'auth.oauth2.tokenHeaderPrefix'), tokenHeaderPrefix: get(request, 'auth.oauth2.tokenHeaderPrefix'),
tokenQueryKey: get(request, 'auth.oauth2.tokenQueryKey'), tokenQueryKey: get(request, 'auth.oauth2.tokenQueryKey'),
autoFetchToken: get(request, 'auth.oauth2.autoFetchToken'), autoFetchToken: get(request, 'auth.oauth2.autoFetchToken'),
autoRefreshToken: get(request, 'auth.oauth2.autoRefreshToken') autoRefreshToken: get(request, 'auth.oauth2.autoRefreshToken'),
additionalParameters: get(request, 'auth.oauth2.additionalParameters', { authorization: [], token: [], refresh: [] })
}; };
break; break;
case 'client_credentials': case 'client_credentials':
@ -223,7 +228,8 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
tokenHeaderPrefix: get(request, 'auth.oauth2.tokenHeaderPrefix'), tokenHeaderPrefix: get(request, 'auth.oauth2.tokenHeaderPrefix'),
tokenQueryKey: get(request, 'auth.oauth2.tokenQueryKey'), tokenQueryKey: get(request, 'auth.oauth2.tokenQueryKey'),
autoFetchToken: get(request, 'auth.oauth2.autoFetchToken'), autoFetchToken: get(request, 'auth.oauth2.autoFetchToken'),
autoRefreshToken: get(request, 'auth.oauth2.autoRefreshToken') autoRefreshToken: get(request, 'auth.oauth2.autoRefreshToken'),
additionalParameters: get(request, 'auth.oauth2.additionalParameters', { authorization: [], token: [], refresh: [] })
}; };
break; break;
} }

View File

@ -142,13 +142,13 @@ const getOAuth2TokenUsingAuthorizationCode = async ({ request, collectionUid, fo
data.scope = scope; data.scope = scope;
} }
requestCopy.url = url;
// Apply additional parameters to token request // Apply additional parameters to token request
if (additionalParameters?.token?.length) { if (additionalParameters?.token?.length) {
applyAdditionalParameters(requestCopy, data, additionalParameters.token); applyAdditionalParameters(requestCopy, data, additionalParameters.token);
} }
requestCopy.data = qs.stringify(data); requestCopy.data = qs.stringify(data);
requestCopy.url = url;
requestCopy.responseType = 'arraybuffer'; requestCopy.responseType = 'arraybuffer';
// Initialize variables to hold request and response data for debugging // Initialize variables to hold request and response data for debugging
@ -400,12 +400,13 @@ const getOAuth2TokenUsingClientCredentials = async ({ request, collectionUid, fo
if (scope) { if (scope) {
data.scope = scope; data.scope = scope;
} }
requestCopy.url = url;
if (additionalParameters?.token?.length) { if (additionalParameters?.token?.length) {
applyAdditionalParameters(requestCopy, data, additionalParameters.token); applyAdditionalParameters(requestCopy, data, additionalParameters.token);
} }
requestCopy.data = qs.stringify(data); requestCopy.data = qs.stringify(data);
requestCopy.url = url;
requestCopy.responseType = 'arraybuffer'; requestCopy.responseType = 'arraybuffer';
// Initialize variables to hold request and response data for debugging // Initialize variables to hold request and response data for debugging
@ -593,12 +594,13 @@ const getOAuth2TokenUsingPasswordCredentials = async ({ request, collectionUid,
if (scope) { if (scope) {
data.scope = scope; data.scope = scope;
} }
requestCopy.url = url;
if (additionalParameters?.token?.length) { if (additionalParameters?.token?.length) {
applyAdditionalParameters(requestCopy, data, additionalParameters.token); applyAdditionalParameters(requestCopy, data, additionalParameters.token);
} }
requestCopy.data = qs.stringify(data); requestCopy.data = qs.stringify(data);
requestCopy.url = url;
requestCopy.responseType = 'arraybuffer'; requestCopy.responseType = 'arraybuffer';
// Initialize variables to hold request and response data for debugging // Initialize variables to hold request and response data for debugging
@ -714,6 +716,8 @@ const refreshOauth2Token = async ({ requestCopy, collectionUid, certsAndProxyCon
if (clientSecret) { if (clientSecret) {
data.client_secret = clientSecret; data.client_secret = clientSecret;
} }
requestCopy.url = url;
if (oAuth.additionalParameters?.refresh?.length) { if (oAuth.additionalParameters?.refresh?.length) {
applyAdditionalParameters(requestCopy, data, oAuth.additionalParameters.refresh); applyAdditionalParameters(requestCopy, data, oAuth.additionalParameters.refresh);
} }
@ -722,7 +726,6 @@ const refreshOauth2Token = async ({ requestCopy, collectionUid, certsAndProxyCon
requestCopy.headers['content-type'] = 'application/x-www-form-urlencoded'; requestCopy.headers['content-type'] = 'application/x-www-form-urlencoded';
requestCopy.headers['Accept'] = 'application/json'; requestCopy.headers['Accept'] = 'application/json';
requestCopy.data = qs.stringify(data); requestCopy.data = qs.stringify(data);
requestCopy.url = url;
requestCopy.responseType = 'arraybuffer'; requestCopy.responseType = 'arraybuffer';
// Initialize variables to hold request and response data for debugging // Initialize variables to hold request and response data for debugging
@ -845,11 +848,7 @@ const generateCodeChallenge = (codeVerifier) => {
}; };
// Apply additional parameters to a request // Apply additional parameters to a request
const applyAdditionalParameters = (requestCopy, data, params) => { const applyAdditionalParameters = (requestCopy, data, params = []) => {
if (!params || !params.length) {
return;
}
params.forEach(param => { params.forEach(param => {
if (!param.enabled || !param.name) { if (!param.enabled || !param.name) {
return; return;
@ -861,12 +860,14 @@ const applyAdditionalParameters = (requestCopy, data, params) => {
break; break;
case 'queryparams': case 'queryparams':
// For query params, add to URL // For query params, add to URL
if (!requestCopy.url.includes('?')) { try {
requestCopy.url += '?'; let url = new URL(requestCopy.url);
} else if (!requestCopy.url.endsWith('&') && !requestCopy.url.endsWith('?')) { url.searchParams.append(param.name, param.value);
requestCopy.url += '&'; requestCopy.url = url.href;
}
catch (error) {
console.error('invalid token/refresh url', requestCopy.url);
} }
requestCopy.url += `${encodeURIComponent(param.name)}=${encodeURIComponent(param.value || '')}`;
break; break;
case 'body': case 'body':
// For body, add to data object // For body, add to data object

View File

@ -40,19 +40,21 @@ const mergeOauth2AdditionalParameters = (ast) => {
const refreshQueryParams = ast?.oauth2_additional_parameters_refresh_queryparams; const refreshQueryParams = ast?.oauth2_additional_parameters_refresh_queryparams;
const refreshBodyValues = ast?.oauth2_additional_parameters_refresh_bodyvalues; const refreshBodyValues = ast?.oauth2_additional_parameters_refresh_bodyvalues;
if (authorizationHeaders?.length || authorizationQueryParams?.length) { if (ast?.auth?.oauth2?.grantType == 'authorization_code') {
additionalParameters['authorization'] = [] if (authorizationHeaders?.length || authorizationQueryParams?.length) {
} additionalParameters['authorization'] = []
if (authorizationHeaders?.length) { }
additionalParameters['authorization'] = [ if (authorizationHeaders?.length) {
...authorizationHeaders?.map(_ => ({ ..._, sendIn: 'headers' })) additionalParameters['authorization'] = [
] ...authorizationHeaders?.map(_ => ({ ..._, sendIn: 'headers' }))
} ]
if (authorizationQueryParams?.length) { }
additionalParameters['authorization'] = [ if (authorizationQueryParams?.length) {
...additionalParameters['authorization'] || [], additionalParameters['authorization'] = [
...authorizationQueryParams?.map(_ => ({ ..._, sendIn: 'queryparams' })) ...additionalParameters['authorization'] || [],
] ...authorizationQueryParams?.map(_ => ({ ..._, sendIn: 'queryparams' }))
]
}
} }
if (tokenHeaders?.length || tokenQueryParams?.length || tokenBodyValues?.length) { if (tokenHeaders?.length || tokenQueryParams?.length || tokenBodyValues?.length) {

View File

@ -276,7 +276,11 @@ const oauth2Schema = Yup.object({
otherwise: Yup.boolean() otherwise: Yup.boolean()
}), }),
additionalParameters: Yup.object({ additionalParameters: Yup.object({
authorization: Yup.array().of(oauth2AuthorizationAdditionalParametersSchema).optional(), authorization: Yup.mixed().when('grantType', {
is: 'authorization_code',
then: Yup.array().of(oauth2AuthorizationAdditionalParametersSchema).required(),
otherwise: Yup.mixed().nullable().optional()
}),
token: Yup.array().of(oauth2AdditionalParametersSchema).optional(), token: Yup.array().of(oauth2AdditionalParametersSchema).optional(),
refresh: Yup.array().of(oauth2AdditionalParametersSchema).optional() refresh: Yup.array().of(oauth2AdditionalParametersSchema).optional()
}) })