React & Express: app.use() routes not working with socket.io - reactjs

React Beginner Here. I'm trying to connect an existing react project and socket.io but now I can't access the routes with app.use(). I can see the console logs of the connections and disconnections but when I login with '/login', It doesn't run. Front end runs at 3000, Server at 9000
Edit: It does run because I receive a token which is sent at the end of it. However I get a status 404 Cannot POST /login.
Socket.js
const express = require('express')
const router = express.Router()
const http = require('http')
const cors = require('cors')
const server = http.createServer(router)
router.use(cors())
const {Server} = require("socket.io")
const io = new Server(server, {
cors: {
origin: 'http://localhost:3000',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
}
})
io.on('connection', (socket) => {
console.log('a user connected')
socket.on('disconnect', () => {
console.log('user disconnected', socket.id)
})
socket.on('chat message', (msg) => {
console.log('message: ' + msg)
io.emit('chat message', msg)
})
})
module.exports = {server}
Server.js
const express = require('express')
const app = express()
const {server} = require('./socket/socket')
const cors = require('cors')
app.use(cors({
origin: ['http://localhost:3000', 'http://localhost:9000', 'http://localhost:3001'],
credentials: true,
}))
app.use(express.json())
app.use(express.urlencoded({extended: true}))
const login = require('./routes/login')
const signup = require('./routes/verify/signup')
const home = require('./routes/home')
const checkRefreshToken = require('./routes/_checkRT')
const logout = require('./routes/_logout')[![enter image description here][1]][1]
const verify = require('./routes/verify/_verify')
app.use('/login', login)
app.use('/signup', signup)
app.use('/', home)
app.use('/check_refresh_token', checkRefreshToken)
app.use('/logout', logout)
app.use('/verification', verify)
const PORT = process.env.PORT || 9000
server.listen(PORT, () => {
console.log(`Listening on http://localhost:${PORT}`)
})
front end messages.jsx
import React from 'react'
import Navbar from '../../components/navbar/navbar'
import io from 'socket.io-client'
const socket = io.connect('http://localhost:9000/')
const Messages = () => {
return (
<div>
<Navbar />
</div>
)
}
export default Messages
Login.js
require('dotenv').config()
const express = require('express')
const router = express.Router()
const jwt = require('jsonwebtoken')
const cors = require('cors')
const bcrypt = require('bcrypt')
const mysql = require('mysql')
const connection = mysql.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE
})
// middleware
router.use(cors({
origin: ['http://localhost:3000', 'http://localhost:9000', 'http://localhost:3001'],
credentials: true,
}))
router.use(express.json())
router.use(express.urlencoded({extended: true}))
/**
* Flow of the code:
* 1. Check if email exists in the database
* 2. If it does, check if the password matches
* 3. If it does, generate an access token
* 4. Then generate a refresh token
* 5. Then send the access token and refresh token to the client
*/
router.post('/', (req, res) =>
{
// Even these doesn't run
// console.log("HERE");
// res.send("HELLO")
const { email, password } = req.body
function getInput() {
return new Promise((resolve, reject) => {
let sql = `
SELECT * FROM account_table
LEFT JOIN section_table
ON account_table.account_section_id = section_table.section_id
WHERE account_email = ?
;`
connection.query(sql, [email], (err, results) => {
if (err) return reject({status: 500, message: 'Internal server error'})
else if (results.length === 0) return reject({status: 401, message: 'Invalid email or password'})
return resolve(results)
})
})
}
function checkPassword(input) {
return new Promise((resolve, reject) => {
bcrypt.compare(password, input[0].account_password, (err, result) => {
if (err) return reject({status: 500, message: 'Internal server error'})
else if (!result) return reject({status: 401, message: 'Invalid email or password'})
return resolve(input)
})
})
}
function generateAccess(result) {
return new Promise((resolve, reject) => {
const userAccessToken = {
user_id: result[0].account_id,
user_section_id: result[0].account_section_id
}
jwt.sign(userAccessToken, process.env.ACCESS_TOKEN_SECRET, {expiresIn: '15m'}, (err, token) => {
if (err) return reject({status: 500, message: 'Internal server error'})
return resolve(token)
})
})
}
function generateRefresh(result) {
return new Promise((resolve, reject) => {
const userAccessToken = {
user_id: result[0].account_id,
user_section_id: result[0].account_section_id
}
jwt.sign({userAccessToken}, process.env.REFRESH_TOKEN_SECRET, (err, token) => {
if (err) return reject({status: 500, message: 'Internal server error'})
return resolve(token)
})
})
}
function insertSend(access, refresh, result) {
return new Promise((resolve, reject) => {
const userInfo = {
user_f_name: result[0].account_first_name,
user_l_name: result[0].account_last_name,
section_grade: result[0].section_grade,
section_strand: result[0].section_strand,
section_name: result[0].section_name,
}
let expiryDate = new Date(Date.now())
expiryDate.setDate(expiryDate.getDate() + 7) // 7 days
let sql = `INSERT INTO refresh_token_table (token_content, token_owner_id, token_timestamp) VALUES (?, ?, ?)`
connection.query(sql, [refresh, result[0].account_id, expiryDate], (err, results) => {
if (err) return reject({status: 500, message: 'Internal server error'})
return resolve(
res.cookie("hello_world_cookie69", refresh, {
origin: "http://localhost:9000",
expires: expiryDate,
httpOnly: true,
secure: true,
sameSite: "strict",
}).json({userInfo, access})
)
})
})
}
try {
let vEmail = await getInput()
let vData = await checkPassword(vEmail)
let accessToken = await generateAccess(vData)
let refreshToken = await generateRefresh(vData)
let send = await insertSend(accessToken, refreshToken, vData)
} catch (err) {
return res.status(err.status).json({ message: err.message })
}
})
module.exports = router

