1

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.

WSBT
  • 33,033
  • 18
  • 128
  • 133
vonwolf
  • 635
  • 3
  • 10
  • 23

2 Answers2

1

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.

vonwolf
  • 635
  • 3
  • 10
  • 23
0

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.

Community
  • 1
  • 1
KJ3
  • 5,168
  • 4
  • 33
  • 53
  • I tried this without success already by adding `.auth({email:user.email, password:user.password})` after the get but the 3rd test stills throws the 401 – vonwolf Oct 14 '15 at 16:34
  • could you show an example of your mocha test authentication you are using in each of your test? Thanks – vonwolf Oct 15 '15 at 06:08