I’m trying to get a simple (!) digest authentication working with node js using an an API from gathercontent.com.
Everything seems to be working except I still get a “Wrong credentials” response that looks like this:
{ success: false, error: 'Wrong Credentials!' }
The code looks like this:
var https = require('https'),
qs = require('querystring');
apikey = "[my api key goes in here]",
pwd = "[my password goes in here]",
crypto = require('crypto');
module.exports.apiCall = function () {
var options = {
host:'abcdefg.gathercontent.com',
port:443,
path:'/api/0.1/get_pages_by_project/get_me',
method:'POST',
headers:{
"Accept":"application/json",
"Content-Type":"application/x-www-form-urlencoded"
}
};
var req = https.request(options, function (res) {
res.on('data', function (d) {
var creds = JSON.parse(d);
var parsedDigest = parseDigest(res.headers['www-authenticate']);
console.log(parsedDigest);
var authopts = {
host:'furthercreative.gathercontent.com',
port:443,
path:'/api/0.1/get_pages_by_project/get_me',
method:'POST',
headers:{
"Accept":"application/json",
"Content-Type":"application/x-www-form-urlencoded",
"Authorization" : getAuthHeader(parsedDigest, apikey, parsedDigest['Digest realm'], pwd)
}
};
console.log(authopts);
console.log('\n\n\n');
var req2 = https.request(authopts, function (res2) {
console.log("statusCode: ", res2.statusCode);
console.log("headers: ", res2.headers);
res2.on('data', function (d2) {
var result = JSON.parse(d2);
});
});
req2.end();
});
});
req.write('id=1234');
req.end();
req.on('error', function (e) {
console.error(e);
});
};
function parseDigest(s){
var parts = s.split(',');
var obj = {};
var nvp = '';
for(var i = 0; i < parts.length; i++){
nvp = parts[i].split('=');
obj[nvp[0]] = nvp[1].replace(/"/gi, '');
}
return obj;
}
function getAuthHeader(digest, apikey, realm, pwd){
var md5 = crypto.createHash('md5');
var s = '';
var nc = '00000001';
var cn = '0a4f113b';
var HA1in = apikey+':'+realm+':'+pwd;
md5 = crypto.createHash('md5');
md5.update(HA1in);
var HA1out = md5.digest('hex');
var HA2in = 'POST:/api/0.1/get_pages_by_project/get_me';
md5 = crypto.createHash('md5');
md5.update(HA2in);
var HA2out = md5.digest('hex');
md5 = crypto.createHash('md5');
var respIn = HA1out + ':' + digest.nonce + ':'+nc+':'+cn+':'+digest.qop+':'+ HA2out;
md5.update(respIn);
var resp = md5.digest('hex');
s = [ 'Digest username="',apikey,'", ',
'realm="',digest['Digest realm'],'", ',
'nonce="',digest.nonce,'", ',
'uri="/api/0.1/get_pages_by_project/get_me", ',
'cnonce="',cn,'", ',
'nc="',nc,'", ',
'qop="',digest.qop,'", ',
'response="',resp,'", ',
'opaque="',digest.opaque,'"'].join('')
return s;
}
I’d try and Curl to it but I’m not sure how!
Any help appreciated!
I see a couple of issues potentially related to your problem. It’s hard to tell which ones are the actual culprits, not knowing anything about gathercontent’s implementation. If you pasted an example of their ‘WWW-Authenticate’ header, it would be much easier to provide specific help.
So I’m speculating what the actual cause is, but here are some actual problems that you should address anyway, to conform to the spec (i.e. protect it from breaking in the future because the server starts doing things slightly differently):
Authorizationheaders you are creating, remove the double quotes aroundnc, and maybe alsoqopqopvalue gathercontent is using. If it’sauth-int, then you’d also have to append the hashed HTTP body toHA2, see #3.2.2.3 of the spec – furthermore, they might be specifying a comma-separated list of qop values for you to choose from – or the server might not send a value forqopat all, i.e. they use the most basic from of HTTP digest auth, in which your implementation would be violating the spec, as then you aren’t allowed to e.g. send acnonce,ncetc.parsedDigest['Digest realm'], i.e. you are assuming that therealmis the first attribute after the initialDigestkeyword. That might or might not be the case, but you should not rely upon it (modify yourparseDigestfunction to strip of the string"Digest "before splitting the rest)parsedDigest, you make the assumption that Digest is always capitalized that way, and thatrealm,nonce, etc. are always in lowercase. According to the spec, these are all case-insensitiveA couple of unrelated issues:
Digest authentication? This is HTTPS, so you might as well doBasic authentication, it’s way easier, and with HTTPS, just as safe. (Answering myself here, after checking out gathercontent: Basic auth is apparently not possible)cnonceshould be random for every request, especially, you shouldn’t copy and paste it from Wikipedia, which makes you more vulnerable (but not an issue here, as all data goes over SSL anyway in your case)Regarding how to curl it – try this: