Files
bruno/packages/bruno-app/src/utils/codegenerator/auth.js
Yash 0bf169562b feat: enhance OAuth2 support in snippet generation (#6592)
* feat: enhance OAuth2 support in snippet generation

* Updated getAuthHeaders function to handle OAuth2 authentication, including token retrieval and placement.
* Added tests for OAuth2 scenarios, ensuring correct Authorization header generation and handling of edge cases.
* Improved error handling for access token retrieval from stored credentials.

* refactor: standardize comparison operators in getAuthHeaders function

* Updated comparison operators in the getAuthHeaders function to use strict equality (===) for improved consistency and reliability in credential checks.

* fix: correct block structure in OAuth2 case of getAuthHeaders function

* Added missing block structure for the 'oauth2' case in the getAuthHeaders function to ensure proper execution flow and maintain code clarity.

* feat: enhance OAuth2 credential retrieval in getAuthHeaders function

* Updated getAuthHeaders function to support retrieval of stored OAuth2 credentials based on collection and item context.
* Improved access token handling by checking for existing credentials before falling back to default values.
* Enhanced test coverage for OAuth2 scenarios to ensure accurate token management and error handling.

* fix: preserve tokenHeaderPrefix value in OAuth2 configuration

* Updated snippet-generator.spec.js to ensure that the tokenHeaderPrefix from OAuth2 configuration is preserved, allowing for empty string scenarios.
* Default to 'Bearer' only if the tokenHeaderPrefix is undefined, enhancing flexibility in token management.

* fix: ensure consistent formatting of authorization header in OAuth2 handling

* Updated getAuthHeaders function to always trim the final result of the authorization header for consistent formatting.
* Adjusted snippet-generator.spec.js to reflect the same trimming logic for the access token, enhancing test reliability.

* fix: clarify token placement handling in getAuthHeaders function

* Updated comments in the getAuthHeaders function to specify that when tokenPlacement is 'url', no auth headers are added, and that token placement in the URL/query params must be managed separately.

* fix: ensure safe handling of OAuth2 credentials in getAuthHeaders function

* Updated getAuthHeaders function to default to an empty array when accessing oauth2Credentials, preventing potential errors when no credentials are available.
2026-01-21 12:23:05 +05:30

124 lines
4.3 KiB
JavaScript

import get from 'lodash/get';
import { find } from 'lodash';
import { interpolate } from '@usebruno/common';
import { getAllVariables } from 'utils/collections/index';
export const getAuthHeaders = (collectionRootAuth, requestAuth, collection = null, item = null) => {
// Discovered edge case where code generation fails when you create a collection which has not been saved yet:
// Collection auth therefore null, and request inherits from collection, therefore it is also null
// TypeError: Cannot read properties of undefined (reading 'mode')
// at getAuthHeaders
if (!collectionRootAuth && !requestAuth) {
return [];
}
const auth = collectionRootAuth && ['inherit'].includes(requestAuth?.mode) ? collectionRootAuth : requestAuth;
switch (auth.mode) {
case 'basic':
const username = get(auth, 'basic.username', '');
const password = get(auth, 'basic.password', '');
const basicToken = Buffer.from(`${username}:${password}`).toString('base64');
return [
{
enabled: true,
name: 'Authorization',
value: `Basic ${basicToken}`
}
];
case 'bearer':
return [
{
enabled: true,
name: 'Authorization',
value: `Bearer ${get(auth, 'bearer.token', '')}`
}
];
case 'apikey':
const apiKeyAuth = get(auth, 'apikey', {});
const key = get(apiKeyAuth, 'key', '');
const value = get(apiKeyAuth, 'value', '');
const placement = get(apiKeyAuth, 'placement', 'header');
if (placement === 'header') {
return [
{
enabled: true,
name: key,
value: value
}
];
}
return [];
case 'oauth2': {
const oauth2Config = get(auth, 'oauth2', {});
const tokenPlacement = get(oauth2Config, 'tokenPlacement', 'header');
const tokenHeaderPrefix = get(oauth2Config, 'tokenHeaderPrefix', 'Bearer');
// Only add header if token placement is 'header'
if (tokenPlacement === 'header') {
// Try to get access token from persisted credentials
let accessToken = '<access_token>';
if (collection && item) {
try {
const grantType = get(oauth2Config, 'grantType', '');
// For implicit grant type, use authorizationUrl; for others, use accessTokenUrl
const urlToLookup = grantType === 'implicit'
? get(oauth2Config, 'authorizationUrl', '')
: get(oauth2Config, 'accessTokenUrl', '');
const credentialsId = get(oauth2Config, 'credentialsId', 'credentials');
const collectionUid = get(collection, 'uid');
if (urlToLookup && collectionUid) {
// Interpolate the URL with variables
const variables = getAllVariables(collection, item);
const interpolatedUrl = interpolate(urlToLookup, variables);
// Look up stored credentials
const credentialsData = find(
collection?.oauth2Credentials || [],
(creds) =>
creds?.url === interpolatedUrl
&& creds?.collectionUid === collectionUid
&& creds?.credentialsId === credentialsId
);
if (credentialsData?.credentials?.access_token) {
accessToken = credentialsData.credentials.access_token;
}
}
} catch (error) {
console.error('Error retrieving OAuth2 access token:', error);
// Fall back to placeholder if lookup fails
}
}
// Build the authorization header value
// If tokenHeaderPrefix is empty, just use the token
// Otherwise, use the format: "prefix token"
// Always trim the final result for consistent formatting
const headerValue = (
tokenHeaderPrefix
? `${tokenHeaderPrefix} ${accessToken}`
: accessToken
).trim();
return [
{
enabled: true,
name: 'Authorization',
value: headerValue
}
];
}
// If tokenPlacement is 'url', this function does not add any auth headers;
// token placement in the URL/query params must be handled elsewhere.
return [];
}
default:
return [];
}
};