3

I am building my first full stack node.js application and I want to use passport for authentication. I came across a tutorial for passport that stored the users info in a users array.

passport-config.js file:

const LocalStrategy = require('passport-local').Strategy
const bcrypt = require('bcrypt')

function initialize(passport, getUserByEmail, getUserById) {
  const authenticateUser = async (email, password, done) => {
    const user = getUserByEmail(email)
    if (user == null) {
      return done(null, false, { message: 'No user with that email' })
    }

    try {
      if (await bcrypt.compare(password, user.password)) {
        return done(null, user)
      } else {
        return done(null, false, { message: 'Password incorrect' })
      }
    } catch (e) {
      return done(e)
    }
  }

  passport.use(new LocalStrategy({ usernameField: 'email' }, authenticateUser))
  passport.serializeUser((user, done) => done(null, user.id))
  passport.deserializeUser((id, done) => {
    return done(null, getUserById(id))
  })
}

module.exports = initialize

server.js file

if (process.env.NODE_ENV !== 'production') {
  require('dotenv').config()
}

const express = require('express')
const app = express()
const bcrypt = require('bcrypt')
const passport = require('passport')
const flash = require('express-flash')
const session = require('express-session')
const methodOverride = require('method-override')

const initializePassport = require('./passport-config')
initializePassport(
  passport,
  email => users.find(user => user.email === email),
  id => users.find(user => user.id === id)
)

const users = []

app.set('view-engine', 'ejs')
app.use(express.urlencoded({ extended: false }))
app.use(flash())
app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false
}))
app.use(passport.initialize())
app.use(passport.session())
app.use(methodOverride('_method'))

app.get('/', checkAuthenticated, (req, res) => {
  res.render('index.ejs', { name: req.user.name })
})

app.get('/login', checkNotAuthenticated, (req, res) => {
  res.render('login.ejs')
})

app.post('/login', checkNotAuthenticated, passport.authenticate('local', {
  successRedirect: '/',
  failureRedirect: '/login',
  failureFlash: true
}))

app.get('/register', checkNotAuthenticated, (req, res) => {
  res.render('register.ejs')
})

app.post('/register', checkNotAuthenticated, async (req, res) => {
  try {
    const hashedPassword = await bcrypt.hash(req.body.password, 10)
    users.push({
      id: Date.now().toString(),
      name: req.body.name,
      email: req.body.email,
      password: hashedPassword
    })
    res.redirect('/login')
  } catch {
    res.redirect('/register')
  }
})

app.delete('/logout', (req, res) => {
  req.logOut()
  res.redirect('/login')
})

function checkAuthenticated(req, res, next) {
  if (req.isAuthenticated()) {
    return next()
  }

  res.redirect('/login')
}

function checkNotAuthenticated(req, res, next) {
  if (req.isAuthenticated()) {
    return res.redirect('/')
  }
  next()
}

app.listen(3000)

I am using a mongoDb database for my application and want to store the users in my database. I have made the necessary changes in the register route and I can successfully store the data in my database.

app.post('/register', checkNotAuthenticated, async (req, res) => {
    try {
      const hashedPassword = await bcrypt.hash(req.body.password, 10)
      
      const users = new User({
        name: req.body.name,
        email: req.body.email,
        password: hashedPassword
    })
      await users.save()
      res.redirect('/login')
               
     }
     
     catch(err){
         console.log(err)
         res.redirect('/register')     }
})

The problem is when I try to login with the stored user's data I keep getting the following error.

Error: data and hash arguments required at Object.compare (C:\AIT715\reactlogin\node_modules\bcrypt\bcrypt.js:208:17) at C:\AIT715\reactlogin\node_modules\bcrypt\promises.js:29:12 at new Promise () at Object.module.exports.promise (C:\AIT715\reactlogin\node_modules\bcrypt\promises.js:20:12) at Object.compare (C:\AIT715\reactlogin\node_modules\bcrypt\bcrypt.js:204:25) at Strategy.authenticateUser [as _verify] (C:\AIT715\reactlogin\passport-config.js:14:24) at processTicksAndRejections (internal/process/task_queues.js:95:5)

I have tried making the following changes to the passport-config.js file but it hasn't worked.

const LocalStrategy = require('passport-local').Strategy
const bcrypt = require('bcrypt')
const User = require('./models/user')

function initialize(passport, getUserByEmail, getUserById) {
  const authenticateUser = async (email, password, done) => {
    const user = await User.find({email:email});
    console.log(user);
    if (user == null) {
      return done(null, false, { message: 'No user with that email' })
    }

    try {
      if (await bcrypt.compare(password, user.password)) {
        return done(null, user)
      } else {
        return done(null, false, { message: 'Password incorrect' })
      }
    } catch (e) {
      return done(e)
    }
  }

Also when I console.log(user) I can the data but when I try console.log(user.email) I get undefined. Can anyone suggest what am I doing wrong here? or what changes do I need to make to the passport-config file to make this work. Any leads or help is highly appreciated.

1
  • Thanks for your comment @fabian . I have made sure that the passwords are indeed raw and hashed. I have a feeling that something is broken in my code after const user = await User.find({email:email}); The reason is that I have tried testing the other conditions such as giving in a user that doesn't exist and I still get the same response. Commented Dec 6, 2021 at 16:29

2 Answers 2

1
const passwordMatch = await bcrypt.compare(password, user.password);

Make sure you are giving raw password and hash password. This will return a boolean value.

Sign up to request clarification or add additional context in comments.

Comments

1

In case, anyone is looking at this in the future; Using findOne instead of find worked for me.

const user = await User.findOne({email:email});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.