web-dev-qa-db-fra.com

Comment authentifier les demandes Supertest avec Passport?

J'utilise Passport.js pour l'authentification (stratégie locale) et les tests avec Mocha et Supertest.

Comment puis-je créer une session et faire des demandes authentifiées avec Supertest?

58
Gal Ben-Haim

Vous devez utiliser superagent pour cela. C'est un module de niveau inférieur et utilisé par supertest. Jetez un oeil à la section Persistance d'un agent :

var request = require('superagent');
var user1 = request.agent();
user1
  .post('http://localhost:4000/signin')
  .send({ user: '[email protected]', password: 'password' })
  .end(function(err, res) {
    // user1 will manage its own cookies
    // res.redirects contains an Array of redirects
  });

Vous pouvez maintenant utiliser user1 pour effectuer des demandes authentifiées.

44
zemirco

Comme le souligne zeMirco, le module superagent sous-jacent prend en charge les sessions et gère automatiquement les cookies pour vous. Cependant, il est possible d'utiliser la fonctionnalité superagent.agent() à partir de supertest, via une fonctionnalité non documentée.

Utilisez simplement require('supertest').agent('url') au lieu de require('supertest')('url'):

var request = require('supertest');
var server = request.agent('http://localhost:3000');

describe('GET /api/getDir', function(){
    it('login', loginUser());
    it('uri that requires user to be logged in', function(done){
    server
        .get('/api/getDir')                       
        .expect(200)
        .end(function(err, res){
            if (err) return done(err);
            console.log(res.body);
            done()
        });
    });
});


function loginUser() {
    return function(done) {
        server
            .post('/login')
            .send({ username: 'admin', password: 'admin' })
            .expect(302)
            .expect('Location', '/')
            .end(onResponse);

        function onResponse(err, res) {
           if (err) return done(err);
           return done();
        }
    };
};
53
Andy

Essaye ça,

  var request=require('supertest');
  var cookie;
  request(app)
  .post('/login')
  .send({ email: "[email protected]", password:'password' })
  .end(function(err,res){
    res.should.have.status(200);
    cookie = res.headers['set-cookie'];
    done();        
  });

  //
  // and use the cookie on the next request
  request(app)
  .get('/v1/your/path')
  .set('cookie', cookie)
  .end(function(err,res){  
    res.should.have.status(200);
    done();        
  });
26
grub

En complément de la réponse d'Andy, pour que Supertest démarre votre serveur pour vous, vous pouvez le faire comme ceci:

var request = require('supertest');

/**
 * `../server` should point to your main server bootstrap file,
 * which has your express app exported. For example:
 * 
 * var app = express();
 * module.exports = app;
 */
var server = require('../server');

// Using request.agent() is the key
var agent = request.agent(server);

describe('Sessions', function() {

  it('Should create a session', function(done) {
    agent.post('/api/session')
    .send({ username: 'user', password: 'pass' })
    .end(function(err, res) {
      expect(req.status).to.equal(201);
      done();
    });
  });

  it('Should return the current session', function(done) {
    agent.get('/api/session').end(function(err, res) {
      expect(req.status).to.equal(200);
      done();
    });
  });
});
10
Kevin C.

Je suis désolé, mais aucune des solutions suggérées ne fonctionne pour moi.

Avec supertest.agent() Je ne peux pas utiliser l'instance app, je dois au préalable exécuter le serveur et spécifier le http://127.0.0.1:port et en plus je ne peux pas utiliser les attentes (assertions) de supertest, je ne peux pas utiliser le supertest-as-promised lib et ainsi de suite ...

Le cas cookies ne fonctionnera pas du tout pour moi.

Donc, ma solution est:

Si vous utilisez Passport.js, il utilise le mécanisme "Jeton de porteur" et vous pouvez utiliser les exemples suivants dans vos spécifications:

var request = require('supertest');
var should = require('should');

var app = require('../server/app.js'); // your server.js file

