From 2fcfdfc3387763c2425bcb72ad1318b26e88b66f Mon Sep 17 00:00:00 2001 From: lohit Date: Thu, 19 Feb 2026 15:41:28 +0000 Subject: [PATCH] fix: `oauth2 credential` management improvements (#7220) * fix: oauth2 credential management improvements Add bru.resetOauth2Credential() API for programmatic credential invalidation from scripts, fix credential clearing to match on credentialsId, expose oauth2 credential variables in test runtime, and add input validation with deduplication to prevent redundant IPC messages. Remove unused collectionGetOauth2CredentialsByUrlAndCredentialsId reducer. * fix: handle invalid URLs in oauth2 callback redirect handler Wrap new URL() calls in try-catch within onWindowRedirect to prevent uncaught TypeError when redirect or callback URLs are invalid. * Update packages/bruno-app/src/utils/codemirror/autocomplete.js Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../src/providers/App/useIpcEvents.js | 7 ++- .../ReduxStore/slices/collections/actions.js | 7 +-- .../ReduxStore/slices/collections/index.js | 27 ++++++----- .../src/utils/codemirror/autocomplete.js | 3 +- .../src/runner/run-single-request.js | 23 +++++++++- packages/bruno-cli/src/store/tokenStore.js | 9 ++++ .../ipc/network/authorize-user-in-window.js | 20 ++++++++- .../bruno-electron/src/ipc/network/index.js | 45 +++++++++++++++++-- packages/bruno-electron/src/store/oauth2.js | 17 +++++++ packages/bruno-electron/src/utils/oauth2.js | 5 +++ packages/bruno-js/src/bru.js | 20 +++++++++ .../bruno-js/src/runtime/script-runtime.js | 2 + packages/bruno-js/src/runtime/test-runtime.js | 4 +- .../bruno-js/src/sandbox/quickjs/shims/bru.js | 6 +++ 14 files changed, 171 insertions(+), 24 deletions(-) diff --git a/packages/bruno-app/src/providers/App/useIpcEvents.js b/packages/bruno-app/src/providers/App/useIpcEvents.js index a890f1979..84b95e1a6 100644 --- a/packages/bruno-app/src/providers/App/useIpcEvents.js +++ b/packages/bruno-app/src/providers/App/useIpcEvents.js @@ -35,7 +35,7 @@ import toast from 'react-hot-toast'; import { useDispatch, useStore } from 'react-redux'; import { isElectron } from 'utils/common/platform'; import { globalEnvironmentsUpdateEvent, updateGlobalEnvironments } from 'providers/ReduxStore/slices/global-environments'; -import { collectionAddOauth2CredentialsByUrl, updateCollectionLoadingState } from 'providers/ReduxStore/slices/collections/index'; +import { collectionAddOauth2CredentialsByUrl, collectionClearOauth2CredentialsByCredentialsId, updateCollectionLoadingState } from 'providers/ReduxStore/slices/collections/index'; import { addLog } from 'providers/ReduxStore/slices/logs'; import { updateSystemResources } from 'providers/ReduxStore/slices/performance'; import { apiSpecAddFileEvent, apiSpecChangeFileEvent } from 'providers/ReduxStore/slices/apiSpec'; @@ -318,6 +318,10 @@ const useIpcEvents = () => { dispatch(collectionAddOauth2CredentialsByUrl(payload)); }); + const removeCollectionOauth2CredentialsClearListener = ipcRenderer.on('main:credentials-clear', (val) => { + dispatch(collectionClearOauth2CredentialsByCredentialsId(val)); + }); + const removeHttpStreamNewDataListener = ipcRenderer.on('main:http-stream-new-data', (val) => { dispatch(streamDataReceived(val)); }); @@ -360,6 +364,7 @@ const useIpcEvents = () => { removeGlobalEnvironmentsUpdatesListener(); removeSnapshotHydrationListener(); removeCollectionOauth2CredentialsUpdatesListener(); + removeCollectionOauth2CredentialsClearListener(); removeHttpStreamNewDataListener(); removeHttpStreamEndListener(); removeCollectionLoadingStateListener(); diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js index 2a048b008..8da7837e8 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -44,7 +44,7 @@ import { updateLastAction, setCollectionSecurityConfig, collectionAddOauth2CredentialsByUrl, - collectionClearOauth2CredentialsByUrl, + collectionClearOauth2CredentialsByUrlAndCredentialsId, initRunRequestEvent, updateRunnerConfiguration as _updateRunnerConfiguration, updateActiveConnections, @@ -2851,9 +2851,10 @@ export const clearOauth2Cache = (payload) => async (dispatch, getState) => { .invoke('clear-oauth2-cache', collectionUid, url, credentialsId) .then(() => { dispatch( - collectionClearOauth2CredentialsByUrl({ + collectionClearOauth2CredentialsByUrlAndCredentialsId({ url, - collectionUid + collectionUid, + credentialsId }) ); resolve(); diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js index ae68f9ccb..dc7220c74 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js @@ -3217,7 +3217,8 @@ export const collectionsSlice = createSlice({ } }, - collectionClearOauth2CredentialsByUrl: (state, action) => { + // Clears a specific credential matching url + collectionUid + credentialsId (used by UI "Clear OAuth2 Cache") + collectionClearOauth2CredentialsByUrlAndCredentialsId: (state, action) => { const { collectionUid, url, credentialsId } = action.payload; const collection = findCollectionByUid(state.collections, collectionUid); if (!collection) return; @@ -3227,21 +3228,23 @@ export const collectionsSlice = createSlice({ const filteredOauth2Credentials = filter( collectionOauth2Credentials, (creds) => - !(creds.url === url && creds.collectionUid === collectionUid) + !(creds.url === url && creds.collectionUid === collectionUid && creds.credentialsId === credentialsId) ); collection.oauth2Credentials = filteredOauth2Credentials; } }, - collectionGetOauth2CredentialsByUrl: (state, action) => { - const { collectionUid, url, credentialsId } = action.payload; + // Clears all credentials matching credentialsId regardless of URL (used by script bru.resetOauth2Credential) + collectionClearOauth2CredentialsByCredentialsId: (state, action) => { + const { collectionUid, credentialsId } = action.payload; const collection = findCollectionByUid(state.collections, collectionUid); - const oauth2Credential = find( - collection?.oauth2Credentials || [], - (creds) => - creds.url === url && creds.collectionUid === collectionUid && creds.credentialsId === credentialsId - ); - return oauth2Credential; + if (!collection) return; + + if (collection.oauth2Credentials) { + collection.oauth2Credentials = collection.oauth2Credentials.filter( + (creds) => creds.credentialsId !== credentialsId + ); + } }, updateFolderAuthMode: (state, action) => { @@ -3676,8 +3679,8 @@ export const { moveCollection, streamDataReceived, collectionAddOauth2CredentialsByUrl, - collectionClearOauth2CredentialsByUrl, - collectionGetOauth2CredentialsByUrl, + collectionClearOauth2CredentialsByUrlAndCredentialsId, + collectionClearOauth2CredentialsByCredentialsId, updateFolderAuth, updateFolderAuthMode, addRequestTag, diff --git a/packages/bruno-app/src/utils/codemirror/autocomplete.js b/packages/bruno-app/src/utils/codemirror/autocomplete.js index ed21e0f76..041624ae5 100644 --- a/packages/bruno-app/src/utils/codemirror/autocomplete.js +++ b/packages/bruno-app/src/utils/codemirror/autocomplete.js @@ -116,7 +116,8 @@ const STATIC_API_HINTS = { 'bru.cookies.jar().deleteCookie(url, name, callback)', 'bru.utils', 'bru.utils.minifyJson(json)', - 'bru.utils.minifyXml(xml)' + 'bru.utils.minifyXml(xml)', + 'bru.resetOauth2Credential(credentialId)' ] }; diff --git a/packages/bruno-cli/src/runner/run-single-request.js b/packages/bruno-cli/src/runner/run-single-request.js index 26bccc8b6..ee46286f9 100644 --- a/packages/bruno-cli/src/runner/run-single-request.js +++ b/packages/bruno-cli/src/runner/run-single-request.js @@ -23,7 +23,8 @@ const protocolRegex = /^([-+\w]{1,25})(:?\/\/|:)/; const { NtlmClient } = require('axios-ntlm'); const { addDigestInterceptor, getHttpHttpsAgents, makeAxiosInstance: makeAxiosInstanceForOauth2 } = require('@usebruno/requests'); const { getCACertificates, transformProxyConfig } = require('@usebruno/requests'); -const { getOAuth2Token } = require('../utils/oauth2'); +const { getOAuth2Token, getFormattedOauth2Credentials } = require('../utils/oauth2'); +const tokenStore = require('../store/tokenStore'); const { encodeUrl, buildFormUrlEncodedPayload, extractPromptVariables, isFormData } = require('@usebruno/common').utils; const onConsoleLog = (type, args) => { @@ -225,6 +226,12 @@ const runSingleRequest = async function ( shouldStopRunnerExecution = true; } + if (result?.oauth2CredentialsToReset?.length) { + for (const credentialId of result.oauth2CredentialsToReset) { + tokenStore.deleteCredentialById(credentialId); + } + } + if (result?.skipRequest) { return { test: { @@ -633,6 +640,8 @@ const runSingleRequest = async function ( console.error('OAuth2 token fetch error:', error.message); } + request.oauth2CredentialVariables = getFormattedOauth2Credentials(); + // Remove oauth2 config from request to prevent it from being sent delete request.oauth2; } @@ -787,6 +796,12 @@ const runSingleRequest = async function ( shouldStopRunnerExecution = true; } + if (result?.oauth2CredentialsToReset?.length) { + for (const credentialId of result.oauth2CredentialsToReset) { + tokenStore.deleteCredentialById(credentialId); + } + } + postResponseTestResults = result?.results || []; logResults(postResponseTestResults, 'Post-Response Tests'); } catch (error) { @@ -858,6 +873,12 @@ const runSingleRequest = async function ( shouldStopRunnerExecution = true; } + if (result?.oauth2CredentialsToReset?.length) { + for (const credentialId of result.oauth2CredentialsToReset) { + tokenStore.deleteCredentialById(credentialId); + } + } + logResults(testResults, 'Tests'); } catch (error) { console.error('Test script execution error:', error); diff --git a/packages/bruno-cli/src/store/tokenStore.js b/packages/bruno-cli/src/store/tokenStore.js index 9dedcfc54..f53fd636a 100644 --- a/packages/bruno-cli/src/store/tokenStore.js +++ b/packages/bruno-cli/src/store/tokenStore.js @@ -29,6 +29,15 @@ const tokenStore = { return false; }, + // Delete all credentials for a given credentialsId (all URLs) + deleteCredentialById(credentialsId) { + if (this.credentials[credentialsId]) { + delete this.credentials[credentialsId]; + return true; + } + return false; + }, + // Get all stored OAuth2 credentials getAllCredentials() { const result = []; diff --git a/packages/bruno-electron/src/ipc/network/authorize-user-in-window.js b/packages/bruno-electron/src/ipc/network/authorize-user-in-window.js index c5580edaf..f713e828d 100644 --- a/packages/bruno-electron/src/ipc/network/authorize-user-in-window.js +++ b/packages/bruno-electron/src/ipc/network/authorize-user-in-window.js @@ -128,15 +128,31 @@ const authorizeUserInWindow = ({ authorizeUrl, callbackUrl, session, additionalH function onWindowRedirect(url) { // Handle redirects as needed + let urlObj; + let callbackUrlObj; + + try { + urlObj = new URL(url); + } catch (e) { + // Invalid redirect URL, skip processing + return; + } + + try { + callbackUrlObj = new URL(callbackUrl); + } catch (e) { + // Invalid callback URL, skip matching but still check for errors below + callbackUrlObj = null; + } // Check if redirect is to the callback URL and contains an authorization code - if (matchesCallbackUrl(new URL(url), new URL(callbackUrl))) { + if (callbackUrlObj && matchesCallbackUrl(urlObj, callbackUrlObj)) { finalUrl = url; window.close(); + return; } // Handle OAuth error responses - const urlObj = new URL(url); if (urlObj.searchParams.has('error')) { const error = urlObj.searchParams.get('error'); const errorDescription = urlObj.searchParams.get('error_description'); diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js index 70ac1d89c..39b43f358 100644 --- a/packages/bruno-electron/src/ipc/network/index.js +++ b/packages/bruno-electron/src/ipc/network/index.js @@ -25,7 +25,7 @@ const { chooseFileToSave, writeFile, getCollectionFormat, hasRequestExtension } const { addCookieToJar, getDomainsWithCookies, getCookieStringForUrl } = require('../../utils/cookies'); const { createFormData } = require('../../utils/form-data'); const { findItemInCollectionByPathname, sortFolder, getAllRequestsInFolderRecursively, getEnvVars, getTreePathFromCollectionToItem, mergeVars, sortByNameThenSequence } = require('../../utils/collection'); -const { getOAuth2TokenUsingAuthorizationCode, getOAuth2TokenUsingClientCredentials, getOAuth2TokenUsingPasswordCredentials, getOAuth2TokenUsingImplicitGrant, updateCollectionOauth2Credentials } = require('../../utils/oauth2'); +const { getOAuth2TokenUsingAuthorizationCode, getOAuth2TokenUsingClientCredentials, getOAuth2TokenUsingPasswordCredentials, getOAuth2TokenUsingImplicitGrant, updateCollectionOauth2Credentials, clearOauth2CredentialsByCredentialsId } = require('../../utils/oauth2'); const { preferencesUtil } = require('../../store/preferences'); const { getProcessEnvVars } = require('../../store/process-env'); const { getBrunoConfig } = require('../../store/bruno-config'); @@ -477,6 +477,25 @@ const registerNetworkIpc = (mainWindow) => { }; }; + const resetOauth2Credentials = ({ oauth2CredentialsToReset, request, collectionUid }) => { + if (!oauth2CredentialsToReset?.length) return; + for (const credentialId of oauth2CredentialsToReset) { + clearOauth2CredentialsByCredentialsId({ collectionUid, credentialsId: credentialId }); + if (request?.oauth2Credentials?.credentialsId === credentialId) { + request.oauth2Credentials = null; + } + const prefix = `$oauth2.${credentialId}.`; + if (request.oauth2CredentialVariables) { + for (const key of Object.keys(request.oauth2CredentialVariables)) { + if (key.startsWith(prefix)) { + delete request.oauth2CredentialVariables[key]; + } + } + } + mainWindow.webContents.send('main:credentials-clear', { collectionUid, credentialsId: credentialId }); + } + }; + const runPreRequest = async ( request, requestUid, @@ -528,6 +547,8 @@ const registerNetworkIpc = (mainWindow) => { collection.globalEnvironmentVariables = scriptResult.globalEnvironmentVariables; + resetOauth2Credentials({ oauth2CredentialsToReset: scriptResult.oauth2CredentialsToReset, request, collectionUid }); + const domainsWithCookies = await getDomainsWithCookies(); mainWindow.webContents.send('main:cookies-update', safeParseJSON(safeStringifyJSON(domainsWithCookies))); } @@ -666,6 +687,8 @@ const registerNetworkIpc = (mainWindow) => { collection.globalEnvironmentVariables = scriptResult.globalEnvironmentVariables; + resetOauth2Credentials({ oauth2CredentialsToReset: scriptResult.oauth2CredentialsToReset, request, collectionUid }); + const domainsWithCookiesPost = await getDomainsWithCookies(); mainWindow.webContents.send('main:cookies-update', safeParseJSON(safeStringifyJSON(domainsWithCookiesPost))); } @@ -816,7 +839,7 @@ const registerNetworkIpc = (mainWindow) => { cancelTokenUid }); - if (request?.oauth2Credentials) { + if (request.oauth2Credentials?.credentials && request.oauth2Credentials?.credentialsId) { mainWindow.webContents.send('main:credentials-update', { credentials: request?.oauth2Credentials?.credentials, url: request?.oauth2Credentials?.url, @@ -825,6 +848,12 @@ const registerNetworkIpc = (mainWindow) => { ...(request?.oauth2Credentials?.folderUid ? { folderUid: request.oauth2Credentials.folderUid } : { itemUid: item.uid }), debugInfo: request?.oauth2Credentials?.debugInfo }); + + const { credentialsId, credentials } = request.oauth2Credentials; + request.oauth2CredentialVariables = request.oauth2CredentialVariables || {}; + Object.entries(credentials).forEach(([key, value]) => { + request.oauth2CredentialVariables[`$oauth2.${credentialsId}.${key}`] = value; + }); } let response, responseTime, axiosDataStream; @@ -1032,6 +1061,8 @@ const registerNetworkIpc = (mainWindow) => { collection.globalEnvironmentVariables = testResults.globalEnvironmentVariables; + resetOauth2Credentials({ oauth2CredentialsToReset: testResults.oauth2CredentialsToReset, request, collectionUid }); + !runInBackground && notifyScriptExecution({ channel: 'main:run-request-event', basePayload: { requestUid, collectionUid, itemUid: item.uid }, @@ -1487,7 +1518,7 @@ const registerNetworkIpc = (mainWindow) => { collection.globalEnvironmentVariables ); - if (request?.oauth2Credentials) { + if (request.oauth2Credentials?.credentials && request.oauth2Credentials?.credentialsId) { mainWindow.webContents.send('main:credentials-update', { credentials: request?.oauth2Credentials?.credentials, url: request?.oauth2Credentials?.url, @@ -1497,6 +1528,12 @@ const registerNetworkIpc = (mainWindow) => { debugInfo: request?.oauth2Credentials?.debugInfo }); + const { credentialsId, credentials } = request.oauth2Credentials; + request.oauth2CredentialVariables = request.oauth2CredentialVariables || {}; + Object.entries(credentials).forEach(([key, value]) => { + request.oauth2CredentialVariables[`$oauth2.${credentialsId}.${key}`] = value; + }); + collection.oauth2Credentials = updateCollectionOauth2Credentials({ itemUid: item.uid, collectionUid, @@ -1738,6 +1775,8 @@ const registerNetworkIpc = (mainWindow) => { collection.globalEnvironmentVariables = testResults.globalEnvironmentVariables; + resetOauth2Credentials({ oauth2CredentialsToReset: testResults.oauth2CredentialsToReset, request, collectionUid }); + notifyScriptExecution({ channel: 'main:run-folder-event', basePayload: eventData, diff --git a/packages/bruno-electron/src/store/oauth2.js b/packages/bruno-electron/src/store/oauth2.js index 41522e37d..bebaf9e9c 100644 --- a/packages/bruno-electron/src/store/oauth2.js +++ b/packages/bruno-electron/src/store/oauth2.js @@ -150,6 +150,23 @@ class Oauth2Store { } } + clearCredentialsByCredentialsId({ collectionUid, credentialsId }) { + try { + let oauth2DataForCollection = this.getOauth2DataOfCollection({ collectionUid }); + let filteredCredentials = oauth2DataForCollection?.credentials?.filter( + (c) => c?.credentialsId !== credentialsId + ); + let newOauth2DataForCollection = { + ...oauth2DataForCollection, + credentials: filteredCredentials + }; + this.updateOauth2DataOfCollection({ collectionUid, data: newOauth2DataForCollection }); + return newOauth2DataForCollection; + } catch (err) { + console.log('error clearing oauth2 credentials by credentialsId from cache', err); + } + } + clearCredentialsForCollection({ collectionUid, url, credentialsId }) { try { let oauth2DataForCollection = this.getOauth2DataOfCollection({ collectionUid, url }); diff --git a/packages/bruno-electron/src/utils/oauth2.js b/packages/bruno-electron/src/utils/oauth2.js index 0a6227df0..6af08a41b 100644 --- a/packages/bruno-electron/src/utils/oauth2.js +++ b/packages/bruno-electron/src/utils/oauth2.js @@ -25,6 +25,10 @@ const clearOauth2Credentials = ({ collectionUid, url, credentialsId }) => { oauth2Store.clearCredentialsForCollection({ collectionUid, url, credentialsId }); }; +const clearOauth2CredentialsByCredentialsId = ({ collectionUid, credentialsId }) => { + oauth2Store.clearCredentialsByCredentialsId({ collectionUid, credentialsId }); +}; + const getStoredOauth2Credentials = ({ collectionUid, url, credentialsId }) => { try { const credentials = oauth2Store.getCredentialsForCollection({ collectionUid, url, credentialsId }); @@ -941,6 +945,7 @@ const updateCollectionOauth2Credentials = ({ collectionUid, itemUid, collectionO module.exports = { persistOauth2Credentials, clearOauth2Credentials, + clearOauth2CredentialsByCredentialsId, getStoredOauth2Credentials, getOAuth2TokenUsingAuthorizationCode, getOAuth2TokenUsingClientCredentials, diff --git a/packages/bruno-js/src/bru.js b/packages/bruno-js/src/bru.js index 5e500cf15..a66f9e2ed 100644 --- a/packages/bruno-js/src/bru.js +++ b/packages/bruno-js/src/bru.js @@ -92,6 +92,8 @@ class Bru { }; // Holds variables that are marked as persistent by scripts this.persistentEnvVariables = {}; + // Holds credential IDs to be reset after script execution + this.oauth2CredentialsToReset = []; this.runner = { skipRequest: () => { this.skipRequest = true; @@ -275,6 +277,24 @@ class Bru { return this.interpolate(this.oauth2CredentialVariables[key]); } + resetOauth2Credential(credentialId) { + if (!credentialId || typeof credentialId !== 'string') { + throw new Error('credentialId must be a non-empty string'); + } + + if (!this.oauth2CredentialsToReset.includes(credentialId)) { + this.oauth2CredentialsToReset.push(credentialId); + } + + // Remove matching credential variables so subsequent getOauth2CredentialVar() calls return undefined + const prefix = `$oauth2.${credentialId}.`; + for (const key of Object.keys(this.oauth2CredentialVariables)) { + if (key.startsWith(prefix)) { + delete this.oauth2CredentialVariables[key]; + } + } + } + hasVar(key) { return Object.hasOwn(this.runtimeVariables, key); } diff --git a/packages/bruno-js/src/runtime/script-runtime.js b/packages/bruno-js/src/runtime/script-runtime.js index 28cd48e75..7032eea73 100644 --- a/packages/bruno-js/src/runtime/script-runtime.js +++ b/packages/bruno-js/src/runtime/script-runtime.js @@ -76,6 +76,7 @@ class ScriptRuntime { runtimeVariables: cleanJson(runtimeVariables), persistentEnvVariables: bru.persistentEnvVariables, globalEnvironmentVariables: cleanJson(globalEnvironmentVariables), + oauth2CredentialsToReset: bru.oauth2CredentialsToReset, results: cleanJson(__brunoTestResults.getResults()), nextRequestName: bru.nextRequest, skipRequest: bru.skipRequest, @@ -193,6 +194,7 @@ class ScriptRuntime { persistentEnvVariables: cleanJson(bru.persistentEnvVariables), runtimeVariables: cleanJson(runtimeVariables), globalEnvironmentVariables: cleanJson(globalEnvironmentVariables), + oauth2CredentialsToReset: bru.oauth2CredentialsToReset, results: cleanJson(__brunoTestResults.getResults()), nextRequestName: bru.nextRequest, skipRequest: bru.skipRequest, diff --git a/packages/bruno-js/src/runtime/test-runtime.js b/packages/bruno-js/src/runtime/test-runtime.js index 88f30a95c..894a028e1 100644 --- a/packages/bruno-js/src/runtime/test-runtime.js +++ b/packages/bruno-js/src/runtime/test-runtime.js @@ -27,13 +27,14 @@ class TestRuntime { collectionName ) { const globalEnvironmentVariables = request?.globalEnvironmentVariables || {}; + const oauth2CredentialVariables = request?.oauth2CredentialVariables || {}; const collectionVariables = request?.collectionVariables || {}; const folderVariables = request?.folderVariables || {}; const requestVariables = request?.requestVariables || {}; const promptVariables = request?.promptVariables || {}; const assertionResults = request?.assertionResults || []; const certsAndProxyConfig = request?.certsAndProxyConfig; - const bru = new Bru(this.runtime, envVariables, runtimeVariables, processEnvVars, collectionPath, collectionVariables, folderVariables, requestVariables, globalEnvironmentVariables, {}, collectionName, promptVariables, certsAndProxyConfig); + const bru = new Bru(this.runtime, envVariables, runtimeVariables, processEnvVars, collectionPath, collectionVariables, folderVariables, requestVariables, globalEnvironmentVariables, oauth2CredentialVariables, collectionName, promptVariables, certsAndProxyConfig); const req = new BrunoRequest(request); const res = new BrunoResponse(response); @@ -109,6 +110,7 @@ class TestRuntime { runtimeVariables: cleanJson(runtimeVariables), globalEnvironmentVariables: cleanJson(globalEnvironmentVariables), persistentEnvVariables: cleanJson(bru.persistentEnvVariables), + oauth2CredentialsToReset: bru.oauth2CredentialsToReset, results: cleanJson(__brunoTestResults.getResults()), nextRequestName: bru.nextRequest }; diff --git a/packages/bruno-js/src/sandbox/quickjs/shims/bru.js b/packages/bruno-js/src/sandbox/quickjs/shims/bru.js index 8f4044ef3..69db5cd2c 100644 --- a/packages/bruno-js/src/sandbox/quickjs/shims/bru.js +++ b/packages/bruno-js/src/sandbox/quickjs/shims/bru.js @@ -89,6 +89,12 @@ const addBruShimToContext = (vm, bru) => { vm.setProp(bruObject, 'getOauth2CredentialVar', getOauth2CredentialVar); getOauth2CredentialVar.dispose(); + let resetOauth2Credential = vm.newFunction('resetOauth2Credential', function (credentialId) { + bru.resetOauth2Credential(vm.dump(credentialId)); + }); + vm.setProp(bruObject, 'resetOauth2Credential', resetOauth2Credential); + resetOauth2Credential.dispose(); + let setGlobalEnvVar = vm.newFunction('setGlobalEnvVar', function (key, value) { bru.setGlobalEnvVar(vm.dump(key), vm.dump(value)); });