Skip to content

feat: add OAuth 1.0 authentication support#7482

Open
lohit-bruno wants to merge 4 commits intousebruno:mainfrom
lohit-bruno:oauth_1.0
Open

feat: add OAuth 1.0 authentication support#7482
lohit-bruno wants to merge 4 commits intousebruno:mainfrom
lohit-bruno:oauth_1.0

Conversation

@lohit-bruno
Copy link
Collaborator

@lohit-bruno lohit-bruno commented Mar 13, 2026

jira

opencollection pr: opencollection-dev/opencollection#25

Description

Add OAuth 1.0 (RFC 5849) authentication with support for HMAC-SHA1/256/512, RSA-SHA1/256/512, and PLAINTEXT signature methods.

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

  • New Features

    • Added OAuth 1.0 authentication (HMAC-SHA1/256/512, RSA-SHA1/256/512, PLAINTEXT).
    • Configure OAuth1 at request and collection levels; choose where params are added (header, query, body).
    • RSA private key support (inline or file) and optional body-hash handling.
    • UI: new OAuth1 auth panels and mode option throughout the app.
  • Tests

    • Comprehensive OAuth1 unit, conversion, CLI, and end-to-end test suites and fixtures.
  • Chores

    • New e2e test script/project for auth tests.

Add full OAuth 1.0 (RFC 5849) authentication with support for
HMAC-SHA1/256/512, RSA-SHA1/256/512, and PLAINTEXT signature methods.
Includes UI components, bru/yml serialization, Postman import, code
generation, CLI support, and comprehensive playwright and unit tests.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 13, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3083202a-c47f-42ba-beea-ff173238d502

📥 Commits

Reviewing files that changed from the base of the PR and between 4d3a7b8 and 4c3bf7e.

📒 Files selected for processing (3)
  • packages/bruno-app/src/components/RequestPane/WSRequestPane/WSAuth/index.js
  • packages/bruno-app/src/utils/codegenerator/auth.js
  • packages/bruno-lang/v2/tests/oauth1.spec.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/bruno-app/src/utils/codegenerator/auth.js

Walkthrough

Adds end-to-end OAuth 1.0a (RFC 5849) support across Bruno: new signing library, UI components, schema/types, CLI/Electron signing and interpolation, converters/filestore support, extensive tests and fixtures, Playwright suites, and re-exports for request signing helpers.

Changes

