/express-mongoose-passport-jwt

Express Mongoose Passport JWT

Primary LanguageJavaScript

Express Mongoose Passport JWT

  1. Initialise repository
    yarn init

  2. Initialise repository with express and body-parser
    yarn add express body-parser

  3. Add nodemon module
    yarn add nodemon --dev

  4. Create server.js

const express = require('express')
const bodyParser = require('body-parser')

const server = express()

// Middleware Plugins
server.use(bodyParser.json())

// Routes
server.use('/', [
  // require('./routes/whatever')
])

// Start the server
server.listen(7000, error => {
  if (error) console.error('Error starting', error)
  else console.log('Started at http://localhost:7000')
})
  1. Add scripts in package.json
"scripts": {
  "dev": "nodemon server.js",
  "seed": "node models/seeds.js",
  "drop": "node models/drop.js",
  "reset": "npm run drop && npm run seed"
}
  1. Add mongoose module
    yarn add mongoose

  2. Add models\init.js

const mongoose = require('mongoose')

// Use the Promise functionality built into Node.js
mongoose.Promise = global.Promise

// Connect to our local database
mongoose
  .connect('mongodb://localhost/secret-documents', { useMongoClient: true })
  .then(() => {
    console.log('Successfully connected to database')
  })
  .catch(error => {
    //   If there was an error connecting to the database
    if (error) console.log('Error connecting to MongoDB database', error)
  })

module.exports = mongoose
  1. Create models\Document.js
const mongoose = require('./init')

const Document = mongoose.model('Document', {
  title: String, // e.g. Sokovia Accords
  content: String // e.g. The Sokovia Accords. Approved by 117 countries, it states that the Avengers shall no longer be a private organization. Instead, they'll operate under the supervision of a United Nations panel, only when and if that panel deems it necessary.
})

module.exports = Document
  1. Create models\seeds.js
const Document = require('./Document')

Document.create([
  {
    title: 'Sokovia Accords',
    content:
      "The Sokovia Accords. Approved by 117 countries, it states that the Avengers shall no longer be a private organization. Instead, they'll operate under the supervision of a United Nations panel, only when and if that panel deems it necessary."
  },
  {
    title: 'Impact on S.H.I.E.L.D.',
    content:
      'I\'m here because the President sent me. The Sokovia Accords are the law of the land now and he\'s concerned you might have some undocumented "assets" working for you.'
  },
  {
    title: 'Regulations',
    content:
      'Any enhanced individuals who agree to sign must register with the United Nations and provide biometric data such as fingerprints and DNA samples.'
  }
])
  .then(documents => {
    console.log('Created documents', documents)
    process.exit()
  })
  .catch(error => {
    console.error('Error creating documents', error)
  })
  1. Create models\drop.js
const Document = require('./Document')

Document.deleteMany().then(() => {
  console.log('Deleted documents')
  process.exit()
})
  1. Create routes\document.js using boilerplate
    (or run Skafold Document with Skafold app)

  2. Add route for document in server.js

// Routes
server.use('/', [
  require('./routes/document')
])
  1. Create check\1.http and test the app
GET http://localhost:7000/document
  1. Add passport middleware
    yarn add passport passport-local passport-local-mongoose

  2. Add passport-jwt middleware
    yarn add passport-jwt

  3. Add User model with Passport plugin (models\User.js)

const mongoose = require('./init')
const passportLocalMongoose = require('passport-local-mongoose')

const userSchema = new mongoose.Schema({
  firstName: String,
  lastName: String
})

// Add passport middleware to User Schema
userSchema.plugin(passportLocalMongoose, {
  usernameField: 'email', // Use email, not the default 'username'
  usernameLowerCase: true, // Ensure that all emails are lowercase
  session: false // Disable sessions as we'll use JWTs
})

const User = mongoose.model('User', userSchema)

module.exports = User
  1. Generate a jwtSecret token using console
    openssl rand -base64 48

  2. Add helper for middleware middleware\auth.js

const passport = require('passport')
const JWT = require('jsonwebtoken')
const PassportJwt = require('passport-jwt')
const User = require('../models/User')

// These should be in .env
// secret (generated using `openssl rand -base64 48` from console)
const jwtSecret =
  'QOOC3nUVl9yTZiH2F0VYjOJhwm2ZkyBjWK7Mzo4bH54cNBBUQmp262S0Tx1eBBTT'
const jwtAlgorithm = 'HS256'
const jwtExpiresIn = '7 days'

passport.use(User.createStrategy())

