GET request from app with http basic authentication (node.js) - node.js

I have an nodejs app which use the http basic authentication in express.js.
In this app I make an http.get request to an external webpage to load and parse the html.
With the basic auth I get in each http.get(url, function(){}) request to the external host an error: "Unauthorized". If I remove the basic authentication, it works fine.
Anybody know's why I'm Unauthorized at an public resource if only my own server has this authentication?
e.g. (pseudocode):
with the express basic authentication, I'm getting "Unauthorized" as body from google.com. without the auth, I get the html
var auth = express.basicAuth(function(user, pass, callback) {
var result = (user === 'john' && pass === 'doe') ? true : false;
callback(null, result);
});
app.configure(function(){
app.use("/", auth, function(next) { next(); });
app.use("/", express.static(__dirname+'/html'));
});
http.get('http://google.com', function(res) {
res.setEncoding('utf8');
var body = '';
res.on('data', function (chunk) {
body = body + chunk;
});
res.on('end', function() {
cb(body);
});
}).on('error', function(err) {
cb('error', err);
});

You need to restructure your app to have your call to Google done inside a callback, after a GET is issued to your server. Here's working code:
var express = require('express');
var request = require('request');
var app = express();
// Authenticator
app.use(express.basicAuth('john', 'doe'));
app.get('/', function(req, res) {
request.get('http://www.google.com', function (err, response, body) {
res.send(body)
});
});
app.listen(process.env.PORT || 8080);
Here are details if you want to do fancier basic authentication: http://blog.modulus.io/nodejs-and-express-basic-authentication
It appears that you're trying to write code synchronously, which will not work. I recommend reading this article for a review of idiomatic Node: http://blog.ponyfoo.com/2013/07/12/teach-yourself-nodejs-in-10-steps

For HTTP Basic/Digest authentication you can also use http-auth module
var express = require('express');
var request = require('request');
// Authentication module.
var auth = require('http-auth');
var basic = auth.basic({
realm: "Simon Area.",
file: __dirname + "/../data/users.htpasswd" // gevorg:gpass, Sarah:testpass ...
});
// Application setup.
var app = express();
app.use(auth.connect(basic));
// Setup route.
app.get('/', function(req, res){
request.get('http://www.google.com', function (err, response, body) {
res.send(body)
});
});

Related

Digest auth using http-auth

Trying to implement Digest auth in nodejs. Below is the code
var http = require('http');
var auth = require('http-auth');
var express = require('express');
var app = express();
var user;
var basic = auth.basic({
realm: 'Sample',
file: __dirname + "/users.htpasswd",
algorithm:'md5'
});
basic.on('success', (result, req) => {
console.log(`User authenticated: ${result.user}`);
user = result.user;
});
basic.on('fail', (result, req) => {
console.log(`User authentication failed: ${result.user}`);
console.log(req.headers.authorization);
});
basic.on('error', (error, req) => {
console.log(`Authentication error: ${error.code + " - " + error.message}`);
});
http.createServer(app).listen(8000);
app.use(auth.connect(basic));
app.get('/', function(req, res) {
console.log(req.headers);
console.log(basic);
res.json('Hello from '+ user);
res.end();
});
app.post('/', function(req, res) {
console.log(req.headers);
console.log(basic);
res.json('Hello from '+ user);
res.end();
});
This is the content of users.htpasswd file:-
ankit:Sample:e4b2d19b03346a1c45ce86ad41b85c5e
Using postman to call the end point with username ankit, pwd ankit & realm Sample, everytime I am getting 401.
Please let me know where I am doing wrong.
Thanks
You're mixing basic auth and digest auth. Replace auth.basic with auth.digest and your code should work as-is.

How to test an express rest api with header parameters using mocha and supertest?

