nodejs HTTP Digest Authentication not working - node.js

I've been digging deep into stack overflow but haven't been able to solve my problem. I'm trying to access an API that uses digest but have had no success, and my co-workers haven't been able to pin down the problem either. I've hit a wall and have come to Stack Overflow to ask my question.
Here is my authentication code:
var https = require("https"),
crypto = require('crypto'),
_ = require('underscore');
var options = {
host: 'api.example.com',
port: 80,
path: '/path/to/uri/',
method: 'GET',
accept: 'application/json',
acceptEncoding: 'gzip, deflate',
connection: 'keep-alive',
rejectUnauthorized: false,
requestCert: true,
agent: false
};
var username = 'username',
password = 'httppassword';
var req = https.get(options, function(res) {
res.setEncoding('utf-8');
console.log(res.url);
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + JSON.stringify(res.headers));
var data = "";
res.on('data', function (chunk) {
data = data + chunk;
});
res.on('end', function(){
console.log(data);
var challengeParams = parseDigest(res.headers['www-authenticate']);
console.log(challengeParams);
var ha1 = crypto.createHash('md5').update(username + ':' + challengeParams.realm + ':' + password).digest('hex');
var ha2 = crypto.createHash('md5').update('GET:' + options.path).digest('hex');
var response = crypto.createHash('md5').update(ha1 + ':' + challengeParams.nonce + ':1::auth:' + ha2).digest('hex');
var authRequestParams = {
username : username,
realm : challengeParams.realm,
nonce : challengeParams.nonce,
uri : options.path,
qop : challengeParams.qop,
response : response,
nc : 1,
cnonce : ''
};
options.headers = { 'Authorization' : renderDigest(authRequestParams) };
console.log(options);
https.get(options, function(res) {
console.log("STATUS: " + res.statusCode);
console.log("HEADERS: " + JSON.stringify(res.headers));
res.setEncoding('utf-8');
var content = '';
res.on('data', function(chunk) {
content += chunk;
}).on('end', function() {
console.log(content);
});
})
});
});
req.on('error' ,function(err){
console.log("request");
console.log(err);
});
req.write('data\n');
req.write('data\n');
req.end();
And here is the challenge header sent back by the API
{ realm: 'API realm',
domain: 'https:/api.example.com/',
qop: 'auth',
nonce: 'UZ43b0FWC9591pMjy1i6H2okVwgMbDVO6fcgcQ' }
EDIT:
I thought it would be helpful for those looking to answer this question for me to provide what I'm actually sending back to the API, so here it is.
{ host: 'api.example.com',
port: 80,
path: '/path/to/uri/',
method: 'GET',
accept: 'application/json',
acceptEncoding: 'gzip, deflate',
connection: 'keep-alive',
rejectUnauthorized: false,
requestCert: true,
agent: false,
headers: { Authorization: 'Digest username="uname", realm="API realm", nonce="UZ43b0FWC9591pMjy1i6H2okVwgMbDVO6fcgcQ", uri="/path/to/uri", qop="auth", response="09c536e22bca031cdbcb289e4065064a", nc="1", cnonce=""' } }

You can use http-auth module that supports digest authentication
// HTTP module
var http = require('http');
// Authentication module.
var auth = require('http-auth');
var digest = auth.digest({
realm: "Simon Area.",
file: __dirname + "/../data/users.htdigest" // vivi:anna, sona:testpass
});
// Creating new HTTP server.
http.createServer(digest, function(req, res) {
res.end("Welcome to private area - " + req.user + "!");
}).listen(1337);

Related

NodeJS Patreon API account link