describe('Some auth-required API', function () {
  var token;

  before(function (done) {
    request(app)
      .post('/auth/local')
      .send({
        email: '[email protected]',
        password: 'the secret'
      })
      .end(function (err, res) {
        if (err) {
          return done(err);
        }

        res.body.should.to.have.property('token');
        token = res.body.token;

        done();
      });
  });

  it('should respond with status code 200 and so on...', function (done) {
    request(app)
      .get('/api/v2/blah-blah')
      .set('authorization', 'Bearer ' + token) // 1) using the authorization header
      .expect(200)
      .expect('Content-Type', /json/)
      .end(function (err, res) {
        if (err) {
          return done(err);
        }

        // some `res.body` assertions...

        done();
      });
  });

  it('should respond with status code 200 and so on...', function (done) {
    request(app)
      .get('/api/v2/blah-blah')
      .query({access_token: token}) // 2) using the query string
      .expect(200)
      .expect('Content-Type', /json/)
      .end(function (err, res) {
        if (err) {
          return done(err);
        }

        // some `res.body` assertions...

        done();
      });
  });
});

Vous voudrez peut-être avoir une fonction d'aide pour authentifier les utilisateurs:

test/auth-helper.js

'use strict';

var request = require('supertest');
var app = require('app.js');

/**
 * Authenticate a test user.
 *
 * @param {User} user
 * @param {function(err:Error, token:String)} callback
 */
exports.authenticate = function (user, callback) {
  request(app)
    .post('/auth/local')
    .send({
      email: user.email,
      password: user.password
    })
    .end(function (err, res) {
      if (err) {
        return callback(err);
      }

      callback(null, res.body.token);
    });
};

Passez une journée productive!

6
Dan K.K.

Je vais supposer que vous utilisez le middleware CookieSession.

Comme grub l'a mentionné, votre objectif est d'obtenir une valeur de cookie à transmettre à votre demande. Cependant, pour une raison quelconque (au moins dans mes tests), le supertest ne déclenchera pas 2 demandes dans le même test. Nous devons donc procéder à une ingénierie inverse pour obtenir la bonne valeur de cookie. Tout d'abord, vous aurez besoin des modules pour construire votre cookie:

var Cookie          = require("express/node_modules/connect/lib/middleware/session/cookie")
  , cookieSignature = require("express/node_modules/cookie-signature")

Oui, c'est moche. Je les mets en haut de mon fichier de test.

Ensuite, nous devons construire la valeur du cookie. Je mets ceci dans un beforeEach pour les tests qui nécessiteraient un utilisateur authentifié:

var cookie = new Cookie()
  , session = {
      passport: {
        user: Test.user.id
      }
    }

var val = "j:" + JSON.stringify(session)
val = 's:' + cookieSignature.sign(val, App.config.cookieSecret)
Test.cookie = cookie.serialize("session",val)

Test.user.id était précédemment défini dans la partie de ma chaîne beforeEach qui définissait l'utilisateur que j'allais "connecter". La structure de session est la façon dont Passport (au moins actuellement) insère les informations utilisateur actuelles dans votre session.

Le var val lignes avec "j:" et "s:" sont extraits du middleware Connect CookieSession sur lequel Passport se repliera si vous utilisez des sessions basées sur les cookies. Enfin, nous sérialisons le cookie. Je mets "session" là-dedans, car c'est ainsi que j'ai configuré mon middleware de session de cookie. Également, App.config.cookieSecret est défini ailleurs, et ce doit être le secret que vous transmettez à votre middleware Express/Connect CookieSession. Je le cache dans Test.cookie pour pouvoir y accéder plus tard.

Maintenant, dans le test réel, vous devez utiliser ce cookie. Par exemple, j'ai le test suivant:

it("should logout a user", function(done) {
  r = request(App.app)
    .del(App.Test.versionedPath("/logout"))
    .set("cookie", Test.cookie)
    // ... other sets and expectations and your .end
}

Notez l'appel à set avec "cookie" et Test.cookie. Cela entraînera la demande d'utilisation du cookie que nous avons construit.

Et maintenant, vous avez simulé votre application en pensant que l'utilisateur est connecté, et vous n'avez plus besoin de faire fonctionner un serveur réel.

3
juanpaco