Authenticated Endpoint Testing with Passport js - node.js

I'm trying to test an authenticated endpoint in my app. My node app uses express, express session, passport-local, react and next for auth.
I've spent way too many hours trying to solve this problem and could not find a solution.
Basically my test would like to:
send a login request and login
send a request to an authenticated route
receive the appropriate response
My issue was that I had no persistence between the login request and the authenticated route request.
When I sent the login request, passport serializes the user and sets req.user and req._passport.session to the appropriate values.
On the next request - the authenticated route, my middleware looks for req.passport._session or req.user but neither exist. I would get the 401 unauthorized response.
I posted my solution below that took me way too long to figure out.

I solved the persistence issue with Chai HTTP - which uses superagent.
The solution was pretty simple once I had the correct tools. I used the tutorial from the chai-http page but changed it to use async await and try catch.
const { assert } = require('chai');
const chai = require('chai');
const { expect } = require('chai');
const chaiHttp = require('chai-http');
chai.use(chaiHttp);
describe('/authenticatedRequest', () => {
it('should respond appropriately', async () => {
const agent = chai.request.agent('http://localhost:8000');
try {
await agent
.post('/rootPath/loginLocal')
.send({ email: 'email#email.com', password: 'password' });
const authenticatedResponse = await agent.get('/rootPAth/authenticatedRoute');
assert.deepEqual(successResponse, workoutRes);
} catch (e) {
console.log(e);
}
});
});
I now see this post How to authenticate Supertest requests with Passport? - which would have saved me a lot of time.
I hope adding having another post will help someone else with this.

Related

Why is my authenticated NodeJS route returning results with an incorrect JWT token?

I have a NodeJS application which I have begun to separate out in to smaller files since the original became a little bloated.
In my index.js I have routes that are protected by a function a freelancer wrote to provide JWT authentication. These routes work as required.
app.use(require('./lib/api-calls/convert.js'));
// Security Enabled index.js
//
const { app } = require ('./lib/deps/init_dependencies.js');
const { enableSecurity } = require("./security");
const main = async () => {
// Enable API JWT Security = Comment out the line below to turn off security.
await enableSecurity(app);
app.get('/v1/createSession:key/:limit', function (req, apiResponse) {
// My route, working well
});
}
main()
I've created /lib/routes/convert.js and am wanting to write new routes in this file which also require JWT authentication. However, I always receive status 200 'OK', regardless of whether the authentication header is correct or not... I'm using Postman to make my calls. Here's my code:
const app = require('express')();
//JWT authentication
const { enableSecurity } = require('../../security');
const main = async () => {
// Enable API JWT Security = Comment out the line below to turn off security.
await enableSecurity(app);
app.get('/v3/convertw3w/:locationValue/:countryCode', function (req, res) {
res.status(200).send({ status: 'OK' });
});
}
main()
module.exports = app;
Can anyone spot the problem? I spent far to long on this last night!
thanks
Just some food for thought here and we do something similar, but if you use
enableSecurity(app)
As middleware on the route and the
next()
function in the middleware you can omit the need to make this a promise, because middelware is designed to process in order of the middleware and the next function tells express to move to the next middleware.
How we do it, is to have the middleware 'auth', because middleware will pass the req and res objects to each one in the stack you can have all your JWT decode in one place.
We typically will pass the token in the header OR the req object, just depends on the mimetype we pass, so in out auth we check if the token is in the header or the req, if so we decode, if it passes decode we pass
next()
in that code block otherwise we res.json({"response":"not authorized"})

Axios on Nodejs wont retain session on requested server while PostMan does

I am able to do the following on PostMan
1) POST method to login to company server.
2) Make other requests as a logged in user on company server.
I have created a nodejs app to communicate with the company server.
I am using axios library to make said communications.
after Logging in to company server, any other calls don't recognize me as an authorized user.
What could be the differences that i could in turn recreate on axios to have that session persistance?
In the browser you use withCredentials in axios - this option automatically saves your session between requests. But in node.js this parameter does not work because axios uses the http node.js module instead of XHR.
In node.js, you can use an axios instance for save cookie between requests.
Simplest way is:
Create instance
const BASE_URL = "https://stackoverflow.com";
// Create instance of axios which utilizes BASE_URL
const axiosInstance = axios.create({ baseURL: BASE_URL });
Write createSession function
const createSession = async () => {
console.log("create session");
const authParams = {
username: "username",
password: "password"
};
const resp = await axios.post(BASE_URL, authParams);
const cookie = resp.headers["set-cookie"][0]; // get cookie from request
axiosInstance.defaults.headers.Cookie = cookie; // attach cookie to axiosInstance for future requests
};
And make call with session cookie
// send Post request to https://stackoverflow.com/protected after created session
createSession().then(() => {
axiosInstance.post('/protected') // with new cookie
})
Be careful, your authorization method may differ from the presented - in this case you can just change the createSession method. If your session has expired, you can login again directly or using axios.interceptors - I attached a link to gist.
Also you can use cookie-jar with axios (link below)
For more info:
https://github.com/axios/axios#creating-an-instance
https://github.com/axios/axios#interceptors
https://gist.github.com/nzvtrk/ebf494441e36200312faf82ce89de9f2
https://github.com/3846masa/axios-cookiejar-support

How to test Login API NodeJs using Mocha and Chai

