mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-29 07:34:07 +00:00
Merge pull request #3697 from Pragadesh-45/feat/digest-auth-updates
Feat/digest auth updates
This commit is contained in:
@@ -2,7 +2,7 @@ const crypto = require('crypto');
|
||||
const { URL } = require('url');
|
||||
|
||||
function isStrPresent(str) {
|
||||
return str && str !== '' && str !== 'undefined';
|
||||
return str && str.trim() !== '' && str.trim() !== 'undefined';
|
||||
}
|
||||
|
||||
function stripQuotes(str) {
|
||||
@@ -15,7 +15,10 @@ function containsDigestHeader(response) {
|
||||
}
|
||||
|
||||
function containsAuthorizationHeader(originalRequest) {
|
||||
return Boolean(originalRequest.headers['Authorization']);
|
||||
return Boolean(
|
||||
originalRequest.headers['Authorization'] ||
|
||||
originalRequest.headers['authorization']
|
||||
);
|
||||
}
|
||||
|
||||
function md5(input) {
|
||||
@@ -24,11 +27,10 @@ function md5(input) {
|
||||
|
||||
function addDigestInterceptor(axiosInstance, request) {
|
||||
const { username, password } = request.digestConfig;
|
||||
|
||||
console.debug(request);
|
||||
console.debug('Digest Auth Interceptor Initialized');
|
||||
|
||||
if (!isStrPresent(username) || !isStrPresent(password)) {
|
||||
console.warn('Required Digest Auth fields are not present');
|
||||
console.warn('Required Digest Auth fields (username/password) are not present');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -37,41 +39,82 @@ function addDigestInterceptor(axiosInstance, request) {
|
||||
(error) => {
|
||||
const originalRequest = error.config;
|
||||
|
||||
// Prevent retry loops
|
||||
if (originalRequest._retry) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
originalRequest._retry = true;
|
||||
|
||||
if (
|
||||
error.response?.status === 401 &&
|
||||
containsDigestHeader(error.response) &&
|
||||
!containsAuthorizationHeader(originalRequest)
|
||||
) {
|
||||
console.debug('Processing Digest Authentication Challenge');
|
||||
console.debug(error.response.headers['www-authenticate']);
|
||||
|
||||
const authDetails = error.response.headers['www-authenticate']
|
||||
.split(', ')
|
||||
.map((v) => v.split('=').map(stripQuotes))
|
||||
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
|
||||
console.debug(authDetails);
|
||||
.split(',')
|
||||
.map((pair) => pair.split('=').map((item) => item.trim()).map(stripQuotes))
|
||||
.reduce((acc, [key, value]) => {
|
||||
const normalizedKey = key.toLowerCase().replace('digest ', '');
|
||||
if (normalizedKey && value !== undefined) {
|
||||
acc[normalizedKey] = value;
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// Validate required auth details
|
||||
if (!authDetails.realm || !authDetails.nonce) {
|
||||
console.warn('Missing required auth details (realm or nonce)');
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
console.debug("Auth Details: \n", authDetails);
|
||||
|
||||
const nonceCount = '00000001';
|
||||
const cnonce = crypto.randomBytes(24).toString('hex');
|
||||
|
||||
if (authDetails.algorithm && authDetails.algorithm.toUpperCase() !== 'MD5') {
|
||||
console.warn(`Unsupported Digest algorithm: ${algo}`);
|
||||
console.warn(`Unsupported Digest algorithm: ${authDetails.algorithm}`);
|
||||
return Promise.reject(error);
|
||||
} else {
|
||||
authDetails.algorithm = 'MD5';
|
||||
}
|
||||
const uri = new URL(request.url).pathname;
|
||||
const HA1 = md5(`${username}:${authDetails['Digest realm']}:${password}`);
|
||||
const HA2 = md5(`${request.method}:${uri}`);
|
||||
const response = md5(`${HA1}:${authDetails.nonce}:${nonceCount}:${cnonce}:auth:${HA2}`);
|
||||
|
||||
const authorizationHeader =
|
||||
`Digest username="${username}",realm="${authDetails['Digest realm']}",` +
|
||||
`nonce="${authDetails.nonce}",uri="${uri}",qop="auth",algorithm="${authDetails.algorithm}",` +
|
||||
`response="${response}",nc="${nonceCount}",cnonce="${cnonce}"`;
|
||||
const uri = new URL(request.url, request.baseURL || 'http://localhost').pathname; // Handle relative URLs
|
||||
const HA1 = md5(`${username}:${authDetails.realm}:${password}`);
|
||||
const HA2 = md5(`${request.method}:${uri}`);
|
||||
const response = md5(
|
||||
`${HA1}:${authDetails.nonce}:${nonceCount}:${cnonce}:auth:${HA2}`
|
||||
);
|
||||
|
||||
const headerFields = [
|
||||
`username="${username}"`,
|
||||
`realm="${authDetails.realm}"`,
|
||||
`nonce="${authDetails.nonce}"`,
|
||||
`uri="${uri}"`,
|
||||
`qop="auth"`,
|
||||
`algorithm="${authDetails.algorithm}"`,
|
||||
`response="${response}"`,
|
||||
`nc="${nonceCount}"`,
|
||||
`cnonce="${cnonce}"`,
|
||||
];
|
||||
|
||||
if (authDetails.opaque) {
|
||||
headerFields.push(`opaque="${authDetails.opaque}"`);
|
||||
}
|
||||
|
||||
const authorizationHeader = `Digest ${headerFields.join(', ')}`;
|
||||
|
||||
// Ensure headers are initialized
|
||||
originalRequest.headers = originalRequest.headers || {};
|
||||
originalRequest.headers['Authorization'] = authorizationHeader;
|
||||
|
||||
console.debug(`Authorization: ${originalRequest.headers['Authorization']}`);
|
||||
|
||||
delete originalRequest.digestConfig;
|
||||
|
||||
return axiosInstance(originalRequest);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user