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;
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)