feat: OAuth 2.0 Client Credentials as Basic Auth - user interface

This commit is contained in:
Mateusz Pietryga 2024-04-25 08:39:04 +02:00
parent 0e4a4307ad
commit 1ec999ebdd
No known key found for this signature in database
GPG Key ID: 549A107FB8327670
9 changed files with 174 additions and 11 deletions

View File

@ -6,9 +6,10 @@ 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 { clearOauth2Cache } from 'utils/network/index';
import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections';
import { clearOauth2Cache } from 'utils/network';
import toast from 'react-hot-toast';
import ClientCredentialsMethodSelector from 'components/RequestPane/Auth/OAuth2/ClientCredentialsMethodSelector';
const OAuth2AuthorizationCode = ({ collection }) => {
const dispatch = useDispatch();
@ -22,7 +23,17 @@ 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(
@ -36,6 +47,7 @@ const OAuth2AuthorizationCode = ({ collection }) => {
accessTokenUrl,
clientId,
clientSecret,
clientSecretMethod,
scope,
state,
pkce,
@ -57,6 +69,7 @@ const OAuth2AuthorizationCode = ({ collection }) => {
accessTokenUrl,
clientId,
clientSecret,
clientSecretMethod,
scope,
state,
pkce: !Boolean(oAuth?.['pkce'])
@ -105,6 +118,7 @@ const OAuth2AuthorizationCode = ({ collection }) => {
onChange={handlePKCEToggle}
/>
</div>
<ClientCredentialsMethodSelector collection={collection} oAuth={oAuth} />
<div className="flex flex-row gap-4">
<button onClick={handleRun} className="submit btn btn-sm btn-secondary w-fit">
Get Access Token

View File

@ -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();
@ -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(
@ -32,6 +33,7 @@ const OAuth2ClientCredentials = ({ collection }) => {
accessTokenUrl,
clientId,
clientSecret,
clientSecretMethod,
scope,
[key]: value
}
@ -60,6 +62,7 @@ const OAuth2ClientCredentials = ({ collection }) => {
</div>
);
})}
<ClientCredentialsMethodSelector collection={collection} oAuth={oAuth} />
<button onClick={handleRun} className="submit btn btn-sm btn-secondary w-fit">
Get Access Token
</button>

View File

@ -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 = ({ item, collection }) => {
const dispatch = useDispatch();
@ -20,7 +21,7 @@ const OAuth2AuthorizationCode = ({ item, 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(
@ -34,6 +35,7 @@ const OAuth2AuthorizationCode = ({ item, collection }) => {
password,
clientId,
clientSecret,
clientSecretMethod,
scope,
[key]: value
}
@ -62,6 +64,7 @@ const OAuth2AuthorizationCode = ({ item, collection }) => {
</div>
);
})}
<ClientCredentialsMethodSelector collection={collection} oAuth={oAuth} />
<button onClick={handleRun} className="submit btn btn-sm btn-secondary w-fit">
Get Access Token
</button>

View File

@ -7,8 +7,9 @@ import { updateAuth } from 'providers/ReduxStore/slices/collections';
import { saveRequest, sendRequest } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
import { inputsConfig } from './inputsConfig';
import { clearOauth2Cache } from 'utils/network/index';
import { clearOauth2Cache } from 'utils/network';
import toast from 'react-hot-toast';
import ClientCredentialsMethodSelector from 'components/RequestPane/Auth/OAuth2/ClientCredentialsMethodSelector';
const OAuth2AuthorizationCode = ({ item, collection }) => {
const dispatch = useDispatch();
@ -22,7 +23,17 @@ 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(
@ -37,6 +48,7 @@ const OAuth2AuthorizationCode = ({ item, collection }) => {
accessTokenUrl,
clientId,
clientSecret,
clientSecretMethod,
state,
scope,
pkce,
@ -59,6 +71,7 @@ const OAuth2AuthorizationCode = ({ item, collection }) => {
accessTokenUrl,
clientId,
clientSecret,
clientSecretMethod,
state,
scope,
pkce: !Boolean(oAuth?.['pkce'])
@ -108,6 +121,7 @@ const OAuth2AuthorizationCode = ({ item, collection }) => {
onChange={handlePKCEToggle}
/>
</div>
<ClientCredentialsMethodSelector item={item} collection={collection} oAuth={oAuth} />
<div className="flex flex-row gap-4">
<button onClick={handleRun} className="submit btn btn-sm btn-secondary w-fit">
Get Access Token

View File

@ -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();
@ -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(
@ -33,6 +34,7 @@ const OAuth2ClientCredentials = ({ item, collection }) => {
accessTokenUrl,
clientId,
clientSecret,
clientSecretMethod,
scope,
[key]: value
}
@ -62,6 +64,7 @@ const OAuth2ClientCredentials = ({ item, collection }) => {
</div>
);
})}
<ClientCredentialsMethodSelector item={item} collection={collection} oAuth={oAuth} />
<button onClick={handleRun} className="submit btn btn-sm btn-secondary w-fit">
Get Access Token
</button>

View File

@ -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;

View File

@ -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 authorization
dispatch(
updateAuth({
mode: 'oauth2',
collectionUid: collection.uid,
itemUid: item.uid,
content: {
...oAuth,
clientSecretMethod: clientSecretMethod
}
})
);
} else {
// Update collection level authorization
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;

View File

@ -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();
@ -20,7 +21,7 @@ const OAuth2AuthorizationCode = ({ 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(
@ -35,6 +36,7 @@ const OAuth2AuthorizationCode = ({ item, collection }) => {
password,
clientId,
clientSecret,
clientSecretMethod,
scope,
[key]: value
}
@ -64,6 +66,7 @@ const OAuth2AuthorizationCode = ({ item, collection }) => {
</div>
);
})}
<ClientCredentialsMethodSelector item={item} collection={collection} oAuth={oAuth} />
<button onClick={handleRun} className="submit btn btn-sm btn-secondary w-fit">
Get Access Token
</button>

View File

@ -723,6 +723,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();