feat(#1003): oauth 2.0 client credentials

This commit is contained in:
Anoop M D 2023-12-18 01:20:16 +05:30
parent 454e0e5260
commit 5649f7c398
12 changed files with 179 additions and 3 deletions

View File

@ -71,6 +71,15 @@ const AuthMode = ({ item, collection }) => {
> >
Digest Auth Digest Auth
</div> </div>
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('oauth2');
}}
>
OAuth
</div>
<div <div
className="dropdown-item" className="dropdown-item"
onClick={() => { onClick={() => {

View File

@ -0,0 +1,16 @@
import styled from 'styled-components';
const Wrapper = styled.div`
label {
font-size: 0.8125rem;
}
.single-line-editor-wrapper {
padding: 0.15rem 0.4rem;
border-radius: 3px;
border: solid 1px ${(props) => props.theme.input.border};
background-color: ${(props) => props.theme.input.bg};
}
`;
export default Wrapper;

View File

@ -0,0 +1,78 @@
import React from 'react';
import get from 'lodash/get';
import { useTheme } from 'providers/Theme';
import { useDispatch } from 'react-redux';
import SingleLineEditor from 'components/SingleLineEditor';
import { updateAuth } from 'providers/ReduxStore/slices/collections';
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
const OAuth2 = ({ item, collection }) => {
const dispatch = useDispatch();
const { storedTheme } = useTheme();
const oAuth = item.draft ? get(item, 'draft.request.auth.oauth2', {}) : get(item, 'request.auth.oauth2', {});
const handleRun = () => dispatch(sendRequest(item, collection.uid));
const handleSave = () => dispatch(saveRequest(item.uid, collection.uid));
const handleUsernameChange = (username) => {
dispatch(
updateAuth({
mode: 'oauth2',
collectionUid: collection.uid,
itemUid: item.uid,
content: {
grantType: oAuth.grantType,
username: username,
password: oAuth.password
}
})
);
};
const handlePasswordChange = (password) => {
dispatch(
updateAuth({
mode: 'oauth2',
collectionUid: collection.uid,
itemUid: item.uid,
content: {
grantType: oAuth.grantType,
username: oAuth.username,
password: password
}
})
);
};
return (
<StyledWrapper className="mt-2 w-full">
<label className="block font-medium mb-2">Username</label>
<div className="single-line-editor-wrapper mb-2">
<SingleLineEditor
value={oAuth.username || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleUsernameChange(val)}
onRun={handleRun}
collection={collection}
/>
</div>
<label className="block font-medium mb-2">Password</label>
<div className="single-line-editor-wrapper">
<SingleLineEditor
value={oAuth.password || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handlePasswordChange(val)}
onRun={handleRun}
collection={collection}
/>
</div>
</StyledWrapper>
);
};
export default OAuth2;

View File

@ -5,11 +5,14 @@ import AwsV4Auth from './AwsV4Auth';
import BearerAuth from './BearerAuth'; import BearerAuth from './BearerAuth';
import BasicAuth from './BasicAuth'; import BasicAuth from './BasicAuth';
import DigestAuth from './DigestAuth'; import DigestAuth from './DigestAuth';
import OAuth2 from './OAuth2';
import StyledWrapper from './StyledWrapper'; import StyledWrapper from './StyledWrapper';
const Auth = ({ item, collection }) => { const Auth = ({ item, collection }) => {
const authMode = item.draft ? get(item, 'draft.request.auth.mode') : get(item, 'request.auth.mode'); const authMode = item.draft ? get(item, 'draft.request.auth.mode') : get(item, 'request.auth.mode');
console.log(item);
const getAuthView = () => { const getAuthView = () => {
switch (authMode) { switch (authMode) {
case 'awsv4': { case 'awsv4': {
@ -24,6 +27,9 @@ const Auth = ({ item, collection }) => {
case 'digest': { case 'digest': {
return <DigestAuth collection={collection} item={item} />; return <DigestAuth collection={collection} item={item} />;
} }
case 'oauth2': {
return <OAuth2 collection={collection} item={item} />;
}
} }
}; };

View File

@ -415,6 +415,10 @@ export const collectionsSlice = createSlice({
item.draft.request.auth.mode = 'digest'; item.draft.request.auth.mode = 'digest';
item.draft.request.auth.digest = action.payload.content; item.draft.request.auth.digest = action.payload.content;
break; break;
case 'oauth2':
item.draft.request.auth.mode = 'oauth2';
item.draft.request.auth.oauth2 = action.payload.content;
break;
} }
} }
} }

View File

@ -498,6 +498,10 @@ export const humanizeRequestAuthMode = (mode) => {
label = 'Digest Auth'; label = 'Digest Auth';
break; break;
} }
case 'oauth2': {
label = 'OAuth 2.0';
break;
}
} }
return label; return label;

View File

@ -64,6 +64,17 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
username: get(request, 'auth.digest.username'), username: get(request, 'auth.digest.username'),
password: get(request, 'auth.digest.password') password: get(request, 'auth.digest.password')
}; };
break;
case 'oauth2':
const grantType = get(request, 'auth.oauth2.grantType');
if (grantType === 'resourceOwnerPasswordCredentials') {
axiosRequest.data = {
grant_type: grantType,
username: get(request, 'auth.oauth2.username'),
password: get(request, 'auth.oauth2.password')
};
}
break;
} }
} }

