Skip to content

Commit

Permalink
feat: OAuth2 - Client Credentials as Basic Auth Header - user interface
Browse files Browse the repository at this point in the history
Add GUI controls for selecting client_secret_method
  • Loading branch information
pietrygamat committed Jan 11, 2025
1 parent c055a43 commit b312193
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import SingleLineEditor from 'components/SingleLineEditor';
import { saveCollectionRoot, sendCollectionOauth2Request } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
import { inputsConfig } from './inputsConfig';
import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections/index';
import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections';
import ClientCredentialsMethodSelector from 'components/RequestPane/Auth/OAuth2/ClientCredentialsMethodSelector';

const OAuth2AuthorizationCode = ({ collection }) => {
const dispatch = useDispatch();
Expand All @@ -20,7 +21,8 @@ const OAuth2AuthorizationCode = ({ collection }) => {

const handleSave = () => dispatch(saveCollectionRoot(collection.uid));

const { callbackUrl, authorizationUrl, accessTokenUrl, clientId, clientSecret, scope, state, pkce } = oAuth;
const { callbackUrl, authorizationUrl, accessTokenUrl, clientId, clientSecret, clientSecretMethod, scope, state, pkce } =
oAuth;

const handleChange = (key, value) => {
dispatch(
Expand All @@ -34,6 +36,7 @@ const OAuth2AuthorizationCode = ({ collection }) => {
accessTokenUrl,
clientId,
clientSecret,
clientSecretMethod,
scope,
state,
pkce,
Expand All @@ -55,6 +58,7 @@ const OAuth2AuthorizationCode = ({ collection }) => {
accessTokenUrl,
clientId,
clientSecret,
clientSecretMethod,
scope,
state,
pkce: !Boolean(oAuth?.['pkce'])
Expand Down Expand Up @@ -92,6 +96,7 @@ const OAuth2AuthorizationCode = ({ collection }) => {
onChange={handlePKCEToggle}
/>
</div>
<ClientCredentialsMethodSelector collection={collection} oAuth={oAuth} />
</StyledWrapper>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import SingleLineEditor from 'components/SingleLineEditor';
import { saveCollectionRoot, sendCollectionOauth2Request } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
import { inputsConfig } from './inputsConfig';
import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections/index';
import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections';
import ClientCredentialsMethodSelector from 'components/RequestPane/Auth/OAuth2/ClientCredentialsMethodSelector';

const OAuth2ClientCredentials = ({ collection }) => {
const dispatch = useDispatch();
Expand All @@ -20,7 +21,7 @@ const OAuth2ClientCredentials = ({ collection }) => {

const handleSave = () => dispatch(saveCollectionRoot(collection.uid));

const { accessTokenUrl, clientId, clientSecret, scope } = oAuth;
const { accessTokenUrl, clientId, clientSecret, clientSecretMethod, scope } = oAuth;

const handleChange = (key, value) => {
dispatch(
Expand All @@ -32,6 +33,7 @@ const OAuth2ClientCredentials = ({ collection }) => {
accessTokenUrl,
clientId,
clientSecret,
clientSecretMethod,
scope,
[key]: value
}
Expand Down Expand Up @@ -60,6 +62,7 @@ const OAuth2ClientCredentials = ({ collection }) => {
</div>
);
})}
<ClientCredentialsMethodSelector collection={collection} oAuth={oAuth} />
</StyledWrapper>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { saveCollectionRoot, sendCollectionOauth2Request } from 'providers/Redux
import StyledWrapper from './StyledWrapper';
import { inputsConfig } from './inputsConfig';
import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections';
import ClientCredentialsMethodSelector from 'components/RequestPane/Auth/OAuth2/ClientCredentialsMethodSelector';

const OAuth2PasswordCredentials = ({ collection }) => {
const dispatch = useDispatch();
Expand All @@ -20,7 +21,7 @@ const OAuth2PasswordCredentials = ({ collection }) => {

const handleSave = () => dispatch(saveCollectionRoot(collection.uid));

const { accessTokenUrl, username, password, clientId, clientSecret, scope } = oAuth;
const { accessTokenUrl, username, password, clientId, clientSecret, clientSecretMethod, scope } = oAuth;

const handleChange = (key, value) => {
dispatch(
Expand All @@ -34,6 +35,7 @@ const OAuth2PasswordCredentials = ({ collection }) => {
password,
clientId,
clientSecret,
clientSecretMethod,
scope,
[key]: value
}
Expand Down Expand Up @@ -62,6 +64,7 @@ const OAuth2PasswordCredentials = ({ collection }) => {
</div>
);
})}
<ClientCredentialsMethodSelector collection={collection} oAuth={oAuth} />
</StyledWrapper>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ 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 ClientCredentialsMethodSelector from 'components/RequestPane/Auth/OAuth2/ClientCredentialsMethodSelector';

const OAuth2AuthorizationCode = ({ item, collection }) => {
const dispatch = useDispatch();
Expand All @@ -20,7 +21,8 @@ const OAuth2AuthorizationCode = ({ item, collection }) => {

const handleSave = () => dispatch(saveRequest(item.uid, collection.uid));

const { callbackUrl, authorizationUrl, accessTokenUrl, clientId, clientSecret, scope, state, pkce } = oAuth;
const { callbackUrl, authorizationUrl, accessTokenUrl, clientId, clientSecret, clientSecretMethod, scope, state, pkce } =
oAuth;

const handleChange = (key, value) => {
dispatch(
Expand All @@ -35,6 +37,7 @@ const OAuth2AuthorizationCode = ({ item, collection }) => {
accessTokenUrl,
clientId,
clientSecret,
clientSecretMethod,
state,
scope,
pkce,
Expand All @@ -57,6 +60,7 @@ const OAuth2AuthorizationCode = ({ item, collection }) => {
accessTokenUrl,
clientId,
clientSecret,
clientSecretMethod,
state,
scope,
pkce: !Boolean(oAuth?.['pkce'])
Expand Down Expand Up @@ -96,6 +100,7 @@ const OAuth2AuthorizationCode = ({ item, collection }) => {
onChange={handlePKCEToggle}
/>
</div>
<ClientCredentialsMethodSelector item={item} collection={collection} oAuth={oAuth} />
</StyledWrapper>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ 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 ClientCredentialsMethodSelector from 'components/RequestPane/Auth/OAuth2/ClientCredentialsMethodSelector';

const OAuth2ClientCredentials = ({ item, collection }) => {
const dispatch = useDispatch();
Expand All @@ -20,7 +21,7 @@ const OAuth2ClientCredentials = ({ item, collection }) => {

const handleSave = () => dispatch(saveRequest(item.uid, collection.uid));

const { accessTokenUrl, clientId, clientSecret, scope } = oAuth;
const { accessTokenUrl, clientId, clientSecret, clientSecretMethod, scope } = oAuth;

const handleChange = (key, value) => {
dispatch(
Expand All @@ -33,6 +34,7 @@ const OAuth2ClientCredentials = ({ item, collection }) => {
accessTokenUrl,
clientId,
clientSecret,
clientSecretMethod,
scope,
[key]: value
}
Expand Down Expand Up @@ -62,6 +64,7 @@ const OAuth2ClientCredentials = ({ item, collection }) => {
</div>
);
})}
<ClientCredentialsMethodSelector item={item} collection={collection} oAuth={oAuth} />
</StyledWrapper>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import styled from 'styled-components';

const Wrapper = styled.div`
font-size: 0.8125rem;
.client-credentials-secret-mode-selector {
padding: 0.5rem 0px;
border-radius: 3px;
border: solid 1px ${(props) => props.theme.input.border};
background-color: ${(props) => props.theme.input.bg};
.client-credentials-secret-label {
width: fit-content;
color: ${(props) => props.theme.colors.text.yellow};
justify-content: space-between;
padding: 0 0.5rem;
}
.dropdown-item {
padding: 0.2rem 0.6rem !important;
}
}
`;

export default Wrapper;
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React, { forwardRef, useEffect, useRef } from 'react';
import Dropdown from 'components/Dropdown';
import StyledWrapper from './StyledWrapper';
import { IconCaretDown } from '@tabler/icons';
import { updateAuth, updateCollectionAuth } from 'providers/ReduxStore/slices/collections';
import { useDispatch } from 'react-redux';
import { humanizeOAuth2ClientSecretMethod } from 'utils/collections';

const ClientCredentialsMethodSelector = ({ item, collection, oAuth }) => {
const clientSecretMethods = ['client_credentials_basic', 'client_credentials_post'];

const dispatch = useDispatch();
const dropDownRef = useRef();
const onDropdownCreate = (ref) => (dropDownRef.current = ref);

const Icon = forwardRef((props, ref) => {
return (
<div ref={ref} className="flex items-center justify-end client-credentials-secret-label select-none">
{humanizeOAuth2ClientSecretMethod(oAuth?.clientSecretMethod)}{' '}
<IconCaretDown className="caret ml-1 mr-1" size={14} strokeWidth={2} />
</div>
);
});

const onClientSecretMethodChange = (clientSecretMethod) => {
if (item) {
// Update request level authentication
dispatch(
updateAuth({
mode: 'oauth2',
collectionUid: collection.uid,
itemUid: item.uid,
content: {
...oAuth,
clientSecretMethod: clientSecretMethod
}
})
);
} else {
// Update collection level authentication
dispatch(
updateCollectionAuth({
mode: 'oauth2',
collectionUid: collection.uid,
content: {
...oAuth,
clientSecretMethod: clientSecretMethod
}
})
);
}
};

useEffect(() => {
!oAuth?.clientSecretMethod && onClientSecretMethodChange(clientSecretMethods[0]);
}, [oAuth.clientSecretMethod]);

return (
<StyledWrapper>
<label className="block font-medium mb-2">Send Client Credentials</label>
<div className="inline-flex items-center cursor-pointer client-credentials-secret-mode-selector w-fit">
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement="bottom-end">
{clientSecretMethods.map((item, index) => (
<div
key={item}
className="dropdown-item"
onClick={() => {
dropDownRef.current.hide();
onClientSecretMethodChange(item);
}}
>
{' '}
{humanizeOAuth2ClientSecretMethod(item)}
{index === 0 ? ` (Default)` : ``}
</div>
))}
</Dropdown>
</div>
</StyledWrapper>
);
};
export default ClientCredentialsMethodSelector;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React from 'react';
import get from 'lodash/get';
import { useTheme } from 'providers/Theme';
import { useDispatch } from 'react-redux';
Expand All @@ -7,6 +7,7 @@ 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 ClientCredentialsMethodSelector from 'components/RequestPane/Auth/OAuth2/ClientCredentialsMethodSelector';

const OAuth2PasswordCredentials = ({ item, collection }) => {
const dispatch = useDispatch();
Expand All @@ -20,7 +21,7 @@ const OAuth2PasswordCredentials = ({ item, collection }) => {

const handleSave = () => dispatch(saveRequest(item.uid, collection.uid));

const { accessTokenUrl, username, password, clientId, clientSecret, scope } = oAuth;
const { accessTokenUrl, username, password, clientId, clientSecret, clientSecretMethod, scope } = oAuth;

const handleChange = (key, value) => {
dispatch(
Expand All @@ -35,6 +36,7 @@ const OAuth2PasswordCredentials = ({ item, collection }) => {
password,
clientId,
clientSecret,
clientSecretMethod,
scope,
[key]: value
}
Expand Down Expand Up @@ -64,6 +66,7 @@ const OAuth2PasswordCredentials = ({ item, collection }) => {
</div>
);
})}
<ClientCredentialsMethodSelector item={item} collection={collection} oAuth={oAuth} />
</StyledWrapper>
);
};
Expand Down
18 changes: 17 additions & 1 deletion packages/bruno-app/src/utils/collections/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ export const moveCollectionItemToRootOfCollection = (collection, draggedItem) =>
draggedItemParent.items = filter(draggedItemParent.items, (i) => i.uid !== draggedItem.uid);
collection.items = sortBy(collection.items, (item) => item.seq);
collection.items.push(draggedItem);
if (draggedItem.type == 'folder') {
if (draggedItem.type === 'folder') {
draggedItem.pathname = path.join(collection.pathname, draggedItem.name);
} else {
draggedItem.pathname = path.join(collection.pathname, draggedItem.filename);
Expand Down Expand Up @@ -733,6 +733,22 @@ export const humanizeGrantType = (mode) => {
return label;
};

export const humanizeOAuth2ClientSecretMethod = (mode) => {
let label = 'N/A';
switch (mode) {
case 'client_credentials_basic': {
label = 'As Basic Auth Header';
break;
}
case 'client_credentials_post': {
label = 'In Request Body';
break;
}
}

return label;
};

export const refreshUidsInItem = (item) => {
item.uid = uuid();

Expand Down

0 comments on commit b312193

Please sign in to comment.