diff --git a/packages/bruno-cli/src/runner/prepare-request.js b/packages/bruno-cli/src/runner/prepare-request.js index f031ff0aa9..2df275a0d5 100644 --- a/packages/bruno-cli/src/runner/prepare-request.js +++ b/packages/bruno-cli/src/runner/prepare-request.js @@ -47,6 +47,25 @@ const prepareRequest = (item = {}, collection = {}) => { if (collectionAuth.mode === 'bearer') { axiosRequest.headers['Authorization'] = `Bearer ${get(collectionAuth, 'bearer.token')}`; } + + if (collectionAuth.mode === 'apikey'){ + const placement = get(collectionAuth, "apikey.placement"); + const key = get(collectionAuth, "apikey.key"); + const value = get(collectionAuth, "apikey.value"); + + if (placement === 'header') { + axiosRequest.headers[key] = value; + } else if (placement === 'queryparams') { + try { + const urlObj = new URL(request.url); + urlObj.searchParams.set(key, value); + axiosRequest.url = urlObj.toString(); + } catch (error) { + console.error('Invalid URL:', request.url, error); + } + } + + } } if (request.auth) { @@ -79,6 +98,24 @@ const prepareRequest = (item = {}, collection = {}) => { if (request.auth.mode === 'bearer') { axiosRequest.headers['Authorization'] = `Bearer ${get(request, 'auth.bearer.token')}`; } + + if (request.auth?.mode === 'apikey') { + const placement = get(request, "auth.apikey.placement"); + const key = get(request, "auth.apikey.key"); + const value = get(request, "auth.apikey.value"); + + if (placement === 'header') { + axiosRequest.headers[key] = value; + } else if (placement === 'queryparams') { + try { + const urlObj = new URL(request.url); + urlObj.searchParams.set(key, value); + axiosRequest.url = urlObj.toString(); + } catch (error) { + console.error('Invalid URL:', request.url, error); + } + } + } if (request.auth.mode === 'wsse') { const username = get(request, 'auth.wsse.username', ''); diff --git a/packages/bruno-cli/tests/runner/prepare-request.spec.js b/packages/bruno-cli/tests/runner/prepare-request.spec.js index 37d3e34d30..ec2e30d810 100644 --- a/packages/bruno-cli/tests/runner/prepare-request.spec.js +++ b/packages/bruno-cli/tests/runner/prepare-request.spec.js @@ -23,3 +23,116 @@ describe('prepare-request: prepareRequest', () => { }); }); }); + +describe('Properly maps inherited auth from collectionRoot', () => { + // Initialize Test Fixtures + let collectionRoot, request; + + beforeEach(() => { + // Reset test fixtures + collectionRoot = { + request: { + auth: {} + } + }; + + request = { + url: 'https://www.usebruno.com', + auth: { + mode: "inherit" + }, + body: { + mode: 'json', + json: '{\n"test": {{someVar}} // comment\n}' + } + }; + }); + + it('If collection auth is apikey in header', () => { + collectionRoot.request.auth = { + mode: "apikey", + apikey: { + key: "x-api-key", + value: "{{apiKey}}", + placement: "header" + } + }; + + const result = prepareRequest(request, collectionRoot); + expect(result.headers).toHaveProperty('x-api-key', '{{apiKey}}'); + }); + + + it('If collection auth is apikey in header and request has existing headers', () => { + collectionRoot.request.auth = { + mode: "apikey", + apikey: { + key: "x-api-key", + value: "{{apiKey}}", + placement: "header" + } + }; + + request['headers'] = [{ name: 'Content-Type', value: 'application/json', enabled: true }]; + + const result = prepareRequest(request, collectionRoot); + expect(result.headers).toHaveProperty('Content-Type', 'application/json'); + expect(result.headers).toHaveProperty('x-api-key', '{{apiKey}}'); + }); + + it('If collection auth is apikey in query parameters', () => { + collectionRoot.request.auth = { + mode: "apikey", + apikey: { + key: "apiKey", + value: "{{apiKey}}", + placement: "queryparams" + } + }; + + const expected = `${request.url}?${collectionRoot.request.auth.apikey.key}=${collectionRoot.request.auth.apikey.value}`; + const result = prepareRequest(request, collectionRoot); + expect(result.url).toEqual(expected); + }); + + it('If collection auth is basic auth', () => { + collectionRoot.request.auth = { + mode: 'basic', + basic: { + username: 'testUser', + password: 'testPass123' + } + }; + + const result = prepareRequest(request, collectionRoot); + const expected = { username: 'testUser', password: 'testPass123' }; + expect(result.auth).toEqual(expected); + }); + + it('If collection auth is bearer token', () => { + collectionRoot.request.auth = { + mode: 'bearer', + bearer: { + token: 'token' + } + }; + + const result = prepareRequest(request, collectionRoot); + expect(result.headers).toHaveProperty('Authorization', 'Bearer token'); + }); + + it('If collection auth is bearer token and request has existing headers', () => { + collectionRoot.request.auth = { + mode: 'bearer', + bearer: { + token: 'token' + } + }; + + request['headers'] = [{ name: 'Content-Type', value: 'application/json', enabled: true }]; + + const result = prepareRequest(request, collectionRoot); + expect(result.headers).toHaveProperty('Authorization', 'Bearer token'); + expect(result.headers).toHaveProperty('Content-Type', 'application/json'); + }); +}); \ No newline at end of file diff --git a/packages/bruno-tests/src/auth/apiKey.js b/packages/bruno-tests/src/auth/apiKey.js new file mode 100644 index 0000000000..683b8c01c8 --- /dev/null +++ b/packages/bruno-tests/src/auth/apiKey.js @@ -0,0 +1,28 @@ +const express = require('express'); +const router = express.Router(); + +const VALID_API_KEY = 'my-secret-api-key'; +const VALID_KEY_NAME = 'api-key'; + +const apiKeyAuthMiddleware = (req, res, next) => { + const apiKeyFromHeader = req.headers[VALID_KEY_NAME.toLowerCase()]; + const apiKeyFromQuery = req.query[VALID_KEY_NAME]; + + const apiKey = apiKeyFromHeader || apiKeyFromQuery; + + if (!apiKey) { + return res.status(401).json({ error: 'API key missing' }); + } + + if (apiKey !== VALID_API_KEY) { + return res.status(403).json({ error: 'Invalid API key' }); + } + + next(); +}; + +router.post('/protected', apiKeyAuthMiddleware, (req, res) => { + res.status(200).json({ message: 'You have accessed a protected route!' }); +}); + +module.exports = router; diff --git a/packages/bruno-tests/src/auth/index.js b/packages/bruno-tests/src/auth/index.js index e26a655294..bc2dd4e49f 100644 --- a/packages/bruno-tests/src/auth/index.js +++ b/packages/bruno-tests/src/auth/index.js @@ -5,6 +5,7 @@ const authBearer = require('./bearer'); const authBasic = require('./basic'); const authWsse = require('./wsse'); const authCookie = require('./cookie'); +const apiKey = require('./apiKey'); const authOAuth2PasswordCredentials = require('./oauth2/passwordCredentials'); const authOAuth2AuthorizationCode = require('./oauth2/authorizationCode'); const authOAuth2ClientCredentials = require('./oauth2/clientCredentials'); @@ -16,5 +17,6 @@ router.use('/bearer', authBearer); router.use('/basic', authBasic); router.use('/wsse', authWsse); router.use('/cookie', authCookie); +router.use('/apiKey', apiKey); module.exports = router;