I’m trying to set up Digest auth to automate testing through Cypress. There are no built-in tools for this, so I have to resort to pure JS.
So far I have only managed to authorize using Postman, all other options didn’t work for me. The same Bruno with Digest Auth gives a 401 status.
I’ve selected the minimum settings with which authorization works for me (status 200), I’ll attach the list by curl-request. The rest of the headers, I think, are substituted automatically and are not displayed in the postman interface.
I would also like to point out that cookies are added after authorization is done. I should extract them from the response request in order to save the session, but that’s the next step, for now I can’t solve the authorization problem.
curl:
curl --location --request POST 'http://exmpl.smth.com/srv/abcbcb/scenario'
--header 'Host: exmpl'
--header 'Cookie: PHPSESSID=qarntgdqk2t6brje3414l4f4t0'
And automatically it also substitutes I think the “Authorization” header. (https://github.com/postmanlabs/postman-runtime/blob/develop/lib/authorizer/digest.js#L447)
headerParams = [USERNAME_EQUALS_QUOTE + username + QUOTE,
REALM_EQUALS_QUOTE + realm + QUOTE,
NONCE_EQUALS_QUOTE + nonce + QUOTE,
URI_EQUALS_QUOTE + uri + QUOTE];
algorithm && headerParams.push(ALGORITHM_EQUALS_QUOTE + algorithm + QUOTE);
headerParams.push(QOP_EQUALS + qop);
nonceCount && headerParams.push(NC_EQUALS + nonceCount);
headerParams.push(CNONCE_EQUALS_QUOTE + clientNonce + QUOTE);
headerParams.push(RESPONSE_EQUALS_QUOTE + reqDigest + QUOTE);
opaque && headerParams.push(OPAQUE_EQUALS_QUOTE + opaque + QUOTE);
return DIGEST_PREFIX + headerParams.join(', ');
I’ve tried writing the code myself, but I inevitably get a 401 status and can’t successfully authorize. Please point out my errors in the code.
Code example:
const CryptoJS = require('crypto-js');
const url = 'http://exmpl.smth.com/srv/abcbcb/scenario';
const username = 'name';
const password = 'pass';
const qop = 'auth'
const nc = '00000002'
async function digestFetch(url, username, password) {
const initialResponse = await fetch(url, {
method: 'POST',
headers: {
'Authorization': 'Digest realm="example", qop="auth", nonce="some_nonce", uri="/path", response="some_response"'
}
});
if (initialResponse.status === 401) {
const authHeader = initialResponse.headers.get('WWW-Authenticate');
const nonce = extractNonce(authHeader);
const opaque = extractOpaque(authHeader);
const realm = extractRealm(authHeader);
console.log(nonce)
console.log(opaque)
console.log(realm)
const A1 = `${username}:${realm}:${password}`;
const A2 = `POST:${url}`;
const HA1 = CryptoJS.MD5(A1).toString();
const HA2 = CryptoJS.MD5(A2).toString();
console.log(`1) HA1: ${HA1}n2) HA2: ${HA2}`)
const responseHash = CryptoJS.MD5(`${HA1}:${nonce}:${nc}:0a4f113b:${qop}:${HA2}`).toString();
const response = `Digest username="${username}", realm="${realm}", nonce="${nonce}", uri="${url}", algorithm="MD5", qop=${qop}, nc=${nc}, cnonce="0a4f113b", response="${responseHash}", opaque="${opaque}"`;
console.log(`Response: ${response}`)
const finalResponse = await fetch(url, {
method: 'POST',
headers: {
'Authorization': response,
'Host': 'exmpl'
}
});
return finalResponse;
}
return initialResponse;
}
function extractNonce(authHeader) {
const match = authHeader.match(/nonce="([^"]+)"/);
return match ? match[1] : null;
}
function extractOpaque(authHeader) {
const matches = authHeader.match(/opaque="([^"]+)"/);
return matches ? matches[1] : null;
}
function extractRealm(authHeader) {
const matches = authHeader.match(/realm="([^"]+)"/);
return matches ? matches[1] : null;
}
digestFetch(url, username, password)
.then(response => {
if (response.ok) {
return response.text();
} else {
throw new Error('Authentication failed. Status code: ' + response.status);
}
})
.then(data => console.log(data))
.catch(error => console.error(error));