Cohort / File(s) Summary
Core OAuth1 implementation
packages/bruno-requests/src/auth/oauth1-request-authorization.ts, packages/bruno-requests/src/auth/oauth1-request-authorization.spec.ts, packages/bruno-requests/src/auth/index.ts, packages/bruno-requests/src/index.ts
New RFC‑5849-compliant OAuth1 authorizer (HMAC/RSA/PLAINTEXT), body-hash support, applyOAuth1ToRequest, computeBodyHash, comprehensive unit tests, and public re-exports.
UI: Request / Folder / Collection auth
packages/bruno-app/src/components/RequestPane/Auth/OAuth1/*, packages/bruno-app/src/components/RequestPane/Auth/AuthMode/index.js, packages/bruno-app/src/components/RequestPane/Auth/index.js, packages/bruno-app/src/components/FolderSettings/Auth*, packages/bruno-app/src/components/CollectionSettings/Auth*
Added OAuth1 UI components, styled wrapper, integrated into auth mode selectors and auth view rendering for request, folder, and collection levels; wired to Redux save/update flows.
State, schema & types
packages/bruno-schema-types/src/common/auth.ts, packages/bruno-schema/src/collections/index.js, packages/bruno-schema-types/...
New AuthOauth1 type, added 'oauth1' to AuthMode union, auth schema for oauth1 added to collection/item schemas.
CLI & Electron signing flows
packages/bruno-cli/src/runner/interpolate-vars.js, packages/bruno-cli/src/runner/prepare-request.js, packages/bruno-cli/src/runner/run-single-request.js, packages/bruno-electron/src/ipc/network/*
Interpolate oauth1 fields, populate axios/IPC oauth1config from collection/request, invoke applyOAuth1ToRequest with error handling; removed stray console.log; added collectionPath propagation and setAuthHeaders signature change in electron prepare-request.
Converters / Filestore / OpenCollection
packages/bruno-converters/src/opencollection/common/auth.ts, packages/bruno-converters/src/opencollection/types.ts, packages/bruno-converters/src/postman/postman-to-bruno.js, packages/bruno-filestore/src/formats/yml/common/auth.ts, packages/bruno-app/src/utils/collections/index.js
Added oauth1 mapping/serialization for OpenCollection, Postman conversion, YAML filestore builder and export serialization including privateKey type handling and defaults.
Runtime helpers & codegen
packages/bruno-app/src/utils/codegenerator/auth.js, packages/bruno-js/src/bruno-request.js
Added oauth1 branch to auth header generation and auth-mode detection.
Language tooling (Bru)
packages/bruno-lang/v2/src/*, packages/bruno-lang/v2/tests/oauth1.spec.js
Added grammar, parsers and serializers for auth:oauth1, file/@file private key handling, and extensive round-trip tests.
Tests, fixtures & runner
tests/auth/oauth1/**, tests/auth/oauth1/oauth1.spec.ts, tests/auth/oauth1/oauth1-runner.spec.ts, packages/bruno-tests/src/auth/oauth1/index.js, playwright.config.ts, package.json
Added OAuth1 test server, 50+ Bru/YML fixtures, Playwright UI and runner tests, new auth project in Playwright config, and new npm script test:e2e:auth.
Redux & providers
packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
Reducers extended to save/update oauth1 payloads for request/folder/collection drafts.
Test utilities
tests/utils/page/actions.ts
Added generic pane-scoped tab selector (selectPaneTab) and wrappers for request/response pane tab selection.

Sequence Diagram

sequenceDiagram
    participant User as Client/User
    participant Prep as Prepare Request
    participant Interp as Interpolator
    participant Sign as OAuth1 Signer
    participant Net as Network Layer
    participant Server as API Server

    User->>Prep: create/send request (may include oauth1config)
    Prep->>Interp: interpolate variables (consumerKey, secrets, privateKey, ...)
    Interp->>Prep: return interpolated oauth1config
    Prep->>Sign: applyOAuth1ToRequest(axiosRequest, collectionPath?)
    Sign->>Sign: collect params (query/body), build base string, compute signature
    Sign->>Prep: attach signature (header/query/body)
    Prep->>Net: send signed request
    Net->>Server: HTTP request with OAuth1 params
    Server->>Server: verify signature
    alt signature valid
        Server->>Net: 200 OK
    else invalid
        Server->>Net: 401 Unauthorized
    end
    Net->>User: response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • naman-bruno
  • bijin-bruno

Poem

✨ A new auth arrives with signature and art,
Keys and secrets playing their part,
HMAC, RSA, PLAINTEXT in line,
Tests and fixtures to prove each sign —
OAuth1 now sings in Bruno’s heart 🎶

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 74.07% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add OAuth 1.0 authentication support' clearly and concisely summarizes the main change: OAuth 1.0 authentication is being added as a new feature.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@lohit-bruno lohit-bruno changed the title feat: add OAuth 1.0 authentication support feat: add OAuth 1.0 authentication support Mar 13, 2026
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: 9

🧹 Nitpick comments (15)
tests/auth/oauth1/fixtures/collections/bru/OAuth1 RSA-SHA1 Body 200.bru (1)

20-49: Consider centralizing the PEM test key to reduce duplication.

If this same RSA key appears in other OAuth1 RSA fixtures, pulling it from a shared fixture source would lower copy/paste drift.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/auth/oauth1/fixtures/collections/bru/OAuth1` RSA-SHA1 Body 200.bru
around lines 20 - 49, The RSA PEM private_key block in the OAuth1 RSA-SHA1
fixture is duplicated; extract the PEM into a single shared fixture (e.g., a
shared "rsa_private_key" fixture) and replace the inline private_key value in
OAuth1 RSA-SHA1 Body 200.bru with a reference to that shared fixture; update any
other OAuth1 RSA fixtures to reference the same shared symbol so all tests
consume the centralized key and eliminate copy/paste drift.
tests/auth/oauth1/fixtures/collections/yml/OAuth1 HMAC-SHA1 Body JSON 200.yml (1)

11-14: Use clearly fake credential literals to reduce secret-scanner noise.

These are test fixtures, but current values can still trip generic secret detectors and create noisy security reports.

Suggested placeholder cleanup
-    consumerKey: consumer_key_1
-    consumerSecret: consumer_secret_1
-    accessToken: access_token_1
-    tokenSecret: token_secret_1
+    consumerKey: oauth1-test-id
+    consumerSecret: oauth1-test-secret
+    accessToken: oauth1-test-token
+    tokenSecret: oauth1-test-token-secret
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/auth/oauth1/fixtures/collections/yml/OAuth1` HMAC-SHA1 Body JSON
200.yml around lines 11 - 14, Replace the realistic-looking credential values in
the fixture by using clearly fake placeholder literals for the keys consumerKey,
consumerSecret, accessToken, and tokenSecret (for example values like
"fake_consumer_key", "fake_consumer_secret", "fake_access_token",
"fake_token_secret") so the test remains meaningful but won't trigger secret
scanners; update the corresponding entries in the OAuth1 HMAC-SHA1 Body JSON
fixture to use those fake placeholders.
tests/auth/oauth1/fixtures/collections/bru/OAuth1 PLAINTEXT Query Params 200.bru (1)

29-32: Consider adding email assertion for parity with other 200 fixtures.

The HMAC-SHA1 200.bru fixture includes an assertion for res.body.resource.email. Adding it here would maintain consistency:

assert {
  res.status: eq 200
  res.body.resource.name: eq oauth1-test-resource
  res.body.resource.email: eq oauth1@example.com
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/auth/oauth1/fixtures/collections/bru/OAuth1` PLAINTEXT Query Params
200.bru around lines 29 - 32, Add an email assertion to the existing response
assertions: inside the assert block that currently checks res.status and
res.body.resource.name (the assertions referencing res.status and
res.body.resource.name: eq oauth1-test-resource), add a line asserting
res.body.resource.email: eq oauth1@example.com so this PLAINTEXT fixture matches
the parity of the HMAC-SHA1 200.bru fixture.
tests/auth/oauth1/fixtures/collections/yml/OAuth1 HMAC-SHA256 Body 200.yml (1)

22-30: Missing email assertion for consistency.

Other 200 fixtures (e.g., OAuth1 HMAC-SHA1 200.bru, OAuth1 RSA-SHA256 200.yml) include an assertion for res.body.resource.email. Consider adding it here for consistency:

    - expression: res.body.resource.email
      operator: eq
      value: oauth1@example.com
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/auth/oauth1/fixtures/collections/yml/OAuth1` HMAC-SHA256 Body 200.yml
around lines 22 - 30, The 200 fixture is missing the email assertion for
consistency with other OAuth1 200 fixtures; add an assertion entry under
runtime.assertions that checks res.body.resource.email equals oauth1@example.com
by inserting an assertion with expression: res.body.resource.email, operator:
eq, and value: oauth1@example.com (matching the existing assertion style used
for res.body.resource.name).
tests/auth/oauth1/fixtures/collections/yml/OAuth1 RSA-SHA1 Body 200.yml (1)

53-61: Consider adding email assertion for consistency.

Other OAuth1 200 fixtures (e.g., HMAC-SHA1, RSA-SHA1) include an assertion for res.body.resource.email. This fixture only checks resource.name. Adding the email check would improve consistency across your test suite.

💡 Suggested addition
 runtime:
   assertions:
     - expression: res.status
       operator: eq
       value: '200'
     - expression: res.body.resource.name
       operator: eq
       value: oauth1-test-resource
+    - expression: res.body.resource.email
+      operator: eq
+      value: oauth1@example.com
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/auth/oauth1/fixtures/collections/yml/OAuth1` RSA-SHA1 Body 200.yml
around lines 53 - 61, This fixture's runtime assertions only check
res.body.resource.name but should also assert res.body.resource.email for
consistency with other OAuth1 200 fixtures; update the assertions array in the
YAML (the block containing the expressions "res.body.resource.name" and
"res.status") to add a new assertion whose expression is
"res.body.resource.email", operator "eq", and value matching the expected test
email used elsewhere in OAuth1 fixtures (use the same email literal used by
HMAC-SHA1/RSA-SHA1 fixtures) so the test verifies both name and email.
tests/auth/oauth1/fixtures/collections/yml/OAuth1 RSA-SHA1 200.yml (1)

9-17: Missing consumerSecret field for schema consistency.

The OAuth1 RSA-SHA1 Body 200.yml fixture explicitly includes consumerSecret: '' while this file omits it entirely. For consistency across fixtures (and to ensure the YAML schema is exercised uniformly), consider adding the field.

💡 Suggested addition
   auth:
     type: oauth1
     consumerKey: consumer_key_1
+    consumerSecret: ''
     accessToken: access_token_1
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/auth/oauth1/fixtures/collections/yml/OAuth1` RSA-SHA1 200.yml around
lines 9 - 17, Add the missing consumerSecret field to the auth block so the
fixture matches the schema used by other OAuth1 RSA-SHA1 fixtures; update the
auth map by adding consumerSecret: '' alongside consumerKey, accessToken,
tokenSecret, signatureMethod, version, addParamsTo, and includeBodyHash to
ensure consistent schema coverage.
tests/auth/oauth1/fixtures/collections/yml/OAuth1 PLAINTEXT Query Params 200.yml (1)

20-28: Consider adding email assertion for consistency.

Most other OAuth1 200 fixtures include assertions for both resource.name and resource.email. This fixture only checks resource.name. Adding the email assertion would maintain test consistency.

💡 Suggested addition
 runtime:
   assertions:
     - expression: res.status
       operator: eq
       value: '200'
     - expression: res.body.resource.name
       operator: eq
       value: oauth1-test-resource
+    - expression: res.body.resource.email
+      operator: eq
+      value: oauth1@example.com
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/auth/oauth1/fixtures/collections/yml/OAuth1` PLAINTEXT Query Params
200.yml around lines 20 - 28, The fixture is missing an assertion for
resource.email; update the runtime.assertions array in the OAuth1 PLAINTEXT
Query Params 200.yml fixture (alongside the existing assertions that check
res.status and res.body.resource.name) by adding an assertion that checks
expression "res.body.resource.email" with operator "eq" and the expected email
value used across other OAuth1 200 fixtures (e.g., "oauth1-test@example.com"),
placing it after the resource.name assertion for consistency.
tests/auth/oauth1/fixtures/collections/yml/OAuth1 RSA-SHA512 200.yml (1)

20-48: PEM keys in test fixtures are properly scoped, but add secret-scan allowlist if CI scanning is implemented.

The inline PEM keys across the OAuth1 test fixtures are correctly limited to test code only and are intentional for testing the privateKey.type: text path. No current secret-scan configuration exists in the repository, so there's no immediate risk. However, when secret scanning (e.g., gitleaks, checkov) is added to CI, create a narrowly scoped allowlist for tests/auth/oauth1/fixtures/collections/ to prevent false positives on these test PEM blocks.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/auth/oauth1/fixtures/collections/yml/OAuth1` RSA-SHA512 200.yml around
lines 20 - 48, This fixture contains an inline PEM private key (the multi-line
"-----BEGIN PRIVATE KEY-----" value in the OAuth1 RSA-SHA512 200.yml fixture)
used only for tests; add a narrowly scoped secret-scan allowlist entry in your
CI secret-scanning config (e.g., gitleaks/checkov rules) that whitelists this
fixture directory or filename pattern and/or the specific YAML "value" field
pattern to suppress false positives, ensuring the rule is limited to
tests/auth/oauth1 fixtures and does not broadly exempt other files.
packages/bruno-cli/src/runner/prepare-request.js (1)

152-170: Extract OAuth1 config mapping into one helper to avoid drift.

Both branches map the same OAuth1 fields. A shared builder keeps this maintainable and prevents subtle divergence later.

♻️ Suggested refactor
+const buildOAuth1Config = (source, basePath) => {
+  return {
+    consumerKey: get(source, `${basePath}.consumerKey`),
+    consumerSecret: get(source, `${basePath}.consumerSecret`),
+    accessToken: get(source, `${basePath}.accessToken`),
+    tokenSecret: get(source, `${basePath}.tokenSecret`),
+    callbackUrl: get(source, `${basePath}.callbackUrl`),
+    verifier: get(source, `${basePath}.verifier`),
+    signatureMethod: get(source, `${basePath}.signatureMethod`),
+    privateKey: get(source, `${basePath}.privateKey`),
+    privateKeyType: get(source, `${basePath}.privateKeyType`),
+    timestamp: get(source, `${basePath}.timestamp`),
+    nonce: get(source, `${basePath}.nonce`),
+    version: get(source, `${basePath}.version`),
+    realm: get(source, `${basePath}.realm`),
+    addParamsTo: get(source, `${basePath}.addParamsTo`),
+    includeBodyHash: get(source, `${basePath}.includeBodyHash`)
+  };
+};
@@
-    if (collectionAuth.mode === 'oauth1') {
-      axiosRequest.oauth1config = {
-        consumerKey: get(collectionAuth, 'oauth1.consumerKey'),
-        consumerSecret: get(collectionAuth, 'oauth1.consumerSecret'),
-        accessToken: get(collectionAuth, 'oauth1.accessToken'),
-        tokenSecret: get(collectionAuth, 'oauth1.tokenSecret'),
-        callbackUrl: get(collectionAuth, 'oauth1.callbackUrl'),
-        verifier: get(collectionAuth, 'oauth1.verifier'),
-        signatureMethod: get(collectionAuth, 'oauth1.signatureMethod'),
-        privateKey: get(collectionAuth, 'oauth1.privateKey'),
-        privateKeyType: get(collectionAuth, 'oauth1.privateKeyType'),
-        timestamp: get(collectionAuth, 'oauth1.timestamp'),
-        nonce: get(collectionAuth, 'oauth1.nonce'),
-        version: get(collectionAuth, 'oauth1.version'),
-        realm: get(collectionAuth, 'oauth1.realm'),
-        addParamsTo: get(collectionAuth, 'oauth1.addParamsTo'),
-        includeBodyHash: get(collectionAuth, 'oauth1.includeBodyHash')
-      };
-    }
+    if (collectionAuth.mode === 'oauth1') {
+      axiosRequest.oauth1config = buildOAuth1Config(collectionAuth, 'oauth1');
+    }
@@
-    if (request.auth.mode === 'oauth1') {
-      axiosRequest.oauth1config = {
-        consumerKey: get(request, 'auth.oauth1.consumerKey'),
-        consumerSecret: get(request, 'auth.oauth1.consumerSecret'),
-        accessToken: get(request, 'auth.oauth1.accessToken'),
-        tokenSecret: get(request, 'auth.oauth1.tokenSecret'),
-        callbackUrl: get(request, 'auth.oauth1.callbackUrl'),
-        verifier: get(request, 'auth.oauth1.verifier'),
-        signatureMethod: get(request, 'auth.oauth1.signatureMethod'),
-        privateKey: get(request, 'auth.oauth1.privateKey'),
-        privateKeyType: get(request, 'auth.oauth1.privateKeyType'),
-        timestamp: get(request, 'auth.oauth1.timestamp'),
-        nonce: get(request, 'auth.oauth1.nonce'),
-        version: get(request, 'auth.oauth1.version'),
-        realm: get(request, 'auth.oauth1.realm'),
-        addParamsTo: get(request, 'auth.oauth1.addParamsTo'),
-        includeBodyHash: get(request, 'auth.oauth1.includeBodyHash')
-      };
-    }
+    if (request.auth.mode === 'oauth1') {
+      axiosRequest.oauth1config = buildOAuth1Config(request, 'auth.oauth1');
+    }

Also applies to: 218-236

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-cli/src/runner/prepare-request.js` around lines 152 - 170, The
OAuth1 mapping is duplicated; extract a helper (e.g.,
buildOAuth1Config(collectionAuth)) that reads oauth1.* keys using get(...) and
returns the config object, then replace both occurrences (the branch where
collectionAuth.mode === 'oauth1' that assigns axiosRequest.oauth1config and the
similar block around lines 218-236) with a call to that helper so both branches
use the same builder and avoid drift.
packages/bruno-tests/src/auth/oauth1/index.js (2)

244-250: Redundant algoMap mapping.

The map keys and values are identical. You can simplify by using signatureMethod directly.

♻️ Simplify signature verification
     case 'RSA-SHA1':
     case 'RSA-SHA256':
     case 'RSA-SHA512': {
-      const algoMap = { 'RSA-SHA1': 'RSA-SHA1', 'RSA-SHA256': 'RSA-SHA256', 'RSA-SHA512': 'RSA-SHA512' };
-      const verifier = crypto.createVerify(algoMap[signatureMethod]);
+      const verifier = crypto.createVerify(signatureMethod);
       verifier.update(baseString);
       return verifier.verify(TEST_RSA_PUBLIC_KEY, signature, 'base64');
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-tests/src/auth/oauth1/index.js` around lines 244 - 250, The
RSA branch creates an unnecessary algoMap with identical keys/values; simplify
the code by calling crypto.createVerify(signatureMethod) directly, keep the
existing verifier.update(baseString) and return
verifier.verify(TEST_RSA_PUBLIC_KEY, signature, 'base64') so the logic around
signatureMethod, baseString, TEST_RSA_PUBLIC_KEY, and verifier.verify remains
unchanged but without the redundant mapping.

557-559: Prefer named exports or consistent module.exports pattern.

The current pattern assigns to module.exports then adds a property. Consider using a single object export for clarity.

♻️ Consolidate exports
-module.exports = router;
-module.exports.TEST_RSA_PRIVATE_KEY = TEST_RSA_PRIVATE_KEY;
+module.exports = router;
+module.exports.TEST_RSA_PRIVATE_KEY = TEST_RSA_PRIVATE_KEY;

Alternatively, use a cleaner pattern:

router.TEST_RSA_PRIVATE_KEY = TEST_RSA_PRIVATE_KEY;
module.exports = router;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-tests/src/auth/oauth1/index.js` around lines 557 - 559, The
file currently assigns module.exports = router and then sets
module.exports.TEST_RSA_PRIVATE_KEY separately; fix this by exporting
consistently with a single object or attaching the key to the router before
exporting. Update the code so either router.TEST_RSA_PRIVATE_KEY =
TEST_RSA_PRIVATE_KEY and then module.exports = router, or replace both lines
with module.exports = { router, TEST_RSA_PRIVATE_KEY } (choosing the pattern
used elsewhere), ensuring references to router and TEST_RSA_PRIVATE_KEY remain
valid.
tests/auth/oauth1/oauth1-runner.spec.ts (1)

13-21: Potential race condition when writing PEM file at module load.

If multiple test workers load this module simultaneously, concurrent existsSync/writeFileSync calls could race. Consider using writeFileSync with a flag like { flag: 'wx' } to fail if file exists, or move setup to a fixture/hook.

♻️ Safer file write
 for (const subdir of ['bru', 'yml']) {
   const pemPath = path.join(fixtureBase, subdir, 'test-private-key.pem');
-  if (!fs.existsSync(pemPath)) {
-    fs.writeFileSync(pemPath, TEST_RSA_PRIVATE_KEY);
-  }
+  try {
+    fs.writeFileSync(pemPath, TEST_RSA_PRIVATE_KEY, { flag: 'wx' });
+  } catch (err) {
+    // File already exists, which is fine
+    if (err.code !== 'EEXIST') throw err;
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/auth/oauth1/oauth1-runner.spec.ts` around lines 13 - 21, The
module-level setup that writes TEST_RSA_PRIVATE_KEY to pemPath can race across
parallel test workers; update the logic around TEST_RSA_PRIVATE_KEY/pemPath to
perform an atomic create-or-ignore instead of existsSync then writeFileSync:
attempt to write using writeFileSync with a failing-when-exists flag (e.g., {
flag: 'wx' }) wrapped in a try/catch that ignores EEXIST, or move the write into
a test fixture/hook that runs single-threaded before tests; ensure you reference
and update the code touching TEST_RSA_PRIVATE_KEY and pemPath in
oauth1-runner.spec.ts so concurrent loads don't clobber each other.
packages/bruno-app/src/components/RequestPane/Auth/OAuth1/index.js (2)

103-105: Use path.basename for cross-platform filename extraction.

Manual splitting with / and \\ works but the codebase already imports a path utility. Using path.basename is more robust and consistent with OS-agnostic guidelines.

♻️ Suggested fix
-  const fileName = isFileRef ? privateKeyValue.split('/').pop().split('\\').pop() : '';
+  const fileName = isFileRef ? path.basename(privateKeyValue) : '';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-app/src/components/RequestPane/Auth/OAuth1/index.js` around
lines 103 - 105, Replace the manual split logic that derives fileName from
privateKeyValue with a cross-platform basename call: when computing fileName
(currently using privateKeyValue.split('/').pop().split('\\').pop()), import/use
the existing path utility and call path.basename(privateKeyValue) when isFileRef
is true (keep privateKeyValue and isFileRef checks as-is) so filename extraction
is robust across OSes.

287-304: Potential double-toggle on checkbox interaction.

The <input> has onChange and the <label> has onClick with e.preventDefault() — but clicking the checkbox directly still triggers onChange. The label's onClick also fires when clicking the checkbox (due to label association), but preventDefault() on the label doesn't prevent the checkbox's native change. This should work correctly, but the pattern is unusual.

A cleaner approach: wrap both in a standard <label> and rely solely on onChange.

♻️ Simplified pattern
-        <div className="flex items-center gap-2">
-          <input
-            type="checkbox"
-            checked={oauth1.includeBodyHash || false}
-            onChange={(e) => handleChange('includeBodyHash', e.target.checked)}
-          />
-          <label
-            className="block cursor-pointer"
-            onClick={(e) => {
-              e.preventDefault(); handleChange('includeBodyHash', !oauth1.includeBodyHash);
-            }}
-          >
-            Include Body Hash
-          </label>
-        </div>
+        <label className="flex items-center gap-2 cursor-pointer">
+          <input
+            type="checkbox"
+            checked={oauth1.includeBodyHash || false}
+            onChange={(e) => handleChange('includeBodyHash', e.target.checked)}
+          />
+          <span>Include Body Hash</span>
+        </label>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-app/src/components/RequestPane/Auth/OAuth1/index.js` around
lines 287 - 304, The checkbox currently handles toggling in two places (input
onChange and label onClick) which can double-trigger; simplify by using a single
control: remove the label's onClick and e.preventDefault(), wrap the <input> and
text inside one <label> (or associate via htmlFor/id) and rely only on the
input's onChange to call handleChange('includeBodyHash', e.target.checked);
reference oauth1.includeBodyHash and the handleChange call to locate where to
change this.
tests/auth/oauth1/oauth1.spec.ts (1)

127-134: Consider a more specific checkbox selector.

page.locator('input[type="checkbox"]') could match multiple checkboxes if more are added to the OAuth1 form. Consider scoping it to the "Include Body Hash" row.

♻️ Suggested selector refinement
-      const checkbox = page.locator('input[type="checkbox"]');
-      const bodyHashLabel = page.locator('label').filter({ hasText: 'Include Body Hash' });
+      const bodyHashRow = fieldRow(page, 'Include Body Hash');
+      const checkbox = bodyHashRow.locator('input[type="checkbox"]');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/auth/oauth1/oauth1.spec.ts` around lines 127 - 134, The selector for
the checkbox is too broad and may match multiple inputs; scope the checkbox
lookup to the "Include Body Hash" row by finding the input relative to the
bodyHashLabel instead of a global input[type="checkbox"]. Replace the standalone
checkbox locator with something like using
bodyHashLabel.locator('input[type="checkbox"]') or
bodyHashLabel.locator('input') (or use a selector that targets the row/container
for the "Include Body Hash" label) so the test uses the checkbox tied to the
bodyHashLabel variable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/bruno-app/src/components/RequestPane/WSRequestPane/WSAuth/index.js`:
- Around line 82-92: The oauth1 UI branch in the WSAuth component is never
reachable because supportedAuthModes (checked in the useEffect that resets mode)
does not include 'oauth1' and thus the effect immediately sets mode to 'none';
fix by either adding 'oauth1' to the supportedAuthModes array or changing the
useEffect logic in WSAuth (the effect that compares current mode against
supportedAuthModes and calls setMode('none')) to preserve 'oauth1' when the
component renders the oauth1 case (or only reset when mode is truly
unsupported), so the oauth1 case in the switch can actually persist and display.

In `@packages/bruno-app/src/utils/codegenerator/auth.js`:
- Around line 58-69: The current function returns an Authorization header string
containing literal placeholders ("<signature>", "<timestamp>", "<nonce>") which
produces invalid OAuth1 auth; update the logic in the auth generation path
(where oauth1Config is read and consumerKey, token, signatureMethod,
oauthVersion are defined) to either (A) compute a proper OAuth1 signature and
runtime oauth_timestamp and oauth_nonce for the cloned request and inject those
concrete values into the Authorization value, or (B) mark OAuth1 unsupported by
returning enabled: false (or an explicit "Unsupported: OAuth1 requires runtime
signing" value) instead of emitting a misleading template; modify the code that
builds the header object so Authorization never contains raw placeholder tokens.

In `@packages/bruno-cli/src/runner/interpolate-vars.js`:
- Around line 280-293: The oauth1config.privateKey field may be an object (RSA
config) whose nested string properties need interpolation; update the
interpolation step for request.oauth1config.privateKey to detect if it's an
object and recursively walk its properties, applying _interpolate to every
string leaf (preserving non-string values), otherwise fall back to
_interpolate(... ) || ''. Modify the block handling
request.oauth1config.privateKey to call a small helper that uses the existing
_interpolate function to transform nested string fields in-place (or return the
interpolated value) so nested {{...}} templates are resolved before signing.

In `@packages/bruno-electron/src/ipc/network/interpolate-vars.js`:
- Around line 364-378: The OAuth1 interpolation block misses interpolating
addParamsTo and includeBodyHash which can change signing behavior; inside the
request.oauth1config handling (where _interpolate is used for consumerKey,
consumerSecret, accessToken, tokenSecret, callbackUrl, verifier,
signatureMethod, privateKey, timestamp, nonce, version, realm) add lines to set
request.oauth1config.addParamsTo =
_interpolate(request.oauth1config.addParamsTo) ||
request.oauth1config.addParamsTo || '' and request.oauth1config.includeBodyHash
= _interpolate(request.oauth1config.includeBodyHash) ||
request.oauth1config.includeBodyHash || '' so both fields respect
variable-driven values and fall back to existing config.

In `@packages/bruno-lang/v2/tests/oauth1.spec.js`:
- Around line 742-775: The test "should survive round-trip with multiline PEM
private key" uses a PEM-like literal in the pem variable that trips scanners;
replace that hardcoded-looking key with a clearly fake marker or generate it at
runtime instead. Update the pem assignment (the pem constant) used in the json
object passed to jsonToBru/bruToJson so it contains a non-sensitive placeholder
(e.g., include "FAKE" or "TEST-KEY" inside the BEGIN/END block) or call a small
helper that constructs a dummy multiline PEM for the test; keep all assertions
(expect(parsed.auth.oauth1.privateKey) and privateKeyType) unchanged. Ensure the
new value still contains line breaks so the round-trip behavior for multiline
strings is exercised.

In `@packages/bruno-requests/src/auth/oauth1-request-authorization.ts`:
- Around line 45-54: The code currently forces oauth_version to '1.0' via a
truthy fallback (e.g. using version || '1.0'), which prevents preserving an
explicitly empty oauth_version and changes the signed parameter set; update the
logic that builds OAuth parameters (the places that reference oauth_version and
any helper like the OAuth1 parameter construction) to only default to '1.0' when
oauth_version is undefined or null (not when it's an empty string), and apply
the same change to the other occurrences that use the truthy fallback so empty
values are preserved in the signed parameters while undefined still falls back
to '1.0'.
- Around line 428-453: The 'body' branch currently overwrites non-form payloads
by initializing URLSearchParams(''), which can drop JSON/raw bodies or add
bodies to GET/HEAD; update the 'body' case in the switch (the code using
isFormUrlEncoded, request.data, URLSearchParams, ctKey and addParamsTo) to first
validate that the request is form-urlencoded and not a GET/HEAD: if
isFormUrlEncoded is false or request.method is GET/HEAD, throw or return an
error indicating OAuth body placement is invalid for non-form requests; only
when isFormUrlEncoded is true should you build URLSearchParams, set request.data
and (if needed) set Content-Type via ctKey or 'Content-Type'.

In `@tests/auth/oauth1/fixtures/collections/yml/OAuth1` HMAC-SHA1 Body JSON
200.yml:
- Around line 17-21: The fixture currently uses addParamsTo: body together with
body.type: json (and includeBodyHash: false), which can cause OAuth params to be
ignored; update the fixture so that either set addParamsTo to header to sign
JSON payloads via the Authorization header, or change body.type from json to
form (form-encoded) when you intend to exercise body-parameter signing; adjust
the addParamsTo and/or body.type entries accordingly to ensure consistent
body-auth behavior.

In `@tests/auth/oauth1/fixtures/collections/yml/OAuth1` RSA-SHA1 Query Params
200.yml:
- Around line 18-48: The YAML fixture contains an embedded RSA private key under
the privateKey field which triggers secret scanners; remove the inline PEM and
either (a) replace it with a reference to a test-only key loader (e.g.,
loadPrivateKeyFromEnv or generateTestKeyAtRuntime) so the key is created/loaded
outside source control, or (b) if you must include it in the PR add the
repository secret-scanner allowlist entry for this fixture in the same PR;
update any tests that consume privateKey to read from the new loader/generator
or env variable instead of the inline PEM.

---

Nitpick comments:
In `@packages/bruno-app/src/components/RequestPane/Auth/OAuth1/index.js`:
- Around line 103-105: Replace the manual split logic that derives fileName from
privateKeyValue with a cross-platform basename call: when computing fileName
(currently using privateKeyValue.split('/').pop().split('\\').pop()), import/use
the existing path utility and call path.basename(privateKeyValue) when isFileRef
is true (keep privateKeyValue and isFileRef checks as-is) so filename extraction
is robust across OSes.
- Around line 287-304: The checkbox currently handles toggling in two places
(input onChange and label onClick) which can double-trigger; simplify by using a
single control: remove the label's onClick and e.preventDefault(), wrap the
<input> and text inside one <label> (or associate via htmlFor/id) and rely only
on the input's onChange to call handleChange('includeBodyHash',
e.target.checked); reference oauth1.includeBodyHash and the handleChange call to
locate where to change this.

In `@packages/bruno-cli/src/runner/prepare-request.js`:
- Around line 152-170: The OAuth1 mapping is duplicated; extract a helper (e.g.,
buildOAuth1Config(collectionAuth)) that reads oauth1.* keys using get(...) and
returns the config object, then replace both occurrences (the branch where
collectionAuth.mode === 'oauth1' that assigns axiosRequest.oauth1config and the
similar block around lines 218-236) with a call to that helper so both branches
use the same builder and avoid drift.

In `@packages/bruno-tests/src/auth/oauth1/index.js`:
- Around line 244-250: The RSA branch creates an unnecessary algoMap with
identical keys/values; simplify the code by calling
crypto.createVerify(signatureMethod) directly, keep the existing
verifier.update(baseString) and return verifier.verify(TEST_RSA_PUBLIC_KEY,
signature, 'base64') so the logic around signatureMethod, baseString,
TEST_RSA_PUBLIC_KEY, and verifier.verify remains unchanged but without the
redundant mapping.
- Around line 557-559: The file currently assigns module.exports = router and
then sets module.exports.TEST_RSA_PRIVATE_KEY separately; fix this by exporting
consistently with a single object or attaching the key to the router before
exporting. Update the code so either router.TEST_RSA_PRIVATE_KEY =
TEST_RSA_PRIVATE_KEY and then module.exports = router, or replace both lines
with module.exports = { router, TEST_RSA_PRIVATE_KEY } (choosing the pattern
used elsewhere), ensuring references to router and TEST_RSA_PRIVATE_KEY remain
valid.

In `@tests/auth/oauth1/fixtures/collections/bru/OAuth1` PLAINTEXT Query Params
200.bru:
- Around line 29-32: Add an email assertion to the existing response assertions:
inside the assert block that currently checks res.status and
res.body.resource.name (the assertions referencing res.status and
res.body.resource.name: eq oauth1-test-resource), add a line asserting
res.body.resource.email: eq oauth1@example.com so this PLAINTEXT fixture matches
the parity of the HMAC-SHA1 200.bru fixture.

In `@tests/auth/oauth1/fixtures/collections/bru/OAuth1` RSA-SHA1 Body 200.bru:
- Around line 20-49: The RSA PEM private_key block in the OAuth1 RSA-SHA1
fixture is duplicated; extract the PEM into a single shared fixture (e.g., a
shared "rsa_private_key" fixture) and replace the inline private_key value in
OAuth1 RSA-SHA1 Body 200.bru with a reference to that shared fixture; update any
other OAuth1 RSA fixtures to reference the same shared symbol so all tests
consume the centralized key and eliminate copy/paste drift.

In `@tests/auth/oauth1/fixtures/collections/yml/OAuth1` HMAC-SHA1 Body JSON
200.yml:
- Around line 11-14: Replace the realistic-looking credential values in the
fixture by using clearly fake placeholder literals for the keys consumerKey,
consumerSecret, accessToken, and tokenSecret (for example values like
"fake_consumer_key", "fake_consumer_secret", "fake_access_token",
"fake_token_secret") so the test remains meaningful but won't trigger secret
scanners; update the corresponding entries in the OAuth1 HMAC-SHA1 Body JSON
fixture to use those fake placeholders.

In `@tests/auth/oauth1/fixtures/collections/yml/OAuth1` HMAC-SHA256 Body 200.yml:
- Around line 22-30: The 200 fixture is missing the email assertion for
consistency with other OAuth1 200 fixtures; add an assertion entry under
runtime.assertions that checks res.body.resource.email equals oauth1@example.com
by inserting an assertion with expression: res.body.resource.email, operator:
eq, and value: oauth1@example.com (matching the existing assertion style used
for res.body.resource.name).

In `@tests/auth/oauth1/fixtures/collections/yml/OAuth1` PLAINTEXT Query Params
200.yml:
- Around line 20-28: The fixture is missing an assertion for resource.email;
update the runtime.assertions array in the OAuth1 PLAINTEXT Query Params 200.yml
fixture (alongside the existing assertions that check res.status and
res.body.resource.name) by adding an assertion that checks expression
"res.body.resource.email" with operator "eq" and the expected email value used
across other OAuth1 200 fixtures (e.g., "oauth1-test@example.com"), placing it
after the resource.name assertion for consistency.

In `@tests/auth/oauth1/fixtures/collections/yml/OAuth1` RSA-SHA1 200.yml:
- Around line 9-17: Add the missing consumerSecret field to the auth block so
the fixture matches the schema used by other OAuth1 RSA-SHA1 fixtures; update
the auth map by adding consumerSecret: '' alongside consumerKey, accessToken,
tokenSecret, signatureMethod, version, addParamsTo, and includeBodyHash to
ensure consistent schema coverage.

In `@tests/auth/oauth1/fixtures/collections/yml/OAuth1` RSA-SHA1 Body 200.yml:
- Around line 53-61: This fixture's runtime assertions only check
res.body.resource.name but should also assert res.body.resource.email for
consistency with other OAuth1 200 fixtures; update the assertions array in the
YAML (the block containing the expressions "res.body.resource.name" and
"res.status") to add a new assertion whose expression is
"res.body.resource.email", operator "eq", and value matching the expected test
email used elsewhere in OAuth1 fixtures (use the same email literal used by
HMAC-SHA1/RSA-SHA1 fixtures) so the test verifies both name and email.

In `@tests/auth/oauth1/fixtures/collections/yml/OAuth1` RSA-SHA512 200.yml:
- Around line 20-48: This fixture contains an inline PEM private key (the
multi-line "-----BEGIN PRIVATE KEY-----" value in the OAuth1 RSA-SHA512 200.yml
fixture) used only for tests; add a narrowly scoped secret-scan allowlist entry
in your CI secret-scanning config (e.g., gitleaks/checkov rules) that whitelists
this fixture directory or filename pattern and/or the specific YAML "value"
field pattern to suppress false positives, ensuring the rule is limited to
tests/auth/oauth1 fixtures and does not broadly exempt other files.

In `@tests/auth/oauth1/oauth1-runner.spec.ts`:
- Around line 13-21: The module-level setup that writes TEST_RSA_PRIVATE_KEY to
pemPath can race across parallel test workers; update the logic around
TEST_RSA_PRIVATE_KEY/pemPath to perform an atomic create-or-ignore instead of
existsSync then writeFileSync: attempt to write using writeFileSync with a
failing-when-exists flag (e.g., { flag: 'wx' }) wrapped in a try/catch that
ignores EEXIST, or move the write into a test fixture/hook that runs
single-threaded before tests; ensure you reference and update the code touching
TEST_RSA_PRIVATE_KEY and pemPath in oauth1-runner.spec.ts so concurrent loads
don't clobber each other.

In `@tests/auth/oauth1/oauth1.spec.ts`:
- Around line 127-134: The selector for the checkbox is too broad and may match
multiple inputs; scope the checkbox lookup to the "Include Body Hash" row by
finding the input relative to the bodyHashLabel instead of a global
input[type="checkbox"]. Replace the standalone checkbox locator with something
like using bodyHashLabel.locator('input[type="checkbox"]') or
bodyHashLabel.locator('input') (or use a selector that targets the row/container
for the "Include Body Hash" label) so the test uses the checkbox tied to the
bodyHashLabel variable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7f137db4-7acc-478d-8726-4950f375cce5

📥 Commits

Reviewing files that changed from the base of the PR and between 994d51b and 4d3a7b8.

📒 Files selected for processing (93)
  • package.json
  • packages/bruno-app/src/components/CollectionSettings/Auth/AuthMode/index.js
  • packages/bruno-app/src/components/CollectionSettings/Auth/Oauth1/index.js
  • packages/bruno-app/src/components/CollectionSettings/Auth/index.js
  • packages/bruno-app/src/components/FolderSettings/Auth/index.js
  • packages/bruno-app/src/components/FolderSettings/AuthMode/index.js
  • packages/bruno-app/src/components/RequestPane/Auth/AuthMode/index.js
  • packages/bruno-app/src/components/RequestPane/Auth/OAuth1/StyledWrapper.js
  • packages/bruno-app/src/components/RequestPane/Auth/OAuth1/index.js
  • packages/bruno-app/src/components/RequestPane/Auth/index.js
  • packages/bruno-app/src/components/RequestPane/WSRequestPane/WSAuth/index.js
  • packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
  • packages/bruno-app/src/utils/codegenerator/auth.js
  • packages/bruno-app/src/utils/collections/index.js
  • packages/bruno-cli/src/runner/interpolate-vars.js
  • packages/bruno-cli/src/runner/prepare-request.js
  • packages/bruno-cli/src/runner/run-single-request.js
  • packages/bruno-converters/src/opencollection/common/auth.ts
  • packages/bruno-converters/src/opencollection/types.ts
  • packages/bruno-converters/src/postman/postman-to-bruno.js
  • packages/bruno-electron/src/ipc/network/index.js
  • packages/bruno-electron/src/ipc/network/interpolate-vars.js
  • packages/bruno-electron/src/ipc/network/prepare-grpc-request.js
  • packages/bruno-electron/src/ipc/network/prepare-request.js
  • packages/bruno-filestore/src/formats/yml/common/auth.ts
  • packages/bruno-js/src/bruno-request.js
  • packages/bruno-lang/v2/src/bruToJson.js
  • packages/bruno-lang/v2/src/collectionBruToJson.js
  • packages/bruno-lang/v2/src/jsonToBru.js
  • packages/bruno-lang/v2/src/jsonToCollectionBru.js
  • packages/bruno-lang/v2/tests/oauth1.spec.js
  • packages/bruno-requests/package.json
  • packages/bruno-requests/src/auth/index.ts
  • packages/bruno-requests/src/auth/oauth1-request-authorization.spec.ts
  • packages/bruno-requests/src/auth/oauth1-request-authorization.ts
  • packages/bruno-requests/src/index.ts
  • packages/bruno-schema-types/src/common/auth.ts
  • packages/bruno-schema/src/collections/index.js
  • packages/bruno-tests/src/auth/index.js
  • packages/bruno-tests/src/auth/oauth1/index.js
  • playwright.config.ts
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 HMAC-SHA1 200.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 HMAC-SHA1 401.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 HMAC-SHA1 Body 200.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 HMAC-SHA1 Body JSON 200.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 HMAC-SHA1 POST 200.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 HMAC-SHA1 Query Params 200.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 HMAC-SHA256 200.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 HMAC-SHA256 401.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 HMAC-SHA256 Body 200.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 HMAC-SHA512 200.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 HMAC-SHA512 401.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 PLAINTEXT 200.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 PLAINTEXT 401.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 PLAINTEXT Body 200.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 PLAINTEXT Query Params 200.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 RSA-SHA1 200.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 RSA-SHA1 Body 200.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 RSA-SHA1 File Key 200.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 RSA-SHA1 Query Params 200.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 RSA-SHA1 Variable Key 200.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 RSA-SHA256 200.bru
  • tests/auth/oauth1/fixtures/collections/bru/OAuth1 RSA-SHA512 200.bru
  • tests/auth/oauth1/fixtures/collections/bru/bruno.json
  • tests/auth/oauth1/fixtures/collections/bru/environments/Local.bru
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 HMAC-SHA1 200.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 HMAC-SHA1 401.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 HMAC-SHA1 Body 200.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 HMAC-SHA1 Body JSON 200.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 HMAC-SHA1 POST 200.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 HMAC-SHA1 Query Params 200.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 HMAC-SHA256 200.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 HMAC-SHA256 401.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 HMAC-SHA256 Body 200.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 HMAC-SHA512 200.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 HMAC-SHA512 401.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 PLAINTEXT 200.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 PLAINTEXT 401.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 PLAINTEXT Body 200.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 PLAINTEXT Query Params 200.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 RSA-SHA1 200.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 RSA-SHA1 Body 200.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 RSA-SHA1 File Key 200.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 RSA-SHA1 Query Params 200.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 RSA-SHA1 Variable Key 200.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 RSA-SHA256 200.yml
  • tests/auth/oauth1/fixtures/collections/yml/OAuth1 RSA-SHA512 200.yml
  • tests/auth/oauth1/fixtures/collections/yml/environments/Local.yml
  • tests/auth/oauth1/fixtures/collections/yml/opencollection.yml
  • tests/auth/oauth1/init-user-data/preferences.json
  • tests/auth/oauth1/oauth1-runner.spec.ts
  • tests/auth/oauth1/oauth1.spec.ts
  • tests/utils/page/actions.ts

Comment on lines +428 to +453
switch (addParamsTo || 'header') {
case 'header':
request.headers['Authorization'] = authorizer.toHeader(oauthData).Authorization;
break;
case 'queryparams': {
const url = new URL(request.url);
Object.entries(oauthData).forEach(([key, value]) => {
if (value) url.searchParams.set(key, value);
});
request.url = url.toString();
break;
}
case 'body': {
const params = new URLSearchParams(isFormUrlEncoded ? request.data : '');
Object.entries(oauthData).forEach(([key, value]) => {
if (value !== undefined) params.set(key, value);
});
request.data = params.toString();

if (!isFormUrlEncoded) {
if (ctKey) {
request.headers[ctKey] = 'application/x-www-form-urlencoded';
} else {
request.headers['Content-Type'] = 'application/x-www-form-urlencoded';
}
}
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

Reject body placement for non-form requests instead of rewriting the payload.

Line 441 initializes URLSearchParams from '' whenever the request is not form-urlencoded, so JSON/raw payloads are discarded and even GET/HEAD requests can gain a synthetic form body. This should fail fast instead of silently mutating the request into something else.

🛠 Suggested fix
     case 'body': {
-      const params = new URLSearchParams(isFormUrlEncoded ? request.data : '');
+      if (!hasBody || !isFormUrlEncoded) {
+        throw new Error('OAuth1 body placement requires a non-GET/HEAD application/x-www-form-urlencoded request');
+      }
+
+      const params = new URLSearchParams(typeof request.data === 'string' ? request.data : '');
       Object.entries(oauthData).forEach(([key, value]) => {
         if (value !== undefined) params.set(key, value);
       });
       request.data = params.toString();
-
-      if (!isFormUrlEncoded) {
-        if (ctKey) {
-          request.headers[ctKey] = 'application/x-www-form-urlencoded';
-        } else {
-          request.headers['Content-Type'] = 'application/x-www-form-urlencoded';
-        }
-      }
       break;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-requests/src/auth/oauth1-request-authorization.ts` around
lines 428 - 453, The 'body' branch currently overwrites non-form payloads by
initializing URLSearchParams(''), which can drop JSON/raw bodies or add bodies
to GET/HEAD; update the 'body' case in the switch (the code using
isFormUrlEncoded, request.data, URLSearchParams, ctKey and addParamsTo) to first
validate that the request is form-urlencoded and not a GET/HEAD: if
isFormUrlEncoded is false or request.method is GET/HEAD, throw or return an
error indicating OAuth body placement is invalid for non-form requests; only
when isFormUrlEncoded is true should you build URLSearchParams, set request.data
and (if needed) set Content-Type via ctKey or 'Content-Type'.

Comment on lines +17 to +21
addParamsTo: body
includeBodyHash: false
body:
type: json
json: '{"test":"data"}'
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

addParamsTo: body with type: json is a risky auth-placement combination.

This can lead to OAuth params being ignored or injected in a way that doesn’t reliably validate body-auth behavior. Prefer signing via header for JSON payload fixtures, or switch body type to form-encoded for body-param tests.

Suggested fixture adjustment (JSON body case)
-    addParamsTo: body
+    addParamsTo: header
     includeBodyHash: false
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/auth/oauth1/fixtures/collections/yml/OAuth1` HMAC-SHA1 Body JSON
200.yml around lines 17 - 21, The fixture currently uses addParamsTo: body
together with body.type: json (and includeBodyHash: false), which can cause
OAuth params to be ignored; update the fixture so that either set addParamsTo to
header to sign JSON payloads via the Authorization header, or change body.type
from json to form (form-encoded) when you intend to exercise body-parameter
signing; adjust the addParamsTo and/or body.type entries accordingly to ensure
consistent body-auth behavior.

…ests

Avoid tripping secret scanners by using obviously fake BEGIN/END markers
and non-sensitive base64 content in serialization and round-trip tests.
OAuth1 requires runtime-computed nonce, timestamp, and signature that
cannot be pre-computed for a static code snippet. Return an empty array
instead of emitting an Authorization header with literal <signature>,
<timestamp>, <nonce> placeholders.
The oauth1 switch branch was dead code since it was not in
supportedAuthModes and the useEffect would reset it to 'none'
before it could render.
bearer: null,
digest: null,
ntlm: null,
oauth2: null,
Copy link
Collaborator

Choose a reason for hiding this comment

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

@lohit-bruno don't we have to set oauth1 to null here

bearer: null,
digest: null,
ntlm: null,
oauth2: null,
Copy link
Collaborator

Choose a reason for hiding this comment

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

@lohit-bruno don't er have to default oauth1 to null, like we are doing for oauth2?

});

test('Verify Add Params To placement via timeline', async ({ pageWithUserData: page }) => {
test.setTimeout(3 * 60 * 1000);
Copy link
Collaborator

Choose a reason for hiding this comment

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

@lohit-bruno we can case the value to a constant, and reuse it everywhere

};

const selectRequestPaneTab = async (page: Page, tabName: string) => {
await selectPaneTab(page, '.request-pane > .px-4', tabName);
Copy link
Collaborator

Choose a reason for hiding this comment

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

@lohit-bruno is it possible to add a better selector for request and response panes? may be we can use testIds

const result = collectionBruToJson(input);

expect(result.auth.oauth1.privateKeyType).toBe('text');
expect(result.auth.oauth1.privateKey).toContain('-----BEGIN FAKE RSA TEST KEY-----');
Copy link
Collaborator

Choose a reason for hiding this comment

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

@lohit-bruno should we also assert with the entire key itself? just to make sure than the initial spaces are not included in the final key.

Copy link
Collaborator

@sanish-bruno sanish-bruno left a comment

Choose a reason for hiding this comment

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

PR Review

Found 5 issue(s): 0 major, 2 minor, 1 suggestion, 2 nits.

Overall this is a very well-structured PR. The OAuth 1.0 implementation follows RFC 5849 carefully, has comprehensive unit and E2E tests covering all signature methods and parameter placements, and follows the existing patterns in the codebase for adding new auth types. The Bru format serialization/deserialization and round-trip tests are thorough.

Summary

  • Test fixture OAuth1 RSA-SHA1 200.bru contains what appears to be accidental debug data in vars:pre-request
  • Debug console.log left in OAuth1 HMAC-SHA1 Body 200.bru fixture
  • Module-level privateKeyCache has no eviction strategy
  • Silent input rejection in handlePrivateKeyChange for @file( values lacks user feedback
  • Good incidental fix of missing break in NTLM case in prepare-request.js

include_body_hash: false
}

vars:pre-request {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: This vars:pre-request block has an empty variable name and a ~3KB repeating string of token_secret_1. This looks like accidentally pasted debug data. The variable is never used (no name) and adds unnecessary noise to the fixture.

Suggested fix: Remove the entire vars:pre-request block.


script:post-response {
console.log(req.getBody());
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: This console.log(req.getBody()) looks like leftover debug output.

Suggested fix: Remove the script:post-response block, or replace with a meaningful assertion.

import crypto from 'node:crypto';
import fs from 'node:fs';
import nodePath from 'node:path';

Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggestion: privateKeyCache is a module-level Map that never evicts entries. In a long-running Electron process, if users switch between collections with different private key files, stale entries accumulate indefinitely. The mtime-based invalidation handles content changes correctly, but entries for deleted or no-longer-used files are never removed.

Consider adding a simple max-size cap (e.g., evict oldest entry when the map exceeds 50 entries), or clearing the cache when a collection is closed.

};

const handlePrivateKeyChange = (val) => {
if (val && /^@file\(/.test(val.trim())) return;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Bug (Minor): This silently swallows any value starting with @file(, giving the user no feedback that their input was rejected. This is necessary to prevent @file() markers from being entered via the text editor (since they're a serialization-level construct), but the silent return is confusing UX.

Suggested fix: Consider showing a brief toast or visual indicator explaining that file references should use the "Upload File" button instead.

password: get(request, 'auth.ntlm.password'),
domain: get(request, 'auth.ntlm.domain')
};
break;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Bug (Minor): Good catch — this break was missing from the pre-existing ntlm case, causing it to fall through to the next case. Worth calling out in the PR description as an incidental fix, since it affects existing NTLM auth behavior (the fall-through would have caused the oauth1 case to also execute after ntlm).

password: authValues.password || ''
};
break;
case AUTH_TYPES.OAUTH1:
Copy link
Collaborator

Choose a reason for hiding this comment

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

@lohit-bruno can we have few test cases that verify the oauth1 import from postman is working as expected

Copy link
Collaborator

@sreelakshmi-bruno sreelakshmi-bruno left a comment

Choose a reason for hiding this comment

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

When using OAuth 1.0 authentication with addParamsTo: body and a GET request with a form-urlencoded body, the body parameters are not included in the signature calculation, but they ARE sent in the request body. This causes signature verification to fail on the server.

Steps to Reproduce

  1. Create a request with:

    • Method: GET
    • Body: Form URL Encoded with foo=bar
    • Auth: OAuth 1.0
    • Add Params To: Body
  2. Send the request to an OAuth 1.0 protected endpoint

  3. Observe 401 Unauthorized response

Expected Behavior

Body parameters (foo=bar) should be included in the OAuth signature base string when addParamsTo: body is selected, regardless of HTTP method. Since Bruno sends the body even for GET requests, the signature should include those parameters.

Copy link
Collaborator

@sreelakshmi-bruno sreelakshmi-bruno left a comment

Choose a reason for hiding this comment

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

Can you also add this in Bruno to Postman conversion?

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.

3 participants