-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
base: main
Are you sure you want to change the base?
OAuth2 Revamp #3867
Conversation
…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
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 { |
There was a problem hiding this comment.
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.
tokenPrefix: 'Bearer', | ||
tokenQueryParamKey: 'access_token', |
There was a problem hiding this comment.
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.
const ExpiryTimer = ({ initialExpiresIn }) => { | ||
const [timeLeft, setTimeLeft] = useState(initialExpiresIn); |
There was a problem hiding this comment.
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.
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); | ||
} | ||
} |
There was a problem hiding this comment.
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?
{inputsConfig.map((input) => { | ||
const { key, label, isSecret } = input; | ||
return ( |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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.
credentialsId, | ||
tokenPlacement, | ||
tokenPrefix, | ||
tokenQueryParamKey, | ||
reuseToken, |
There was a problem hiding this comment.
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'); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
thanks for the review @pietrygamat ! |
client_id: clientId, | ||
client_secret: clientSecret |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only
if (credentialsPlacement === "body"
<div | ||
className="dropdown-item" | ||
onClick={() => { | ||
dropdownTippyRef.current.hide(); | ||
handleChange('credentialsPlacement', 'basic_auth_header'); | ||
}} | ||
> | ||
Basic Auth Header | ||
</div> |
There was a problem hiding this comment.
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.
OAuth2 Revamp