Supertest, test secure REST API - node.js

I am writing an integration test for a REST API protected by a jwt.
One API operation POST /user/token is returning a jwt given a username and a password and this token is then used for a list of operations such as:
GET /user/:id
Where the route is using jwt({secret: secret.secretToken}), so the token is included into the HTTP header Authorization.
When testing with supertest, I can have nested testing but I want to first get the token, then use this token for testing other operations.
POST /user/token => 12345
GET /user/:id, `Authorization Bearer 12345`
GET /user/:foo, `Authorization Bearer 12345`
How to avoid generating a new token for every operation testing (see below) but use only a single one generate by POST /user/token.
it('should get a valid token for user: user1', function(done) {
request(url)
.post('/user/token')
.send({ _id: user1._id, password: user1.password })
.expect(200) // created
.end(function(err, res) {
// test operation GET /user/:id

You want to perform single POST to /user/token and then use the token received in every test case? If so, then use the before hook of the test framework you are using (Mocha?) and store the token to a variable, e.g.
describe('My API tests', function() {
var token = null;
before(function(done) {
request(url)
.post('/user/token')
.send({ _id: user1._id, password: user1.password })
.end(function(err, res) {
token = res.body.token; // Or something
done();
});
});
it('should get a valid token for user: user1', function(done) {
request('/get/user')
.set('Authorization', 'Bearer ' + token)
.expect(200, done);
});
});

Need to set Authorization as 'Bearer ' + token
var token = null;
before(function(done) {
request(url)
.post('/user/token')
.send({ _id: user1._id, password: user1.password })
.end(function(err, res) {
token = res.body.token; // Or something
done();
});
});
it('should get a valid token for user: user1', function(done) {
request('/get/user')
.set('Authorization', 'Bearer ' + token)
.expect(200, done);
});

Related

how to write TDD to check cookies

i'm trying to test if the response containing any cookies by using this unit test :
describe("given user is found", () => {
it("should return 200 status , access token and refresh token", async () => {
const response = await request(app)
.post("/api/v1/auth/signIn")
.send({
email: "sandra#gmail.com",
password: "sandra",
})
.expect(200);
expect(response.cookie).toBe(
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MjEsImlhdCI6MTY2MDQ0NDg0MSwiZXhwIjoxNjYxMDQ5NjQxfQ.pniLcI6wXqWooQgO1oH4ZvDgoQ-ul3jVYytT8FpJEzU"
);
});
});
but it returns that it's undefinend and i concoluded that because the cookie parser is not there , so how can i test if access token is found or not??

Use JWT token in multiple test cases mocha node-js

How I create a reusable function which gives me a JWT token, so I can execute my test cases in which token is required without calling the login function again and again in each test case file
account.js
describe("Account", () => {
var token;
describe("/POST Login", () => {
it("it should gives the token", (done) => {
chai.request(server)
.post('api/v1/account')
.set('Accept', 'application/json')
.send({ "email": "john#gmail.com", "password": "123456" })
.end((err, res) => {
res.should.have.status(200);
res.body.should.have.property("token");
token = res.body.token //----------------TOKEN SET
done();
});
});
});
describe("/GET account", () => {
it("it should get the user account", (done) => {
chai.request(server)
.get('api/v1/account')
.set('x-auth-token', token)
.end((err, res) => {
res.should.have.status(200);
done();
});
});
});
});
category
describe("Category", () => {
var token;
//Login function duplicate in both the files
describe("/POST Login", () => {
it("it should gives the token", (done) => {
chai.request(server)
.post('api/v1/account')
.set('Accept', 'application/json')
.send({ "email": "john#gmail.com", "password": "123456" })
.end((err, res) => {
res.should.have.status(200);
res.body.should.have.property("token");
token = res.body.token //----------------TOKEN SET
done();
});
});
});
describe("/GET category", () => {
it("it should get the user account", (done) => {
chai.request(server)
.get('api/v1/account')
.set('x-auth-token', token)
.end((err, res) => {
res.should.have.status(200);
done();
});
});
});
});
I want to get the token from other file and used in different cases. What is the best approach todo this?
I'm basing my response on the fact that you have mentioned unit tests. Usually, with a unit test, you're testing a small piece of functionality. This means, that you want to test a small piece of logic within a bigger component/piece of logic and you are not interested in testing other components (like for example in your case testing your API. What you usually want to test is how your logic should behave in the case you will receive a 200 success response from the API or what happens with your logic if you receive a 400 or 500. I would recommend to mock your API calls for your testing by using a library like nock:
https://www.npmjs.com/package/nock
The way that you are trying to implement it it might be a bit more complicated. If you want to do this kind of testing I wouldn't pick jest/mocha as test runners. I would prepare a postman collection (probably you already have that) and then I would utilise newman to run my collection and actually do the integration testing that you want. You can read further here: Running collections on the command line with Newman
There are different approaches as well, but the one above can be a good one.
Use a before hook that would always login users and generates a token that can be used in your new test file.
let token;
before('Login user', async () => {
const response = await chai.request(server)
.post('api/v1/account')
.set('Accept', 'application/json')
.send({ "email": "john#gmail.com", "password": "123456" })
token = res.body.token;
});

What is the proper way to test routes secured with jwt token?

While the following test passes I feel I'm doing this wrong. Am I expected to log in every time i need to test a secure route? I've tried passing global vars around after i get the initial token but passing vars i'm finding extremely counter intuitive. Passing variables in a before() call presents me same issue as passing / accessing global vars inside nested promises.
describe('Users', function(done) {
var testToken = 'my-test-token'
it('logs in', function(done) { // <= Pass in done callback
var rT = 'tttttt'
chai.request(urlroot)
.post('/login')
.type('form')
.send({ email: 'test_user_1#this.com', password: '9999' })
.end(function(err, res) {
expect(res).to.have.status(200);
expect(res.body.token).to.be.a('string');
done()
});
});
it('gets all users', function(done) { // <= Pass in done callback
// console.log(urlroot + '/users');
chai.request(urlroot)
.post('/login')
.type('form')
.send({ email: 'test_user_1#this.com', password: '9999' })
.end(function(err, res) {
chai.request(urlapi)
.get('/users?secret_token='+res.body.token)
.end(function(err, res){
console.log('data', res.body);
// expect(res.body).to.be.json()
})
});
});
});
What I do is use before() method to call my authenticate service to get the token in the same way that the aplication would, and store into a variable.
Something like:
var token = "";
before(async () => {
//Get token
token = "Bearer " + await getToken();
});
Then, in every test you want to use the credentials use .set()
it('...', function (done) {
chai
.request(url)
.set("Authorization", token) //Call .set() before .get()
.get("/users")
//...
})

Can't made authenticated requests with Mocha and Supertest

I'm trying to test an API with Mocha and Supertest without lucky to make it work.
I have the following code:
var supertest = require('supertest');
describe('Routing', function() {
var url = 'http://example.com';
var server = supertest.agent(url);
var credentials = {
user: 'username',
pass: 'password'
};
describe('Login', function() {
it('should login ok given valid credentials', function(done) {
server
.post('/login.php')
.send(credentials)
.end(function(err, res) {
if (err) {
throw err;
}
server.saveCookies(res);
done();
});
});
it('should correctly make an authenticated request', function(done){
server
.get('/api/me/accounts?_=1449865354112')
.end(function(err,res) {
if (err) {
throw err;
}
res.status.should.be.equal(200);
done();
});
});
});
});
The login request works fine, I get authenticated. The second call throws a 401 status.
I read the documentation but I can't make it work.
What is wrong?
thanks!
UPDATE:
I finally get authenticated by sending the params using .field('user', 'myUsername') and .field('pass', 'myPassword').
Also I have to persist the cookie between calls:
cookie = res.headers['set-cookie']; when I get authenticated, and .set('cookie', cookie) in the next requests.
.send() is for your data. .auth() is for your credentials. Try:
it('should login ok given valid credentials', function(done) {
server
.post('/login.php')
.auth(credentials)
.send({"some": "value"})
.expect(200)
.end(function(err, res) {
if (err) {
done(error);
}
server.saveCookies(res);
done();
});
});
See http://visionmedia.github.io/superagent/docs/test.html for a bit more information on supertest.
That is because the session (cookie) is not persisted between your two tests.
First you should do the two calls inside the same test.
Second i remember that i have used superagent to persist the session between two calls to the same server. But it seems that supertest now expose the agent to persist the session.
var supertest = require('supertest');
var app = express();
var agent = supertest.agent(app);
// then you can persist cookie
agent
.post('/login.php')
.auth(credentials)
...
edit :
here is an example of how i have used superagent for tests :
var request = require('superagent');
var postData= {
email: 'john#test.com',
password: 'test'
};
var user1 = request.agent();
user1.post('http://localhost:3000/user/login')
.send(postData)
.end(function (err, res) {
expect(err).to.not.exist;
expect(res.status).to.equal(200);
var result = res.body;
expect(result.data.message).to.equal('Login successful');
user1.get('http://localhost:3000/user')
.end(function (err, res) {
expect(err).to.not.exist;
expect(res.status).to.equal(200);
var result = res.body;
expect(result.data.email).to.equal('john#test.com');
done();
});
});

Why am I getting a 401 response in my tests?

I am trying to test a route with authentication in my Node / Express / Mongoose back-end.
Here's the test file
var should = require('should');
var _ = require('lodash');
var async = require('async');
var app = require('../../../../app');
var request = require('supertest');
var mongoose = require('mongoose');
var User = mongoose.model('User');
var Firm = mongoose.model('Firm');
var firm, user, userPassword, createdFirm, loggedInUser;
describe('GET /api/firms', function(){
beforeEach(function (done) {
firm = new Firm({
company: 'My test company',
corporateMail: 'test.com'
});
userPassword = 'password';
user = new User({
fistname: 'Fake User',
lastname: 'Fake User',
email: 'test#test.com',
job: 'Partner',
firmName:firm.company,
password:userPassword,
isActivated:true,
_firmId:firm._id
});
function createFirm(cb){
request(app)
.post('/api/firms')
.send(firm)
.expect(201)
.end(function(err, res){
if ( err ) throw err;
createdFirm = res.body;
cb();
});
}
function createUser(cb){
request(app)
.post('/api/common/users')
.send(user)
.expect(200)
.end(function(err, res){
createdUser = res.body;
if ( err ) throw err;
cb();
});
};
async.series([function(cb){
createFirm(cb);
}, function(cb){
createUser(cb);
}], done);
});
afterEach(function (done) {
firm.remove();
user.remove();
done();
});
it('should respond with 401 error', function(done) {
request(app)
.get('/api/firms')
.expect(401)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
it('should login', function(done) {
request(app)
.post('/auth/local')
.send({email:user.email, password:user.password})
.expect(200)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
it('should respond with 200 after login', function(done) {
request(app)
.get('/api/firms')
.expect(200)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
});
In the workflow the firm object is created first and then returns its Id so I can create the user with the firmId as a reference.
I would like to test the /api/firms route after the user is authenticated but in spite of my various attempts (using superagent, logging in the before section) I always get a 401 response in the last should section instead of an expected 200.
Actually the important thing to keep in mind is, as KJ3 said, how the authentication is set up. In my case I forgot to mention that I was using jwt. The way it works is the following, you supply a username and a password and the server returns a token created with jwt.
So it makes sense to send the token back for each request in the tests.
The way to achieve this is first by storing the token after authentication in the before section
function createUser(cb){
request(app)
.post('/api/users')
.send(user)
.expect(200)
.end(function(err, res){
if ( err ) throw err;
authToken = res.body.token;
cb();
});
};
Then by adding .set in the request with the token in the correct format ('Bearer ' + token , which is defined in the authentication service):
it('should respond with 200', function(done) {
var authToken = 'Bearer ' + createdUser.token;
request(app)
.get('/api/firms')
.set('Authorization', authToken)
.expect(200)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
In the case the test sends a 200 back, which is expected and sends a 401 if the .set(...) is commented out.
Good news is that this is achieved with supertest, so no need to add anything, less good news is that you need to add the .set(...) to each test request.
If you were to go through the last 2 tests in a browser, depending on how you have it setup, yes it would work thanks to cookies and sessions, but here the /api/firms test is independent of the auth/local test. So a 401 is the correct response.
It really depends on how your auth is setup, but you need to authenticate on the /api/firms test too. Either by sending the credentials again (every single one of my mocha tests authenticates each time) or implement sessions into the tests, see this SO post for some direction.

Resources