I have to test my rest api. Some routes require a value in the http requests headers for the user authentication token.
I have separated my interesting bussiness logic in pure javascript code but I can't find a way to test the routes that require a token in the headers of the http request.
Any other alternatives to mocha and/or supertest are welcome.
With supertest, you can set a header parameter with the set keyword :
api.get('/aroute/')
...
.set('headerParameterName', value)
...
Here is an example of testing a express server API with token authorization using supertest :
app.js:
var express = require('express');
var app = express();
var jwt = require('jsonwebtoken');
var expressJwt = require('express-jwt');
var secret = 'my-secret';
app.get('/get-token', function(req, res) {
var token = jwt.sign({foo: 'bar'}, secret);
res.send({token: token});
});
app.post(
'/test',
expressJwt({
secret: secret
}),
function(req, res) {
res.send({message: 'You could use the route!'});
}
);
app.use(function(err, req, res, next) {
res.status(err.status || 500).send({error: err.message});
});
app.listen(4040, function() {
console.log('server up and running at 4040 port');
});
module.exports = app;
test.js:
var request = require('supertest');
var app = require('./app.js');
describe('Test Route with Token', function() {
var token = '';
before(function(done) {
request(app)
.get('/get-token')
.end(function(err, res) {
var result = JSON.parse(res.text);
token = result.token;
done();
});
});
it('should not be able to consume the route /test since no token was sent', function(done) {
request(app)
.post('/test')
.expect(401, done);
});
it('should be able to consume the route /test since token valid was sent', function(done) {
request(app)
.post('/test')
.set('Authorization', 'Bearer ' + token)
.expect(200, done);
});
});

How to configure express js 4 to serve some pages in http and others in https?

Is there any way to configure a node js application with express js 4 to serve some pages under http protocol and other, those which need more security, in https?
I describe my problem: I'm developing a shop online and I want to display certain pages, like the products list or the product detail views under http, and others which I think need more security, like login or the shopping cart views, under https protocol.
I have tried the express-force-ssl module, but it isn't working. The following code snippet is not from my app (which is too dirty) it is just an example which alos doesn't work for me:
var express = require('express');
var forceSSL = require('express-force-ssl');
var fs = require('fs');
var http = require('http');
var https = require('https');
var ssl_options = {
key: fs.readFileSync('./server-private-key.pem'),
cert: fs.readFileSync('./server-certificate.pem'),
ca: fs.readFileSync('./server-certificate-signing-request.pem')
};
var app = express();
var server = http.createServer(app);
var secureServer = https.createServer(ssl_options, app);
app.use(forceSSL);
app.get('/', function (req, res, next) {
res.send('Hello')
});
app.get('/user/:name', function (req, res, next) {
var user = req.params.name;
res.send('Hello ' + user + '')
});
app.get('/login', forceSSL, function (req, res, next) {
res.send('Hello<br/>Goodbye')
});
app.get('/logout', forceSSL, function (req, res, next) {
res.send('Hello')
});
secureServer.listen(443)
server.listen(8085)
console.log('server started');
The result is that when I launch the application, with url http://localhost:8085, the server automatically redirects it to https://localhost and serves all pages in https protocol.
What I want is to start on http://localhost:8085, navigate to http://localhost/user/userA, then from it go to https://localhost/login and, if click on "Hello" link, I would like to be redirected to http://localhost:8085.
Is there any missing code to get the behavior I want or even any other way to reach it without express-force-ssl module?
I have asked to the author of express-force-ssl module and he has told me that the redirect behavior works as expected. Here is the post.
But diving a little more in its code I've created a custom plugin to solve my problem. Here is the code:
var parseUrl = require('url').parse;
var isSecure = function (req) {
if (req.secure) {
return true;
}
else if (
req.get('X-Forwarded-Proto') &&
req.get('X-Forwarded-Proto').toLowerCase &&
req.get('X-Forwarded-Proto').toLowerCase() === 'https') {
return true;
}
return false;
};
exports = module.exports = function (req, res, next) {
if (isSecure(req)) {
if (req.method === "GET") {
var httpPort = req.app.get('httpPort') || 80;
var fullUrl = parseUrl(req.protocol + '://' + req.header('Host') + req.originalUrl);
res.redirect('http://' + fullUrl.hostname + ':' + httpPort + req.originalUrl);
}
else {
next();
}
}
else {
next();
}
};
It's very similar to force-ssl file but here we manage the opposite action, i.e., here I redirect to http when a route is forced to it. So it's needed to add the function to every route we want to see under http protocol:
var express = require('express');
var forceSSL = require('express-force-ssl');
var fs = require('fs');
var http = require('http');
var https = require('https');
var useHttp = require('./useHttp');
var ssl_options = {
key: fs.readFileSync('./server-private-key.pem'),
cert: fs.readFileSync('./server-certificate.pem')
};
var app = express();
var server = http.createServer(app);
var secureServer = https.createServer(ssl_options, app);
app.get('/', useHttp, function (req, res, next) {
res.send('Hello')
});
app.get('/user/:name', useHttp, function (req, res, next) {
var user = req.params.name;
res.send('Hello ' + user + '')
});
app.get('/login', forceSSL, function (req, res, next) {
res.send('Hello<br/>Goodbye')
});
app.get('/logout', forceSSL, function (req, res, next) {
res.send('Hello')
});
app.set('httpsPort', 9090);
app.set('httpPort', 8085);
secureServer.listen(9090)
server.listen(8085)
console.log('server started');
As you can see I need now to specify in all routes which protocol use: useHttp for http or forceSSL for https.
Although I'm not comfortable at all with this solution because I have to specify in all routes which kind of protocol I want. But at least it works. So I would be very pleased if someone finds any other solution, for isntance, adding in middleware layer a function to manage all http requests and just redirect to https when it is specified with forceSSL. By the moment, this works.

