node.jsauthenticationadvanced-rest-client

I am getting an error when trying to test my login using ARC. I'm using NodeJS, Express, JWT and AtlasMongoDB


I'm trying to implement a login system using Nodejs, Express, JWT and MongoDB Atlas. The users are entered manually in the "data.js" file and are being shown in MongoDB Atlas but when I try to authenticate using the Advanced REST client (ARC) I'm getting the following error:

{
"message": "Invalid email or password"
}

The following is the code:

index.js:

import express from 'express';
import mongoose from 'mongoose';
import seedRouter from './routes/seedRoutes.js';
import productRouter from './routes/productRoutes.js';
import userRouter from './routes/userRoutes.js';

mongoose
  .connect(
    'mongodb+srv://e-shopper123456:SimWkjLCoKjwCAlZ@e-shopper.ms9xt.mongodb.net/fsb?retryWrites=true&w=majority'
  )
  .then(() => {
    console.log('Successfully connected to the database.');
  })
  .catch((err) => {
    console.log(err.message);
  });

const app = express();
const PORT = 3001;

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

app.use('/api/seed', seedRouter);
app.use('/api/products', productRouter);
app.use('/api/users', userRouter);

app.use((err, req, res, next) => {
  res.status(500).send({ message: err.message });
});

app.listen(PORT, () => {
  console.log(`Server listening on port: [${PORT}]`);
});

data.js

import bcrypt from 'bcryptjs';

const data = {
  users: [
    {
      name: 'Matthew',
      email: 'matthew@test.com',
      password: bcrypt.hashSync('123456'),
    },
    {
      name: 'James',
      email: 'james@test.com',
      password: bcrypt.hashSync('123456'),
    },
  ],

  products: [
    {
      // _id: '1',
      slug: 'Playstation 5 Disc Version',
      name: 'Playstation 5 Disc Version',
      description: '',
      image: '/images/playstation5.jpeg',
      price: 499.99,
      countInStock: 0,
      rating: 4,
      numReviews: 30,
    },
    {
      // _id: '2',
      slug: 'Microsoft Xbox Series X Digital Edition 1TB',
      name: 'Microsoft Xbox Series X Digital Edition 1TB',
      description: 'This is a Microsoft console.',
      image: '/images/xboxseriesx.jpeg',
      price: 919,
      countInStock: 4,
      rating: 3.5,
      numReviews: 10,
    },
    {
      // _id: '3',
      slug: 'Call of Duty®: Vanguard - Standard Edition',
      name: 'Call of Duty®: Vanguard - Standard Edition',
      description:
        'The world continues to sink in the world’s largest and bloodiest conflict, but the seemingly desperate course of World War II is finally beginning to turn for the better. Now a handful of those chosen must rise to complete their task and change the face of war once and for all.',
      image: '/images/callofduty.jpeg',
      price: 59.99,
      countInStock: 5,
      rating: 4.5,
      numReviews: 12,
    },
    {
      // _id: '4',
      slug: 'Football Manager 2022',
      name: 'Football Manager 2022',
      description: '',
      image: '/images/footballmanager.jpeg',
      price: 54.99,
      countInStock: 8,
      rating: 5,
      numReviews: 15,
    },
    {
      // _id: '5',
      slug: 'World of Warcraft®: Shadowlands - Base Edition',
      name: 'World of Warcraft®: Shadowlands - Base Edition',
      description: '',
      image: '/images/worldofwarcraft.jpeg',
      price: 19.99,
      countInStock: 10,
      rating: 5,
      numReviews: 20,
    },
  ],
};

export default data;

utils.js

import jwt from 'jsonwebtoken';

export const generateToken = (user) => {
  return jwt.sign(
    {
      _id: user._id,
      name: user.name,
      email: user.email,
    },
    'RANDOM_TOKEN',
    {
      expiresIn: '48h',
    }
  );
};

models -> user.js

import mongoose from 'mongoose';

const userSchema = new mongoose.Schema(
  {
    name: { type: String, required: true },
    email: {
      type: String,
      required: [true, 'Please provide an email'],
      match:
        /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/,
    },
    password: { type: String, required: [true, 'Please provide a password'] },
  },
  {
    timestamps: true,
  }
);

const user = mongoose.model('user', userSchema);
export default user;

routes -> userRoutes.js

import express from 'express';
import bcrypt from 'bcryptjs';
import User from '../models/user.js';
import expressAsyncHandler from 'express-async-handler';
import { generateToken } from '../utils.js';

const userRouter = express.Router();

userRouter.post(
  '/login',

  expressAsyncHandler(async (req, res) => {
    await User.findOne({ email: req.body.email }).then((user) => {
      if (user) {
        if (bcrypt.compareSync(req.body.paswword, user.password)) {
          res.send({
            _id: user._id,
            name: user.name,
            email: user.email,
            token: generateToken(user),
          });
          return;
        }
      }
      res.status(401).send({ message: 'Invalid email or password' });
    });
  })
);

export default userRouter;

Solution

  • You have a miss spell there: req.body.paswword, user.password.

    By the way, you need to hash the password you received in the same way they are saved in database.

    So you should do something like that:

    bcrypt.compareSync(bcrypt.hashSync(req.body.password), user.password)