Обновлять!!!

В конце концов я решил заебаться и просто попытался защитить свой API в качестве первого шага к аутентификации. Вот мои коммиты на сегодня (пока).

Прежде всего, я взял за основу свой код этот потрясающий блог Джонатана М.Х.. Я добавил еще несколько зависимостей, таких как Passport, Passport-JWT и JWTWebtoken.

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

Итак, я объявил метод для создания JWT в моей пользовательской модели. Не знаю, правильно ли я это делаю, но я старался сделать свои контроллеры тощими, а модели — толстыми.

На этом пути я столкнулся с множеством проблем, таких как отсутствие переменной jwt. Итак, я рассмотрел, как работает module.exports, и, похоже, у меня очень поверхностное понимание предмета. Исходя из своего понимания, я сделал файл, в котором размещались все мои модули паспорта и JWT.

Не знаю, правильно ли я это делаю, но я все равно пытался. Попутно я также обнаружил распространенную ошибку Node.js, которая называется круговая зависимость. Я чуть не ударился головой о стол, потому что мой экспортируемый модуль не определен. Я понял, что перезаписываю свой собственный модуль на своей модели. Видимо, я вызываю его дважды, поэтому я сделал небольшой хак и назвал свою модель предпоследним (перед моим окончательным экспортом).

var jwt = require('jsonwebtoken');
var passport = require("passport");
var passportJWT = require("passport-jwt");
var ExtractJwt = passportJWT.ExtractJwt;
var JwtStrategy = passportJWT.Strategy;
exports.jwt = jwt;
exports.passportJWT = passportJWT;
exports.ExtractJwt = ExtractJwt;
exports.JwtStrategy = JwtStrategy;
exports.jwtOptions = {
  jwtFromRequest : ExtractJwt.fromAuthHeaderWithScheme('bearer'),
  secretOrKey : 'tasmanianDevil' //FIXME: change into this to env vars
};
var Users = require('../models/users'); // Only call this after everything else is done so YOU don't overwrite exports!!
exports.strategy = new JwtStrategy(this.jwtOptions, function(jwt_payload, next) {
  console.log('payload received', jwt_payload);
  Users.findById(jwt_payload.id, function(err, doc) {
    if (err) { res.send(err); }
    if (doc) {
      next(null, doc);
    } else {
      next(null, false);
    }
  });
});
passport.use(this.strategy);
exports.passport = passport;

Итак, я также переработал свой server.js, чтобы он мог вместить паспорт. Как обычно, я не уверен, что делаю это правильно,

const config = require('./api/config');
const passport = config.passport;
app.use(passport.initialize());
// some code...
usersRoutes(app, passport);

Теперь, когда у меня есть паспорт для аутентификации запросов, я реализовал его как промежуточное ПО в своих маршрутах.

// This route has to go first
  app.route('/api/users/login')
  .post(users.login);
app.route('/secret')
  .get(passport.authenticate('jwt', { session: false }), function(req, res){
    res.json("Success you can see me");
  });
app.route('/api/users/:id')
  .get(users.single)
  .post(users.update)
  .delete(users.delete);
app.route('/api/profile')
  .get(passport.authenticate('jwt', { session: false }), profile.get)
  .post(passport.authenticate('jwt', { session: false }), profile.update);

Я не знаю, есть ли способ сделать это СУХИМ, но пока этого достаточно. Лучше, чем ничего, верно?

В любом случае, я также изменил схему извлечения авторизации заголовков с JWT на Bearer. В основном потому, что я вижу, что OAuth использует Bearer в качестве префикса. Так что я только готовлюсь к этому. Конечно, это опять же, очередная догадка. Может ли кто-нибудь просветить меня?

jwtFromRequest : ExtractJwt.fromAuthHeaderWithScheme('bearer'),

Пройдя все проверки в middleware, я обнаружил, что в результате передается объект req.user. И так, я сделал новый контроллер явно для пользователя и вся информация будет извлекаться с помощью полезной нагрузки id в JWT. Еще раз, я не знаю, распространено ли это в мире Node.js, но я все равно это сделал. Чтобы проиллюстрировать, проверьте этот маршрут.

'use strict';
const mongoose = require('mongoose');
const Users = mongoose.model('Users');
exports.get = function(req, res) {
  res.json(req.user); // Format the fields
};
exports.update = function(req, res) {
  var params = req.body;
  params.password = Users.hashPassword(params.password);
Users.findOneAndUpdate({ _id: req.user.id }, { $set: params }, { new: true, runValidators: true, context: 'query' }, function(err, doc) {
    if (err) { res.send(err); }
    res.json(doc);
  });
};

Без передачи какого-либо идентификатора в параметрах или в теле запроса идентификатор уже предопределен на основе проверок промежуточного программного обеспечения.

Что дальше?

После долгих исследований JWT и аутентификации я обнаружил, что существуют стандарты, особенно в реализации JWT. Срок действия, правильная полезная нагрузка json, содержимое заголовков, токены обновления и многое другое. Ух! Так устрашающе. Но я, наверное, все равно их сделаю.

Сейчас я сосредоточусь на стандартах JWT, так что это мой следующий шаг.

Если вы читаете это, спасибо, что дочитали до конца! До встречи в моих следующих постах! 👋