Аутентификация API Node.js при помощи JWT

Аутентификация API Node.js при помощи JWT

JWT – это json web token.

Он формируется путем кодирования данных при помощи секретного слова. Более подробно о нем написано: https://jwt.io/

Также будем использовать библиотеку passport: http://www.passportjs.org/

И так поехали. Структура проекта:

config
  config.json
models
  db.js
routes
  index.js
app.js

Файл app.js:

const express = require('express');
const bodyParser = require('body-parser');
const router = require('./routes');
const app = express();

const passport = require('passport');
const passportJWT = require('passport-jwt');
const ExtractJWT = passportJWT.ExtractJwt;
const Strategy = passportJWT.Strategy;

const db = require('./models/db');
const config = require('./config/config');

const PORT = process.env.PORT || 3000

app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());

const params = {
  secretOrKey: config.secret,
  jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken()
};

let strategy = new Strategy(params, function(payload, done){
  db
    .getUserId(payload.id)
    .then((results)=>{
      if (results.length == 0) {
        return done(new Error('Юзер не найден'), null)
      } else {
        return done(null, {
          id: results._id
        })
      }
    })
    .catch((err)=>{
      return done(err);
    })
})

passport.use(strategy);

app.use('/', router);

app.use(function(req, res, next){
  const err = new Error('Ни хрена не найдено!');
  err.status = 404;
  next(err);   
});

app.use(function(err, req, res, next){
  res.status(err.status || 500);
  res.json({
    message: err.message,
    error: err
  })     
})

const server = app.listen(PORT, function () {  
  console.log('Сервер пашет на порту: ' + server.address().port);
})

Что здесь нового:

Подключаем библиотеки passport и passport-jwt

const passport = require('passport');
 const passportJWT = require('passport-jwt');
 const ExtractJWT = passportJWT.ExtractJwt;
 const Strategy = passportJWT.Strategy;

Далее указываем параметры:

const params = {
  secretOrKey: config.secret,
  jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken()
};

secretOrKey – секретное слово берется из файла config.json:

{
  "secret" : "очень секретное слово"
}

Далее описываем стратегию:

let strategy = new Strategy(params, function(payload, done){
  db
    .getUserId(payload.id)
    .then((results)=>{
      if (results.length == 0) {
        return done(new Error('Юзер не найден'), null)
      } else {
        return done(null, {
          id: results._id
        })
      }
    })
    .catch((err)=>{
      return done(err);
    })
})

Ищет пользователя по id в базе и возвращает id

Далее юзаем стратегию:

passport.use(strategy);

Файл db.js:

const MongoClient = require('mongodb').MongoClient;
const ObjectID = require('mongodb').ObjectID;
const url = "путь подключения к базе";

const baza = 'test1';

module.exports.getUser = function(email) {
  return new Promise((resolve, reject)=>{
    MongoClient
      .connect(url, function(err, client){
        if (err) {
          reject(err);
        }
        client
          .db(baza)
          .collection('users')
          .find({ "email": email})
          .toArray(function(err, results){
            if (err) {
              reject(err)
            }
            client.close();
            resolve(results);
          })
    })
  })
}

module.exports.getUserId = function(userId) {
  return new Promise((resolve, reject) => {
      const id = new ObjectID(userId);
      MongoClient
          .connect(url, function(err, client) {
              if (err) {
                  reject(err);
              }
              client
                  .db(baza)
                  .collection('users')
                  .find({ _id: id })
                  .toArray(function(err, results){
                      if (err) {
                          reject(err);
                      }
                      client.close();
                      resolve(results);
                  })

      });         
  })
}

Файл routes/index.js:

const express = require('express');
const router = express.Router();
const db = require('../models/db');
const bcrypt = require('bcryptjs');

const jwt = require('jwt-simple');

const config = require('../config/config');

let passport = require('passport');

let auth = passport.authenticate('jwt', {
  session: false
});

const isValidPassword = function(user, password) {
  return bcrypt.compareSync(password, user.password);
}

router.get('/', (req, res)=>{
  res.json({
    message: 'Добро пожаловать!'
  })       
});

router.get('/secret', auth, (req, res)=>{
  res.json({
    message: 'Секретная страница!'
  })   
});

router.post('/login', (req, res, next)=>{
  db
    .getUser(req.body.email)
    .then((results)=>{
      if (isValidPassword(results[0], req.body.password)) {
        let payload ={
          id: results[0]._id
        }
        let token = jwt.encode(payload, config.secret);
        res.json({token: token});
      } else {
        const err = new Error('Не верный логин или пароль!');
        err.status = 400;
        next(err); 
      }
    })
    .catch((err)=>{
      next(err);
    })
})

module.exports = router;

Что у нас здесь нового:

Подключаем библиотеку ‘jwt-simple’ – для формирования токена:

const jwt = require('jwt-simple');

Мидделвэр auth – теперь стал намного проще:

let auth = passport.authenticate('jwt', {
  session: false
});

В Роуте login – теперь если с пользователем все ок, то токен формируется:

        let payload ={
          id: results[0]._id
        }
        let token = jwt.encode(payload, config.secret);

И также теперь при обращении на secret, нужно в заголовках в параметре ‘
Authorization’ – указывать Bearer, потом пробел, потом токен

Вот и все, вроде тоже не сложно!