HMAC Authentication with Supertest - node.js

I need to intercept the supertest request to sign my requests as our endpoints all require to be signed.
We follow a similar HMAC Signing process as AWS, I can generate the signature fine. But I see no easy way to intercept the request to iterate through the http request headers etc...
Ideally I thought I could do:
it("return 401 if auth header is signed wrong", function (done) {
api
.post('/v1/users/sessions')
.use(function(request, response){
hmac.signRequest(request, wrongSecret, wrongAccess);
})
.expect(401)
.end(function (err, res) {
if (err) return done(err);
done();
});
On the Super-Agent project there appears to be .use(fn) method. Which has the req,res passed through as params to the callback.
But that seems to not apply to the supertest agent.
Am I missing something or is there another simple way to do this?

Related

Maintaining session while testing using SuperTest

I am looking to use supertest to test API requests and responses. Following is what I have tried so far.
route.test.js
const testUtils = require('./setupTestUtils');
let authenticateUser = request.agent(app);
before(function(done){
testUtils.login(authenticateUser, userCredentials).then((res) => {
expect(res.statusCode).to.equal(200);
done();
}, (err) => {
console.log(err);
done(err);
});
});
setupTestUtils.js
function login (rest, testUserLogin) {
let defer = Q.defer();
rest.post('/login')
.send(testUserLogin)
.expect(200)
.end(function () {
rest.get('/loggedin')
.expect((res) => {
if (err) {
console.log('ERROR: ' + JSON.stringify(err));
defer.reject(err);
} else {
defer.resolve(res);
}
})
.end();
});
return defer.promise;
}
In my app.js, I use passport to authenticate. After authentication, I use the session.regenerate function to regenerate the session ID to avoid session fixation.
The initial post request to login passes without any failure. However, the subsequent GET request 'loggedIn' fails. This function internally uses the req.isAuthenticated() function from passport. This always returns false.
On investigation, I found that the session ID between the regenerated session and the request object (for req.isAuthenticated()) is different.
From my search, I understand that the cookies should be maintained automatically by the use of 'agent' from supertest. However that doesnt seem to be the case for me. I have also tried maintaining the cookies from the initial response. That doesnt seem to work for me either. " res.headers['set-cookie'] " comes in as undefined (not sure why that is happening either).
Can someone please help me understand what I am missing here.?
Am using versions - Supertest #v6.0.1 and passport #v0.4.1
I found the solution to my issue in an old github issue raised on supertest's page. Linking it here for reference.
Essentially, the supertest runs express in insecure port and I had configured my session otherwise. Ideally, we would have to check the environment before setting this variable to false - as represented here.
Hope this saves someone the time I spent!

microservice with node and needle

I'm trying to make my monolithic application written in node and express to a microservice application in node, needle(to communicate between microservices) and express. My architecture is as follows,
I have a microservice called gateway_service to handle all the requests from frontend UI
I have another microservice called users_service to handle requests related to users, users_service uses passport local strategy to manage sessions and this session id is sent as a cookie by passport
When I try to change my application to microservice I'm doing the following
Whenever I receive a request from my client say, postman in gateway_service I can see the cookie coming through header, and I'm able to pass it to user_service by setting headers option as mentioned in needle.
How to send the cookie that I receive from user_service to the postman back to he can save the cookie(in case of first hit)??
I'm adding a small code snippet, hoping it will help?
Am I doing it in the right way??
when I'm doing as shown below Im getting the error "Cannot set headers after they are sent to the client"
function (req, res, next) {
var options = {
headers: req.headers,
parse_cookies:true
}
needle.post(url + "login", req.body, options, function (error, response, body) {
if (!error) {
res.writeHead(response.statusCode,response.headers);
res.send(body)
console.log(res);
next();
} else {
console.log("error in info from users micro service");
res.send(error);
next();
}
});
},

"Error: Can't set headers after they are sent" in Express app with Node

I am new to Express and Node, and when testing a protected REST endpoint in my app using Advanced REST Client the data is returned from the endpoint to the Client as expected, however the console logs
"Error: Can't set headers after they are sent"
which stops the server. Searching here on SO, this seems to occur when sending more than one response but I don't see that in my code:
router.get('/books', userAuthenticated, function (req, res, next) {
Book.find({}, function(err, docs){
if(err) {
res.send(err)
} else {
res.send(docs);
// next();
// return;
}
})
});
Is this error expected when sending a request/response to/from the Client or am I missing something in handling the error on the server?
Express is a node.js server that features a concept called "middleware", where multiple layers of code get a chance to operate on a request.
In that case, not only do you need to check that your function is not sending back a response twice, but you have to check that other middleware are not sending back a response as well.
In your case, the code indicates that middleware called "userAuthenticated" is being invoked before this function. You should check if that middleware is already sending a response.
I don't think the problem was in the middleware. It looks like I was calling done() twice in passport deserialize. I commented out the first instance and the problem disappeared in all my routes. Although, I am not sure if I should comment out the first or second instance but I'll work on that next.
passport.deserializeUser(function(obj, done) {
console.log(obj);
Account.findById(obj, function(err, user) {
console.log(user);
//done(err, user);
});
return done(null, obj);
});

Node.js - Unit Testing Middleware

I have an api with a middleware function which I use to filter incoming requests. The functions checks the present of a token in the header, then makes two calls to the database, one to check the token and one to get some information and pass it on to the request object, if the 1st call was successful.
I am struggling to understand how to unit test this functions, by mocking up the request object and also the database calls.
middleware.js
exports.checkToken = function (req, res, next) {
if (!req.get('token')) {
return res.status(400).json('Bad request');
}
var token = req.get('token'); //get token from the header
User.findOne({'token': token}, function(err, user) {
// skipped error checking or no user found
Account.findOne({'_id': user.account}, function(err, account) {
// skipped error checking or no account found
req.somevalue = account;
return next();
});
});
};
Currently I am using mocha, chai and sinon and was thinking of the following:
mock User.findOne and Account.findOne using sinon.stub()
not really sure what to do about the req, res and next objects. How to emulate these?
I think the best choice is to use supertest.
https://www.npmjs.com/package/supertest
This package allow to run tests that emulate the full request cicle on the application.

Are the req and res objects somehow accessible globally in NodeExpress?

req.session.username = user.username;
I'm using supertest to test route handling. In my log in handling code I have the following if a user logs in successfully -
req.session.username = user.username;
But in the supertest call back function I don't have access to the req object.
request(app)
.post('/login')
.send({username: 'dummy_username', password: 'valid_password'})
.end(function(err, res){
if (err) { return done(err); }
expect(err).toBe(null);
expect(res.status).toEqual(200);
done();
});
I would like to add in something like expect(req.session.username).toBe('dummy_username') but obviously I can't I do this when req is not available to me. So is there a way of referencing the req object?
Supertest is for testing the responses only, since testing the request (and the server-side manipulations thereof) would be testing implementation details instead of behavior. supertest isn't the right tool for this job. You can write pure unit tests for some of your server side functions, OR you can have the /login route include the user's information in the response body (which is typical) and have supertest verify that information matches what was in the request.

Resources