I am trying to implement the usage of JWT but it seems that my token is not being stored.
this is the creating of the token...
router.post('/index', async (req, res) => {
let post = { //retreive user input
username: req.body.username,
password: req.body.password,
}
try {
const user = await dbb('users').first('*').where({username: post.username});
if(!user) {
res.render(path.resolve('./myviews/index'), { //display if user is not found
info: 'The Username Or Password You Have Entered Does Not Match Our Records'
});
}
if(user) {
const validPass = await bcrypt.compare(post.password, user.password);
if(validPass){
const token = jwt.sign(`${user}`, process.env.ACCESS_TOKEN);
res.json({token: token});
res.redirect('/home');
} else {
res.render(path.resolve('./myviews/index'), { //display if user is not found
info: 'The Username Or Password You Have Entered Does Not Match Our Records'
});
}
}
} catch(e) {
console.log(e);
}
});
this is the function that is supposed to use the token...
function authToken(req, res, next) {
const authHeader = req.headers['authorization'];
if(typeof authHeader !== 'undefined') {
console.log("here")
const bearer = authHeader.split(' ');
const bearerToken = bearer[1];
req.token = bearerToken;
next();
} else {
res.sendStatus(401);
}
im wondering if anyone can help me understand where i am going wrong
Related
cookies just keeps adding on and on when I register a new user.
userRoutes.js:
const { registerUser, loginUser, registerVerify } = require("./userController");
const express=require('express')
const router=express.Router()
router
.route('/register')
.post(registerUser)
module.exports=router
userController.js:
const User = require("./userModel");
const validator = require("validator");
const jwt=require('jsonwebtoken')
const sendToken = function (email) {
let data = {
expiresIn: process.env.JWT_EXPIRE_SHORT,
email,
};
let token = jwt.sign(data, process.env.JWT_SECRET_KEY);
return token;
};
exports.registerUser = async (req, res, next) => {
try {
const { userName, email, password, confirmPassword } = req.body;
let user = [
{ userName, email, password, confirmPassword, createdAt: Date.now() },
];
//passwords do not match error
if (password !== confirmPassword) {
throw "Password doesn't match Confirm Password.";
}
//email not valid error
if (!validator.isEmail(email)) {
throw "Email not valid.";
}
//user already logged in error
if (await User.findOne({ email })) {
throw "User already exists. Please login";
}
//create user
await User.create(user);
//assign token
let token = sendToken(email);
//clear cookie
res.status(200).clearCookie('token').cookie(token, 'token', {maxAge: 360000}).json({
message: "Please fill the required text send via email",
token,
});
} catch (err) {
// console.log(err)
res.status(400).json({
message: "Signup Not Successfull",
err,
});
}
};
I've tried:
router
.route('/register',{ credentials: 'same-origin'})
.post(registerUser)
router
.route('/register',{ credentials: 'include'})
.post(registerUser)
router
.route('/register',{ withCredentials: true})
.post(registerUser)
Here is the correct way for creating a cookie:
res.cookie('token', token, {maxAge: 360000});
Note: You don't need to clear and create a new one.
when you send a cookie with the same key it overrides itself.
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.
I want to make a middleware to check the user. I'm using JWT and cookies for this. I retrieved the cookie and decrypted it (it has been encrypted in the login controller function). Then I used jwt.verify(). But I've received this error message : JsonWebTokenError: jwt malformed.
I've seen that it may mean that the token wasn't "the right formated" one. But I can't figure it out.
checkUser function :
exports.checkUser = async(req, res, next) => {
const cryptedToken = req.cookies.snToken;
console.log("cryptedToken01", cryptedToken); //displays a string consists of 3 parts, separated by /
const token = cryptojs.AES.decrypt(cryptedToken, process.env.COOKIE_KEY).toString();
console.log("token01", token); // displays a longer monolithic string
if (token) {
jwt.verify(token, process.env.COOKIE_KEY, async(err, verifiedJwt) => {
if (err) {
console.log("err inside jwt verify", err); // displays an error mesassage (JsonWebTokenError: jwt malformed)
console.log("res.locals", res.locals); //displays res.locals [Object: null prototype] {}
res.locals.user = null;
res.cookie("snToken", "", { maxAge: 1 });
next();
} else {
let user = await User.findByPk(verifiedJwt.userId);
res.locals.user = user;
next();
}
});
} else {
res.locals.user = null;
next();
}
};
my login function :
exports.login = async(req, res) => {
try {
const user = await User.findOne({ where: { email: req.body.email } });
if (!user) {
return res.status(403).send({ error: 'The login information (email) is incorrect!' });
}
bcrypt
.compare(req.body.password, user.password)
.then((isPasswordValid) => {
if (!isPasswordValid) {
return res.status(403).send({ error: 'The login information (pwd) is incorrect!' });
} else {
const newToken = jwt.sign(
{ userId: user.id },
process.env.COOKIE_KEY, { expiresIn: "24h" }
);
const newCookie = { token: newToken, userId: user.id };
const cryptedToken = cryptojs.AES.encrypt(JSON.stringify(newCookie), process.env.COOKIE_KEY).toString();
res.cookie('snToken', cryptedToken, {
httpOnly: true,
maxAge: 86400000
});
//res.status(200).send({ message: 'The user is successfully connected!', data: user });
res.status(200).send({ message: 'The user is successfully connected!', data: user, cryptedToken: cryptedToken });
}
});
} catch (error) {
res.send({ error: 'An error has occured while trying to log in!' });
}
}
The call of these middlwares in my app.js:
app.get('*', checkUser);
In your current code, you get a Hex encoded ASCII string after decryption
7b22746f6b656e223a2265794a68624763694f694a49557a49314e694973496e523563434936496b705856434a392e65794a3163325679535751694f6a45314c434a70595851694f6a45324e4445314e6a45324d545173496d5634634349364d5459304d5459304f4441784e48302e693670564f486443473456445154362d3749644545536f326251467765394d4b34554a316f363676564334222c22757365724964223a31357d,
which contains your cookie as a stringified JSON.
Instead of toString() after decrypting, which causes the hex encoded output, call toString(cryptojs.enc.Utf8) to get a JSON String and then parse it to an object:
const bytes = cryptojs.AES.decrypt(cryptedToken, process.env.COOKIE_KEY);
const cookie = JSON.parse(bytes.toString(cryptojs.enc.Utf8));
console.log("token", cookie.token);
the result is a correct JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjE1LCJpYXQiOjE2NDE1NjE2MTQsImV4cCI6MTY0MTY0ODAxNH0.i6pVOHdCG4VDQT6-7IdEESo2bQFwe9MK4UJ1o66vVC4
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.
I am trying to complete jwt sign in I am trying to create the jwt token in login.Then I am trying to use it within my user-questions route.
I am using a react front end.
Is this the correct way to do so?
I am currently getting error
const token = req.cookies.auth;
[0] ^
[0]
[0] ReferenceError: req is not defined
Below is my routes login code which assigns the token once the my sql server return that the values for email and password exist. User-questions tries to use this jwt. and I have also included how the token is verfied in a function
Verfiy users
app.get("/user-questions", verifyToken, function(req, res) {
app.use(function(req, res, next) {
// decode token
if (token) {
jwt.verify(token, "secret", function(err, token_data) {
if (err) {
console.info("token did not work");
return res.status(403).send("Error");
} else {
req.user_data = token_data;
sql.connect(config, function(err) {
if (err) console.log(err);
// create Request object
var request = new sql.Request();
// query to the database and get the records
request.execute("dbo.ViewQuestions", function(err, recordset) {
if (err) console.log(err);
// send records as a response
res.json(recordset);
next();
});
});
}
});
} else {
console.info("no token");
console.log("no token");
return res.status(403).send("No token");
}
});
});
Login route
app.post("/login", async (req, response) => {
try {
await sql.connect(config);
var request = new sql.Request();
var Email = req.body.email;
var Password = req.body.password;
console.log({ Email, Password });
request.input("Email", sql.VarChar, Email);
request.input("Password", sql.VarChar, Password);
var queryString =
"SELECT * FROM TestLogin WHERE email = #Email AND password = #Password";
//"SELECT * FROM RegisteredUsers WHERE email = #Email AND Password = HASHBYTES('SHA2_512', #Password + 'skrrt')";
const result = await request.query(queryString);
if (result.recordsets[0].length > 0) {
console.info("/login: login successful..");
console.log(req.body);
token = jwt.sign(
{ Email },
"secretkey",
{ expiresIn: "30s" },
(err, token) => {
res.json({
token
});
res.cookie("auth", token);
res.send("ok");
}
);
} else {
console.info("/login: bad creds");
response.status(400).send("Incorrect email and/or Password!");
}
} catch (err) {
console.log("Err: ", err);
response.status(500).send("Check api console.log for the error");
}
});
Verify users
// Verify Token
function verifyToken(req, res, next) {
// Get auth header value
const bearerHeader = req.headers["authorization"];
// Check if bearer is undefined
if (typeof bearerHeader !== "undefined") {
// Split at the space
const bearer = bearerHeader.split(" ");
// Get token from array
const bearerToken = bearer[1];
// Set the token
req.token = bearerToken;
// Next middleware
next();
} else {
// Forbidden
res.sendStatus(403);
}
}
Please advise if this in theory should work. And if not please advise how to resolve.
EDIT :
The error has been resolved however now simply my jwt tokens to do not work. As when logged in and I manually route to user-questions it does not load the component and within the console it says 403 not available (this is set in the code when the jwt token is not working).
UPDATE:
How would I include
['authorization'] = 'Bearer ' + token;
into
handleSubmit(e) {
e.preventDefault();
if (this.state.email.length < 8 || this.state.password.length < 8) {
alert(`please enter the form correctly `);
} else {
const data = { email: this.state.email, password: this.state.password };
fetch("/login", {
method: "POST", // or 'PUT'
headers: {
Accept: "application/json, text/plain, */*",
"Content-Type": "application/json",
},
body: JSON.stringify(data)
})
// .then(response => response.json())
.then(data => {
console.log("Success:", data);
})
.catch(error => {
console.error("Error:", error);
});
}
}
There are a couple of errors with your code:
In your /login route:
You are trying to set the "auth" cookie after the response is being sent
You are trying to send a response twice, once via res.json and once via res.send
You are assigning to a token variable that no longer exists (token = jwt.sign(...))
In your verifyToken method:
This method is only verifying that the request has a token set, it's not validating or decoding it. I would consider moving your jwt.verify() call to this method.
In your /user-questions route:
You're calling app.use inside of app.get, when both of these are intended to be called at the root level. Remove your app.use call.
You need to grab token from the request, ex. const { token } = req;
You are sending a response via res.json(), but you are still calling next() afterwards. From the Express docs:
If the current middleware function does not end the request-response cycle, it must call next() to pass control to the next middleware function.
This is how I would make these changes:
/login route:
app.post("/login", async (req, response) => {
try {
await sql.connect(config);
var request = new sql.Request();
var Email = req.body.email;
var Password = req.body.password;
console.log({ Email, Password });
request.input("Email", sql.VarChar, Email);
request.input("Password", sql.VarChar, Password);
var queryString =
"SELECT * FROM TestLogin WHERE email = #Email AND password = #Password";
//"SELECT * FROM RegisteredUsers WHERE email = #Email AND Password = HASHBYTES('SHA2_512', #Password + 'skrrt')";
const result = await request.query(queryString);
if (result.recordsets[0].length > 0) {
console.info("/login: login successful..");
console.log(req.body);
jwt.sign(
{ Email },
"secretkey",
{ expiresIn: "30s" },
(err, token) => res.cookie('auth', token).json({ token })
);
} else {
console.info("/login: bad creds");
response.status(400).send("Incorrect email and/or Password!");
}
} catch (err) {
console.log("Err: ", err);
response.status(500).send("Check api console.log for the error");
}
});
verifyToken method:
// Verify Token
function verifyToken(req, res, next) {
// Get auth header value
const bearerHeader = req.headers["authorization"];
// Check if bearer is undefined
if (typeof bearerHeader !== "undefined") {
// Split at the space
const bearer = bearerHeader.split(" ");
// Get token from array
const bearerToken = bearer[1];
// verify the token and store it
jwt.verify(bearerToken, "secret", function(err, decodedToken) {
if (err) {
console.info("token did not work");
return res.status(403).send("Error");
}
// Set the token
req.token = bearerToken;
req.decodedToken = decodedToken;
next();
});
} else {
// Forbidden
res.sendStatus(403);
}
}
/user-questions route:
app.get("/user-questions", verifyToken, function(req, res) {
// if a request has made it to this point, then we know they have a valid token
// and that token is available through either req.token (encoded)
// or req.decodedToken
sql.connect(config, function(err) {
if (err) console.log(err);
// create Request object
var request = new sql.Request();
// query to the database and get the records
request.execute("dbo.ViewQuestions", function(err, recordset) {
if (err) console.log(err);
// send records as a response
res.json(recordset);
});
});
});