From 2dd0424d8fe34702f75a37421382dccf9f500b74 Mon Sep 17 00:00:00 2001 From: Sanjai Kumar <161328623+sanjaikumar-bruno@users.noreply.github.com> Date: Thu, 10 Apr 2025 14:49:21 +0530 Subject: [PATCH] Add @usebruno/requests package with digest authentication support (#4417) * Add @usebruno/requests package with digest authentication support --------- Co-authored-by: sanjai0py Co-authored-by: ramki-bruno --- .github/workflows/tests.yml | 2 + package-lock.json | 23 +++++++++++- package.json | 4 +- packages/bruno-cli/package.json | 1 + .../bruno-cli/src/runner/prepare-request.js | 14 +++++++ .../src/runner/run-single-request.js | 7 +++- packages/bruno-electron/package.json | 1 + .../bruno-electron/src/ipc/network/index.js | 2 +- packages/bruno-requests/.gitignore | 22 +++++++++++ packages/bruno-requests/package.json | 32 ++++++++++++++++ packages/bruno-requests/rollup.config.js | 37 +++++++++++++++++++ .../src/auth}/digestauth-helper.js | 4 +- packages/bruno-requests/src/auth/index.ts | 1 + packages/bruno-requests/src/index.ts | 1 + packages/bruno-requests/tsconfig.json | 21 +++++++++++ .../auth/digest/Digest Auth 200.bru | 21 +++++++++++ .../auth/digest/Digest Auth 401.bru | 20 ++++++++++ .../collection/auth/digest/folder.bru | 3 ++ 18 files changed, 209 insertions(+), 7 deletions(-) create mode 100644 packages/bruno-requests/.gitignore create mode 100644 packages/bruno-requests/package.json create mode 100644 packages/bruno-requests/rollup.config.js rename packages/{bruno-electron/src/ipc/network => bruno-requests/src/auth}/digestauth-helper.js (97%) create mode 100644 packages/bruno-requests/src/auth/index.ts create mode 100644 packages/bruno-requests/src/index.ts create mode 100644 packages/bruno-requests/tsconfig.json create mode 100644 packages/bruno-tests/collection/auth/digest/Digest Auth 200.bru create mode 100644 packages/bruno-tests/collection/auth/digest/Digest Auth 401.bru create mode 100644 packages/bruno-tests/collection/auth/digest/folder.bru diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6f6be0570..e733dab4d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -29,6 +29,7 @@ jobs: npm run build --workspace=packages/bruno-query npm run sandbox:bundle-libraries --workspace=packages/bruno-js npm run build --workspace=packages/bruno-converters + npm run build --workspace=packages/bruno-requests # tests - name: Test Package bruno-js @@ -75,6 +76,7 @@ jobs: npm run build --workspace=packages/bruno-common npm run sandbox:bundle-libraries --workspace=packages/bruno-js npm run build --workspace=packages/bruno-converters + npm run build --workspace=packages/bruno-requests - name: Run tests run: | diff --git a/package-lock.json b/package-lock.json index 751b048ef..aa8bd03ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,8 @@ "packages/bruno-lang", "packages/bruno-tests", "packages/bruno-toml", - "packages/bruno-graphql-docs" + "packages/bruno-graphql-docs", + "packages/bruno-requests" ], "devDependencies": { "@faker-js/faker": "^7.6.0", @@ -7992,6 +7993,10 @@ "resolved": "packages/bruno-query", "link": true }, + "node_modules/@usebruno/requests": { + "resolved": "packages/bruno-requests", + "link": true + }, "node_modules/@usebruno/schema": { "resolved": "packages/bruno-schema", "link": true @@ -26431,6 +26436,7 @@ "@usebruno/js": "0.12.0", "@usebruno/lang": "0.12.0", "@usebruno/node-machine-id": "^2.0.0", + "@usebruno/requests": "^0.1.0", "@usebruno/schema": "0.7.0", "@usebruno/vm2": "^3.9.13", "about-window": "^1.15.2", @@ -27705,6 +27711,21 @@ "typescript": "^4.8.4" } }, + "packages/bruno-requests": { + "name": "@usebruno/requests", + "version": "0.1.0", + "license": "MIT", + "devDependencies": { + "@rollup/plugin-commonjs": "^23.0.2", + "@rollup/plugin-node-resolve": "^15.0.1", + "@rollup/plugin-typescript": "^9.0.2", + "rollup": "3.29.5", + "rollup-plugin-dts": "^5.0.0", + "rollup-plugin-peer-deps-external": "^2.2.4", + "rollup-plugin-terser": "^7.0.2", + "typescript": "^4.8.4" + } + }, "packages/bruno-schema": { "name": "@usebruno/schema", "version": "0.7.0", diff --git a/package.json b/package.json index f7d4996dd..ae61c8294 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "packages/bruno-lang", "packages/bruno-tests", "packages/bruno-toml", - "packages/bruno-graphql-docs" + "packages/bruno-graphql-docs", + "packages/bruno-requests" ], "homepage": "https://usebruno.com", "devDependencies": { @@ -39,6 +40,7 @@ "dev:electron": "npm run dev --workspace=packages/bruno-electron", "dev:electron:debug": "npm run debug --workspace=packages/bruno-electron", "build:bruno-common": "npm run build --workspace=packages/bruno-common", + "build:bruno-requests": "npm run build --workspace=packages/bruno-requests", "build:bruno-converters": "npm run build --workspace=packages/bruno-converters", "build:bruno-query": "npm run build --workspace=packages/bruno-query", "build:graphql-docs": "npm run build --workspace=packages/bruno-graphql-docs", diff --git a/packages/bruno-cli/package.json b/packages/bruno-cli/package.json index 2de5c7142..7347f78fb 100644 --- a/packages/bruno-cli/package.json +++ b/packages/bruno-cli/package.json @@ -51,6 +51,7 @@ "@usebruno/js": "0.12.0", "@usebruno/lang": "0.12.0", "@usebruno/vm2": "^3.9.13", + "@usebruno/requests": "^0.1.0", "aws4-axios": "^3.3.0", "axios": "^1.8.3", "axios-ntlm": "^1.4.2", diff --git a/packages/bruno-cli/src/runner/prepare-request.js b/packages/bruno-cli/src/runner/prepare-request.js index b9efac616..7e7f5d3ac 100644 --- a/packages/bruno-cli/src/runner/prepare-request.js +++ b/packages/bruno-cli/src/runner/prepare-request.js @@ -65,6 +65,13 @@ const prepareRequest = (item = {}, collection = {}) => { } } } + + if (collectionAuth.mode === 'digest') { + axiosRequest.digestConfig = { + username: get(collectionAuth, 'digest.username'), + password: get(collectionAuth, 'digest.password') + }; + } } if (request.auth && request.auth.mode !== 'inherit') { @@ -115,6 +122,13 @@ const prepareRequest = (item = {}, collection = {}) => { 'X-WSSE' ] = `UsernameToken Username="${username}", PasswordDigest="${digest}", Nonce="${nonce}", Created="${ts}"`; } + + if (request.auth.mode === 'digest') { + axiosRequest.digestConfig = { + username: get(request, 'auth.digest.username'), + password: get(request, 'auth.digest.password') + }; + } } request.body = request.body || {}; diff --git a/packages/bruno-cli/src/runner/run-single-request.js b/packages/bruno-cli/src/runner/run-single-request.js index c8e137b7b..774587614 100644 --- a/packages/bruno-cli/src/runner/run-single-request.js +++ b/packages/bruno-cli/src/runner/run-single-request.js @@ -24,7 +24,7 @@ const { getCookieStringForUrl, saveCookies, shouldUseCookies } = require('../uti const { createFormData } = require('../utils/form-data'); const protocolRegex = /^([-+\w]{1,25})(:?\/\/|:)/; const { NtlmClient } = require('axios-ntlm'); - +const { addDigestInterceptor } = require('@usebruno/requests'); const onConsoleLog = (type, args) => { console[type](...args); @@ -333,6 +333,11 @@ const runSingleRequest = async function ( delete request.awsv4config; } + if (request.digestConfig) { + addDigestInterceptor(axiosInstance, request); + delete request.digestConfig; + } + /** @type {import('axios').AxiosResponse} */ response = await axiosInstance(request); diff --git a/packages/bruno-electron/package.json b/packages/bruno-electron/package.json index 11820c568..dbc921211 100644 --- a/packages/bruno-electron/package.json +++ b/packages/bruno-electron/package.json @@ -33,6 +33,7 @@ "@usebruno/node-machine-id": "^2.0.0", "@usebruno/schema": "0.7.0", "@usebruno/vm2": "^3.9.13", + "@usebruno/requests": "^0.1.0", "about-window": "^1.15.2", "aws4-axios": "^3.3.0", "axios": "^1.8.3", diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js index 325ff0391..cee37e0eb 100644 --- a/packages/bruno-electron/src/ipc/network/index.js +++ b/packages/bruno-electron/src/ipc/network/index.js @@ -14,7 +14,7 @@ const { NtlmClient } = require('axios-ntlm'); const { VarsRuntime, AssertRuntime, ScriptRuntime, TestRuntime } = require('@usebruno/js'); const { interpolateString } = require('./interpolate-string'); const { resolveAwsV4Credentials, addAwsV4Interceptor } = require('./awsv4auth-helper'); -const { addDigestInterceptor } = require('./digestauth-helper'); +const { addDigestInterceptor } = require('@usebruno/requests'); const prepareGqlIntrospectionRequest = require('./prepare-gql-introspection-request'); const { prepareRequest } = require('./prepare-request'); const interpolateVars = require('./interpolate-vars'); diff --git a/packages/bruno-requests/.gitignore b/packages/bruno-requests/.gitignore new file mode 100644 index 000000000..f6eabff32 --- /dev/null +++ b/packages/bruno-requests/.gitignore @@ -0,0 +1,22 @@ +# dependencies +node_modules +yarn.lock +pnpm-lock.yaml +package-lock.json +.pnp +.pnp.js + +# testing +coverage + +# production +dist + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/packages/bruno-requests/package.json b/packages/bruno-requests/package.json new file mode 100644 index 000000000..f43820549 --- /dev/null +++ b/packages/bruno-requests/package.json @@ -0,0 +1,32 @@ +{ + "name": "@usebruno/requests", + "version": "0.1.0", + "license": "MIT", + "main": "dist/cjs/index.js", + "module": "dist/esm/index.js", + "types": "dist/index.d.js", + "files": [ + "dist", + "src", + "package.json" + ], + "scripts": { + "clean": "rimraf dist", + "prebuild": "npm run clean", + "build": "rollup -c", + "prepack": "npm run test && npm run build" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "^23.0.2", + "@rollup/plugin-node-resolve": "^15.0.1", + "@rollup/plugin-typescript": "^9.0.2", + "rollup": "3.29.5", + "rollup-plugin-dts": "^5.0.0", + "rollup-plugin-peer-deps-external": "^2.2.4", + "rollup-plugin-terser": "^7.0.2", + "typescript": "^4.8.4" + }, + "overrides": { + "rollup": "3.29.5" + } +} diff --git a/packages/bruno-requests/rollup.config.js b/packages/bruno-requests/rollup.config.js new file mode 100644 index 000000000..fa04da640 --- /dev/null +++ b/packages/bruno-requests/rollup.config.js @@ -0,0 +1,37 @@ +const { nodeResolve } = require('@rollup/plugin-node-resolve'); +const commonjs = require('@rollup/plugin-commonjs'); +const typescript = require('@rollup/plugin-typescript'); +const dts = require('rollup-plugin-dts'); +const { terser } = require('rollup-plugin-terser'); +const peerDepsExternal = require('rollup-plugin-peer-deps-external'); + +const packageJson = require('./package.json'); + +module.exports = [ + { + input: 'src/index.ts', + output: [ + { + file: packageJson.main, + format: 'cjs', + sourcemap: true, + exports: 'named' + }, + { + file: packageJson.module, + format: 'esm', + sourcemap: true, + exports: 'named' + } + ], + plugins: [ + peerDepsExternal(), + nodeResolve({ + extensions: ['.js', '.ts', '.tsx', '.json', '.css'] + }), + commonjs(), + typescript({ tsconfig: './tsconfig.json' }), + terser() + ] + } +]; diff --git a/packages/bruno-electron/src/ipc/network/digestauth-helper.js b/packages/bruno-requests/src/auth/digestauth-helper.js similarity index 97% rename from packages/bruno-electron/src/ipc/network/digestauth-helper.js rename to packages/bruno-requests/src/auth/digestauth-helper.js index f01ba86df..25911a6b3 100644 --- a/packages/bruno-electron/src/ipc/network/digestauth-helper.js +++ b/packages/bruno-requests/src/auth/digestauth-helper.js @@ -25,7 +25,7 @@ function md5(input) { return crypto.createHash('md5').update(input).digest('hex'); } -function addDigestInterceptor(axiosInstance, request) { +export function addDigestInterceptor(axiosInstance, request) { const { username, password } = request.digestConfig; console.debug('Digest Auth Interceptor Initialized'); @@ -122,5 +122,3 @@ function addDigestInterceptor(axiosInstance, request) { } ); } - -module.exports = { addDigestInterceptor }; diff --git a/packages/bruno-requests/src/auth/index.ts b/packages/bruno-requests/src/auth/index.ts new file mode 100644 index 000000000..cd302427c --- /dev/null +++ b/packages/bruno-requests/src/auth/index.ts @@ -0,0 +1 @@ +export { addDigestInterceptor } from './digestauth-helper'; \ No newline at end of file diff --git a/packages/bruno-requests/src/index.ts b/packages/bruno-requests/src/index.ts new file mode 100644 index 000000000..19b02f764 --- /dev/null +++ b/packages/bruno-requests/src/index.ts @@ -0,0 +1 @@ +export { addDigestInterceptor } from './auth'; \ No newline at end of file diff --git a/packages/bruno-requests/tsconfig.json b/packages/bruno-requests/tsconfig.json new file mode 100644 index 000000000..6a74f54cf --- /dev/null +++ b/packages/bruno-requests/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "outDir": "./dist", + "rootDir": "./src", + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, + "moduleResolution": "node", + "declaration": true, + "declarationDir": "./dist/types", + "allowJs": true, + "checkJs": false + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.js"], + "exclude": ["node_modules", "dist"] +} \ No newline at end of file diff --git a/packages/bruno-tests/collection/auth/digest/Digest Auth 200.bru b/packages/bruno-tests/collection/auth/digest/Digest Auth 200.bru new file mode 100644 index 000000000..7efd6bee0 --- /dev/null +++ b/packages/bruno-tests/collection/auth/digest/Digest Auth 200.bru @@ -0,0 +1,21 @@ +meta { + name: Digest Auth 200 + type: http + seq: 1 +} + +get { + url: https://httpbin.org/digest-auth/auth/foo/passwd + body: none + auth: digest +} + +auth:digest { + username: foo + password: passwd +} + +assert { + res.status: eq 200 + res.body.authenticated: isTruthy +} diff --git a/packages/bruno-tests/collection/auth/digest/Digest Auth 401.bru b/packages/bruno-tests/collection/auth/digest/Digest Auth 401.bru new file mode 100644 index 000000000..52f3698ae --- /dev/null +++ b/packages/bruno-tests/collection/auth/digest/Digest Auth 401.bru @@ -0,0 +1,20 @@ +meta { + name: Digest Auth 401 + type: http + seq: 2 +} + +get { + url: https://httpbin.org/digest-auth/auth/foo/passw + body: none + auth: digest +} + +auth:digest { + username: foo + password: passwd +} + +assert { + res.status: eq 401 +} diff --git a/packages/bruno-tests/collection/auth/digest/folder.bru b/packages/bruno-tests/collection/auth/digest/folder.bru new file mode 100644 index 000000000..6b16b9610 --- /dev/null +++ b/packages/bruno-tests/collection/auth/digest/folder.bru @@ -0,0 +1,3 @@ +meta { + name: digest +}