feat: add WSSE authentication support to gRPC requests (#5455)

* feat: add WSSE authentication support to gRPC requests

- Introduced WSSE authentication mode in GrpcAuth component.
- Updated supported authentication modes to include WSSE.
- Refactored gRPC event handlers to streamline authentication header setting.
- Added notes regarding limitations of complex auth modes in gRPC.

* fix: update authentication header retrieval in setAuthHeaders function

- Refactored the setAuthHeaders function to correctly retrieve WSSE and OAuth2 refresh token URLs from the request object instead of the collectionAuth object.
- Added comprehensive tests for various authentication modes, ensuring proper inheritance and request-level overrides for AWS v4, basic, bearer, digest, NTLM, WSSE, API key, and OAuth2 authentication methods.

* chore: remove outdated comments on gRPC authentication limitations
This commit is contained in:
sanish chirayath
2025-09-01 19:19:14 +05:30
committed by GitHub
parent 3fa05d32cb
commit abddc98767
5 changed files with 978 additions and 119 deletions

View File

@@ -30,6 +30,10 @@ const GrpcAuthMode = ({ item, collection }) => {
name: 'OAuth2',
mode: 'oauth2'
},
{
name: 'WSSE Auth',
mode: 'wsse'
},
{
name: 'Inherit',
mode: 'inherit'

View File

@@ -6,6 +6,7 @@ import BearerAuth from '../../Auth/BearerAuth';
import BasicAuth from '../../Auth/BasicAuth';
import ApiKeyAuth from '../../Auth/ApiKeyAuth';
import OAuth2 from '../../Auth/OAuth2/index';
import WsseAuth from '../../Auth/WsseAuth';
import StyledWrapper from './StyledWrapper';
import { humanizeRequestAuthMode } from 'utils/collections';
import { getTreePathFromCollectionToItem } from 'utils/collections/index';
@@ -13,7 +14,10 @@ import { updateRequestAuthMode, updateAuth } from 'providers/ReduxStore/slices/c
import { saveRequest } from 'providers/ReduxStore/slices/collections/actions';
// List of auth modes supported by gRPC
const supportedGrpcAuthModes = ['basic', 'bearer', 'apikey', 'oauth2', 'none', 'inherit'];
// Note: Only header-based auth modes work with gRPC
// Complex auth modes like AWS Sig v4, Digest, and NTLM require axios interceptors
// and cannot be supported in gRPC requests as of now
const supportedGrpcAuthModes = ['basic', 'bearer', 'apikey', 'oauth2', 'wsse', 'none', 'inherit'];
const GrpcAuth = ({ item, collection }) => {
const dispatch = useDispatch();
@@ -83,6 +87,9 @@ const GrpcAuth = ({ item, collection }) => {
case 'oauth2': {
return <OAuth2 collection={collection} item={item} updateAuth={updateAuth} request={request} save={save} />;
}
case 'wsse': {
return <WsseAuth collection={collection} item={item} updateAuth={updateAuth} request={request} save={save} />;
}
case 'inherit': {
const source = getEffectiveAuthSource();

View File

@@ -11,118 +11,7 @@ const { getProcessEnvVars } = require('../../store/process-env');
const { getOAuth2TokenUsingPasswordCredentials, getOAuth2TokenUsingClientCredentials, getOAuth2TokenUsingAuthorizationCode } = require('../../utils/oauth2');
const { interpolateString } = require('./interpolate-string');
const path = require('node:path');
const setGrpcAuthHeaders = (grpcRequest, request, collectionRoot) => {
const collectionAuth = get(collectionRoot, 'request.auth');
if (collectionAuth && request.auth?.mode === 'inherit') {
if (collectionAuth.mode === 'basic') {
grpcRequest.basicAuth = {
username: get(collectionAuth, 'basic.username'),
password: get(collectionAuth, 'basic.password')
};
}
if (collectionAuth.mode === 'bearer') {
grpcRequest.headers['Authorization'] = `Bearer ${get(collectionAuth, 'bearer.token')}`;
}
if (collectionAuth.mode === 'apikey') {
grpcRequest.headers[collectionAuth.apikey?.key] = collectionAuth.apikey?.value;
}
if (collectionAuth.mode === 'oauth2') {
const grantType = get(collectionAuth, 'oauth2.grantType');
if (grantType === 'client_credentials') {
grpcRequest.oauth2 = {
grantType,
accessTokenUrl: get(collectionAuth, 'oauth2.accessTokenUrl'),
clientId: get(collectionAuth, 'oauth2.clientId'),
clientSecret: get(collectionAuth, 'oauth2.clientSecret'),
scope: get(collectionAuth, 'oauth2.scope'),
credentialsPlacement: get(collectionAuth, 'oauth2.credentialsPlacement'),
tokenPlacement: get(collectionAuth, 'oauth2.tokenPlacement'),
tokenHeaderPrefix: get(collectionAuth, 'oauth2.tokenHeaderPrefix'),
tokenQueryKey: get(collectionAuth, 'oauth2.tokenQueryKey')
};
} else if (grantType === 'password') {
grpcRequest.oauth2 = {
grantType,
accessTokenUrl: get(collectionAuth, 'oauth2.accessTokenUrl'),
username: get(collectionAuth, 'oauth2.username'),
password: get(collectionAuth, 'oauth2.password'),
clientId: get(collectionAuth, 'oauth2.clientId'),
clientSecret: get(collectionAuth, 'oauth2.clientSecret'),
scope: get(collectionAuth, 'oauth2.scope'),
credentialsPlacement: get(collectionAuth, 'oauth2.credentialsPlacement'),
tokenPlacement: get(collectionAuth, 'oauth2.tokenPlacement'),
tokenHeaderPrefix: get(collectionAuth, 'oauth2.tokenHeaderPrefix'),
tokenQueryKey: get(collectionAuth, 'oauth2.tokenQueryKey')
};
}
}
}
if (request.auth && request.auth.mode !== 'inherit') {
if (request.auth.mode === 'basic') {
grpcRequest.basicAuth = {
username: get(request, 'auth.basic.username'),
password: get(request, 'auth.basic.password')
};
}
if (request.auth.mode === 'bearer') {
grpcRequest.headers['Authorization'] = `Bearer ${get(request, 'auth.bearer.token')}`;
}
if (request.auth.mode === 'oauth2') {
const grantType = get(request, 'auth.oauth2.grantType');
if (grantType === 'client_credentials') {
grpcRequest.oauth2 = {
grantType,
clientId: get(request, 'auth.oauth2.clientId'),
clientSecret: get(request, 'auth.oauth2.clientSecret'),
scope: get(request, 'auth.oauth2.scope'),
accessTokenUrl: get(request, 'auth.oauth2.accessTokenUrl'),
tokenPlacement: get(request, 'auth.oauth2.tokenPlacement'),
credentialsPlacement: get(request, 'auth.oauth2.credentialsPlacement'),
tokenHeaderPrefix: get(request, 'auth.oauth2.tokenHeaderPrefix'),
tokenQueryKey: get(request, 'auth.oauth2.tokenQueryKey')
};
} else if (grantType === 'password') {
grpcRequest.oauth2 = {
grantType,
username: get(request, 'auth.oauth2.username'),
password: get(request, 'auth.oauth2.password'),
clientId: get(request, 'auth.oauth2.clientId'),
clientSecret: get(request, 'auth.oauth2.clientSecret'),
scope: get(request, 'auth.oauth2.scope'),
accessTokenUrl: get(request, 'auth.oauth2.accessTokenUrl'),
tokenPlacement: get(request, 'auth.oauth2.tokenPlacement'),
credentialsPlacement: get(request, 'auth.oauth2.credentialsPlacement'),
tokenHeaderPrefix: get(request, 'auth.oauth2.tokenHeaderPrefix'),
tokenQueryKey: get(request, 'auth.oauth2.tokenQueryKey')
};
} else if (grantType === 'authorization_code') {
grpcRequest.oauth2 = {
grantType,
...get(request, 'auth.oauth2')
};
}
}
if (request.auth.mode === 'apikey') {
grpcRequest.headers[request.auth.apikey?.key] = request.auth.apikey?.value;
}
}
return grpcRequest;
}
const { setAuthHeaders } = require('./prepare-request');
const prepareRequest = async (item, collection, environment, runtimeVariables, certsAndProxyConfig = {}) => {
const request = item.draft ? item.draft.request : item.request;
@@ -182,7 +71,7 @@ const prepareRequest = async (item, collection, environment, runtimeVariables, c
oauth2CredentialVariables: request.oauth2CredentialVariables,
}
grpcRequest = setGrpcAuthHeaders(grpcRequest, request, collectionRoot);
grpcRequest = setAuthHeaders(grpcRequest, request, collectionRoot);
if (grpcRequest.oauth2) {
let requestCopy = cloneDeep(grpcRequest);

View File

@@ -43,8 +43,8 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
};
break;
case 'wsse':
const username = get(request, 'auth.wsse.username', '');
const password = get(request, 'auth.wsse.password', '');
const username = get(collectionAuth, 'wsse.username', '');
const password = get(collectionAuth, 'wsse.password', '');
const ts = new Date().toISOString();
const nonce = crypto.randomBytes(16).toString('hex');
@@ -193,7 +193,7 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
axiosRequest.oauth2 = {
grantType: grantType,
accessTokenUrl: get(request, 'auth.oauth2.accessTokenUrl'),
refreshTokenUrl: get(collectionAuth, 'oauth2.refreshTokenUrl'),
refreshTokenUrl: get(request, 'auth.oauth2.refreshTokenUrl'),
username: get(request, 'auth.oauth2.username'),
password: get(request, 'auth.oauth2.password'),
clientId: get(request, 'auth.oauth2.clientId'),
@@ -215,7 +215,7 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
callbackUrl: get(request, 'auth.oauth2.callbackUrl'),
authorizationUrl: get(request, 'auth.oauth2.authorizationUrl'),
accessTokenUrl: get(request, 'auth.oauth2.accessTokenUrl'),
refreshTokenUrl: get(collectionAuth, 'oauth2.refreshTokenUrl'),
refreshTokenUrl: get(request, 'auth.oauth2.refreshTokenUrl'),
clientId: get(request, 'auth.oauth2.clientId'),
clientSecret: get(request, 'auth.oauth2.clientSecret'),
scope: get(request, 'auth.oauth2.scope'),
@@ -251,7 +251,7 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
axiosRequest.oauth2 = {
grantType: grantType,
accessTokenUrl: get(request, 'auth.oauth2.accessTokenUrl'),
refreshTokenUrl: get(collectionAuth, 'oauth2.refreshTokenUrl'),
refreshTokenUrl: get(request, 'auth.oauth2.refreshTokenUrl'),
clientId: get(request, 'auth.oauth2.clientId'),
clientSecret: get(request, 'auth.oauth2.clientSecret'),
scope: get(request, 'auth.oauth2.scope'),

View File

@@ -0,0 +1,959 @@
const crypto = require('node:crypto');
// Mock crypto.randomBytes to return predictable values for testing
jest.mock('node:crypto', () => ({
...jest.requireActual('node:crypto'),
randomBytes: jest.fn(() => Buffer.from('1234567890abcdef', 'hex'))
}));
// Mock the lodash get function with a more sophisticated mock
const mockGet = jest.fn();
jest.mock('lodash', () => ({
get: mockGet,
each: jest.fn(),
filter: jest.fn(),
find: jest.fn()
}));
// Import the function to test
const { setAuthHeaders } = require('../src/ipc/network/prepare-request');
describe('setAuthHeaders', () => {
let mockAxiosRequest;
let mockRequest;
let mockCollectionRoot;
beforeEach(() => {
// Reset all mocks
jest.clearAllMocks();
// Reset crypto mock to return predictable values
crypto.randomBytes.mockReturnValue(Buffer.from('1234567890abcdef', 'hex'));
// Setup default mock objects
mockAxiosRequest = {
headers: {}
};
mockRequest = {
auth: {
mode: 'none'
}
};
mockCollectionRoot = {
request: {
auth: null
}
};
// Setup a more sophisticated mock for lodash get function
mockGet.mockImplementation((obj, path, defaultValue) => {
if (!obj) return defaultValue;
const keys = path.split('.');
let current = obj;
for (const key of keys) {
if (current && typeof current === 'object' && key in current) {
current = current[key];
} else {
return defaultValue;
}
}
return current;
});
});
describe('Collection-level authentication inheritance', () => {
test('should inherit AWS v4 authentication from collection', () => {
mockCollectionRoot.request.auth = {
mode: 'awsv4',
awsv4: {
accessKeyId: 'test-access-key',
secretAccessKey: 'test-secret-key',
sessionToken: 'test-session-token',
service: 's3',
region: 'us-east-1',
profileName: 'default'
}
};
mockRequest.auth.mode = 'inherit';
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.awsv4config).toEqual({
accessKeyId: 'test-access-key',
secretAccessKey: 'test-secret-key',
sessionToken: 'test-session-token',
service: 's3',
region: 'us-east-1',
profileName: 'default'
});
});
test('should inherit basic authentication from collection', () => {
mockCollectionRoot.request.auth = {
mode: 'basic',
basic: {
username: 'testuser',
password: 'testpass'
}
};
mockRequest.auth.mode = 'inherit';
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.basicAuth).toEqual({
username: 'testuser',
password: 'testpass'
});
});
test('should inherit bearer authentication from collection', () => {
mockCollectionRoot.request.auth = {
mode: 'bearer',
bearer: {
token: 'test-token'
}
};
mockRequest.auth.mode = 'inherit';
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.headers['Authorization']).toBe('Bearer test-token');
});
test('should inherit digest authentication from collection', () => {
mockCollectionRoot.request.auth = {
mode: 'digest',
digest: {
username: 'testuser',
password: 'testpass'
}
};
mockRequest.auth.mode = 'inherit';
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.digestConfig).toEqual({
username: 'testuser',
password: 'testpass'
});
});
test('should inherit NTLM authentication from collection', () => {
mockCollectionRoot.request.auth = {
mode: 'ntlm',
ntlm: {
username: 'testuser',
password: 'testpass',
domain: 'testdomain'
}
};
mockRequest.auth.mode = 'inherit';
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.ntlmConfig).toEqual({
username: 'testuser',
password: 'testpass',
domain: 'testdomain'
});
});
test('should inherit WSSE authentication from collection', () => {
mockCollectionRoot.request.auth = {
mode: 'wsse',
wsse: {
username: 'testuser',
password: 'testpass'
}
};
mockRequest.auth.mode = 'inherit';
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.headers['X-WSSE']).toMatch(/UsernameToken Username="testuser", PasswordDigest="[^"]+", Nonce="1234567890abcdef", Created="[^"]+"/);
});
test('should inherit API key authentication from collection (header placement)', () => {
mockCollectionRoot.request.auth = {
mode: 'apikey',
apikey: {
key: 'X-API-Key',
value: 'test-api-key',
placement: 'header'
}
};
mockRequest.auth.mode = 'inherit';
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.headers['X-API-Key']).toBe('test-api-key');
});
test('should inherit API key authentication from collection (query params placement)', () => {
mockCollectionRoot.request.auth = {
mode: 'apikey',
apikey: {
key: 'api_key',
value: 'test-api-key',
placement: 'queryparams'
}
};
mockRequest.auth.mode = 'inherit';
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.apiKeyAuthValueForQueryParams).toEqual({
key: 'api_key',
value: 'test-api-key',
placement: 'queryparams'
});
});
test('should skip API key authentication when key is empty', () => {
mockCollectionRoot.request.auth = {
mode: 'apikey',
apikey: {
key: '',
value: 'test-api-key',
placement: 'header'
}
};
mockRequest.auth.mode = 'inherit';
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.headers['']).toBeUndefined();
});
});
describe('OAuth2 authentication inheritance', () => {
test('should inherit OAuth2 password grant from collection', () => {
mockCollectionRoot.request.auth = {
mode: 'oauth2',
oauth2: {
grantType: 'password',
accessTokenUrl: 'https://example.com/token',
refreshTokenUrl: 'https://example.com/refresh',
username: 'testuser',
password: 'testpass',
clientId: 'test-client',
clientSecret: 'test-secret',
scope: 'read write',
credentialsPlacement: 'body',
credentialsId: 'test-credentials',
tokenPlacement: 'header',
tokenHeaderPrefix: 'Bearer',
tokenQueryKey: 'access_token',
autoFetchToken: true,
autoRefreshToken: true,
additionalParameters: { authorization: [], token: [], refresh: [] }
}
};
mockRequest.auth.mode = 'inherit';
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.oauth2).toEqual({
grantType: 'password',
accessTokenUrl: 'https://example.com/token',
refreshTokenUrl: 'https://example.com/refresh',
username: 'testuser',
password: 'testpass',
clientId: 'test-client',
clientSecret: 'test-secret',
scope: 'read write',
credentialsPlacement: 'body',
credentialsId: 'test-credentials',
tokenPlacement: 'header',
tokenHeaderPrefix: 'Bearer',
tokenQueryKey: 'access_token',
autoFetchToken: true,
autoRefreshToken: true,
additionalParameters: { authorization: [], token: [], refresh: [] }
});
});
test('should inherit OAuth2 authorization_code grant from collection', () => {
mockCollectionRoot.request.auth = {
mode: 'oauth2',
oauth2: {
grantType: 'authorization_code',
callbackUrl: 'https://example.com/callback',
authorizationUrl: 'https://example.com/auth',
accessTokenUrl: 'https://example.com/token',
refreshTokenUrl: 'https://example.com/refresh',
clientId: 'test-client',
clientSecret: 'test-secret',
scope: 'read write',
state: 'random-state',
pkce: true,
credentialsPlacement: 'body',
credentialsId: 'test-credentials',
tokenPlacement: 'header',
tokenHeaderPrefix: 'Bearer',
tokenQueryKey: 'access_token',
autoFetchToken: true,
autoRefreshToken: true,
additionalParameters: { authorization: [], token: [], refresh: [] }
}
};
mockRequest.auth.mode = 'inherit';
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.oauth2).toEqual({
grantType: 'authorization_code',
callbackUrl: 'https://example.com/callback',
authorizationUrl: 'https://example.com/auth',
accessTokenUrl: 'https://example.com/token',
refreshTokenUrl: 'https://example.com/refresh',
clientId: 'test-client',
scope: 'read write',
state: 'random-state',
pkce: true,
credentialsPlacement: 'body',
clientSecret: 'test-secret',
credentialsId: 'test-credentials',
tokenPlacement: 'header',
tokenHeaderPrefix: 'Bearer',
tokenQueryKey: 'access_token',
autoFetchToken: true,
autoRefreshToken: true,
additionalParameters: { authorization: [], token: [], refresh: [] }
});
});
test('should inherit OAuth2 implicit grant from collection', () => {
mockCollectionRoot.request.auth = {
mode: 'oauth2',
oauth2: {
grantType: 'implicit',
callbackUrl: 'https://example.com/callback',
authorizationUrl: 'https://example.com/auth',
clientId: 'test-client',
scope: 'read write',
state: 'random-state',
credentialsId: 'test-credentials',
tokenPlacement: 'header',
tokenHeaderPrefix: 'Bearer',
tokenQueryKey: 'access_token',
autoFetchToken: true,
additionalParameters: { authorization: [], token: [], refresh: [] }
}
};
mockRequest.auth.mode = 'inherit';
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.oauth2).toEqual({
grantType: 'implicit',
callbackUrl: 'https://example.com/callback',
authorizationUrl: 'https://example.com/auth',
clientId: 'test-client',
scope: 'read write',
state: 'random-state',
credentialsId: 'test-credentials',
tokenPlacement: 'header',
tokenHeaderPrefix: 'Bearer',
tokenQueryKey: 'access_token',
autoFetchToken: true,
additionalParameters: { authorization: [], token: [], refresh: [] }
});
});
test('should inherit OAuth2 client_credentials grant from collection', () => {
mockCollectionRoot.request.auth = {
mode: 'oauth2',
oauth2: {
grantType: 'client_credentials',
accessTokenUrl: 'https://example.com/token',
refreshTokenUrl: 'https://example.com/refresh',
clientId: 'test-client',
clientSecret: 'test-secret',
scope: 'read write',
credentialsPlacement: 'body',
credentialsId: 'test-credentials',
tokenPlacement: 'header',
tokenHeaderPrefix: 'Bearer',
tokenQueryKey: 'access_token',
autoFetchToken: true,
autoRefreshToken: true,
additionalParameters: { authorization: [], token: [], refresh: [] }
}
};
mockRequest.auth.mode = 'inherit';
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.oauth2).toEqual({
grantType: 'client_credentials',
accessTokenUrl: 'https://example.com/token',
refreshTokenUrl: 'https://example.com/refresh',
clientId: 'test-client',
clientSecret: 'test-secret',
scope: 'read write',
credentialsPlacement: 'body',
credentialsId: 'test-credentials',
tokenPlacement: 'header',
tokenHeaderPrefix: 'Bearer',
tokenQueryKey: 'access_token',
autoFetchToken: true,
autoRefreshToken: true,
additionalParameters: { authorization: [], token: [], refresh: [] }
});
});
});
describe('Request-level authentication (overrides collection)', () => {
test('should set AWS v4 authentication at request level', () => {
mockCollectionRoot.request.auth = {
mode: 'awsv4',
awsv4: {
accessKeyId: 'test-access-key',
secretAccessKey: 'test-secret-key',
sessionToken: 'test-session-token',
service: 's3',
region: 'us-east-1',
profileName: 'default'
}
}
mockRequest.auth = {
mode: 'awsv4',
awsv4: {
accessKeyId: 'request-access-key',
secretAccessKey: 'request-secret-key',
sessionToken: 'request-session-token',
service: 's3',
region: 'us-west-2',
profileName: 'production'
}
};
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.awsv4config).toEqual({
accessKeyId: 'request-access-key',
secretAccessKey: 'request-secret-key',
sessionToken: 'request-session-token',
service: 's3',
region: 'us-west-2',
profileName: 'production'
});
});
test('should set basic authentication at request level', () => {
mockCollectionRoot.request.auth = {
mode: 'basic',
basic: {
username: 'testuser',
password: 'testpass'
}
};
mockRequest.auth = {
mode: 'basic',
basic: {
username: 'requestuser',
password: 'requestpass'
}
};
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.basicAuth).toEqual({
username: 'requestuser',
password: 'requestpass'
});
});
test('should set bearer authentication at request level', () => {
mockCollectionRoot.request.auth = {
mode: 'bearer',
bearer: {
token: 'test-token'
}
};
mockRequest.auth = {
mode: 'bearer',
bearer: {
token: 'request-token'
}
};
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.headers['Authorization']).toBe('Bearer request-token');
});
test('should set digest authentication at request level', () => {
mockCollectionRoot.request.auth = {
mode: 'digest',
digest: {
username: 'testuser',
password: 'testpass'
}
};
mockRequest.auth = {
mode: 'digest',
digest: {
username: 'requestuser',
password: 'requestpass'
}
};
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.digestConfig).toEqual({
username: 'requestuser',
password: 'requestpass'
});
});
test('should set NTLM authentication at request level', () => {
mockCollectionRoot.request.auth = {
mode: 'ntlm',
ntlm: {
username: 'testuser',
password: 'testpass',
domain: 'testdomain'
}
};
mockRequest.auth = {
mode: 'ntlm',
ntlm: {
username: 'requestuser',
password: 'requestpass',
domain: 'requestdomain'
}
};
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.ntlmConfig).toEqual({
username: 'requestuser',
password: 'requestpass',
domain: 'requestdomain'
});
});
test('should set WSSE authentication at request level', () => {
mockCollectionRoot.request.auth = {
mode: 'wsse',
wsse: {
username: 'testuser',
password: 'testpass'
}
};
mockRequest.auth = {
mode: 'wsse',
wsse: {
username: 'requestuser',
password: 'requestpass'
}
};
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.headers['X-WSSE']).toMatch(/UsernameToken Username="requestuser", PasswordDigest="[^"]+", Nonce="1234567890abcdef", Created="[^"]+"/);
});
test('should set API key authentication at request level (header placement)', () => {
mockCollectionRoot.request.auth = {
mode: 'apikey',
apikey: {
key: 'X-Request-API-Key',
value: 'test-api-key',
placement: 'header'
}
};
mockRequest.auth = {
mode: 'apikey',
apikey: {
key: 'X-Request-API-Key',
value: 'request-api-key',
placement: 'header'
}
};
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.headers['X-Request-API-Key']).toBe('request-api-key');
});
test('should set API key authentication at request level (query params placement)', () => {
mockCollectionRoot.request.auth = {
mode: 'apikey',
apikey: {
key: 'X-Request-API-Key',
value: 'test-api-key',
placement: 'header'
}
};
mockRequest.auth = {
mode: 'apikey',
apikey: {
key: 'request_api_key',
value: 'request-api-key',
placement: 'queryparams'
}
};
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.apiKeyAuthValueForQueryParams).toEqual({
key: 'request_api_key',
value: 'request-api-key',
placement: 'queryparams'
});
});
test('should set OAuth2 password grant at request level', () => {
mockCollectionRoot.request.auth = {
mode: 'oauth2',
oauth2: {
grantType: 'password',
accessTokenUrl: 'https://collection.com/token',
refreshTokenUrl: 'https://collection.com/refresh',
username: 'collectionuser',
password: 'collectionpass',
clientId: 'collection-client',
clientSecret: 'collection-secret',
scope: 'read',
credentialsPlacement: 'header',
credentialsId: 'collection-credentials',
tokenPlacement: 'query',
tokenHeaderPrefix: 'Token',
tokenQueryKey: 'token',
autoFetchToken: false,
autoRefreshToken: false,
additionalParameters: { authorization: [], token: [], refresh: [] }
}
};
mockRequest.auth = {
mode: 'oauth2',
oauth2: {
grantType: 'password',
accessTokenUrl: 'https://request.com/token',
refreshTokenUrl: 'https://request.com/refresh',
username: 'requestuser',
password: 'requestpass',
clientId: 'request-client',
clientSecret: 'request-secret',
scope: 'read',
credentialsPlacement: 'header',
credentialsId: 'request-credentials',
tokenPlacement: 'query',
tokenHeaderPrefix: 'Token',
tokenQueryKey: 'token',
autoFetchToken: false,
autoRefreshToken: false,
additionalParameters: { authorization: [], token: [], refresh: [] }
}
};
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.oauth2).toEqual({
grantType: 'password',
accessTokenUrl: 'https://request.com/token',
refreshTokenUrl: 'https://request.com/refresh',
username: 'requestuser',
password: 'requestpass',
clientId: 'request-client',
clientSecret: 'request-secret',
scope: 'read',
credentialsPlacement: 'header',
credentialsId: 'request-credentials',
tokenPlacement: 'query',
tokenHeaderPrefix: 'Token',
tokenQueryKey: 'token',
autoFetchToken: false,
autoRefreshToken: false,
additionalParameters: { authorization: [], token: [], refresh: [] }
});
});
test('should set OAuth2 authorization_code grant at request level', () => {
mockCollectionRoot.request.auth = {
mode: 'oauth2',
oauth2: {
grantType: 'password',
callbackUrl: 'https://collection.com/callback',
authorizationUrl: 'https://collection.com/auth',
accessTokenUrl: 'https://collection.com/token',
refreshTokenUrl: 'https://collection.com/refresh',
username: 'collectionuser',
password: 'collectionpass',
clientId: 'collection-client',
clientSecret: 'collection-secret',
}
};
mockRequest.auth = {
mode: 'oauth2',
oauth2: {
grantType: 'authorization_code',
callbackUrl: 'https://request.com/callback',
authorizationUrl: 'https://request.com/auth',
accessTokenUrl: 'https://request.com/token',
refreshTokenUrl: 'https://request.com/refresh',
clientId: 'request-client',
clientSecret: 'request-secret',
scope: 'read',
state: 'request-state',
pkce: false,
credentialsPlacement: 'body',
credentialsId: 'request-credentials',
tokenPlacement: 'header',
tokenHeaderPrefix: 'Bearer',
tokenQueryKey: 'access_token',
autoFetchToken: true,
autoRefreshToken: true,
additionalParameters: { authorization: [], token: [], refresh: [] }
}
};
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.oauth2).toEqual({
grantType: 'authorization_code',
callbackUrl: 'https://request.com/callback',
authorizationUrl: 'https://request.com/auth',
accessTokenUrl: 'https://request.com/token',
refreshTokenUrl: 'https://request.com/refresh',
clientId: 'request-client',
clientSecret: 'request-secret',
scope: 'read',
state: 'request-state',
pkce: false,
credentialsPlacement: 'body',
credentialsId: 'request-credentials',
tokenPlacement: 'header',
tokenHeaderPrefix: 'Bearer',
tokenQueryKey: 'access_token',
autoFetchToken: true,
autoRefreshToken: true,
additionalParameters: { authorization: [], token: [], refresh: [] }
});
});
test('should set OAuth2 implicit grant at request level', () => {
mockCollectionRoot.request.auth = {
mode: 'oauth2',
oauth2: {
grantType: 'implicit',
callbackUrl: 'https://collection.com/callback',
authorizationUrl: 'https://collection.com/auth',
clientId: 'collection-client',
scope: 'read',
state: 'collection-state',
credentialsId: 'collection-credentials',
tokenPlacement: 'header',
tokenHeaderPrefix: 'Bearer',
tokenQueryKey: 'access_token',
autoFetchToken: true,
additionalParameters: { authorization: [], token: [], refresh: [] }
}
};
mockRequest.auth = {
mode: 'oauth2',
oauth2: {
grantType: 'implicit',
callbackUrl: 'https://request.com/callback',
authorizationUrl: 'https://request.com/auth',
clientId: 'request-client',
scope: 'read',
state: 'request-state',
credentialsId: 'request-credentials',
tokenPlacement: 'query',
tokenHeaderPrefix: 'Token',
tokenQueryKey: 'token',
autoFetchToken: false,
additionalParameters: { authorization: [], token: [], refresh: [] }
}
};
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.oauth2).toEqual({
grantType: 'implicit',
callbackUrl: 'https://request.com/callback',
authorizationUrl: 'https://request.com/auth',
clientId: 'request-client',
credentialsId: 'request-credentials',
scope: 'read',
state: 'request-state',
tokenPlacement: 'query',
tokenHeaderPrefix: 'Token',
tokenQueryKey: 'token',
autoFetchToken: false,
additionalParameters: { authorization: [], token: [], refresh: [] }
});
});
test('should set OAuth2 client_credentials grant at request level', () => {
mockCollectionRoot.request.auth = {
mode: 'oauth2',
oauth2: {
grantType: 'client_credentials',
accessTokenUrl: 'https://collection.com/token',
refreshTokenUrl: 'https://collection.com/refresh',
clientId: 'collection-client',
clientSecret: 'collection-secret',
scope: 'read',
credentialsPlacement: 'body',
credentialsId: 'collection-credentials',
tokenPlacement: 'header',
tokenHeaderPrefix: 'Bearer',
tokenQueryKey: 'access_token',
autoFetchToken: true,
autoRefreshToken: true,
additionalParameters: { authorization: [], token: [], refresh: [] }
}
};
mockRequest.auth = {
mode: 'oauth2',
oauth2: {
grantType: 'client_credentials',
accessTokenUrl: 'https://request.com/token',
refreshTokenUrl: 'https://request.com/refresh',
clientId: 'request-client',
clientSecret: 'request-secret',
scope: 'read',
credentialsPlacement: 'body',
credentialsId: 'request-credentials',
tokenPlacement: 'header',
tokenHeaderPrefix: 'Bearer',
tokenQueryKey: 'access_token',
autoFetchToken: true,
autoRefreshToken: true,
additionalParameters: { authorization: [], token: [], refresh: [] }
}
};
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.oauth2).toEqual({
grantType: 'client_credentials',
accessTokenUrl: 'https://request.com/token',
refreshTokenUrl: 'https://request.com/refresh',
clientId: 'request-client',
clientSecret: 'request-secret',
scope: 'read',
credentialsPlacement: 'body',
credentialsId: 'request-credentials',
tokenPlacement: 'header',
tokenHeaderPrefix: 'Bearer',
tokenQueryKey: 'access_token',
autoFetchToken: true,
autoRefreshToken: true,
additionalParameters: { authorization: [], token: [], refresh: [] }
});
});
});
describe('Edge cases and error handling', () => {
test('should handle missing collection auth gracefully', () => {
mockCollectionRoot.request.auth = null;
mockRequest.auth = {
mode: 'basic',
basic: {
username: 'testuser',
password: 'testpass'
}
};
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result.basicAuth).toEqual({
username: 'testuser',
password: 'testpass'
});
});
test('should handle missing request auth gracefully', () => {
mockRequest.auth = null;
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result).toBe(mockAxiosRequest);
expect(result.headers).toEqual({});
});
test('should handle missing auth mode gracefully', () => {
mockRequest.auth = {};
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result).toBe(mockAxiosRequest);
expect(result.headers).toEqual({});
});
test('should handle unknown auth mode gracefully', () => {
mockRequest.auth = {
mode: 'unknown'
};
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result).toBe(mockAxiosRequest);
expect(result.headers).toEqual({});
});
test('should handle missing OAuth2 grant type gracefully', () => {
mockRequest.auth = {
mode: 'oauth2',
oauth2: {}
};
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result).toBe(mockAxiosRequest);
expect(result.oauth2).toBeUndefined();
});
test('should handle unknown OAuth2 grant type gracefully', () => {
mockRequest.auth = {
mode: 'oauth2',
oauth2: {
grantType: 'unknown_grant'
}
};
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result).toBe(mockAxiosRequest);
expect(result.oauth2).toBeUndefined();
});
test('should return the modified axiosRequest object', () => {
mockRequest.auth = {
mode: 'bearer',
bearer: {
token: 'test-token'
}
};
const result = setAuthHeaders(mockAxiosRequest, mockRequest, mockCollectionRoot);
expect(result).toBe(mockAxiosRequest);
expect(result.headers['Authorization']).toBe('Bearer test-token');
});
});
});