diff --git a/packages/bruno-app/src/components/RequestPane/WSRequestPane/WSAuth/index.js b/packages/bruno-app/src/components/RequestPane/WSRequestPane/WSAuth/index.js
index 187df08fd..2ca88195f 100644
--- a/packages/bruno-app/src/components/RequestPane/WSRequestPane/WSAuth/index.js
+++ b/packages/bruno-app/src/components/RequestPane/WSRequestPane/WSAuth/index.js
@@ -93,6 +93,17 @@ const WSAuth = ({ item, collection }) => {
case 'inherit': {
const source = getEffectiveAuthSource();
+ // Check if inherited auth is OAuth2 - not supported for WebSockets
+ if (source?.auth?.mode === 'oauth2') {
+ return (
+ <>
+
+ OAuth 2 not yet supported by WebSockets. Using no auth instead.
+
+ >
+ );
+ }
+
// Only show inherited auth if it's one of the supported types
if (source && supportedAuthModes.includes(source.auth?.mode)) {
return (
diff --git a/packages/bruno-electron/src/ipc/network/ws-event-handlers.js b/packages/bruno-electron/src/ipc/network/ws-event-handlers.js
index 3f24ad8ba..e11aadaf6 100644
--- a/packages/bruno-electron/src/ipc/network/ws-event-handlers.js
+++ b/packages/bruno-electron/src/ipc/network/ws-event-handlers.js
@@ -249,6 +249,34 @@ const prepareWsRequest = async (item, collection, environment, runtimeVariables,
}
}
+ // Add API key to the URL if placement is queryparams
+ if (wsRequest.apiKeyAuthValueForQueryParams && wsRequest.apiKeyAuthValueForQueryParams.placement === 'queryparams') {
+ try {
+ const urlObj = new URL(wsRequest.url);
+
+ const globalEnvironmentVariables = request.globalEnvironmentVariables;
+ const promptVariables = collection?.promptVariables || {};
+
+ const interpolationOptions = {
+ globalEnvironmentVariables,
+ envVars,
+ runtimeVariables,
+ promptVariables,
+ processEnvVars
+ };
+
+ const key = interpolateString(wsRequest.apiKeyAuthValueForQueryParams.key, interpolationOptions);
+ const value = interpolateString(wsRequest.apiKeyAuthValueForQueryParams.value, interpolationOptions);
+
+ urlObj.searchParams.set(key, value);
+ wsRequest.url = urlObj.toString();
+ } catch (error) {
+ console.error('Error adding API key to WebSocket URL:', error);
+ }
+ }
+
+ delete wsRequest.apiKeyAuthValueForQueryParams;
+
interpolateVars(wsRequest, envVars, runtimeVariables, processEnvVars);
return wsRequest;
@@ -462,5 +490,6 @@ const registerWsEventHandlers = (window) => {
module.exports = {
registerWsEventHandlers,
- wsClient
+ wsClient,
+ prepareWsRequest
};
diff --git a/packages/bruno-electron/tests/network/prepare-ws-request.spec.js b/packages/bruno-electron/tests/network/prepare-ws-request.spec.js
new file mode 100644
index 000000000..89ea77efb
--- /dev/null
+++ b/packages/bruno-electron/tests/network/prepare-ws-request.spec.js
@@ -0,0 +1,55 @@
+// Mock dependencies before requiring the module
+const { prepareWsRequest } = require('../../src/ipc/network/ws-event-handlers');
+
+describe('prepareWsRequest: API Key Query Params', () => {
+ const createMockItem = (authConfig = {}) => ({
+ uid: 'test-item-uid',
+ request: {
+ url: 'ws://localhost:3001',
+ headers: [],
+ body: {
+ mode: 'raw',
+ ws: []
+ },
+ auth: authConfig,
+ vars: { req: [], res: [] },
+ script: { req: '', res: '' }
+ }
+ });
+
+ const createMockCollection = (collectionAuth = null) => ({
+ uid: 'test-collection-uid',
+ pathname: '/test/path',
+ root: {
+ request: {
+ headers: [],
+ auth: collectionAuth || { mode: 'none' }
+ }
+ },
+ brunoConfig: {},
+ globalEnvironmentVariables: {},
+ promptVariables: {},
+ items: []
+ });
+
+ describe('API Key with Query Params placement', () => {
+ it('should append API key to URL when placement is queryparams', async () => {
+ const item = createMockItem({
+ mode: 'apikey',
+ apikey: {
+ key: 'apiKey',
+ value: 'test-api-key-123',
+ placement: 'queryparams'
+ }
+ });
+ const collection = createMockCollection();
+ const environment = { variables: [] };
+ const runtimeVariables = {};
+
+ const result = await prepareWsRequest(item, collection, environment, runtimeVariables);
+
+ expect(result.url).toContain('apiKey=test-api-key-123');
+ expect(result.url).toBe('ws://localhost:3001/?apiKey=test-api-key-123');
+ });
+ });
+});
diff --git a/packages/bruno-tests/src/ws/index.js b/packages/bruno-tests/src/ws/index.js
index d89973dc2..8ed0f4d46 100644
--- a/packages/bruno-tests/src/ws/index.js
+++ b/packages/bruno-tests/src/ws/index.js
@@ -33,6 +33,12 @@ wss.on('connection', function connection(ws, request) {
return ws.send(JSON.stringify({
headers: request.headers
}));
+ } else if ('func' in obj && obj.func === 'query') {
+ const url = new URL(request.url, `http://${request.headers.host}`);
+ const query = Object.fromEntries(url.searchParams.entries());
+ return ws.send(JSON.stringify({
+ query: query
+ }));
} else {
return ws.send(JSON.stringify({
data: JSON.parse(Buffer.from(data).toString())
diff --git a/tests/websockets/fixtures/collection/ws-test-request-with-query.bru b/tests/websockets/fixtures/collection/ws-test-request-with-query.bru
new file mode 100644
index 000000000..1dbc55384
--- /dev/null
+++ b/tests/websockets/fixtures/collection/ws-test-request-with-query.bru
@@ -0,0 +1,19 @@
+meta {
+ name: ws-test-request-with-query
+ type: ws
+ seq: 3
+}
+
+ws {
+ url: ws://localhost:8081/ws?testParam=testValue&anotherParam={{variable}}
+ auth: inherit
+}
+
+body:ws {
+ name: message 1
+ content: '''
+ {
+ "func":"query"
+ }
+ '''
+}
diff --git a/tests/websockets/query.spec.ts b/tests/websockets/query.spec.ts
new file mode 100644
index 000000000..85241cc31
--- /dev/null
+++ b/tests/websockets/query.spec.ts
@@ -0,0 +1,20 @@
+import { test, expect } from '../../playwright';
+import { buildWebsocketCommonLocators } from '../utils/page/locators';
+
+const BRU_REQ_NAME = /^ws-test-request-with-query$/;
+
+test.describe.serial('query params', () => {
+ test('query params are returned if passed', async ({ pageWithUserData: page }) => {
+ const locators = buildWebsocketCommonLocators(page);
+
+ // Open the most recent collection
+ await page.locator('#sidebar-collection-name').click();
+
+ // Click on the required request
+ await page.getByTitle(BRU_REQ_NAME).click();
+ await locators.runner().click();
+
+ // Check if the message has the query params
+ await expect(locators.messages().nth(2).locator('.text-ellipsis')).toHaveText(/\"(testParam)\"\:\s+\"testValue\"/);
+ });
+});