mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-09 06:55:03 +00:00
fix(bru-1928): bruno-cli oauth2 updates (#5729)
This commit is contained in:
@@ -59,6 +59,7 @@
|
||||
"axios-ntlm": "^1.4.2",
|
||||
"chai": "^4.3.7",
|
||||
"chalk": "^3.0.0",
|
||||
"debug": "^4.4.3",
|
||||
"decomment": "^0.9.5",
|
||||
"form-data": "^4.0.0",
|
||||
"fs-extra": "^10.1.0",
|
||||
|
||||
@@ -210,6 +210,10 @@ const builder = async (yargs) => {
|
||||
type: 'string',
|
||||
description: 'Tags to exclude from the run'
|
||||
})
|
||||
.option('verbose', {
|
||||
type: 'boolean',
|
||||
description: 'Allow verbose output for debugging purposes'
|
||||
})
|
||||
.example('$0 run request.bru', 'Run a request')
|
||||
.example('$0 run request.bru --env local', 'Run a request with the environment set to local')
|
||||
.example('$0 run request.bru --env-file env.bru', 'Run a request with the environment from env.bru file')
|
||||
@@ -285,7 +289,8 @@ const handler = async function (argv) {
|
||||
noproxy,
|
||||
delay,
|
||||
tags: includeTags,
|
||||
excludeTags
|
||||
excludeTags,
|
||||
verbose
|
||||
} = argv;
|
||||
const collectionPath = process.cwd();
|
||||
|
||||
@@ -411,6 +416,9 @@ const handler = async function (argv) {
|
||||
if (noproxy) {
|
||||
options['noproxy'] = true;
|
||||
}
|
||||
if (verbose) {
|
||||
options['verbose'] = true;
|
||||
}
|
||||
if (cacert && cacert.length) {
|
||||
if (insecure) {
|
||||
console.error(chalk.red(`Ignoring the cacert option since insecure connections are enabled`));
|
||||
|
||||
@@ -17,6 +17,7 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc
|
||||
const collectionVariables = request?.collectionVariables || {};
|
||||
const folderVariables = request?.folderVariables || {};
|
||||
const requestVariables = request?.requestVariables || {};
|
||||
const oauth2CredentialVariables = request?.oauth2CredentialVariables || {};
|
||||
// we clone envVars because we don't want to modify the original object
|
||||
envVariables = cloneDeep(envVariables);
|
||||
|
||||
@@ -43,6 +44,7 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc
|
||||
...envVariables,
|
||||
...folderVariables,
|
||||
...requestVariables,
|
||||
...oauth2CredentialVariables,
|
||||
...runtimeVariables,
|
||||
process: {
|
||||
env: {
|
||||
@@ -172,6 +174,7 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc
|
||||
request.oauth2.clientSecret = _interpolate(request.oauth2.clientSecret) || '';
|
||||
request.oauth2.scope = _interpolate(request.oauth2.scope) || '';
|
||||
request.oauth2.credentialsPlacement = _interpolate(request.oauth2.credentialsPlacement) || '';
|
||||
request.oauth2.credentialsId = _interpolate(request.oauth2.credentialsId) || '';
|
||||
request.oauth2.tokenPlacement = _interpolate(request.oauth2.tokenPlacement) || '';
|
||||
request.oauth2.tokenHeaderPrefix = _interpolate(request.oauth2.tokenHeaderPrefix) || '';
|
||||
request.oauth2.tokenQueryKey = _interpolate(request.oauth2.tokenQueryKey) || '';
|
||||
@@ -183,6 +186,7 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc
|
||||
request.oauth2.clientSecret = _interpolate(request.oauth2.clientSecret) || '';
|
||||
request.oauth2.scope = _interpolate(request.oauth2.scope) || '';
|
||||
request.oauth2.credentialsPlacement = _interpolate(request.oauth2.credentialsPlacement) || '';
|
||||
request.oauth2.credentialsId = _interpolate(request.oauth2.credentialsId) || '';
|
||||
request.oauth2.tokenPlacement = _interpolate(request.oauth2.tokenPlacement) || '';
|
||||
request.oauth2.tokenHeaderPrefix = _interpolate(request.oauth2.tokenHeaderPrefix) || '';
|
||||
request.oauth2.tokenQueryKey = _interpolate(request.oauth2.tokenQueryKey) || '';
|
||||
@@ -190,6 +194,39 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Interpolate additional parameters for all OAuth2 grant types
|
||||
if (request.oauth2.additionalParameters) {
|
||||
// Interpolate authorization parameters
|
||||
if (Array.isArray(request.oauth2.additionalParameters.authorization)) {
|
||||
request.oauth2.additionalParameters.authorization.forEach((param) => {
|
||||
if (param && param.enabled !== false) {
|
||||
param.name = _interpolate(param.name) || '';
|
||||
param.value = _interpolate(param.value) || '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Interpolate token parameters
|
||||
if (Array.isArray(request.oauth2.additionalParameters.token)) {
|
||||
request.oauth2.additionalParameters.token.forEach((param) => {
|
||||
if (param && param.enabled !== false) {
|
||||
param.name = _interpolate(param.name) || '';
|
||||
param.value = _interpolate(param.value) || '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Interpolate refresh parameters
|
||||
if (Array.isArray(request.oauth2.additionalParameters.refresh)) {
|
||||
request.oauth2.additionalParameters.refresh.forEach((param) => {
|
||||
if (param && param.enabled !== false) {
|
||||
param.name = _interpolate(param.name) || '';
|
||||
param.value = _interpolate(param.value) || '';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (request.awsv4config) {
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
const { getOAuth2Token } = require('@usebruno/requests');
|
||||
const tokenStore = require('./tokenStore');
|
||||
|
||||
module.exports = {
|
||||
getOAuth2Token: (oauth2Config) => getOAuth2Token(oauth2Config, tokenStore)
|
||||
};
|
||||
@@ -9,6 +9,7 @@ const { mergeHeaders, mergeScripts, mergeVars, mergeAuth, getTreePathFromCollect
|
||||
const { buildFormUrlEncodedPayload } = require('../utils/form-data');
|
||||
const path = require('node:path');
|
||||
const { isLargeFile } = require('../utils/filesystem');
|
||||
const { getFormattedOauth2Credentials } = require('../utils/oauth2');
|
||||
|
||||
const STREAMING_FILE_SIZE_THRESHOLD = 20 * 1024 * 1024; // 20MB
|
||||
|
||||
@@ -93,27 +94,37 @@ const prepareRequest = async (item = {}, collection = {}) => {
|
||||
axiosRequest.oauth2 = {
|
||||
grantType,
|
||||
accessTokenUrl: get(collectionAuth, 'oauth2.accessTokenUrl'),
|
||||
refreshTokenUrl: get(collectionAuth, 'oauth2.refreshTokenUrl'),
|
||||
clientId: get(collectionAuth, 'oauth2.clientId'),
|
||||
clientSecret: get(collectionAuth, 'oauth2.clientSecret'),
|
||||
scope: get(collectionAuth, 'oauth2.scope'),
|
||||
credentialsPlacement: get(collectionAuth, 'oauth2.credentialsPlacement'),
|
||||
credentialsId: get(collectionAuth, 'oauth2.credentialsId'),
|
||||
tokenPlacement: get(collectionAuth, 'oauth2.tokenPlacement'),
|
||||
tokenHeaderPrefix: get(collectionAuth, 'oauth2.tokenHeaderPrefix'),
|
||||
tokenQueryKey: get(collectionAuth, 'oauth2.tokenQueryKey')
|
||||
tokenQueryKey: get(collectionAuth, 'oauth2.tokenQueryKey'),
|
||||
autoFetchToken: get(collectionAuth, 'oauth2.autoFetchToken'),
|
||||
autoRefreshToken: get(collectionAuth, 'oauth2.autoRefreshToken'),
|
||||
additionalParameters: get(collectionAuth, 'oauth2.additionalParameters', { authorization: [], token: [], refresh: [] })
|
||||
};
|
||||
} else if (grantType === 'password') {
|
||||
axiosRequest.oauth2 = {
|
||||
grantType,
|
||||
accessTokenUrl: get(collectionAuth, 'oauth2.accessTokenUrl'),
|
||||
refreshTokenUrl: get(collectionAuth, 'oauth2.refreshTokenUrl'),
|
||||
username: get(collectionAuth, 'oauth2.username'),
|
||||
password: get(collectionAuth, 'oauth2.password'),
|
||||
clientId: get(collectionAuth, 'oauth2.clientId'),
|
||||
clientSecret: get(collectionAuth, 'oauth2.clientSecret'),
|
||||
scope: get(collectionAuth, 'oauth2.scope'),
|
||||
credentialsPlacement: get(collectionAuth, 'oauth2.credentialsPlacement'),
|
||||
credentialsId: get(collectionAuth, 'oauth2.credentialsId'),
|
||||
tokenPlacement: get(collectionAuth, 'oauth2.tokenPlacement'),
|
||||
tokenHeaderPrefix: get(collectionAuth, 'oauth2.tokenHeaderPrefix'),
|
||||
tokenQueryKey: get(collectionAuth, 'oauth2.tokenQueryKey')
|
||||
tokenQueryKey: get(collectionAuth, 'oauth2.tokenQueryKey'),
|
||||
autoFetchToken: get(collectionAuth, 'oauth2.autoFetchToken'),
|
||||
autoRefreshToken: get(collectionAuth, 'oauth2.autoRefreshToken'),
|
||||
additionalParameters: get(collectionAuth, 'oauth2.additionalParameters', { authorization: [], token: [], refresh: [] })
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -218,29 +229,39 @@ const prepareRequest = async (item = {}, collection = {}) => {
|
||||
|
||||
if (grantType === 'client_credentials') {
|
||||
axiosRequest.oauth2 = {
|
||||
grantType,
|
||||
grantType: grantType,
|
||||
accessTokenUrl: get(request, 'auth.oauth2.accessTokenUrl'),
|
||||
refreshTokenUrl: get(request, 'auth.oauth2.refreshTokenUrl'),
|
||||
clientId: get(request, 'auth.oauth2.clientId'),
|
||||
clientSecret: get(request, 'auth.oauth2.clientSecret'),
|
||||
scope: get(request, 'auth.oauth2.scope'),
|
||||
accessTokenUrl: get(request, 'auth.oauth2.accessTokenUrl'),
|
||||
tokenPlacement: get(request, 'auth.oauth2.tokenPlacement'),
|
||||
credentialsPlacement: get(request, 'auth.oauth2.credentialsPlacement'),
|
||||
credentialsId: get(request, 'auth.oauth2.credentialsId'),
|
||||
tokenPlacement: get(request, 'auth.oauth2.tokenPlacement'),
|
||||
tokenHeaderPrefix: get(request, 'auth.oauth2.tokenHeaderPrefix'),
|
||||
tokenQueryKey: get(request, 'auth.oauth2.tokenQueryKey')
|
||||
tokenQueryKey: get(request, 'auth.oauth2.tokenQueryKey'),
|
||||
autoFetchToken: get(request, 'auth.oauth2.autoFetchToken'),
|
||||
autoRefreshToken: get(request, 'auth.oauth2.autoRefreshToken'),
|
||||
additionalParameters: get(request, 'auth.oauth2.additionalParameters', { authorization: [], token: [], refresh: [] })
|
||||
};
|
||||
} else if (grantType === 'password') {
|
||||
axiosRequest.oauth2 = {
|
||||
grantType,
|
||||
grantType: grantType,
|
||||
accessTokenUrl: get(request, 'auth.oauth2.accessTokenUrl'),
|
||||
refreshTokenUrl: get(request, 'auth.oauth2.refreshTokenUrl'),
|
||||
username: get(request, 'auth.oauth2.username'),
|
||||
password: get(request, 'auth.oauth2.password'),
|
||||
clientId: get(request, 'auth.oauth2.clientId'),
|
||||
clientSecret: get(request, 'auth.oauth2.clientSecret'),
|
||||
scope: get(request, 'auth.oauth2.scope'),
|
||||
accessTokenUrl: get(request, 'auth.oauth2.accessTokenUrl'),
|
||||
tokenPlacement: get(request, 'auth.oauth2.tokenPlacement'),
|
||||
credentialsPlacement: get(request, 'auth.oauth2.credentialsPlacement'),
|
||||
credentialsId: get(request, 'auth.oauth2.credentialsId'),
|
||||
tokenPlacement: get(request, 'auth.oauth2.tokenPlacement'),
|
||||
tokenHeaderPrefix: get(request, 'auth.oauth2.tokenHeaderPrefix'),
|
||||
tokenQueryKey: get(request, 'auth.oauth2.tokenQueryKey')
|
||||
tokenQueryKey: get(request, 'auth.oauth2.tokenQueryKey'),
|
||||
autoFetchToken: get(request, 'auth.oauth2.autoFetchToken'),
|
||||
autoRefreshToken: get(request, 'auth.oauth2.autoRefreshToken'),
|
||||
additionalParameters: get(request, 'auth.oauth2.additionalParameters', { authorization: [], token: [], refresh: [] })
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -367,6 +388,7 @@ const prepareRequest = async (item = {}, collection = {}) => {
|
||||
axiosRequest.collectionVariables = request.collectionVariables;
|
||||
axiosRequest.folderVariables = request.folderVariables;
|
||||
axiosRequest.requestVariables = request.requestVariables;
|
||||
axiosRequest.oauth2CredentialVariables = getFormattedOauth2Credentials();
|
||||
|
||||
return axiosRequest;
|
||||
};
|
||||
|
||||
@@ -20,11 +20,11 @@ const path = require('path');
|
||||
const { parseDataFromResponse } = require('../utils/common');
|
||||
const { getCookieStringForUrl, saveCookies } = require('../utils/cookies');
|
||||
const { createFormData } = require('../utils/form-data');
|
||||
const { getOAuth2Token } = require('./oauth2');
|
||||
const protocolRegex = /^([-+\w]{1,25})(:?\/\/|:)/;
|
||||
const { NtlmClient } = require('axios-ntlm');
|
||||
const { addDigestInterceptor } = require('@usebruno/requests');
|
||||
const { getCACertificates } = require('@usebruno/requests');
|
||||
const { getOAuth2Token } = require('../utils/oauth2');
|
||||
const { encodeUrl } = require('@usebruno/common').utils;
|
||||
|
||||
const onConsoleLog = (type, args) => {
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
// In-memory token store implementation for OAuth2 tokens
|
||||
const tokenStore = {
|
||||
tokens: new Map(),
|
||||
|
||||
// Save a token with optional expiry information
|
||||
async saveToken(serviceId, account, token) {
|
||||
this.tokens.set(`${serviceId}:${account}`, token);
|
||||
return true;
|
||||
},
|
||||
|
||||
// Get a token
|
||||
async getToken(serviceId, account) {
|
||||
return this.tokens.get(`${serviceId}:${account}`);
|
||||
},
|
||||
|
||||
// Delete a token
|
||||
async deleteToken(serviceId, account) {
|
||||
return this.tokens.delete(`${serviceId}:${account}`);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = tokenStore;
|
||||
50
packages/bruno-cli/src/store/tokenStore.js
Normal file
50
packages/bruno-cli/src/store/tokenStore.js
Normal file
@@ -0,0 +1,50 @@
|
||||
// In-memory credential store implementation for OAuth2 credentials
|
||||
const tokenStore = {
|
||||
credentials: {},
|
||||
|
||||
// Save credentials
|
||||
async saveCredential({ url, credentialsId, credentials }) {
|
||||
if (!this.credentials[credentialsId]) {
|
||||
this.credentials[credentialsId] = {};
|
||||
}
|
||||
this.credentials[credentialsId][url] = credentials;
|
||||
return true;
|
||||
},
|
||||
|
||||
// Get credentials
|
||||
async getCredential({ url, credentialsId }) {
|
||||
return this.credentials[credentialsId]?.[url];
|
||||
},
|
||||
|
||||
// Delete credentials
|
||||
async deleteCredential({ url, credentialsId }) {
|
||||
if (this.credentials[credentialsId]?.[url]) {
|
||||
delete this.credentials[credentialsId][url];
|
||||
// Clean up empty credentialsId objects
|
||||
if (Object.keys(this.credentials[credentialsId]).length === 0) {
|
||||
delete this.credentials[credentialsId];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// Get all stored OAuth2 credentials
|
||||
getAllCredentials() {
|
||||
const result = [];
|
||||
for (const [credentialsId, urlMap] of Object.entries(this.credentials)) {
|
||||
for (const [url, credentials] of Object.entries(urlMap)) {
|
||||
if (credentials) {
|
||||
result.push({
|
||||
url,
|
||||
credentialsId,
|
||||
credentials
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = tokenStore;
|
||||
33
packages/bruno-cli/src/utils/oauth2.js
Normal file
33
packages/bruno-cli/src/utils/oauth2.js
Normal file
@@ -0,0 +1,33 @@
|
||||
const { getOAuth2Token: _getOAuth2Token } = require('@usebruno/requests');
|
||||
const tokenStore = require('../store/tokenStore');
|
||||
const { getOptions } = require('./bru');
|
||||
|
||||
/**
|
||||
* Formats OAuth2 credentials into variables that can be accessed via bru.getOauth2CredentialVar()
|
||||
* @returns {Object} Formatted OAuth2 credential variables
|
||||
*/
|
||||
const getFormattedOauth2Credentials = () => {
|
||||
const oauth2Credentials = tokenStore.getAllCredentials();
|
||||
let credentialsVariables = {};
|
||||
|
||||
oauth2Credentials.forEach(({ credentialsId, credentials }) => {
|
||||
if (credentials) {
|
||||
Object.entries(credentials).forEach(([key, value]) => {
|
||||
credentialsVariables[`$oauth2.${credentialsId}.${key}`] = value;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return credentialsVariables;
|
||||
};
|
||||
|
||||
const getOAuth2Token = (oauth2Config) => {
|
||||
let options = getOptions();
|
||||
let verbose = options?.verbose;
|
||||
return _getOAuth2Token(oauth2Config, tokenStore, verbose);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getFormattedOauth2Credentials,
|
||||
getOAuth2Token
|
||||
};
|
||||
Reference in New Issue
Block a user