Related

React web app gets a set-cookie header but doesn't save it

I have a very simple login system, which takes a username and a password from a user, and when the form is submitted the data is posted to an express http app.
The login function at the react app is as following:
async function login(event){
event.preventDefault();
setRequestState(REQUEST_STATE.pending)
const request = `${SERVER_URL}/login`;
const formData = new FormData();
formData.append("username",data["username"])
formData.append("password",data["password"])
const response = await fetch(request, {
method: 'POST',
body: formData,
enctype:"multipart/form-data",
credentials: 'same-origin',
})
if(!response.ok){
if(response.status === 401){
console.log("username or password are inccorect")
setDisplayError(response.status)
}
if(response.status === 500){
console.log("internal server error")
setDisplayError(response.status)
}
setRequestState(REQUEST_STATE.no_request)
return;
}
setRequestState(REQUEST_STATE.success)
// navigate("/dashboard")
}
the response is successful consistently when the right data is entered.
I use express-sessions to handle sessions, the setup code for the sessions is as following:
const express = require('express');
const cors = require('cors')
const loginRouter = require('./src/routes/login.js');
const session = require('express-session')
const app = express()
app.use(
cors({
origin:true,
credentials:'same-origin',
}))
app.use(session({
secret: 'test',
resave: false,
saveUninitialized: false,
// store: sessionStore,
cookie: {
secure: false,
sameSite: 'none',
}
}));
app.use(express.urlencoded({extended: true}))
app.use('/login', loginRouter)
app.listen(process.env.PORT ,()=>{
console.log(`Server is listening on port ${process.env.PORT}`)
})
login router is as following:
const express = require('express')
const multer = require('multer')
const router = express.Router()
const {getLogin,postLogin, authenticateLogin} = require('../controllers/login')
upload = multer({})
router.post("/", upload.array(0), authenticateLogin, postLogin)
router.get("/", getLogin)
module.exports=router
login controller is as following:
const {pool} = require('../../sqlconfig')
const bcrypt = require('bcrypt');
const postLogin = (req, res) => {
return res.status(200).send({success:true});
}
const getLogin = (req, res) => {
// console.log("get this login betch")
console.log(req.session.id)
if(req.session.userId){
return res.status(200).send({success:true});
}
// console.log("couldn't get login")
return res.status(401).send({success:false});
}
const authenticateLogin = async (req, res, next) => {
const dict = JSON.parse(JSON.stringify(req.body));
const username = dict["username"];
const password = dict["password"];
if(!dict || !username || !password) {
// console.log("Access deneid")
return res.status(401).send({error:"Access denied"});
}
try{
const query = "SELECT passwrd FROM admins WHERE admins.username=?";
const [result] = await pool.execute(query,[username])
if(result.length <= 0){
return res.status(401).send({error:"Access denied"});
}
const passwordInDB = result[0].passwrd;
if(!passwordInDB){
return res.status(401).send({error:"Access denied"});
}
const match = await bcrypt.compare(password, passwordInDB);
// console.log(match)
if(!match){
return res.status(401).send({error:"Access denied"});
}
const queryUserId = "SELECT id FROM admins WHERE admins.username=?";
const [resultUserId] = await pool.execute(queryUserId,[username])
req.session.userId = resultUserId[0].id;
console.log("logged in successfuly!")
console.log(req.session)
next()
}
catch(e){
console.log(e)
}
}
module.exports={
authenticateLogin,
postLogin,
getLogin
}
when login post is called, a session does get created,
and a cookie is sent back to the react application:
Response
but doesn't get saved:
saved cookies

