Как проверить аутентификацию с помощью jwt внутри файла cookie с помощью супертеста, паспорта и JEST

Привет, ребята, я сейчас пытаюсь сделать что-то похожее на то, что размещено здесь: Как для аутентификации запросов супертеста с помощью паспорта?

поскольку я хотел бы протестировать другие конечные точки, для которых требуется аутентификация, но, кроме того, необходимо передать файл jwt. Прямо сейчас я протестировал его в POSTMAN и в браузере, и кажется, что он работает нормально, но мои тестовые примеры продолжают ломаться. У меня есть маршрут POST для входа в систему, который настроен так:

AccountService.js

// Login POST route
  router.post('/account_service/login', (req, res, next) => {
    passport.authenticate('local-login', (err, user, info) => {
      try {
        if (err) {
          const error = new Error('An Error occurred: Cannot find user');
          return next(error);
        } else if (!user) {
          return res.redirect('/account_service/login');
        }
        req.login(user, { session: false }, (error) => {
          if (error) {
            return next(error);
          }
          const email = req.body.email;
          const role = req.user[0].role;
          const id = req.user[0].id;

          const user = {
            email: email,
            role: role,
            id: id
          };
          const accessToken = jwt.sign(user, config.ACCESS_TOKEN_SECRET, {
            expiresIn: 28800 // expires in 8 hours
          });
          const cookie = req.cookies.cookieName;
          if (cookie === undefined) {
            // set a new cookie
            console.log('setting new cookie');
            res.cookie('jwt', accessToken, { maxAge: 900000, httpOnly: true });
            res.send({ token: accessToken });
          } else {
            // cookie was already present
            console.log('cookie exists', cookie);
          }
          res.redirect('/account_service/profile');
        });
      } catch (error) {
        return next(error);
      }
    })(req, res, next);
  });

После аутентификации пользователя я назначаю ему веб-токен JSON и помещаю его в файл cookie, чтобы он сохранялся в заголовках для авторизованных запросов. Вот пример:

AccountService.js

// Get all users
  router.get('/account_service/all_users', passport.authenticate('jwt', { session: false }), (req, res, next) => {
    const sql = 'select * from user';
    const params = [];
    db.all(sql, params, (err, rows) => {
      if (err) {
        res.status(500).json({ error: err.message });
        return;
      }
      res.json({
        message: 'success',
        data: rows
      });
    });
  });

Я использую password.authenticate, чтобы убедиться, что jwt действителен. Этот запрос GET работает только после того, как я вхожу в систему с учетной записью администратора.

В моем файле паспорта я настроил его так:

Passport.js

const LocalStrategy = require('passport-local').Strategy;
const db = require('../database.js');
const bcrypt = require('bcrypt');
const config = require('../config/config.js');
const JwtStrategy = require('passport-jwt').Strategy;

const cookieExtractor = function (req) {
  var token = null;
  if (req && req.cookies) token = req.cookies.jwt;
  return token;
};

module.exports = function (passport) {
  passport.serializeUser(function (user, done) {
    done(null, user);
  });
  passport.deserializeUser(function (user, done) {
    done(null, user);
  });
  passport.use('local-login', new LocalStrategy({
    usernameField: 'email',
    passwordField: 'password',
    passReqToCallback: true
  }, (req, email, password, done) => {
    try {
      const sql = `select * from user WHERE email = "${email}"`;
      const params = [];
      db.all(sql, params, (err, row) => {
        if (err) {
          return done(err);
        }
        if (!row.length || !bcrypt.compareSync(password, row[0].password)) {
          return done(null, false, req.flash('loginMessage', 'Inavalid username/password combination. Please try again.'));
        }
        return done(null, row);
      });
    } catch (error) {
      return done(error);
    }
  }));

  const opts = {};
  opts.jwtFromRequest = cookieExtractor; // check token in cookie
  opts.secretOrKey = config.ACCESS_TOKEN_SECRET;
  // eslint-disable-next-line camelcase
  passport.use(new JwtStrategy(opts, function (jwtPayload, done) {
    try {
      const sql = `select * from user WHERE email = "${jwtPayload.email}"`;
      const params = [];
      db.all(sql, params, (err, row) => {
        if (err) {
          return done(err);
        }
        if (!row.length || !bcrypt.compareSync('admin', jwtPayload.role)) {
          return done(null, false, { message: '403 Forbidden' });
        }
        return done(null, row);
      });
    } catch (error) {
      return done(error);
    }
  }));
};

Вот где я запутываюсь, когда мои тестовые примеры ломаются. Я пытаюсь войти в систему перед тестовыми примерами, чтобы разрешить выполнение других моих тестовых примеров, но в итоге получаю ошибку 401. Вот мои тестовые примеры:

accountservice.test.js

const app = require('../../app');
const supertest = require('supertest');
const http = require('http');