I am new to Test driven development and I want to test my login API but I cant seem to understand fully how to implement tests with Database and what is the proper way to do it?
First, I am also not an expert in this topic but i have been using this method for quite some time. If anyone find that what i'm writing is wrong or somewhat misleading, please correct me. I am very open to critics and opinions.
As the name suggests, TDD method requires you to write the test before the implementation. Basically, you write the test, see it's failing, write the implementation and repeat until the test is passed.
If you are using express, you may want to use the supertest module. They way to use it is similar to superagent. You can install it by running
npm install supertest --save-dev
I am going to show you a very simple example of how to use it with mocha and chai.
So here's an example of express app:
// file: app.js
const express = require('express');
const app = express();
// your middlewares setup goes here
const server = app.listen(8000, () => {
console.log('Server is listening on port 8000');
});
module.exports = app;
And here's the example test case of the login API:
// file: test/api.js
const request = require('supertest');
const app = require('../app');
const expect = require('chai').expect;
describe('Login API', function() {
it('Should success if credential is valid', function(done) {
request(app)
.post('/api/v1/login')
.set('Accept', 'application/json')
.set('Content-Type', 'application/json')
.send({ username: 'username', password: 'password' })
.expect(200)
.expect('Content-Type', /json/)
.expect(function(response) {
expect(response.body).not.to.be.empty;
expect(response.body).to.be.an('object');
})
.end(done);
});
});
You may run it with this command
node_modules/mocha/bin/mocha test/**/*.js
The example above assumes that you will implement the login API using POST method at /api/v1/login path. It also assumes that you will receive and respond data with json format.
What the example test case does that it tries to send a POST request to /api/v1/login with the following data:
{
username: 'username',
password: 'password'
}
Then, it expects that your API will respond with 200 response code as shown in this line:
.expect(200)
If it receive a response with code other than 200, the test will fail.
Then, it expect that the Content-Type of your response to be application/json. If the expectation does not meet the reality, the test will also fail.
This code below:
.expect(function(response) {
expect(response.body).not.to.be.empty;
expect(response.body).to.be.an('object');
})
It checks the response from your server. You can use the chai's expect inside the function body as shown above. You may notice that supertest also provide expect method. But, the way to use both supertest's expect and chai's expect is different.
And finally, call end function with done callback so that the test case can be run properly.
You may want to check supertest documentation to get more details on how to use it.
Establishing database connection before testing
If you need to maintain a database connection before running all the test case, here's the idea:
Create another file inside the test directory. For example, database_helper.js. Then, write the following code:
before(function(done) {
// write database connection code here
// call done when the connection is established
});
I've tried it with mongoose before, and it worked for me.
I hope that helps.

Can't Test Authorization by Third Party: Node/Express/Supertest/Facebook

I'm writing tests for a very early stage Node/Express app. I've made the decision to use only FB logins for most users, so many of my controllers depend on the authenticated state from FB. But I'm not clear how to either test this, or how to preserve the session with the logged in user in a way that passport, which I'm using, will understand.
var expect = require("chai").expect;
var mongoose = require('mongoose');
var User = require('../models/users');
var nock = require('nock');
var parser = require('cookie-parser')
var session = require('supertest-session');
var agent = require('superagent');
var _ = require('lodash');
var app = require('../app');
var testSession = null;
var fbRedirectURL;
beforeEach(function () {
testSession = session(app);
});
it('should authorize with FB', function (done) {
testSession.get('/auth/facebook')
.expect(302)
.end(function(err, res){
fbRedirectURL=decodeURIComponent(res.headers.location);
done()
});
});
it('should get FB data', function (done) {
testSession.get(fbRedirectURL)
.expect(200)
.end(function(err,res) {
console.log()
done()
})
});
These tests work, but the second throws an error: and I am not clear how I use this to maintain a session through other tests, or if I have to somehow mock that, or otherwise try to fool passport.
I've looked high and low, and haven't found much of anything that seems to address the issue of testing with third-party authentication services, and it seems problematic. Are there resources I haven't found? Even passport-facebook's documentation seems to glide over the topic of testing code that depends on FB authenication, and provides a test suite for only their own code.
Thanks in advance.
I couldn't make headway, and I've moved to using E2E testing with nightwatch.js and selenium. Took about an hour to figure it out, and get tests of FB logins: now I just have to see if I can integrate mocha tests with the nightwatch ones…

testing oauth authenticated routes

I'm using koa-passport & koa to handle my login and registration using twitter oauth. It works great, but I'm having difficulties understanding how I should test my authenticated routes using supertest and mocha.
Most examples I have seen involve using supertest to send a username and password to a login endpoint to first create a user session. My problem is that I don't have a username/passport auth strategy. I need to emulate an oauth login process that will set the appropriate session variables, and therefore test certain routes.
Any clue on how I can achieve this?
My solution these days to this problem is to basically insert a mock authenticated user in the middleware stack temporarily when needed. Here's my basic code for express.
var Layer = require('express/lib/router/layer');
var app = require('../../../server');
exports.login = login;
exports.logout = logout;
function login(user){
var fn = function insertUser(req, res, next){
req.user = user;
next();
}
var layer = new Layer('/', {
sesitive: false,
strict: false,
end: false
}, fn);
layer.route = undefined;
app._router.stack.unshift(layer);
}
function logout(){
app._router.stack.shift();
}
And then within your tests, you call:
it('should allow the user to edit something', function(done){
login(this.user);
// perform supertest task
logout();
});
This is obviously pretty rudimentary... but seems to do the job for testing purposes.

Resources