How to resolve the Socket.io error "ERR_NAME_NOT_RESOLVED

When I run my MERN Stack project on my localhost and get my app's render page, when I click on a button in my app, that button does and displays nothing. When I inspect my client-side render page, I see the following error:polling.js:311 GET http://%22http/socket.io/?EIO=4&transport=polling&t=O7Mtvxd net::ERR_NAME_NOT_RESOLVED. I don't know what it means, I searched the meaning on the internet but without success. Here is my backend index.js file:
const express = require('express')
const cors = require('cors')
const mongoose = require('mongoose')
require("dotenv").config()
const app = express()
const http = require('http')
const server = http.createServer(app)
const io = require('socket.io')(server)
const UserRoutes = require('./routes/User')
const AuthRoutes = require('./routes/Auth')
const PostRoutes = require('./routes/Post')
const PORT = process.env.PORT || 5000
const {MONGODB_URI} = require("./config")
app.use(cors())
app.use(express.json())
app.use((req, res, next) => {
io.req = req
req.io = io
next()
})
app.use('/api/auth', AuthRoutes)
app.use('/api/user', UserRoutes)
app.use('/api/post', PostRoutes)
require('./socket')(io)
mongoose
.connect(MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
})
.then(() => {
console.log('database connected')
server.listen(PORT, () => console.log(`server started on port ${PORT}`))
})
.catch((err) => console.log(err))
my backend socket.js file :
const User = require('./models/User')
const jwt = require('jsonwebtoken')
module.exports = (io) => {
io.on('connection', (socket) => {
if (io.req) {
socket.broadcast.emit('friend-login-status', { user_id: io.req.userId })
addSocketIdInDB(socket.id, io.req.userId)
socket.on('disconnect', () => {
socket.broadcast.emit('friend-logout-status', {
user_id: io.req.userId,
})
io.req.userId = null
})
}
})
}
async function addSocketIdInDB(socket_id, user_id) {
const user = await User.findById(user_id)
if (socket_id) {
user.socketId = socket_id
}
await user.save()
}
I looked on this question after this but without success. I work on Microsoft Edge .

ReactJS axios post: returns null when it comes/goes from/to backend

I am stuck on this problem for 2 days. I am sending POSTrequest from frontend to the backend (and other GET requests too but the problem is only with POST). However, when my data goes to the backend it does not post anything to the rest api even though response is 200 OK. That's why when in response it should have given the posted data, it can't find it and gives null. This is my POST code in backend index.js:
const { response, request } = require('express');
require('dotenv').config()
const express = require('express');
const morgan = require('morgan');
const Contact = require('./models/contact.cjs');
const cors = require('cors')
const app = express();
app.use(express.json())
app.use(express.static('build'))
app.use(cors())
morgan.token('body', req => {
return JSON.stringify(req.body)
})
app.use(morgan(':method :url :status :res[content-length] - :response-time ms :body'));
const generateId = () => {
const randNum = Math.floor(Math.random() * 5000)
return randNum;
}
app.post('/api/persons', (req, res) => {
const body = req.body
console.log(body)
if (!body.name || !body.number) {
return res.status(400).json({
error: "missing data"
})
} else if (Contact.find({name: body.name})) {
Contact.findOneAndUpdate({name: body.name}, {$set: {number: body.number}}, {new:true})
.then(updatedContacts =>
res.json(updatedContacts)
)
.catch(err => console.log(err))
} else {
const contact = Contact({
id: generateId(),
name: body.name,
number: body.number,
date: new Date()
})
contact.save()
.then(savedContact => {
console.log(savedContact)
res.json(savedContact)
})
.catch(err => {
console.log(err)
})
}
})
const PORT = process.env.PORT
app.listen(PORT, () => {
console.log(`Server is working on ${PORT}`)
})
and this is how my frontend sends data to backend: contacts.js:
const create = (newObject) => {
const readyToPost = {
method: 'post',
url: `${baseUrl}`,
data: newObject,
headers: {'Content-Type': 'application/json'},
json: true
}
const request = axios(readyToPost)
return request.then(response => {
console.log(response.data)
return response.data
})
.catch(err => {
console.log(err)
})
}
And this is my react app's frontend.
Any ideas about why my data becomes null?
Any help would be appreciated!
Due to the synchronous nature of your code, the condition Contact.find({name: body.name}) was always returning the Query object which is true due to which the else if block was getting executed even when there was no such document. After entering the else if block, since there was no match, so findOneAndUpdate() was returning null.
Use findOne() instead of find(). find() returns a cursor which is empty but true whereas findOne() returns the first document matched (if matched) or else it will return null (if not matched).
// index.js (Backend)
app.post("/api/persons", async (req, res) => {
const body = req.body;
if (!body.name || !body.number) {
return res.status(400).json({
error: "missing data",
});
}
// Using findOne() instead of find(). Returns null if record not found.
const existing = await Contact.findOne({ name: body.name });
if (existing) {
Contact.findOneAndUpdate(
{ name: body.name },
{ $set: { number: body.number } },
{ new: true }
)
.then((updatedContacts) => {
console.log(updatedContacts);
res.status(200).json(updatedContacts);
})
.catch((err) => console.log(err));
} else {
const contact = Contact({
id: generateId(),
name: body.name,
number: body.number,
date: new Date(),
});
contact
.save()
.then((savedContact) => {
console.log(savedContact);
res.status(201).json(savedContact);
})
.catch((err) => {
console.log(err);
});
}
});

Set interval gets clear on browser refresh in my react application

I am using the setInterval function to do an API call to get refresh token after some interval. Every time if I refresh the browser setInterval timer gets clear and starts counting from zero. My access token gets expired and the refresh token never getting a call and user logged out. Is there any way to deal with this?
useEffect(() => {
const interval = setInterval(() => {
setTokenByReSendingAuth(); //dispatch action
}, 300000);
return () => clearInterval(interval);
}, [setTokenByReSendingAuth]);
Using MERN:
Heres your dependencies:
jwt-decode: https://www.npmjs.com/package/jwt-decode
passport-jwt: http://www.passportjs.org/packages/passport-jwt/
passport: https://www.npmjs.com/package/passport
jsonwebtoken: https://www.npmjs.com/package/jsonwebtoken
bcryptjs: https://www.npmjs.com/package/bcryptjs
Create an express server.js like this:
const express = require("express");
const cors = require("cors");
const mongoose = require("mongoose");
const path = require("path");
const passport = require("passport");
const db = require("./config/.env").mongoURI; //uri in .env file
const app = express();
const port = process.env.PORT || 5000;
app.use(cors());
app.use(express.json());
mongoose.connect(db, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true,
});
const connection = mongoose.connection;
connection.once("open", () => {
console.log("MongoDB database connection established successfully");
});
const myRouter = require("./routes/example.js");
app.use(passport.initialize()); // used to attatch token to request headers
require("./config/passport")(passport);
app.use("/example", myRouter);
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/build"));
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
});
}
app.listen(port, () => {
console.log(`Server is running on port: ${port}`);
});
Install your dependencies:
"dependencies": {
"axios": "^0.21.1",
"bcryptjs": "^2.4.3",
"concurrently": "^5.3.0",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"jsonwebtoken": "^8.5.1",
"mongoose": "^5.11.8",
"passport": "^0.4.1",
"passport-jwt": "^4.0.0",
"validator": "^13.5.2",
"nodemon": "^2.0.7"
},
Add this to your package.json in whatever directory your server is in:
"devDependencies": {
"nodemon": "^2.0.7"
},
"scripts": {
"start": "node server.js",
"server": "nodemon server.js"
},
Now to the Auth part:
Make a config folder for your passport and URI:
In a .env file:
module.exports = {
mongoURI: "mongodb+srv://",
secretOrKey: "abunchofrandomcharacterscreatedwithbcrypt",
};
Make a passport.js file:
This adds the user's token to all request headers, it is automatically running since we used it in our server.js file.
const JwtStrategy = require("passport-jwt").Strategy;
const ExtractJwt = require("passport-jwt").ExtractJwt;
const mongoose = require("mongoose");
const User = mongoose.model("users");
const keys = require("./.env");
const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = keys.secretOrKey;
module.exports = (passport) => {
passport.use(
new JwtStrategy(opts, (jwt_payload, done) => {
User.findById(jwt_payload.id)
.then((user) => {
if (user) {
return done(null, user);
}
return done(null, false);
})
.catch((err) => console.log(err));
})
);
};
Make a middleware folder for your backend:
Add an auth.js file:
const jwt = require("jsonwebtoken");
const config = require("../config/.env").secretOrKey;
function authUser(req, res, next) {
const authHeader = req.header("Authorization");
const token = authHeader && authHeader.split(" ")[1];
// Check for token
if (!token)
return res.status(401).json({ msg: "No token, authorization denied" });
try {
// Verify token
const decoded = jwt.verify(token, config);
// Add user from payload
req.user = decoded;
next();
} catch (e) {
res.status(400).json({ msg: "Token is not valid" });
}
}
module.exports = {
authUser,
};
This file is attached to your routes in the header, like this:
router.post("/example/get", authUser, (req, res) => {
const { reqData } = req.body; //dont ever put user ids in here
.catch((err) => {
res.status(400).json({ msg: err });
});
});
The route to login and register should look like this:
const router = require("express").Router();
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
const { authUser } = require("../middleware/auth"); //used in the header of auth needed requests
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const keys = require("../config/.env");
const validateRegisterInput = require("../validation/register"); //checks and validates user register inputs
const validateLoginInput = require("../validation/login"); //checks and validates user register inputs
const User = require("../models/user");
const { Permissions } = require("../models/permissions");
//uses a middleware to validate register inputs, checks if user data exists in db, salts and hashes the password.
router.post("/register", (req, res) => {
const { errors, isValid } = validateRegisterInput(req.body);
if (!isValid) {
return res.status(400).json(errors);
}
User.findOne({ email: req.body.email }).then((user) => {
if (user) {
return res.status(400).json({ email: "Email already exists" });
} else {
const newUser = new User({
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
password: req.body.password,
});
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
newUser
.save()
.then((user) => res.json(user))
.catch((err) => console.log(err));
});
});
}
});
});
//login creds are req through this route, the details are compared to the db user collection, and the user data that matches the decoded password and username will be responed back through the token.
router.post("/login", (req, res) => {
const { errors, isValid } = validateLoginInput(req.body);
if (!isValid) {
return res.status(400).json(errors);
}
const email = req.body.email;
const password = req.body.password;
User.findOne({ email }).then((user) => {
if (!user) {
return res.status(404).json({ email: "Email not found" });
}
bcrypt.compare(password, user.password).then((isMatch) => {
if (isMatch) {
const payload = {
id: user.id,
firstName: user.firstName,
};
jwt.sign(
payload,
keys.secretOrKey,
{
expiresIn: 31556926, //expires in a year
},
(err, token) => {
res.json({
success: true,
token: "Bearer " + token,
});
}
);
} else {
return res
.status(400)
.json({ passwordincorrect: "Password incorrect" });
}
});
});
});
module.exports = router;
That's basically it for the backend auth routing side of things, but for the client to get there token in the browser you need to add this stuff to the client:
In your index.js add this outside a component to run on every render no matter what:
This checks to see if there's a jwttoken in the browser, it decodes it and sets the user data into the state to be used globally. It also redirects the user.
import setAuthToken from "./utils/setAuthToken";
import jwt_decode from "jwt-decode";
if (localStorage.jwtToken) {
// Set auth token header auth
const token = localStorage.jwtToken;
setAuthToken(token);
// Decode token and get user info and exp
const decoded = jwt_decode(token);
// Set user and isAuthenticated
store.dispatch(setCurrentUser(decoded)); // using redux, can easily also just use contextApi or something else
// Check for expired token
const currentTime = Date.now() / 1000; // to get in milliseconds
if (decoded.exp < currentTime) {
// Logout user
store.dispatch(logoutUser());
// Redirect to login
window.location.href = "./";
}
}
Create login, register and logout functions:
import axios from "axios";
import setAuthToken from "../../utils/setAuthToken";
import jwt_decode from "jwt-decode";
import { SET_CURRENT_USER } from "./authTypes"; //puts user data into state
import { showSnackbar } from "../inventory/inventoryActions";
export const registerUser = (userData) => (dispatch) => {
axios
.post("/users/register", userData)
.then(() => {
console.log("logged in")
})
.catch(() => {
console.log("something wrong")
});
};
export const loginUser = (userData) => (dispatch) => {
axios
.post("/users/login", userData)
.then((res) => {
const { token } = res.data;
localStorage.setItem("jwtToken", token);
setAuthToken(token);
const decoded = jwt_decode(token);
dispatch(setCurrentUser(decoded));
dispatch(showSnackbar(`Successfully signed in!`, "success", 3000));
})
.catch(() => {
console.log("somethings wrong")
});
};
export const setCurrentUser = (decoded) => { // used in loginUser
return {
type: SET_CURRENT_USER,
payload: decoded,
};
};
//removes token from localstorage
export const logoutUser = () => {
return (dispatch) => {
localStorage.removeItem("jwtToken");
setAuthToken(false);
dispatch(setCurrentUser({}));
};
};
If you have any private components you only want logged in users to access use this PrivateRoute Component wrapper:
This redirects any user not logged in to the home page
import React from "react";
import { Route, Redirect } from "react-router-dom";
import { connect } from "react-redux";
import PropTypes from "prop-types";
const PrivateRoute = ({ component: Component, auth, ...rest }) => (
<Route
{...rest}
render={(props) =>
auth.isAuthenticated === true ? (
<Component {...props} />
) : (
<Redirect to="/" />
)
}
/>
);
PrivateRoute.propTypes = {
auth: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => ({
auth: state.auth,
});
export default connect(mapStateToProps)(PrivateRoute);
Use it as a react-router-dom element:
<PrivateRoute exact path="/example" component={privateComponentExample} />
If you have any questions, let me know. :)

