Skip to content

Commit

Permalink
feat: Implement OAuth 1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
pietrygamat committed Sep 2, 2024
1 parent e4e5e9f commit 96650ba
Show file tree
Hide file tree
Showing 17 changed files with 496 additions and 7 deletions.
1 change: 1 addition & 0 deletions packages/bruno-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"html-loader": "^3.0.1",
"html-webpack-plugin": "^5.5.0",
"mini-css-extract-plugin": "^2.4.5",
"oauth-1.0a": "^2.2.6",
"postcss": "^8.4.35",
"style-loader": "^3.3.1",
"tailwindcss": "^3.4.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const AuthMode = ({ item, collection }) => {
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);
const authMode = item.draft ? get(item, 'draft.request.auth.mode') : get(item, 'request.auth.mode');

const authModes = ['awsv4', 'basic', 'bearer', 'digest', 'oauth2', 'inherit', 'none'];
const authModes = ['awsv4', 'basic', 'bearer', 'digest', 'oauth1', 'oauth2', 'inherit', 'none'];

const Icon = forwardRef((props, ref) => {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import styled from 'styled-components';

const Wrapper = styled.div`
label {
font-size: 0.8125rem;
}
.single-line-editor-wrapper {
max-width: 400px;
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;
82 changes: 82 additions & 0 deletions packages/bruno-app/src/components/RequestPane/Auth/OAuth1/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React, { useRef } 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';
import { inputsConfig } from './inputsConfig';
import Dropdown from 'components/Dropdown';

const OAuth1 = ({ item, collection }) => {
const dispatch = useDispatch();
const { storedTheme } = useTheme();

const oAuth1 = item.draft ? get(item, 'draft.request.auth.oauth1', {}) : get(item, 'request.auth.oauth1', {});

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

const dropdownTippyRef = useRef();
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);

const handleChange = (key, val) => {
console.log(key, val);
dispatch(
updateAuth({
mode: 'oauth1',
collectionUid: collection.uid,
itemUid: item.uid,
content: {
...oAuth1, [key]: val
}
})
);
}

return (
<StyledWrapper className="mt-2 flex w-full gap-4 flex-col">
{inputsConfig.map((input) => {
const { key, label, options } = input;
return (
<div className="flex flex-col w-full gap-1" key={`input-${key}`}>
<label className="block font-medium">{label}</label>
{options ?
<div className="inline-flex items-center cursor-pointer grant-type-mode-selector w-fit">
<Dropdown icon={(<div>{ oAuth1[key] || '<select>'}</div>)}
onCreate={onDropdownCreate}
placement="bottom-end"
children={options.map((option) => (
<div
className="dropdown-item"
onClick={() => {
handleChange(key, option.key ? option.key : option);
}}>
{option.value ? option.value : option}
</div>
))}
/>
</div>

:
<div className="single-line-editor-wrapper">
<SingleLineEditor
value={oAuth1[key] || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleChange(key, val)}
onRun={handleRun}
collection={collection}
item={item}
/>
</div>
}
</div>
);
})}
</StyledWrapper>
);
};

export default OAuth1;
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const inputsConfig = [
{
key: 'consumerKey',
label: 'Consumer Key',
},
{
key: 'consumerSecret',
label: 'Consumer Secret'
},
{
key: 'requestTokenUrl',
label: 'Request Token URL'
},
{
key: 'accessTokenUrl',
label: 'Access Token URL'
},
{
key: 'authorizeUrl',
label: 'Authorize URL'
},
{
key: 'callbackUrl',
label: 'Callback URL'
},
{
key: 'accessToken',
label: 'Access Token'
},
{
key: 'accessTokenSecret',
label: 'Access Token Secret'
},
{
key: 'authorizationMethod',
label: 'Authorization Method',
options: [
{
key: 'authorizationHeader',
value: 'Authorization Header'
},
{
key: 'requestBody',
value: 'Request Body'
},
{
key: 'queryParam',
value: 'Query Parameter'
}
]
},
{
key: 'signatureMethod',
label: 'Signature Method',
options: ['HMAC-SHA1', 'HMAC-SHA256', 'RSA-SHA1', 'PLAINTEXT']
}
];

export { inputsConfig };
10 changes: 7 additions & 3 deletions packages/bruno-app/src/components/RequestPane/Auth/index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React from 'react';
import get from 'lodash/get';
import StyledWrapper from './StyledWrapper';
import { humanizeRequestAuthMode } from 'utils/collections';
import AuthMode from './AuthMode';
import AwsV4Auth from './AwsV4Auth';
import BearerAuth from './BearerAuth';
import BasicAuth from './BasicAuth';
import DigestAuth from './DigestAuth';
import StyledWrapper from './StyledWrapper';
import { humanizeRequestAuthMode } from 'utils/collections/index';
import OAuth2 from './OAuth2/index';
import OAuth1 from './OAuth1';
import OAuth2 from './OAuth2';

const Auth = ({ item, collection }) => {
const authMode = item.draft ? get(item, 'draft.request.auth.mode') : get(item, 'request.auth.mode');
Expand All @@ -29,6 +30,9 @@ const Auth = ({ item, collection }) => {
case 'digest': {
return <DigestAuth collection={collection} item={item} />;
}
case 'oauth1': {
return <OAuth1 collection={collection} item={item} />;
}
case 'oauth2': {
return <OAuth2 collection={collection} item={item} />;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,10 @@ export const collectionsSlice = createSlice({
item.draft.request.auth.mode = 'digest';
item.draft.request.auth.digest = action.payload.content;
break;
case 'oauth1':
item.draft.request.auth.mode = 'oauth1';
item.draft.request.auth.oauth1 = action.payload.content;
break;
case 'oauth2':
item.draft.request.auth.mode = 'oauth2';
item.draft.request.auth.oauth2 = action.payload.content;
Expand Down
18 changes: 18 additions & 0 deletions packages/bruno-app/src/utils/collections/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,20 @@ export const transformCollectionToSaveToExportAsFile = (collection, options = {}
password: get(si.request, 'auth.digest.password', '')
};
break;
case 'oauth1':
di.request.auth.oauth1 = {
username: get(si.request, 'auth.oauth1.username', ''),
consumerKey: get(si.request, 'auth.oauth1.consumerKey', ''),
consumerSecret: get(si.request, 'auth.oauth1.consumerSecret', ''),
requestTokenUrl: get(si.request, 'auth.oauth1.requestTokenUrl', ''),
accessTokenUrl: get(si.request, 'auth.oauth1.accessTokenUrl', ''),
authorizeUrl: get(si.request, 'auth.oauth1.authorizeUrl', ''),
callbackUrl: get(si.request, 'auth.oauth1.callbackUrl', ''),
accessToken: get(si.request, 'auth.oauth1.accessToken', ''),
accessTokenSecret: get(si.request, 'auth.oauth1.accessTokenSecret', ''),
signatureMethod: get(si.request, 'auth.oauth1.signatureMethod', '')
};
break;
case 'oauth2':
let grantType = get(si.request, 'auth.oauth2.grantType', '');
switch (grantType) {
Expand Down Expand Up @@ -657,6 +671,10 @@ export const humanizeRequestAuthMode = (mode) => {
label = 'Digest Auth';
break;
}
case 'oauth1': {
label = 'OAuth 1.0';
break;
}
case 'oauth2': {
label = 'OAuth 2.0';
break;
Expand Down
5 changes: 5 additions & 0 deletions packages/bruno-electron/src/ipc/network/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const { addDigestInterceptor } = require('./digestauth-helper');
const { shouldUseProxy, PatchedHttpsProxyAgent } = require('../../utils/proxy-util');
const { chooseFileToSave, writeBinaryFile, writeFile } = require('../../utils/filesystem');
const { getCookieStringForUrl, addCookieToJar, getDomainsWithCookies } = require('../../utils/cookies');
const { addOAuth1Authorization } = require('./oauth1-helper');
const {
resolveOAuth2AuthorizationCodeAccessToken,
transformClientCredentialsRequest,
Expand Down Expand Up @@ -211,6 +212,10 @@ const configureRequest = async (
}

const axiosInstance = makeAxiosInstance();
if (request.oauth1) {
interpolateVars(request, envVars, runtimeVariables, processEnvVars);
await addOAuth1Authorization(request);
}

if (request.oauth2) {
let requestCopy = cloneDeep(request);
Expand Down
4 changes: 4 additions & 0 deletions packages/bruno-electron/src/ipc/network/interpolate-vars.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ const interpolateVars = (request, envVars = {}, runtimeVariables = {}, processEn
delete request.auth;
}

if (request?.oauth1) {
Object.keys(request.oauth1).forEach(key => request.oauth1[key] = _interpolate(request.oauth1[key]));
}

if (request?.oauth2?.grantType) {
let username, password, scope, clientId, clientSecret;
switch (request.oauth2.grantType) {
Expand Down
Loading

0 comments on commit 96650ba

Please sign in to comment.