I'm trying to connect user accounts on my website to patreon. I keep getting an access_denied error message in response to step 3. I'm following this documentation.
My node server code looks like this:
socket.on("patreon_register",function(code,user){
var reqString = "api.patreon.com/oauth2/token?code="
+code
+"&grant_type=authorization_code&client_id="
+settings.patreon.Client_ID
+"&client_secret="
+settings.patreon.Client_Secret
+"&redirect_uri="
+"http%3A%2F%2Fwww.levisinger.com%2F%3Fpage%3Dpatreon_success",
req = querystring.stringify({
"code": code,
"grant_type": "authorization_code",
"client_id": settings.patreon.Client_ID,
"client_secret": settings.patreon.Client_Secret,
"redirect_uri": "http%3A%2F%2Fwww.levisinger.com%2F%3Fpage%3Dpatreon_success"
}),
post_options = {
host: 'api.patreon.com',
port: '80',
path: '/oauth2/token',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(req)
}
};
// Set up the request
console.log(req);
var post_req = http.request(post_options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log(chunk);
if(
chunk.access_token &&
chunk.refresh_token &&
chunk.expires_in &&
chunk.scope &&
chunk.token_type
){
Auth.linkPatreon(user,chunk,function(err,res){
if(err){ socket.emit('patreon_register',false,res); }
else { socket.emit('patreon_register',true,res); }
});
}
});
});
// post the data
post_req.write(req);
post_req.end();
});
The req variable that's actually sent to the server looks like this (changed my codes to generic values of course)
code=MY_RESPONSE_CODE&grant_type=authorization_code&client_id=MY_CLIENT_ID&client_secret=MY_CLIENT_SECRET&redirect_uri=MY_RESPONSE_URI
Any ideas?
In the end, my server looks like this and is working:
socket.on("patreon_register",function(code,user){
var req = querystring.stringify({
code: code,
grant_type: "authorization_code",
client_id: settings.patreon.Client_ID,
client_secret: settings.patreon.Client_Secret,
redirect_uri: settings.patreon.redirect_uri
}),
post_options = {
host: 'api.patreon.com',
port: '80',
path: '/oauth2/token',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(req)
}
};
// Set up the request
console.log(req);
var post_req = http.request(post_options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
chunk = JSON.parse(chunk);
console.log(chunk);
if(!chunk["error"]){
console.log("Linking!");
Auth.linkPatreon(user,chunk,function(err,res){
if(err){ socket.emit('patreon_register',false,res); }
else { socket.emit('patreon_register',true,res); }
console.log("Linked!");
});
}
});
});

NodeJS authorizationError: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE',

I have tried all possible solution to solve this error "UNABLE_TO_VERIFY_LEAF_SIGNATURE". GET request works fine. But POST is not working due to certificate issue,
Any help to resolve this would be great.
Below is the code snippet
function dohttpRequest(requestdata,filename){
//var localoptions=getOptions(requestdata);
console.log('entered dohttpRequest');
console.log(requestdata);
var localoptions = {
//hostname: 'www.tele-servicingafrica.philips.com',
//hostname: '192.168.1.205',
hostname: '192.168.1.195',
key: fs.readFileSync('ssl/sca.key'),
cert: fs.readFileSync('ssl/sca.crt'),
ca: fs.readFileSync('ssl/sia.crt'),
rejectUnauthorized: false,
strictssl:false,
port: 443,
path: '/ATS/ATS.Service1.svc/sftp/UploadFile',
method: "POST",
json: true,
headers: {
"content-type": "application/json",
"content-length":requestdata.length
},
}
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
var req = https.request(localoptions,function(res){
console.log('entered request');
res.setEncoding('utf8');
console.log(res);
console.log(req);
res.on('data', function (chunk) {
console.log('Status: ' + res.statusCode);
if(res.statusCode=="200"){
fs.unlink(filename);
console.log('Status: ' + res.statusCode);
}
});
});

send email via google http rest api

I am trying to send email from NodeJS running on a Linux server to Google Gmail RESR HTTP API. Not using libraries, just sending https. I have figured out the OAuth part, have an access token and get responses from google. But I cannot get past various error messages. I have posted the code below. It is not obvious but EmailSend() is called after I get the access token from google, so yeah it is being called.
var emailStr = new Buffer(
"Content-Type: text/plain; charset=\"UTF-8\"\n" +
"MIME-Version: 1.0\n" +
"Content-Transfer-Encoding: 7bit\n" +
"to: SOMEONE#gmail.com\n" +
"from: SOMEONEELSE#MYDOMAIN.com\n" +
"subject: Subject Text\n\n" +
"The actual message text goes here"
).toString("base64").replace(/\+/g, '-').replace(/\//g, '_');
//var emailBase64UrlSafe = Rtrim( emailStr, '=' );
//var emailBase64UrlSafe = JsStrToUrlSafe ( emailStr );
var emailBase64UrlSafe = emailStr;
var http = require('https');
function EmailSend() {
var post_data = emailBase64UrlSafe;
var post_options = {
hostname: 'www.googleapis.com',
port: '443',
path: '/gmail/v1/users/me/messages/send',
method: 'POST',
headers: {
"Authorization": 'Bearer '+googleAccessKey['access_token'],
"Content-Type" : "application/json; charset=UTF-8"
},
};
console.log( post_options );
var post_req = http.request(post_options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('Response: ' + chunk);
});
});
post_req.write(JSON.stringify({ "raw": emailBase64UrlSafe }));
post_req.end();
}; /* end EmailSend() */
Response: {
"error": {
"errors": [
{
"domain": "global",
"reason": "failedPrecondition",
"message": "Bad Request"
}
],
"code": 400,
"message": "Bad Request"
}
Resources used:
https://www.rfc-editor.org/rfc/rfc2822#appendix-A
https://developers.google.com/identity/protocols/OAuth2ServiceAccount
https://nodejs.org/api/https.html#https_https_request_options_callback
Send email using Google API with only access token
Tried it for myself, and it worked!
var http = require('https');
var mail = new Buffer(
"From: example#gmail.com\n" +
"To: example#gmail.com\n" +
"Subject: Subject Text\n\n" +
"Message text"
).toString("base64").replace(/\+/g, '-').replace(/\//g, '_');
var post_options = {
hostname: 'www.googleapis.com',
port: '443',
path: '/gmail/v1/users/me/messages/send',
method: 'POST',
headers: {
"Authorization": 'Bearer <ACCESS_TOKEN>',
"Content-Type" : "application/json"
}
};
var post_req = http.request(post_options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('Response: ' + chunk);
});
});
post_req.write(JSON.stringify({ "raw": mail }));
post_req.end();

