I am making a login route with jwt token and express. When I create an account, the password is hashed by bcrypt. When I log in, I need to compare them. I tried with simplest password but it didn't work. So here is my try :
const express = require("express"); const bcrypt = require("bcryptjs"); const jwt = require("jsonwebtoken"); const User = require("../models/User"); const { verifyAdmin } = require("../middleware/authMiddleware"); const router = express.Router(); router.post("/login", async (req, res) => { const { username, password } = req.body; try { const user = await User.findOne({ username }); if (!user) { return res.status(400).json({ message: "Invalid credentials" }); } // Log the received and hashed passwords for debugging console.log("Received password:", password); console.log("Stored hashed password:", user.password); const isMatch = await bcrypt.compare(password, user.password); console.log("Password match result:", isMatch); if (!isMatch) { return res.status(400).json({ message: "Invalid credentials" }); } const payload = { user: { id: user.id, userType: user.userType, }, }; jwt.sign( payload, process.env.JWT_SECRET, { expiresIn: "1h" }, (err, token) => { if (err) throw err; res.json({ token, user }); } ); } catch (err) { console.error(err.message); res.status(500).send("Server error"); } }); // Create User router.post("/create", async (req, res) => { const { name, username, password, userType, process } = req.body; try { const hashedPassword = await bcrypt.hash(password, 10); const newUser = new User({ name, username, password: hashedPassword, userType, process: userType === "Process Department" ? process : undefined, }); await newUser.save(); res.status(201).json({ message: "User created successfully" }); } catch (error) { res.status(500).json({ error: error.message }); } }); module.exports = router; The problem is, my route returns "Invalid credentials".
Create user frontend -->
// src/redux/reducers/userSlice.js import { createSlice, createAsyncThunk } from "@reduxjs/toolkit"; import api from "../../api/axiosConfig"; export const createUser = createAsyncThunk( "user/createUser", async (userData, thunkAPI) => { try { const response = await api.post("/auth/create", userData ); return response.data; } catch (error) { return thunkAPI.rejectWithValue(error.response.data.message); } } ); const userSlice = createSlice({ name: "user", initialState: { isLoading: false, success: false, error: null, }, reducers: { resetState: (state) => { state.isLoading = false; state.success = false; state.error = null; }, }, extraReducers: (builder) => { builder .addCase(createUser.pending, (state) => { state.isLoading = true; state.success = false; state.error = null; }) .addCase(createUser.fulfilled, (state) => { state.isLoading = false; state.success = true; state.error = null; }) .addCase(createUser.rejected, (state, action) => { state.isLoading = false; state.success = false; state.error = action.payload; }); }, }); export const { resetState } = userSlice.actions; export default userSlice.reducer; Login frontend -->
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit"; import api from "../../api/axiosConfig"; export const login = createAsyncThunk( "auth/login", async ({ username, password }, thunkAPI) => { try { const response = await api.post("/auth/login", { username, password, }); return response.data; } catch (error) { return thunkAPI.rejectWithValue(error.response.data.message); } } ); export const logout = createAsyncThunk("auth/logout", async (_, thunkAPI) => { // perform any necessary cleanup here return true; }); const authSlice = createSlice({ name: "auth", initialState: { isLoading: false, user: null, error: null, }, reducers: {}, extraReducers: (builder) => { builder .addCase(login.pending, (state) => { state.isLoading = true; state.error = null; }) .addCase(login.fulfilled, (state, action) => { state.isLoading = false; state.user = action.payload.user; state.error = null; }) .addCase(login.rejected, (state, action) => { state.isLoading = false; state.user = null; state.error = action.payload; }) .addCase(logout.fulfilled, (state) => { state.user = null; state.error = null; }); }, }); export default authSlice.reducer; My console information
Received password: vishesh Stored hashed password: $2a$10$xxZgmq63qYyIRZ5DypwjbOF4fthtMm/m4qJE6sOytsCVxJzn2SRQy Password match result: false
$2a$10$xxZgmq63qYyIRZ5DypwjbOF4fthtMm/m4qJE6sOytsCVxJzn2SRQydoes not appear to be valid for your passwordvishesh: When using bcrypt to generate a hash from password and salt, the hash is different, namely$2a$10$xxZgmq63qYyIRZ5DypwjbO9sI.9V5Gs/loT0eWMO.0xFtGCTUEOYa(see here), which can be verified with a third library, e.g. CyberChef (see here). So check your data please.pre('save')hook set-up? For exampleif(this.isModified('password'))where you hash the password? If so you don't need to manually hash the password in yourrouter.post("/create")route. The middleware will handle it on user creation. You might be unwittingly hashing the password twice.compare()call is failing. The reason is that the OP's data is inconsistent. If you use the posted password and the salt of the posted hash, the result is a different hash than the posted one. So the data is inconsistent (for whatever reason), which is the reason for the failed verification.