Why is a user able to login and have a json web token is issued even though user has entered incorrect password?

1.below are the AuthRoutes.js , User.js , RequireAuth.js files with the code.
2.Ive tried changing the email to an invalid un-captured one , and it does report the error "Invalid email or password" which is correct, however when the user enters the incorrect password for that user ,
3. a JSON web token is issued instead of posting the error invalid Email or password as indicated in the code.
const express = require('express');
const mongoose = require('mongoose')
const jwt = require('jsonwebtoken');
const User = mongoose.model('User');
const router = express.Router();
router.post('/signup', async (req,res)=>{
const {name,email,password} = req.body;
try{
const user = new User({name,email,password});
await user.save();
const token = jwt.sign({ userId: user._id},'MY_SECRET_KEY');
res.send({token});
} catch (err) {
return res.status(422).send(err.message);
}
});
router.post('/signin', async (req, res) =>{
const { email, password} = req.body;
if (!email || !password) {
return res.status(422).send({error:'Must provide email and password '})
}
const user = await User.findOne ({ email});
if(!user) {
return res.status(422).send ({error:'Invalid email or password'});
}
try{
await user.comparePassword(password);
const token = jwt.sign ({userId: user._id},'MY_SECRET_KEY');
res.send ({ token });
} catch (err){
return res.status(422).send({error:'Invalid password or email'})
}
});
module.exports = router;
const jwt = require ('jsonwebtoken');
const mongoose = require ('mongoose');
const User = mongoose.model('User');
module.exports = (req,res,next) =>{
const {authorization} = req.headers.body;
//authorization === 'Bearer dfkndlkfgdkgmdlkgmdlkgdkfgmdlfkg(token)'
if (!authorization) {
return res.status (401).send ({error:"You must be logged in"});
}
const token = authorization.replace('Bearer ','');
jwt.verify(token, 'MY_SECRET_KEY', async (err, payload)=>{
if (err){
return res.status(401).send({error:'You must be logged in'});
}
const {userId} = payload;
const user = await User.findById(userId);
req.user = user;
next();
});
};
const mongoose = require('mongoose');
const bcrypt = require ('bcrypt');
const userSchema = new mongoose.Schema({
name:{
type:String,
required: true,
},
email:{
type: String,
unique: true,
required: true,
},
password:{
type:String,
required: true
}
});
userSchema.pre('save', function (next){
const user = this;
if(!user.isModified('password')){
return next();
}
bcrypt.genSalt(10, (err,salt) =>{
if (err){
return next (err);
}
bcrypt.hash(user.password, salt, (err, hash) => {
if (err) {
return next (err);
}
user.password = hash;
next ();
});
});
});
userSchema.methods.comparePassword = function(candidatePassword){
return new Promise ((resolve,reject) => {
bcrypt.compare (candidatePassword, this.password, (err,isMatch) => {
if (err){
return reject (err);
} else {
return resolve(true);
}
});
});
};
mongoose.model('User', userSchema);
Its because your comparePassword is returning a result that isn't checked for / caught.
Change your promise to:
return new Promise ((resolve,reject) => {
bcrypt.compare(candidatePassword, this.password, (err, res) => {
if (err){
reject(err);
} else {
resolve(res);
}
});
});
And then your check to:
const result = await user.comparePassword(password);
if (!result) return res.status(422).send({error:'Incorrect email and/or password'});

Resources