Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OAuth2 Revamp #3867

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open

OAuth2 Revamp #3867

wants to merge 11 commits into from

Conversation

helloanoop
Copy link
Contributor

OAuth2 Revamp

pietrygamat and others added 8 commits September 23, 2024 20:50
…ad of api endpoint (#1999)

Setting oauth2 authorization no longer equals overwriting user-specified data in a request. The pre-requests made to obtain oauth2 access_token are now separated from actual API request.
Results of oauth2 authorization flow (i.e. access_token but also refresh_token, id_token, scope or any other information returned from token request) are stored in a collection specific cache. It is persisted in the file system, and will be automatically reused when executing requests until the cache is purged (using Clear Cache button available in all related views).
…o be usable by scripts

The new variable 'credentials' is now available in 'req' object. It is added automatically during request preparation if oauth2 method is used and is value is either evaluated or retrieved from collection oauth2 cache.
…vel Get Access Token action

The actual the authorization request is now part of request preparation, and its response is returned for post-request script processing.
According to RFC6749 Section 7.1, The client MUST NOT use an access token
if it does not understand the token type.
At this point bruno only understands 'bearer' token_type.
…utput pane

fix: typo - rename OAuth2PasswordCredentials component
fix: typo - Use the same name for AuthMode - OAuth 2.0 in collection and request level
@helloanoop helloanoop changed the title Feat/oauth2 improvements OAuth2 Revamp Jan 23, 2025
@pietrygamat
Copy link
Contributor

The UI here looks great and token decoder is way ahead of what the competition offers, so make sure to post a promotional video after releasing this!
🥇

@@ -11,6 +11,47 @@ const Wrapper = styled.div`
border: solid 1px ${(props) => props.theme.input.border};
background-color: ${(props) => props.theme.input.bg};
}

.token-placement-selector {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about extracting token placement with corresponding HeaderPrefix/QueryParam field into a separate component? Between CSS, change handlers, dropdownRefs and currently hardcoded translatable strings, it's a lot of duplication.

Comment on lines 57 to 58
tokenPrefix: 'Bearer',
tokenQueryParamKey: 'access_token',
Copy link
Contributor

@pietrygamat pietrygamat Jan 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both of these default values are saved in bru file, even if the tokenPlacement option only needs one. It would be better if unused values are not automatically pushed into request configurations.

Comment on lines 102 to 103
const ExpiryTimer = ({ initialExpiresIn }) => {
const [timeLeft, setTimeLeft] = useState(initialExpiresIn);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This timer resets when changing tabs.

Comment on lines 36 to 50
const handleFetchOauth2Credentials = async () => {
let requestCopy = cloneDeep(request);
requestCopy.oauth2 = requestCopy?.auth.oauth2;
requestCopy.headers = {};
toggleFetchingToken(true);
try {
await dispatch(fetchOauth2Credentials({ request: requestCopy, collection }));
toggleFetchingToken(false);
}
catch(error) {
console.error('could not fetch the token!');
console.error(error);
toggleFetchingToken(false);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indicate a token fetch success with a toast?

Comment on lines 129 to 131
{inputsConfig.map((input) => {
const { key, label, isSecret } = input;
return (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, make this section collapsible. The Oauth2TokenViewer is so pretty to look at, the users may want to have in view all the time, but they may sometimes have to scroll down to the Get Token button.

ipcRenderer.invoke('clear-oauth2-cache', collectionUid, url, credentialsId).then(resolve => {
dispatch(collectionClearOauth2CredentialsByUrl({ collectionUid, url, credentialsId }));
resolve();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm getting a toast: Resolve is not a function.

Comment on lines 70 to 74
credentialsId,
tokenPlacement,
tokenPrefix,
tokenQueryParamKey,
reuseToken,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a lot of places where the new fields need keeping track of. It seems it can be replaced with simply:

dispatch(updateAuth({
      mode: 'oauth2',
      collectionUid: collection.uid,
      itemUid: item.uid,
      content: {
        ...oAuth,
        [key]: value
      }
    }))

className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
handleChange('tokenPlacement', 'header');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can these enum-like values: header, url be externalized somehow? They are used in views, in bru files, schema definitions, soon - as keys in translation files. Maybe this refactor is a good opportunity to take care of that as well?

requestCopy.url = url;

const axiosInstance = makeAxiosInstance();
const response = await axiosInstance(requestCopy);
Copy link
Contributor

@pietrygamat pietrygamat Jan 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This request fails due to certificate validation issues (expired certificate), despite having disabled SSL/TLS Certificate verification. Note, the electron browser authentication does work (login prompt is shown). The problem is also that it fails silently - no toast, no timeline information about the cause.

requestCopy.url = url;

const axiosInstance = makeAxiosInstance();
const response = await axiosInstance(requestCopy);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This request is sent with Header:

Authorization: 'Basic Og==' 

There are several places in the code like this, where any auth is confused with Basic auth resulting in storing base64 representation of ":" in Authorization header.

  // todo: we have things happening in two places w.r.t basic auth
  // need to refactor this in the future
  // the request.auth (basic auth) object gets set inside the prepare-request.js file
  if (request.auth) {
    const username = _interpolate(request.auth.username) || '';
    const password = _interpolate(request.auth.password) || '';
    // use auth header based approach and delete the request.auth object
    request.headers['Authorization'] = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`;
    delete request.auth;
  }

Before implementing #2106 this is critical, because many servers will look at Basic Auth rather than post body for client credentials, which are part of the OAuth2 flow. I think this refactor is also the time to resolve these TODOs.

~ basic auth credentials should be assigned to `request.basicAuth` instead `request.auth` object
~ added credentials_placement option, fixed headers issue client credentials flow
~ cache input field values when grant type select box value changes
~ updated logic for - cache input field values when grant type select box value changes
~ updated token expiry timer component logic
~ changed tokenPrefix to tokenHeaderPrefix
~ updated the logic for token timer component
@lohit-bruno
Copy link
Collaborator

thanks for the review @pietrygamat !

Comment on lines +134 to +135
client_id: clientId,
client_secret: clientSecret
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only

if (credentialsPlacement === "body"

Comment on lines +145 to +153
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
handleChange('credentialsPlacement', 'basic_auth_header');
}}
>
Basic Auth Header
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sending client creds in Authorization header is the preferred choice (servers MUST support it), and including them in request body is optional alternative (servers MAY support it). So it would make sense for Bruno to default to basic_auth_header and have it as a first option.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants