Skip to content

Conversation

@sanish-bruno
Copy link
Collaborator

@sanish-bruno sanish-bruno commented Dec 4, 2025

Description

Contribution Checklist:

  • I've used AI significantly to create this pull request
  • 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.

Note: Keeping the PR small and focused helps make it easier to review and merge. If you have multiple changes you want to make, please consider submitting them as separate pull requests.

Publishing to New Package Managers

Please see here for more information.

Summary by CodeRabbit

  • Bug Fixes

    • Resolved potential error when processing certain request URLs
  • Refactor

    • Reorganized gRPC authentication request handling to enhance OAuth 2.0 token management with improved flexibility for token placement options

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 4, 2025

Walkthrough

This PR refactors OAuth2 handling in gRPC request processing by extracting it into a centralized configureRequest function and integrating it into gRPC event handlers. Additionally, a null-check guard is added to prevent potential errors when matching URLs against regex patterns in certificate utilities.

Changes

Cohort / File(s) Change Summary
OAuth2 Configuration Refactoring
packages/bruno-electron/src/ipc/network/prepare-grpc-request.js, packages/bruno-electron/src/ipc/network/grpc-event-handlers.js
Extracted OAuth2 handling logic into new configureRequest function that centralizes token computation for grant types (authorization_code, client_credentials, password) and token placement (header vs. query parameter). Added calls to configureRequest in grpc:start-connection and grpc:load-methods-reflection handlers.
URL Matching Guard
packages/bruno-electron/src/ipc/network/cert-utils.js
Added null-check guard for requestUrl before applying regex match to prevent potential errors when URL is undefined or null.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • OAuth2 extraction logic: Verify that oauth2Credentials assignment and token injection behavior is preserved across both header and query parameter placement paths
  • Event handler integration: Confirm configureRequest is called with correct parameter signatures in both handlers
  • URL matching robustness: Ensure null/undefined handling doesn't mask other edge cases

Possibly related PRs

Suggested labels

size/M

Suggested reviewers

  • helloanoop
  • lohit-bruno
  • naman-bruno
  • bijin-bruno

Poem

🔐 OAuth flows now centralized,
gRPC requests harmonized,
Tokens placed with care and grace,
Null guards keep bugs out of place. ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title directly addresses the main fix: gRPC OAuth2 now properly receives SSL cert and proxy config, which aligns with the core changes across the three modified files.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b18d582 and d47c442.

📒 Files selected for processing (3)
  • packages/bruno-electron/src/ipc/network/cert-utils.js (1 hunks)
  • packages/bruno-electron/src/ipc/network/grpc-event-handlers.js (3 hunks)
  • packages/bruno-electron/src/ipc/network/prepare-grpc-request.js (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CODING_STANDARDS.md)

**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation, never tabs
Use single quotes for strings instead of double quotes
Always add semicolons at the end of statements
No trailing commas in code
Always use parentheses around parameters in arrow functions, even for single parameters
For multiline constructs, put opening braces on the same line with a minimum of 2 elements for multiline formatting
No newlines inside function parentheses
Space before and after the arrow in arrow functions: () => {}
No space between function name and parentheses: func() not func ()
Semicolons should go at the end of the line, not on a new line
Function names should be concise and descriptive
Add JSDoc comments to provide details to abstractions
Avoid single-line abstractions where all that is being done is increasing the call stack with one additional function
Add meaningful comments to explain complex code flow instead of obvious comments

Files:

  • packages/bruno-electron/src/ipc/network/cert-utils.js
  • packages/bruno-electron/src/ipc/network/grpc-event-handlers.js
  • packages/bruno-electron/src/ipc/network/prepare-grpc-request.js
🧠 Learnings (2)
📓 Common learnings
Learnt from: bijin-bruno
Repo: usebruno/bruno PR: 6263
File: packages/bruno-requests/src/auth/oauth2-helper.ts:249-249
Timestamp: 2025-12-02T07:24:50.299Z
Learning: In OAuth2 Basic Auth headers for Bruno, clientSecret is optional and can be omitted. When constructing the Authorization header in `packages/bruno-requests/src/auth/oauth2-helper.ts`, use `clientSecret || ''` instead of `clientSecret!` to properly handle cases where only clientId is provided, per community requests.
📚 Learning: 2025-12-02T07:24:50.299Z
Learnt from: bijin-bruno
Repo: usebruno/bruno PR: 6263
File: packages/bruno-requests/src/auth/oauth2-helper.ts:249-249
Timestamp: 2025-12-02T07:24:50.299Z
Learning: In OAuth2 Basic Auth headers for Bruno, clientSecret is optional and can be omitted. When constructing the Authorization header in `packages/bruno-requests/src/auth/oauth2-helper.ts`, use `clientSecret || ''` instead of `clientSecret!` to properly handle cases where only clientId is provided, per community requests.

Applied to files:

  • packages/bruno-electron/src/ipc/network/prepare-grpc-request.js
🧬 Code graph analysis (1)
packages/bruno-electron/src/ipc/network/cert-utils.js (1)
packages/bruno-cli/src/runner/run-single-request.js (2)
  • hostRegex (76-76)
  • hostRegex (261-261)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: SSL Tests - Linux
  • GitHub Check: SSL Tests - macOS
  • GitHub Check: SSL Tests - Windows
  • GitHub Check: Unit Tests
  • GitHub Check: Playwright E2E Tests
  • GitHub Check: CLI Tests
🔇 Additional comments (5)
packages/bruno-electron/src/ipc/network/cert-utils.js (1)

70-70: Good defensive guard.

The null check prevents potential runtime errors when requestUrl is undefined or null before calling .match().

packages/bruno-electron/src/ipc/network/prepare-grpc-request.js (1)

127-127: LGTM on named export.

The named export for configureRequest properly extends the module's public API while maintaining backward compatibility with the default export.

packages/bruno-electron/src/ipc/network/grpc-event-handlers.js (3)

11-11: LGTM on import.

The import correctly destructures the named export from prepare-grpc-request.


57-66: Correct integration of OAuth2 configuration.

The configureRequest call is properly placed after request preparation and certificate configuration, with correct parameter passing and awaiting.


205-214: Consistent OAuth2 configuration in reflection handler.

The configureRequest integration follows the same correct pattern as the start-connection handler.

Comment on lines +16 to +66
const configureRequest = async (grpcRequest, request, collection, envVars, runtimeVariables, processEnvVars, promptVariables, certsAndProxyConfig) => {
if (grpcRequest.oauth2) {
let requestCopy = cloneDeep(grpcRequest);
const { oauth2: { grantType, tokenPlacement, tokenHeaderPrefix, tokenQueryKey } = {} } = requestCopy || {};
let credentials, credentialsId, oauth2Url, debugInfo;
switch (grantType) {
case 'authorization_code':
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingAuthorizationCode({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
if (tokenPlacement == 'header') {
grpcRequest.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`;
} else {
try {
const url = new URL(grpcRequest.url);
url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
grpcRequest.url = url?.toString();
} catch (error) { }
}
break;
case 'client_credentials':
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingClientCredentials({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
if (tokenPlacement == 'header') {
grpcRequest.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`;
} else {
try {
const url = new URL(grpcRequest.url);
url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
grpcRequest.url = url?.toString();
} catch (error) { }
}
break;
case 'password':
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingPasswordCredentials({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
if (tokenPlacement == 'header') {
grpcRequest.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`;
} else {
try {
const url = new URL(grpcRequest.url);
url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
grpcRequest.url = url?.toString();
} catch (error) { }
}
break;
}
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Empty catch blocks silently swallow URL parsing errors.

Lines 33, 47, and 61 have empty catch blocks that suppress any errors from URL parsing. This could hide configuration issues or invalid URLs, making debugging difficult.

Apply this diff to log errors:

         } else {
           try {
             const url = new URL(grpcRequest.url);
             url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
             grpcRequest.url = url?.toString();
-          } catch (error) { }
+          } catch (error) {
+            console.error('Failed to parse URL for OAuth2 token placement:', error);
+          }
         }

Apply similar changes to the other two catch blocks at lines 47 and 61.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const configureRequest = async (grpcRequest, request, collection, envVars, runtimeVariables, processEnvVars, promptVariables, certsAndProxyConfig) => {
if (grpcRequest.oauth2) {
let requestCopy = cloneDeep(grpcRequest);
const { oauth2: { grantType, tokenPlacement, tokenHeaderPrefix, tokenQueryKey } = {} } = requestCopy || {};
let credentials, credentialsId, oauth2Url, debugInfo;
switch (grantType) {
case 'authorization_code':
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingAuthorizationCode({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
if (tokenPlacement == 'header') {
grpcRequest.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`;
} else {
try {
const url = new URL(grpcRequest.url);
url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
grpcRequest.url = url?.toString();
} catch (error) { }
}
break;
case 'client_credentials':
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingClientCredentials({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
if (tokenPlacement == 'header') {
grpcRequest.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`;
} else {
try {
const url = new URL(grpcRequest.url);
url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
grpcRequest.url = url?.toString();
} catch (error) { }
}
break;
case 'password':
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingPasswordCredentials({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
if (tokenPlacement == 'header') {
grpcRequest.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`;
} else {
try {
const url = new URL(grpcRequest.url);
url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
grpcRequest.url = url?.toString();
} catch (error) { }
}
break;
}
}
};
const configureRequest = async (grpcRequest, request, collection, envVars, runtimeVariables, processEnvVars, promptVariables, certsAndProxyConfig) => {
if (grpcRequest.oauth2) {
let requestCopy = cloneDeep(grpcRequest);
const { oauth2: { grantType, tokenPlacement, tokenHeaderPrefix, tokenQueryKey } = {} } = requestCopy || {};
let credentials, credentialsId, oauth2Url, debugInfo;
switch (grantType) {
case 'authorization_code':
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingAuthorizationCode({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
if (tokenPlacement == 'header') {
grpcRequest.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`;
} else {
try {
const url = new URL(grpcRequest.url);
url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
grpcRequest.url = url?.toString();
} catch (error) {
console.error('Failed to parse URL for OAuth2 token placement:', error);
}
}
break;
case 'client_credentials':
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingClientCredentials({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
if (tokenPlacement == 'header') {
grpcRequest.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`;
} else {
try {
const url = new URL(grpcRequest.url);
url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
grpcRequest.url = url?.toString();
} catch (error) {
console.error('Failed to parse URL for OAuth2 token placement:', error);
}
}
break;
case 'password':
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingPasswordCredentials({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
if (tokenPlacement == 'header') {
grpcRequest.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`;
} else {
try {
const url = new URL(grpcRequest.url);
url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
grpcRequest.url = url?.toString();
} catch (error) {
console.error('Failed to parse URL for OAuth2 token placement:', error);
}
}
break;
}
}
};
🤖 Prompt for AI Agents
In packages/bruno-electron/src/ipc/network/prepare-grpc-request.js around lines
16 to 66, the three empty catch blocks at ~lines 33, 47 and 61 silently swallow
URL parsing errors; update each catch to log the error (and the grpcRequest.url
or tokenQueryKey/context) instead of being empty so configuration/invalid-URL
issues are visible — use the module's logger (e.g. processLogger.error) if
available otherwise console.error, include a short message plus the caught error
and the URL being parsed, and keep the original behavior (don’t rethrow) so
token placement still falls back to the URL not being modified.

Comment on lines +21 to +64
switch (grantType) {
case 'authorization_code':
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingAuthorizationCode({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
if (tokenPlacement == 'header') {
grpcRequest.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`;
} else {
try {
const url = new URL(grpcRequest.url);
url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
grpcRequest.url = url?.toString();
} catch (error) { }
}
break;
case 'client_credentials':
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingClientCredentials({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
if (tokenPlacement == 'header') {
grpcRequest.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`;
} else {
try {
const url = new URL(grpcRequest.url);
url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
grpcRequest.url = url?.toString();
} catch (error) { }
}
break;
case 'password':
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingPasswordCredentials({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
if (tokenPlacement == 'header') {
grpcRequest.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`;
} else {
try {
const url = new URL(grpcRequest.url);
url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
grpcRequest.url = url?.toString();
} catch (error) { }
}
break;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Extract duplicated token placement logic.

The token placement logic (lines 26-34, 40-48, 54-62) is duplicated across all three grant type cases. Only the OAuth2 token fetching differs between cases.

Extract the common logic into a helper function:

+const placeOAuth2Token = (grpcRequest, credentials, tokenPlacement, tokenHeaderPrefix, tokenQueryKey) => {
+  if (tokenPlacement === 'header') {
+    grpcRequest.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`;
+  } else {
+    try {
+      const url = new URL(grpcRequest.url);
+      url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
+      grpcRequest.url = url?.toString();
+    } catch (error) {
+      console.error('Failed to parse URL for OAuth2 token placement:', error);
+    }
+  }
+};
+
 const configureRequest = async (grpcRequest, request, collection, envVars, runtimeVariables, processEnvVars, promptVariables, certsAndProxyConfig) => {
   if (grpcRequest.oauth2) {
     let requestCopy = cloneDeep(grpcRequest);
     const { oauth2: { grantType, tokenPlacement, tokenHeaderPrefix, tokenQueryKey } = {} } = requestCopy || {};
     let credentials, credentialsId, oauth2Url, debugInfo;
     switch (grantType) {
       case 'authorization_code':
         interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
         ({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingAuthorizationCode({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
         grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
-        if (tokenPlacement == 'header') {
-          grpcRequest.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`;
-        } else {
-          try {
-            const url = new URL(grpcRequest.url);
-            url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
-            grpcRequest.url = url?.toString();
-          } catch (error) { }
-        }
+        placeOAuth2Token(grpcRequest, credentials, tokenPlacement, tokenHeaderPrefix, tokenQueryKey);
         break;
       case 'client_credentials':
         interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
         ({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingClientCredentials({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
         grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
-        if (tokenPlacement == 'header') {
-          grpcRequest.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`;
-        } else {
-          try {
-            const url = new URL(grpcRequest.url);
-            url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
-            grpcRequest.url = url?.toString();
-          } catch (error) { }
-        }
+        placeOAuth2Token(grpcRequest, credentials, tokenPlacement, tokenHeaderPrefix, tokenQueryKey);
         break;
       case 'password':
         interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
         ({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingPasswordCredentials({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
         grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
-        if (tokenPlacement == 'header') {
-          grpcRequest.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`;
-        } else {
-          try {
-            const url = new URL(grpcRequest.url);
-            url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
-            grpcRequest.url = url?.toString();
-          } catch (error) { }
-        }
+        placeOAuth2Token(grpcRequest, credentials, tokenPlacement, tokenHeaderPrefix, tokenQueryKey);
         break;
     }
   }
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
switch (grantType) {
case 'authorization_code':
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingAuthorizationCode({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
if (tokenPlacement == 'header') {
grpcRequest.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`;
} else {
try {
const url = new URL(grpcRequest.url);
url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
grpcRequest.url = url?.toString();
} catch (error) { }
}
break;
case 'client_credentials':
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingClientCredentials({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
if (tokenPlacement == 'header') {
grpcRequest.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`;
} else {
try {
const url = new URL(grpcRequest.url);
url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
grpcRequest.url = url?.toString();
} catch (error) { }
}
break;
case 'password':
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingPasswordCredentials({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
if (tokenPlacement == 'header') {
grpcRequest.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`;
} else {
try {
const url = new URL(grpcRequest.url);
url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
grpcRequest.url = url?.toString();
} catch (error) { }
}
break;
}
const placeOAuth2Token = (grpcRequest, credentials, tokenPlacement, tokenHeaderPrefix, tokenQueryKey) => {
if (tokenPlacement === 'header') {
grpcRequest.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`;
} else {
try {
const url = new URL(grpcRequest.url);
url?.searchParams?.set(tokenQueryKey, credentials?.access_token);
grpcRequest.url = url?.toString();
} catch (error) {
console.error('Failed to parse URL for OAuth2 token placement:', error);
}
}
};
const configureRequest = async (grpcRequest, request, collection, envVars, runtimeVariables, processEnvVars, promptVariables, certsAndProxyConfig) => {
if (grpcRequest.oauth2) {
let requestCopy = cloneDeep(grpcRequest);
const { oauth2: { grantType, tokenPlacement, tokenHeaderPrefix, tokenQueryKey } = {} } = requestCopy || {};
let credentials, credentialsId, oauth2Url, debugInfo;
switch (grantType) {
case 'authorization_code':
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingAuthorizationCode({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
placeOAuth2Token(grpcRequest, credentials, tokenPlacement, tokenHeaderPrefix, tokenQueryKey);
break;
case 'client_credentials':
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingClientCredentials({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
placeOAuth2Token(grpcRequest, credentials, tokenPlacement, tokenHeaderPrefix, tokenQueryKey);
break;
case 'password':
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingPasswordCredentials({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
placeOAuth2Token(grpcRequest, credentials, tokenPlacement, tokenHeaderPrefix, tokenQueryKey);
break;
}
}
};
🤖 Prompt for AI Agents
In packages/bruno-electron/src/ipc/network/prepare-grpc-request.js around lines
21 to 64, the token placement logic (setting grpcRequest.oauth2Credentials and
placing the access token into either the Authorization header or URL query) is
duplicated in each grant_type case; extract this into a single helper function
that accepts the grpcRequest, credentials, oauth2Url, credentialsId, debugInfo,
tokenPlacement, tokenHeaderPrefix, tokenQueryKey and original request (for
folderUid) and performs: set grpcRequest.oauth2Credentials with folderUid, then
if tokenPlacement === 'header' set grpcRequest.headers['Authorization'] to
`${tokenHeaderPrefix} ${credentials?.access_token}`, else try to append
credentials?.access_token to the grpcRequest.url search params (preserving the
current silent error behavior). Replace the duplicated blocks in each case with
a call to this helper after fetching credentials (keeping the existing
interpolateVars and token fetch calls intact).

interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars, promptVariables);
({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingAuthorizationCode({ request: requestCopy, collectionUid: collection.uid, certsAndProxyConfig }));
grpcRequest.oauth2Credentials = { credentials, url: oauth2Url, collectionUid: collection.uid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid };
if (tokenPlacement == 'header') {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use strict equality operators.

Lines 26, 40, and 54 use loose equality (==) instead of strict equality (===). As per coding guidelines, always use strict equality to avoid type coercion issues.

Apply this diff:

-        if (tokenPlacement == 'header') {
+        if (tokenPlacement === 'header') {

Apply the same change to lines 40 and 54.

Also applies to: 40-40, 54-54

🤖 Prompt for AI Agents
In packages/bruno-electron/src/ipc/network/prepare-grpc-request.js around lines
26, 40, and 54, the code uses loose equality (==) for comparisons; replace those
with strict equality (===) at each occurrence to avoid type coercion issues and
conform to project coding guidelines. Ensure you update the three comparisons at
lines 26, 40, and 54 to use === and run lint/tests to confirm no other
occurrences remain.

@sanish-bruno sanish-bruno changed the title fix: grpc oauth2 fix: grpc oauth2 call not taking ssl cert and proxy config Dec 5, 2025
@sanish-bruno sanish-bruno changed the title fix: grpc oauth2 call not taking ssl cert and proxy config fix: grpc oauth2 call is not taking ssl cert and proxy config Dec 5, 2025
@sanish-bruno sanish-bruno changed the title fix: grpc oauth2 call is not taking ssl cert and proxy config fix: gRPC oauth2 call is not taking ssl cert and proxy config Dec 5, 2025
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.

1 participant