http request does not emit 'end' after proxy

I need to be able to use the http request body in my request proxy application and then again in the actual web service. I am using restreamer to 'reset' the stream (and even wrote the middleware myself with no change). The web service receives the body just fine, but because end is never emitted, I cannot continue with the request.
Testing with postman, sending a raw body, with content type set. Any suggestions would be greatly appreciated. Thanks!
var express = require('express')
, bodyParser = require('body-parser')
, http = require('http')
, restreamer = require('connect-restreamer')
, httpProxy = require('http-proxy')
var app = express();
app.use(function (req, res, next) {
var body = '';
req.on('data', function (chunk) {
body += chunk.toString('utf8');
});
req.on('end', function (chunk) {
req.body = JSON.parse(body)
next();
});
});
app.use(restreamer());
var proxy = httpProxy.createServer();
app.all('*', function (req, res) {
proxy.web(req, res, {
target: 'http://localhost:8001'
});
});
http.createServer(app).listen(8000);
app2 = express();
app2.use(function (req, res, next) {
var body = '';
req.on('data', function (chunk) {
body += chunk.toString('utf8');
});
req.on('end', function (chunk) {
req.body = JSON.parse(body)
next();
});
});
app2.all('*', function (req, res) {
res.send(req.body)
});
http.createServer(app2).listen(8001);
Using the request library in my application, it worked:
var request = require('request')
request.post({
url: 'http://localhost:8000',
json: {content: 123, type: "greeting", access_token: "here i am"}
},function(err, res,data){
console.log('return:' ,err, data)
});
But using curl with a file containing the same message, it would not work:
curl -X POST -d #data.json http://localhost:8000 --header "Content-Type:application/json"
I compared the request objects against each other and found a few differences and when I got to the request header for content-length, I found that editing it the "correct" length would end the steam (and the web server would send a response).
I will make the modifications needed and commit to connect-restreamer module.

Rally: v2.0 API Authorization within Node.js to handle POST requests

I created a small server in Node.js to handle Rally POST requests. This has been working until I updated to the Rally v2.0 API. With the new authorization model, I'm not sure what I have to do to my server so that I no longer get 'Not authorized to perform action: Invalid Key'. I read the Rally authorization doc but I'm not sure how to apply it within the server. Here's what I have that doesn't work:
var express = require('express');
var app = express();
var sys = require('util');
var client = require('restler');
var userNamePassword = { 'username': 'myusername', 'password': 'mypassword' };
app.use(express.bodyParser());
app.all('/rally/projectpermission/create', function(req, res)
{
client.get("https://rally.eng.xxxx.com/slm/webservice/v2.0/security/authorize", userNamePassword)
.on('complete', function(data, response)
{
var result = JSON.parse(data);
var operationResult = result['OperationResult'];
securityToken = operationResult['SecurityToken'];
var p = 'https://rally.eng.xxxx.com/slm/webservice/v2.0/projectpermission/create?key=_SECURITY_TOKEN_'.replace('_SECURITY_TOKEN_', securityToken);
client.postJson(p, req.body, userNamePassword)
.on('complete', function(data, response)
{
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.json(response.statusCode);
})
.on('success', function(data, response)
{
console.log('success: ' + data);
})
.on('fail', function(data, response)
{
console.log('fail: ' + data);
})
.on('error', function(err, response)
{
console.log('error: ' + err);
});
});
});
app.listen(3000);
As long as you're including the credentials on the get request to /security/authorize it should work. Are you getting a valid key back?
We have also recently released a Node.js toolkit for working with Rally's WSAPI. It handles all of this authentication for you and also makes a lot of the other operations like querying easier.
Check it out: https://github.com/rallytools/rally-node

Resources