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

feat: Implement OAuth1 authorization method #2989

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

Conversation

pietrygamat
Copy link
Contributor

@pietrygamat pietrygamat commented Sep 1, 2024

Description

This pull request aims to add OAuth1.0a as supported authorization mode to Bruno.
resolves: #1004

The OAuth1 flow as defined in https://oauth.net/core/1.0a consists of two distinct stages (and more substages):

  1. Obtaining access token
    • Obtain temporary request token
    • Obtain user authorization
    • Obtain final access token
  2. Using access token to authorize API request

Step 2, while not exactly trivial, can be accomplished by scripting, but what I think is really important for API client like Bruno, is the ability to assist user in Step 1- actually obtaining the token.

The approach currently used for OAuth2 Authorization Code flow is a great example of how the solution could look like.

Implementation details

When OAuth1 authorization is used for request, the Authorization header is evaluated using oauth-1.0a library and included in the request during execution, based on the following parameters:

  • Consumer Key
  • Consumer Secret
  • Access Token
  • Access Token Secret
  • RSA Private Key (RSA-SHA1 signature method)

In case user does not yet have Access Token, they must provide the following information about API they want to access:

  • Request Token URL
  • Access Token URL

And then also:

  • Authorize URL and Callback URL (in 3-legged flow)
  • Verifier code - if provided by service (in 2-legged flow)

Then, during request execution Bruno will make additional requests, and will open the browser window to allow user sign-in with the external provider and ask for permission.

Demo

Screencast.from.2024-09-02.21-41-41.mp4

Todos

This flow should support several variations, including

  • Implement at collection level
  • Two-legged flow (e.g. Adobe Magento)
  • Three-legged flow (e.g. Twitter)
  • variable Signature methods
    • HMAC_SHA* - works with real services
    • RSA_SHA* - using external private key - should work
    • PLAINTEXT - should work
  • variable Parameter Transmission Method
    • send in Authorization Header
    • send in Form-Encoded Body
    • send in Request Query Parameters

Some additional considerations:

  • Make sure Bruno settings (mTLS, Proxy) are used for Authroization requests
  • configurable additional parameters for each of flow calls (may be required by some APIs)
  • GUI hints and auto-disabling un-needed fields based on other selections
  • Browser session management / provider logout
  • Error handling
  • Scripting support (e.g. possibility to extract access token)
  • Testing in real environments with various requirements

Contribution Checklist:

  • The pull request only addresses one issue or adds one feature.
  • The pull request does not introduce any breaking changes
  • I have added screenshots or gifs to help explain the change if applicable.
  • I have read the contribution guidelines.
  • Create an issue and link to the pull request.

@pietrygamat pietrygamat changed the title Feature/1004/oauth1 feat: Implement OAuth1 authorization method Sep 2, 2024
@pietrygamat pietrygamat force-pushed the feature/1004/oauth1 branch 2 times, most recently from 96650ba to 6c272bc Compare September 2, 2024 19:35
@pietrygamat pietrygamat mentioned this pull request Sep 2, 2024
@pietrygamat pietrygamat force-pushed the feature/1004/oauth1 branch 7 times, most recently from cd12fb9 to 9b4a1b1 Compare September 3, 2024 17:50
@pietrygamat pietrygamat force-pushed the feature/1004/oauth1 branch 3 times, most recently from a5cb10d to e1ea8c0 Compare September 15, 2024 18:40
@pietrygamat pietrygamat marked this pull request as ready for review January 9, 2025 23:04
@helloanoop helloanoop assigned ramki-bruno and unassigned lohxt1 Jan 13, 2025
@ramki-bruno
Copy link
Collaborator

Time to go read the OAuth 1.0(and rev A) spec.

@ramki-bruno
Copy link
Collaborator

@pietrygamat mind sharing the details on what you are using to test it in local?

@pietrygamat
Copy link
Contributor Author

I used the example oauth1 server as a docker container: https://github.com/BeryJu/oauth1-test-server

}
const authHeader = oauth.toHeader(oauth.authorize(accessTokenRequestData, signingToken));
console.log("Requesting Access Token from", accessTokenRequestData.url);
const accessTokenResponse = await axios.post(accessTokenRequestData.url, {},{ headers: authHeader });
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TODO 1: we need to send a separate request to access token url. This implementation creates a basic axios.post, but more complete solution should preconfigure the request according to global / collection settings (mTLS, proxy config). Possibly out of scope for this PR, but should be addressed if this is to be merged.

TODO 2: Some APIs may expect such request to include specific cookies or headers, and so there should be a way for the user to add additional headers and/or additional request parameters. Bruno faces this problem with OAuth2, like explained here, and the same issue may very well be present in OAuth1. It may be considered lower priority.

// https://oauth.net/core/1.0a/#anchor13
// 9.1.1. Normalize Request Parameters
const isPostWithUrlEncodedFormData = (request) => {
return request.headers['content-type'] === 'application/x-www-form-urlencoded' && request.method === 'POST';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Possibly outside of this PR's scope, but there should be a more generic way of reading well-known headers (content-type here, authorization few lines below, etc) within the code base that avoids pitfalls related to case-sensitive string comparison. Note, content-type may have been set by a user script as e.g. Content-Type.

Or maybe there already is a better way?

window.on('ready-to-show', () => window.show());
window.webContents.on('certificate-error', (event, url, error, certificate, callback) => {
event.preventDefault();
callback(!preferencesUtil.shouldVerifyTls());
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can honor No TLS verification setting like this, but this should also work with custom CA and proxy settings configured in Bruno. This is the same problem as there is with OAuth2 flow, so I guess it's outside of scope for now?

Comment on lines +58 to +68
const relativeFile = (file) => {
if (file) {
if (isWindowsOS()) {
return slash((path.win32.relative(collection.pathname, file)));
} else {
return path.posix.relative(collection.pathname, file);
}
} else {
return '';
}
};
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This may be outdated method of getting relative path to a local file with recent changes to file picker, not sure.

@@ -0,0 +1,79 @@
const inputsConfig = [
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some of these inputs are required to fill (consumer key/secret), and some will be filled by Bruno during execution if found empty (verifier, access token). Some will only make sense under certain conditions (rsa-private key is only needed when using RSA signing method, but then it's mandatory).

The UX improvements like hints or auto-disabling controls depending on other choices may be introduced in the future, but I guess it's better left out of this PR.

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.

Auth: OAuth 1.0
3 participants