Updating post http request length in node.js

I'm using node.js to post a http request. the code works with if i define my post data ahead of the 'options' field, but if I initially set my post_data string to empty and update it later it doesn't pick up the new length. How would I get it to do that ? I'm looking to send multiple posts of varying lengths to the same place in a loop so need to be able to do this.
var post_data=''; //if i set my string content here rather than later on it works
var options = {
host: '127.0.0.1',
port: 8529,
path: '/_api/cursor',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': post_data.length
}
};
var req = http.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('BODY: ' + chunk);
});
});
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
post_data = 'a variable length string goes here';//the change in length to post_data is not //recognised
req.write(post_data);
req.end();
'Content-Length': post_data.length
You ran this before setting post_data.
If you want to set post_data after creating the object, you'll need to set it manually later:
options.headers['Content-Length'] = post_data.length;
Note that you must set that before calling http.request().
Posting data is a matter of sending a query string (just like the way you would send it with an URL after the ?) as the request body.
This also requires to declare Content-Type and Content-Length values so the server knows how to interpret the data.
var querystring = require('querystring');
var data = querystring.stringify({
username: yourUsernameValue,
password: yourPasswordValue
});
var options = {
host: 'my.url',
port: 80,
path: '/login',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': data.length
}
};
var req = http.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log("body: " + chunk);
});
});
req.write(data);
req.end();
You need to replace:
'Content-Length': post_data.length
for:
'Content-Length': Buffer.byteLength(post_data, 'utf-8')
See https://github.com/strongloop/express/issues/1870

Google Closure Compiler moved ?? It's giving a 302 error

I'm using nodejs 0.4.7 to make the request, this is my code:
var post_data = JSON.stringify({
'compilation_level' : 'ADVANCED_OPTIMIZATIONS',
'output_format': 'json',
'warning_level' : 'QUIET',
'js_code' : code
});
var post_options = {
host: 'closure-compiler.appspot.com',
port: '80',
path: 'compile',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': post_data.length
}
};
var post_req = http.request(post_options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('Response: ' + chunk);
});
});
post_req.write(post_data);
post_req.end();
And the response I get is
Response: <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
here.
</BODY></HTML>
Why is this happening ? What am I doing wrong ? In the tutorial it says I'm suposed to make the POST request to http://closure-compiler.appspot.com/compile...
You're trying to send json data:
var post_data = JSON.stringify({
'compilation_level' : 'ADVANCED_OPTIMIZATIONS',
'output_format': 'json',
'warning_level' : 'QUIET',
'js_code' : code
});
Google Closure Compiler API wants standard form data, so you want to use querystring instead. Also you need to indicate the output format you want (compiled code I assume), as specified by their documentation:
var post_data = querystring.stringify({
'compilation_level' : 'ADVANCED_OPTIMIZATIONS',
'output_format': 'json',
'output_info': 'compiled_code',
'warning_level' : 'QUIET',
'js_code' : code
});
Path is better declared like so:
path: '/compile',
Here is the full proof of concept code:
var http = require('http');
var querystring = require('querystring');
var code ="// ADD YOUR CODE HERE\n" +
"function hello(name) {\n" +
" alert('Hello, ' + name);\n" +
"}\n" +
"hello('New user');\n";
var post_data = querystring.stringify({
'compilation_level' : 'ADVANCED_OPTIMIZATIONS',
'output_format': 'json',
'output_info': 'compiled_code',
'warning_level' : 'QUIET',
'js_code' : code
});
var post_options = {
host: 'closure-compiler.appspot.com',
port: '80',
path: '/compile',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': post_data.length
}
};
var post_req = http.request(post_options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('Response: ' + chunk);
});
});
post_req.write(post_data);
post_req.end();
Running it with node.js produces the following:
$ node test.js
Response: {"compiledCode":"alert(\"Hello, New user\");"}

Resources