const express = require('express');
const router = express.Router();
const auth = require('../../middleware/auth');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const config = require('config');
const { check, validationResult } = require('express-validator');
const User = require('../../models/User');
// #route Get api/auth
// #desc Test route
// #access Public
router.get('/', auth, async (req, res) => {
try {
const user = await User.findById(req.user.id).select('-password');
res.json(user);
} catch(err) {
console.error(err.message);
res.status(500).send('Server Error')
}
});
// #route POST api/auth
// #desc Authenticate User And Get Token
// #access Public
router.post('/',
[
check('email', 'Please include a valid email').isEmail(),
check('password', 'Password is required').exists()
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array()});
}
const { email, password } = req.body;
try {
// See if user exists
let user = await User.findOne({ email})
if (!user) {
return res
.status(400)
.json({ errors: [ { msg: 'Invalid Credentials Email' } ] });
}
// Make Sure Password matches
const isMatch = await bcrypt.compare(password, user.password);
if(!isMatch) {
return res
.status(400)
.json({ errors: [ { msg: 'Invalid Credentials Password' } ] });
}
const payload = {
user: {
id: user.id
}
}
jwt.sign(
payload,
config.get('jwtSecret'),
{ expiresIn: 360000 },
(err, token) => {
if(err) throw err;
res.json({ token });
}
);
} catch(err) {
console.error(err.message);
res.status(500).send('Server error')
}
});
module.exports = router
In my database I have email and password
in my postman when i make POST request to https://localhost:5000/api/auth
the email is correct however I keep getting password is not correct with this const isMatch = await bcrypt.compare(password, user.password);
if(!isMatch) {
return res
.status(400)
.json({ errors: [ { msg: 'Invalid Credentials Password' } ] });
}
i console logged both password and user.password and it is the same value, i dont understand why !isMatch is keep getting triggered
can anyone help me
The syntax of bcrypt.compare() is:
bcrypt.compare(plaintextPassword, hash)...
The first parameter is plaintext password and second is hash of the real password so it will not be the same value. As you mentioned that your password and user.password is the same value, I guess you forgot to hash user password before saving it to the DB. Please check the docs for more details.
Related
I put the wrong password but it gets login successfully and the data which is inside a particular user is as a new user login. I want you to please tell me a error.
Link of an online hosted project: NOTEBOOK/login
Link of GitHub repository: GITHUB/notebook
The login folder is inside routes/auth.js
const express = require('express');
const router = express.Router();
const User = require('../models/User')
const { body, validationResult } = require('express-validator');
var bcrypt = require('bcryptjs');
var jwt = require('jsonwebtoken');
let fetchuser = require('../middleware/fetchuser')
const dotenv = require('dotenv');
dotenv.config();
const secret=process.env.YOUR_SECRET
router.post('/login', [
body('email', 'Enter a valid Email').isEmail(),
body('password', 'Password cannot be blank').exists()
], async (req, res) => {
let success=false;
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { email, password } = req.body;
try {
// check whether user with this email exist
let user = await User.findOne({ email });
if (!user) {
return res.status(400).json({success, error: "Please login using correct credentials" })
};
let passwordCompare = bcrypt.compare(password, user.password);
if (!passwordCompare) {
return res.status(400).json({success, error: "Please login using correct credentials" })
};
const data = {
user: {
id: user.id
}
}
var authtoken = jwt.sign(data, secret);
success=true;
res.json({success, authtoken })
} catch (error) {
console.log(error.message);
res.status(500).send("Internal server error has occured")
}
})
you need to wait for the promise to resolve, you can opt for any of the below options
const passwordCompare = await bcrypt.compare(password, user.password);
OR
const passwordCompare = bcrypt.compareSync(password, user.password);
OR
bcrypt.compare(password, user.password).then(function(result) {
// result == true
});
I am trying to show the page only if the Jsonwebtoken is verified and the user is logged on to the website, else show him the sign-in page.
However, I can see the token is generated in MongoDB, and also when I console log I can see that it is all good. But the issue is when I try to verify it using an already generated jwt token i.e.
req.cookies.signinToken
it shows an error.
Please the detail code below:
On app.js
const dotenv = require("dotenv");
const mongoose = require("mongoose");
const express = require("express");
const app = express();
const jwt = require("jsonwebtoken");
const cookieParser = require("cookie-parser");
dotenv.config({ path: "./config.env" });
require("./db/connection");
app.use(express.json());
app.use(cookieParser());
app.use(require("./router/route"));
const PORT = process.env.PORT;
app.listen(5000, () => {
console.log(`server running on ${PORT}`);
});
On route.js
const express = require("express");
const bcrypt = require("bcrypt");
const router = express.Router();
const jwt = require("jsonwebtoken");
require("../db/connection");
const User = require("../model/newUserSchema");
const auth = require("../middleware/auth");
// router.get("/", (req, res) => {
// res.send("hello am backend sever");
// });
//Signup or Register Part
router.post("/signup", async (req, res) => {
const { username, email, cpassword, retypePassword } = req.body;
if (!username || !email || !cpassword || !retypePassword) {
return res.status(422).json({ error: "please enter valid details" });
}
try {
const UserExist = await User.findOne({ email: email });
if (UserExist) {
return res.status(422).json({ error: "email already exist" });
} else if (cpassword !== retypePassword) {
return res.status(422).json({ error: "password incorrect" });
} else {
const user = new User({
username,
email,
cpassword,
retypePassword,
});
const userResgister = await user.save();
if (userResgister) {
return res.status(201).json({ message: "signup successfully" });
}
}
} catch (error) {
console.log(error);
}
});
//Login Part
router.post("/signin", async (req, res) => {
try {
const { email, cpassword } = req.body;
if (!email || !cpassword) {
return res.status(400).json({ error: " please enter valid credentials" });
}
const userLogin = await User.findOne({ email: email });
const token = userLogin.generateAuthToken();
res.cookie("signinToken", token, {
expires: new Date(Date.now() + 25892000000),
httpOnly: true,
});
if (userLogin) {
const isMatch = await bcrypt.compare(cpassword, userLogin.cpassword);
if (isMatch) {
return res.status(200).json({ message: "sigin in scuccessfully" });
} else {
return res.status(400).json({ error: " Invalid credentials" });
}
} else {
return res.status(400).json({ error: " Invalid credentials " });
}
} catch (error) {
console.log(error);
}
});
//watchlistpage
router.get("/watchlist", auth, (req, res) => {
console.log(" this is jwt token test " + req.cookies.signinToken);
res.send(req.rootuser);
console.log(req.rootuser);
});
module.exports = router;
On newUserSchema.js:
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const newUserSchema = new mongoose.Schema({
username: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
cpassword: {
type: String,
required: true,
},
retypePassword: {
type: String,
required: true,
},
tokens: [
{
token: {
type: String,
required: true,
},
},
],
});
newUserSchema.pre("save", async function (next) {
if (this.isModified("cpassword")) {
this.cpassword = await bcrypt.hash(this.cpassword, 12);
this.retypePassword = await bcrypt.hash(this.retypePassword, 12);
}
next();
});
newUserSchema.methods.generateAuthToken = async function () {
try {
let token = jwt.sign({ _id: this._id }, process.env.SECRETKEY);
this.tokens = this.tokens.concat({ token: token });
await this.save();
return token;
} catch (error) {
console.log(error);
}
};
const User = mongoose.model("newUser", newUserSchema);
module.exports = User;
On auth.js (this is also my middleware)
const jwt = require("jsonwebtoken");
const User = require("../model/newUserSchema");
const Auth = async (req, res, next) => {
try {
console.log(JSON.stringify(req.cookies.signinToken) + " this is jwt token test");
const token = req.cookies.signinToken;
const verifytoken = jwt.verify(token, process.env.SECRETKEY);
const rootuser = await User.findOne({ _id: verifytoken._id, "tokens.token": token });
if (!rootuser) {
throw new Error("user not found");
}
req.token = token;
req.rootuser = rootuser;
req.UserID = rootuser._id;
next();
} catch (error) {
res.status(401).send("Unauthorized access");
console.log(error);
}
};
module.exports = Auth;
The API call result in postman
The terminal error :
when I try to console log on route.js inside Signin page i see promise pending
const token = userLogin.generateAuthToken();
console.log(token);
res.cookie("signinToken", token, {
expires: new Date(Date.now() + 25892000000),
httpOnly: true,
});
Could you please help to correct the error also please let me know why is this error coming?
Thanks a million in advance for any tips or suggestions.
Hi Thanks for the help
I just saw that my token was returning a promise, I did not add the keyword await before the token other thing was I was trying to access it before the validation, hence it was showing me nodata and error.
Please see the correct code below:
//Login Part
router.post("/signin", async (req, res) => {
try {
const { email, cpassword } = req.body;
if (!email || !cpassword) {
return res.status(400).json({ error: " please enter valid credentials" });
}
const userLogin = await User.findOne({ email: email });
if (userLogin) {
const isMatch = await bcrypt.compare(cpassword, userLogin.cpassword);
const token = await userLogin.generateAuthToken();
console.log(token);
res.cookie("signinToken", token, {
expires: new Date(Date.now() + 25892000000),
httpOnly: true,
});
if (isMatch) {
return res.status(200).json({ message: "sigin in scuccessfully" });
} else {
return res.status(400).json({ error: " Invalid credentials" });
}
} else {
return res.status(400).json({ error: " Invalid credentials " });
}
} catch (error) {
console.log(error);
}
});
I hope this might help other learners too.
Thanks.
const router = require("express").Router();
const mongoose = require("mongoose");
const User = require("../models/Users");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
// Route 1: create new user at /api/createuser
router.post("/createuser", async (req, res) => {
try {
console.log(req.body);
const salt = await bcrypt.genSaltSync(10);
const hash = await bcrypt.hashSync(req.body.password, salt);
password = hash;
const user = new User({
name: req.body.name,
email: req.body.email,
password: password,
});
user
.save()
.then(() => {
res.json({ message: "User created successfully" });
})
.catch((err) => {
res.json({ message: "Error: " + err });
});
console.log(password);
} catch (err) {
res.json({ message: err });
}
});
// Route 2: Login user at /api/login
router.post("/login", async (req, res) => {
try {
console.log("login endpoint triggered");
const user = await User.findOne({ email: req.body.email });
if (!user) {
res.json({ message: "User does not exist" });
}
const passwordIsValid = await bcrypt.compare(
req.body.password,
user.password
);
if (!passwordIsValid) {
res.json({ message: "Invalid password" });
} else {
const data = {
id: user._id,
};
const token = await jwt.sign(data, process.env.SECRET);
res.json(token);
}
} catch (error) {
res.json({ message: error });
}
});
module.exports = router;
Whenever I am testing the login endpoint, my app crashes if i try to put incorrect password or unregistered email.
It says Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
I have only sent one response to the client even then it is showing this error.
In the terminal, it is showing error at catch block of login endpoint.
Can anyone look into it and tell me why am i getting this error.
Make sure that when the response is an error, the rest of the middleware is not executed any more. For example, put a return before the res.json statement:
if (!user) {
return res.json({ message: "User does not exist" });
}
Otherwise, the "Invalid password" json might get issued after this one, but two res.jsons in one response lead to the observed error.
I was following a tutorial at Authentication in NodeJS With Express and Mongo - CodeLab #1
I got everything to work perfectly, but the tutorial does not address how to log out a user.
From what I can tell, the session is being saved on Mongoose Atlas, which is the database I am using. When I log a user in with Postman, I get a token back. But I am not sure how to configure the /logout route.
Here is my code:
//routes/user.js
const express = require("express");
const { check, validationResult } = require("express-validator");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const router = express.Router();
const auth = require("../middleware/auth");
const User = require("../models/User");
/**
* #method - POST
* #param - /signup
* #description - User SignUp
*/
//Signup
router.post(
"/signup",
[
check("username", "Please Enter a Valid Username")
.not()
.isEmpty(),
check("email", "Please enter a valid email").isEmail(),
check("password", "Please enter a valid password").isLength({
min: 6
})
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
errors: errors.array()
});
}
const {
username,
email,
password
} = req.body;
try {
let user = await User.findOne({
email
});
if (user) {
return res.status(400).json({
msg: "User Already Exists"
});
}
user = new User({
username,
email,
password
});
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);
await user.save();
const payload = {
user: {
id: user.id
}
};
jwt.sign(
payload,
"randomString", {
expiresIn: 10000
},
(err, token) => {
if (err) throw err;
res.status(200).json({
token
});
}
);
} catch (err) {
console.log(err.message);
res.status(500).send("Error in Saving");
}
}
);
// Login
router.post(
"/login",
[
check("email", "Please enter a valid email").isEmail(),
check("password", "Please enter a valid password").isLength({
min: 6
})
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
errors: errors.array()
});
}
const { email, password } = req.body;
try {
let user = await User.findOne({
email
});
if (!user)
return res.status(400).json({
message: "User Not Exist"
});
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch)
return res.status(400).json({
message: "Incorrect Password !"
});
const payload = {
user: {
id: user.id
}
};
jwt.sign(
payload,
"randomString",
{
expiresIn: 3600
},
(err, token) => {
if (err) throw err;
res.status(200).json({
token
});
}
);
} catch (e) {
console.error(e);
res.status(500).json({
message: "Server Error"
});
}
}
);
// router.route("/logout").get(function (req, res, next) {
// if (expire(req.headers)) {
// delete req.user;
// return res.status(200).json({
// "message": "User has been successfully logged out"
// });
// } else {
// return next(new UnauthorizedAccessError("401"));
// }
// });
router.get("/me", auth, async (req, res) => {
try {
// request.user is getting fetched from Middleware after token authentication
const user = await User.findById(req.user.id);
res.json(user);
} catch (e) {
res.send({ message: "Error in Fetching user" });
}
});
router.get('/logout', isAuthenticated, function (req, res) {
console.log('User Id', req.user._id);
User.findByIdAndRemove(req.user._id, function (err) {
if (err) res.send(err);
res.json({ message: 'User Deleted!' });
})
});
module.exports = router;
function isAuthenticated(req, res, next) {
console.log("req: " + JSON.stringify(req.headers.authorization));
// if (!(req.headers && req.headers.authorization)) {
// return res.status(400).send({ message: 'You did not provide a JSON web token in the authorization header' });
//}
};
///middleware/auth.js
const jwt = require("jsonwebtoken");
module.exports = function (req, res, next) {
const token = req.header("token");
if (!token) return res.status(401).json({ message: "Auth Error" });
try {
const decoded = jwt.verify(token, "randomString");
req.user = decoded.user;
next();
} catch (e) {
console.error(e);
res.status(500).send({ message: "Invalid Token" });
}
};
///models/User.js
const mongoose = require("mongoose");
const UserSchema = mongoose.Schema({
username: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now()
}
});
// export model user with UserSchema
module.exports = mongoose.model("user", UserSchema);
So my question is, how can I implement a /logout route so that if the user clicks the logout button and invokes that route, their token is destroyed. I am only asking about the back-end part. I can handle using axios.
Thanks.
From what I see, you are not saving any session data or storing tokens anywhere - which is great. You are simply appending the token to your headers in requests to the API.
So the only thing you can do is possibly expire the token in the /logout route
and then ensure you delete the token on the client - could be localStorage, sessionStorage etc - your client code needs to kill the token so it cannot be included again.
Side note:
You are not extending the token lifetime anywhere, so even if the user keeps interacting on the website, the token expiration is not being updated. You will need to manually refresh the token/generate a new token to have a sliding expiration.
I would suggest you save the token in cookies rather. Set the cookie to HttpOnly, Secure, and specify the domain. This is far more secure and will allow you to also expire the cookie from the API. If any scripts you include get compromised, they can access all your users’ tokens easily.
Example:
import {serialize} from 'cookie';
import jsend from 'jsend';
...
const token = jwt.sign(
{
id: validationResult.value.id // whatever you want to add to the token, here it is the id of a user
},
privateKeyBuffer,
{
expiresIn: process.env.token_ttl,
algorithm: 'RS256'
});
const cookieOptions = {
httpOnly: true,
path: '/',
maxAge: process.env.token_ttl,
expires: new Date(Date.now() + process.env.token_ttl),
sameSite: process.env.cookie_samesite, // strict
domain: process.env.cookie_domain, // your domain
secure: process.env.cookie_secure // true
};
const tokenCookie = await serialize('token', token, cookieOptions);
res.setHeader('Set-Cookie', [tokenCookie]);
res.setHeader('Content-Type', 'application/json');
res.status(200).json(jsend.success(true));
Then in logout:
// grab from req.cookies.token and validate
const token = await extractToken(req);
// you can take action if it's invalid, but not really important
if(!token) {
...
}
// this is how we expire it - the options here must match the options you created with!
const cookieOptions = {
httpOnly: true,
path: '/',
maxAge: 0,
expires: 0,
sameSite: process.env.cookie_samesite, // strict
domain: process.env.cookie_domain, // your domain
secure: process.env.cookie_secure // true
};
// set to empty
const tokenCookie = await serialize('token', '', cookieOptions);
res.setHeader('Set-Cookie', [tokenCookie]);
res.setHeader('Content-Type', 'application/json');
res.status(200).json(jsend.success(true));
As you have used JWT the backend will always check 2 things
1. Proper token
2. If time is over for that particular (You should handle this one)
For 2nd point, if the user time is up than from frontend you may delete the token if you have stored the token in localstorage.
For logout when user clicks on logout just delete jwt from localstorage and redirect to login or other page
My problem is when I make a request to POST /users/login with true data I receive no response instead the catch throw 400
routs file :
const express = require("express");
const router = new express.Router();
const User = require("../models/user");
router.post("/users/login", async (req, res) => {
try {
const user = await User.findByCredentials(
req.body.email,
req.body.password
);
// console.log(user);
res.send({ user });
} catch (e) {
res.status(400).send();
}
});
module.exports = router;
user middleware
userSchema.static.findByCredentials = async (email, password) => {
const user = await User.findOne({ email: email });
if (!user) {
throw new Error("Unable to find the user");
}
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
throw new Error("uncorect password");
}
return user;
};
Not all the code here, but I am sure the problem is in these lines,
In user middleware it should be statics(plural)
userSchema.statics.findByCredentials = async (email, password) => {
})