View File

@ -23,7 +23,7 @@ const { outdentString } = require('../../v1/src/utils');
*/ */
const grammar = ohm.grammar(`Bru { const grammar = ohm.grammar(`Bru {
BruFile = (meta | http | query | headers | auths | bodies | varsandassert | script | tests | docs)* BruFile = (meta | http | query | headers | auths | bodies | varsandassert | script | tests | docs)*
auths = authawsv4 | authbasic | authbearer | authdigest auths = authawsv4 | authbasic | authbearer | authdigest | authOAuth2
bodies = bodyjson | bodytext | bodyxml | bodysparql | bodygraphql | bodygraphqlvars | bodyforms | body bodies = bodyjson | bodytext | bodyxml | bodysparql | bodygraphql | bodygraphqlvars | bodyforms | body
bodyforms = bodyformurlencoded | bodymultipart bodyforms = bodyformurlencoded | bodymultipart
@ -80,6 +80,7 @@ const grammar = ohm.grammar(`Bru {
authbasic = "auth:basic" dictionary authbasic = "auth:basic" dictionary
authbearer = "auth:bearer" dictionary authbearer = "auth:bearer" dictionary
authdigest = "auth:digest" dictionary authdigest = "auth:digest" dictionary
authOAuth2 = "auth:oauth2" dictionary
body = "body" st* "{" nl* textblock tagend body = "body" st* "{" nl* textblock tagend
bodyjson = "body:json" st* "{" nl* textblock tagend bodyjson = "body:json" st* "{" nl* textblock tagend
@ -366,6 +367,21 @@ const sem = grammar.createSemantics().addAttribute('ast', {
} }
}; };
}, },
authOAuth2(_1, dictionary) {
const auth = mapPairListToKeyValPairs(dictionary.ast, false);
const grantTypeKey = _.find(auth, { name: 'grantType' });
const usernameKey = _.find(auth, { name: 'username' });
const passwordKey = _.find(auth, { name: 'password' });
return {
auth: {
oauth2: {
grantType: grantTypeKey ? grantTypeKey.value : '',
username: usernameKey ? usernameKey.value : '',
password: passwordKey ? passwordKey.value : ''
}
}
};
},
bodyformurlencoded(_1, dictionary) { bodyformurlencoded(_1, dictionary) {
return { return {
body: { body: {

View File

@ -87,6 +87,16 @@ const jsonToBru = (json) => {
bru += '\n}\n\n'; bru += '\n}\n\n';
} }
if (auth && auth.oauth2) {
bru += `auth:oauth2 {
${indentString(`grantType: ${auth.oauth2.grantType}`)}
${indentString(`username: ${auth.oauth2.username}`)}
${indentString(`password: ${auth.oauth2.password}`)}
}
`;
}
if (auth && auth.awsv4) { if (auth && auth.awsv4) {
bru += `auth:awsv4 { bru += `auth:awsv4 {
${indentString(`accessKeyId: ${auth.awsv4.accessKeyId}`)} ${indentString(`accessKeyId: ${auth.awsv4.accessKeyId}`)}

View File

@ -22,6 +22,12 @@ headers {
~transaction-id: {{transactionId}} ~transaction-id: {{transactionId}}
} }
auth:oauth2 {
grantType: resourceOwnerPasswordCredentials
username: john
password: secret
}
auth:awsv4 { auth:awsv4 {
accessKeyId: A12345678 accessKeyId: A12345678
secretAccessKey: thisisasecret secretAccessKey: thisisasecret

View File

@ -45,6 +45,11 @@
} }
], ],
"auth": { "auth": {
"oauth2": {
"grantType": "resourceOwnerPasswordCredentials",
"username": "john",
"password": "secret"
},
"awsv4": { "awsv4": {
"accessKeyId": "A12345678", "accessKeyId": "A12345678",
"secretAccessKey": "thisisasecret", "secretAccessKey": "thisisasecret",

View File

@ -101,12 +101,23 @@ const authDigestSchema = Yup.object({
.noUnknown(true) .noUnknown(true)
.strict(); .strict();
const oAuth2Schema = Yup.object({
grantType: Yup.string()
.oneOf(['clientCredentials', 'resourceOwnerPasswordCredentials'])
.required('grantType is required'),
username: Yup.string().nullable(),
password: Yup.string().nullable()
})
.noUnknown(true)
.strict();
const authSchema = Yup.object({ const authSchema = Yup.object({
mode: Yup.string().oneOf(['none', 'awsv4', 'basic', 'bearer', 'digest']).required('mode is required'), mode: Yup.string().oneOf(['none', 'awsv4', 'basic', 'bearer', 'digest', 'oauth2']).required('mode is required'),
awsv4: authAwsV4Schema.nullable(), awsv4: authAwsV4Schema.nullable(),
basic: authBasicSchema.nullable(), basic: authBasicSchema.nullable(),
bearer: authBearerSchema.nullable(), bearer: authBearerSchema.nullable(),
digest: authDigestSchema.nullable() digest: authDigestSchema.nullable(),
oauth2: oAuth2Schema.nullable()
}) })
.noUnknown(true) .noUnknown(true)
.strict(); .strict();