function register(req, res, next) {
  const user = new User({
    email: req.body.email,
    firstName: req.body.firstName,
    lastName: req.body.lastName
  })
  // Create the user with the specified password
  User.register(user, req.body.password, (error, user) => {
    if (error) {
      // Our register middleware failed
      next(error)
      return
    }
    // Store user so we can access it in our handler
    req.user = user
    // Success!
    next()
  })
}

passport.use(
  new PassportJwt.Strategy(
    // Options
    {
      // Where will the JWT be passed in the HTTP request?
      // e.g. Authorization: Bearer xxxxxxxxxx
      jwtFromRequest: PassportJwt.ExtractJwt.fromAuthHeaderAsBearerToken(),
      // What is the secret
      secretOrKey: jwtSecret,
      // What algorithm(s) were used to sign it?
      algorithms: [jwtAlgorithm]
    },
    // When we have a verified token
    (payload, done) => {
      // Find the real user from our database using the `id` in the JWT
      User.findById(payload.sub)
        .then(user => {
          // If user was found with this id
          if (user) {
            done(null, user)
          } else {
            // If not user was found
            done(null, false)
          }
        })
        .catch(error => {
          // If there was failure
          done(error, false)
        })
    }
  )
)

function signJWTForUser(req, res) {
  // Get the user (either just signed in or signed up)
  const user = req.user
  // Create a signed token
  const token = JWT.sign(
    // payload
    {
      email: user.email
    },
    // secret
    jwtSecret,
    {
      algorithm: jwtAlgorithm,
      expiresIn: jwtExpiresIn,
      subject: user._id.toString()
    }
  )
  // Send the token
  res.json({ token })
}

module.exports = {
  initialize: passport.initialize(),
  register,
  signIn: passport.authenticate('local', { session: false }),
  requireJWT: passport.authenticate('jwt', { session: false }),
  signJWTForUser
}
  1. Add route for auth (routes\auth.js)
const express = require('express')
const authMiddleware = require('../middleware/auth')

const router = express.Router()

// Register
router.post(
  '/auth/register',
  // middleware that handles the registration process
  authMiddleware.register,
  // json handler
  authMiddleware.signJWTForUser
)

// Sign in
router.post(
  '/auth',
  // middleware that handles the sign in process
  authMiddleware.signIn,
  // json handler
  authMiddleware.signJWTForUser
)

module.exports = router
  1. Add route for auth in server.js
// Routes
server.use('/', [
  require('./routes/auth'),
  require('./routes/document')
])
  1. Update document route to use JWT middleware
const authMiddleware = require('../middleware/auth')
  1. Add authMiddleware.requireJWT as 2nd argument to every route (see example below)
// GET - Read all document
router.get('/documents', authMiddleware.requireJWT, (req, res) => {
  Document.find()
  // Once it has loaded these documents
  .then(documents => {
    // Send them back as the response
    res.json(documents)
  })
  .catch(error => {
    res.status(400).json({ error: error.message })
  })
})
  1. Update check\1.http and test the auth routes
### Registration
POST http://localhost:7000/auth/register
Content-Type: application/json

{
  "email": "glenn.dimaliwat@gmailcom",
  "firstName": "Glenn",
  "lastName" : "Dimaliwat",
  "password" : "password123"
}

### Sign In
POST http://localhost:7000/auth/
Content-Type: application/json

{
  "email": "glenn.dimaliwat@gmailcom",
  "password" : "password123"
}
  1. Also in check\1.http, Copy the token after successful sign in and test the product routes with JWT authentication
###

GET http://localhost:7000/documents
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImdsZW5uLmRpbWFsaXdhdEBnbWFpbGNvbSIsImlhdCI6MTUxMjU2MzQ1MCwiZXhwIjoxNTEzMTY4MjUwLCJzdWIiOiI1YTI3ZTJhNTI0NjQ1NTFlYTAyOTE1YzQifQ.QKQLXZgcYNS_57E9fm4C12IAKUXnN9hChmkD36PrZi4

###
GET http://localhost:7000/documents/5a27db9a6c728b182aa14b41
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImdsZW5uLmRpbWFsaXdhdEBnbWFpbGNvbSIsImlhdCI6MTUxMjU2MzQ1MCwiZXhwIjoxNTEzMTY4MjUwLCJzdWIiOiI1YTI3ZTJhNTI0NjQ1NTFlYTAyOTE1YzQifQ.QKQLXZgcYNS_57E9fm4C12IAKUXnN9hChmkD36PrZi4