Skip to content

Commit

Permalink
Merge pull request #3697 from Pragadesh-45/feat/digest-auth-updates
Browse files Browse the repository at this point in the history
Feat/digest auth updates
  • Loading branch information
lohit-bruno authored Jan 2, 2025
2 parents 5fe9208 + d32f987 commit 52672e6
Showing 1 changed file with 60 additions and 17 deletions.
77 changes: 60 additions & 17 deletions packages/bruno-electron/src/ipc/network/digestauth-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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) {
Expand All @@ -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;
}

Expand All @@ -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 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 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 username="${username}",realm="${authDetails['Digest realm']}",` +
`nonce="${authDetails.nonce}",uri="${uri}",qop="auth",algorithm="${authDetails.algorithm}",` +
`response="${response}",nc="${nonceCount}",cnonce="${cnonce}"`;
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);
}

Expand Down

0 comments on commit 52672e6

Please sign in to comment.