describe('Account Service', () => {
  let server;
  let request;

  beforeAll((done) => {
    server = http.createServer(app);
    server.listen(done);
    request = supertest.agent(server);
    request.post('/account_service/login')
      .send({ email: '[email protected]', password: 'admin' })
      .end(function (err, res) {
        if (err) {
          return done(err);
        }
        console.log(res);
        done();
      });
  });

  afterAll((done) => {
    server.close(done);
  });

  it('Test request all users endpoint | GET request', async done => {
    const response = await request.get('/account_service/all_users');
    expect(response.status).toBe(200);
    expect(response.body.message).toBe('success');
    expect(response.body.data.length).toBe(3);
    done();
  });
});

Но мои тестовые примеры терпят неудачу, поскольку я получаю ошибку 401, когда ожидается успешный код 200.

Я попытался придумать способ извлечь jwt из файла cookie после вызова входа в систему, чтобы я мог настроить заголовки для кода запроса GET / account_service / all_users, но не смог найти способ с помощью Supertest. Я видел это сообщение: Проверка аутентифицированных маршрутов с помощью JWT не выполняется с использованием Мокко + супертест + паспорт но увидел что токен достает с тела.


person Michael Gee    schedule 21.08.2020    source источник


Ответы (1)


После того, как я возился с моим кодом, у меня возникли проблемы с хранением в памяти и запуском асинхронных функций db.run, которые вызывались каждый раз, когда я запускал свой сервер. Поэтому я использовал файл для хранения своих данных и снова провел тесты, и в итоге все заработало!

Вот ошибочный код:

const sqlite3 = require('sqlite3').verbose();
const md5 = require('md5');

const DBSOURCE = ':memory:';

const db = new sqlite3.Database(DBSOURCE, (err) => {
  if (err) {
    // Cannot open database
    console.error(err.message);
    throw err;
  } else {
    db.run(`CREATE TABLE user (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name text, 
        email text UNIQUE, 
        password text, 
        status text,
        comments text, 
        photos text,
        CONSTRAINT email_unique UNIQUE (email)
        )`,
    (err) => {
      if (err) {
        // Table already created
        console.log('Table already created');
      } else {
        // Table just created, creating some rows
        const insert = 'INSERT INTO user (name, email, password, status, comments, photos) VALUES (?,?,?,?,?,?)';
        db.run(insert, ['user_delete', '[email protected]', md5('admin123456'), 'pending_deleted', 'comment1,comment2', 'https://giphy.com/gifs/9jumpin-wow-nice-well-done-xT77XWum9yH7zNkFW0']);
        db.run(insert, ['user_no_delete', '[email protected]', md5('user123456'), 'active', 'comment1', 'https://giphy.com/gifs/cartoon-we-bare-bears-wbb-NeijdlusjcduU']);
        db.run(insert, ['mikey', '[email protected]', md5('mikey123'), 'pending_deleted', 'comment1', 'https://giphy.com/gifs/wwe-shocked-vince-mcmahon-gdKAVlnm3bmKI']);
      }
    });
  }
});

module.exports = db;

Я просто сохранил эти данные в файле и вместо этого использовал этот код:

const sqlite3 = require('sqlite3').verbose();
const DBSOURCE = 'mockdb.sqlite';

// Data inserted inside file
/*
db.run(insert, ['user_delete', '[email protected]', bcrypt.hashSync('admin123456', saltRounds), 'pending_deleted', 'comment1,comment2', 'https://giphy.com/gifs/9jumpin-wow-nice-well-done-xT77XWum9yH7zNkFW0', bcrypt.hashSync('user', saltRounds)]);
db.run(insert, ['user_no_delete', '[email protected]', bcrypt.hashSync('user123456', saltRounds), 'active', 'comment1', 'https://giphy.com/gifs/cartoon-we-bare-bears-wbb-NeijdlusjcduU', bcrypt.hashSync('user', saltRounds)]);
db.run(insert, ['mikey', '[email protected]', bcrypt.hashSync('mikey123', saltRounds), 'pending_deleted', 'comment1', 'https://giphy.com/gifs/wwe-shocked-vince-mcmahon-gdKAVlnm3bmKI', bcrypt.hashSync('user', saltRounds)]);
db.run(insert, ['admin', '[email protected]', bcrypt.hashSync('admin', saltRounds), 'active', 'admincomments', 'adminphoto', bcrypt.hashSync('admin', saltRounds)]);
  console.log('last hit in database');
});
*/

const db = new sqlite3.Database(DBSOURCE, (err) => {
  if (err) {
    // Cannot open database
    console.error(err.message);
    throw err;
  }
  console.log('Connection successful!');
});

module.exports = db;

Я тоже использовал supertest.agent.

const app = require('../../app');
const supertest = require('supertest');
const http = require('http');
const db = require('../../database/database.js');

describe('Account Service', () => {
  let server;
  let request;
  // Find cookie management option.
  beforeAll(async (done) => {
    server = http.createServer(app);
    server.listen(done);
    request = supertest.agent(server);
    done();
  });

И это сработало и успешно решило мою проблему!

person Michael Gee    schedule 25.08.2020