I setup JWT in my express app and I get this error : invalid token. But when I copy this token to jwt.io, exp date is correct and data ok.
Here is my controller :
module.exports.signIn = async (req, res) => {
const { email, password } = req.body;
const user = await UserModel.findOne({ email });
if (!user) {
return res.status(400).json({
message: "User not found."
});
}
const isValidPassword = await bcrypt.compare(password, user.password);
if (!isValidPassword) {
return res.status(400).json({
message: "Invalid password."
});
}
const token = jwt.sign({ id: user._id }, process.env.SECRET_KEY, {
expiresIn: "10h"
});
res.status(200).json({
message: "User logged in.",
token
});
};
And my middleware :
const jwt = require("jsonwebtoken");
module.exports.verifyToken = (req, res, next) => {
const token = req.headers.authorization;
console.log(token);
if (!token) {
return res.status(403).json({
message: "No token provided."
});
}
jwt.verify(token, process.env.SECRET_KEY, (err, decoded) => {
if (err) {
return res.status(401).json({
message: "Unauthorized."
});
}
req.userId = decoded.id;
next();
});
};
What I do is :
sign in
copy token to Headers authorizations
make my get request to another route that has the verifyToken
And then I get the error.
When I console.log(token) I get Bearer ...[the token]
Any idea why ?
You need four arguments in the verify function
jwt.verify(token, process.env.PUBLIC_KEY, {}, (err, decoded) => {
if (err !== null && err instanceof TokenExpiredError) {
cb('TOKEN_EXP');
return Json.builder(Response.HTTP_UNAUTHORIZED_TOKEN_EXP);
}
if (err instanceof JsonWebTokenError) {
cb('IN_VALID_TOKEN');
return Json.builder(Response.HTTP_UNAUTHORIZED_INVALID_TOKEN);
}
cb(decoded);
});
Related
Here I'm carrying out the GET method to list the data by authenticating the login credentials, but when I pass the token in value in the header it directly catch the error message. is anything I'm doing wrong?
Authentication Middleware - authentication.js
const jwt = require("jsonwebtoken");
const authenticate = (req, res, next) => {
const access_token = req.headers["authorization"];
if (!access_token) return res.status(401).send("Access denied! no token provided.");
try {
const decoded = jwt.verify(access_token, "SECRET_JWT_CODE");
req.receive = decoded;
next();
} catch (error) {
res.status(400).send("invalid token.");
}
};
module.exports = authenticate;
console.log(req.headers)
GET method
const authenticate = require("./authentication.js");
router.get("/admin", authenticate, async (req, res) => {
try {
const receive = await SomeModel.find();
res.json(receive);
} catch (err) {
res.send(err);
}
});
login
router.post("/admin/sign_in", (req, res) => {
if (!req.body.email || !req.body.password) {
res.json({ error: "email and password is required" });
return;
}
login
.findOne({ email: req.body.email })
.then((admin) => {
if (!admin) {
res.json({ err: "user does not exist" });
} else {
if (!bcrypt.compareSync(req.body.password, admin.password)){
res.json({ err: "password does not match" });
} else {
const token = jwt.sign(
{
id: admin._id,
email: admin.email,
},
SECRET_JWT_CODE
);
res.json({
responseMessage: "Everything worked as expected",
access_token: token,
});
}
}
})
.catch((err) => {
err.message;
});
});
The token is in the form Bearer <token> so you need to split it before verifying it:
const jwt = require("jsonwebtoken");
const authenticate = (req, res, next) => {
const access_token = req.headers["Authorization"];
if (!access_token) return res.status(401).send("Access denied! no token provided.");
const splitToken = access_token.split(' ');
if (splitToken.length !== 2) return res.status(401).send("Access denied! invalid token.");
const token = splitToken[1];
try {
const decoded = jwt.verify(token, "SECRET_JWT_CODE");
req.receive = decoded;
next();
} catch (error) {
res.status(400).send("invalid token.");
}
};
module.exports = authenticate;
Also, make sure that you pass the token via the Authorization tab in Postman.
It should be available in req.headers["Authorization"] (capitalized) in the expected Bearer <token> format.
So I create my route user and I use this tutorial https://www.bezkoder.com/node-js-jwt-authentication-mysql/ to add the token and Authentication Token + validation name. Everything work great. But then when I beginning to create my route Comment (acces for every user), I had normaly the auth but even with the token Postman send me an "No token provided!". I have to add this token and autorize the acces but I don't know how. If somebody know how to do it, it would be great :D thx ! This is how I add my token on Postman
enter image description here
Here is my code:
My route comment:
const express = require("express");
const router = express.Router();
const commentCtrl = require("../controllers/comment");
const { authJwt } = require("../middleware");
router.post("/upload", [authJwt.verifyToken], commentCtrl.createComment);
module.exports = router;
The middleware token :
const jwt = require("jsonwebtoken");
const config = require("../config/auth.config.js");
verifyToken = (req, res, next) => {
let token = req.headers["x-access-token"];
if (!token) {
return res.status(403).send({
message: "No token provided!"
});
}
jwt.verify(token, config.secret, (err, decoded) => {
if (err) {
return res.status(401).send({
message: "Unauthorized!"
});
}
req.id = decoded.id;
next();
});
};
const authJwt = {
verifyToken: verifyToken,
};
module.exports = authJwt;
const authJwt = require("./authJwt");
const verifySignUp = require("./verifySignUp");
module.exports = {
authJwt,
verifySignUp
};
My verification SignUp:
const { User } = require("../models");
checkDuplicateEmail = async (req, res, next) => {
const user = await User.findOne({
where: {
email: req.body.email
}
}).then(user => {
if (user) {
res.status(400).send({
message: "Failed! Email is already in use!"
});
return;
}
next();
});
};
const verifySignUp = {
checkDuplicateEmail: checkDuplicateEmail
};
module.exports = verifySignUp;
And here is my user.controllers when is send the token:
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const config = require("../config/auth.config");
const { User } = require("../models");
module.exports.signup = async (req, res) => {
if (!req.body.email || !req.body.password) {
res.status(400).send({
status: false,
message: 'une erreur dans create est survenu'
});
} else {
let { nom, prenom, email, password, status} = req.body;
const salt = await bcrypt.genSalt(10);
password = await bcrypt.hash(password, salt)
const user = await User.create({
nom, prenom, email, password, status
})
.then((user) => res.status(201).send(user)).catch((error) => {
console.log(error);
res.status(400).send(error);
});
}
};
module.exports.login = async (req, res) => {
const user = await User.findOne({
where: {
email: req.body.email
}
})
.then(user => {
if (!user) {
return res.status(404).send({ message: "User Not found." });
}
const passwordIsValid = bcrypt.compareSync(req.body.password, user.password);
if (!passwordIsValid) {
return res.status(401).send({
accessToken: null,
message: "Invalid Password!"
});
}
var token = jwt.sign({ id: user.id }, config.secret, {
expiresIn: 86400 // 24 hours
});
res.status(200).send({
id: user.id,
nom: user.nom,
email: user.email,
password: user.password,
accessToken: token
});
})
.catch(err => {
res.status(500).send({ message: err.message });
});
};
While it would have helped if you had provided a screenshot of where you are putting your token on postman, make sure you are using the appropriate header. According to your code, you are searching the 'x-access-token' header, so in postman, you should attach your jwt token to the same header property. It should be something like this:
postman x-access-token demo
Picture of error in postman
//This is route
router.get("/all/posts",isSignedIn,isAuthenticated, getAllPosts)
//This is controller
exports.getAllPosts = (req, res) => {
Post.find()
.populate("postedBy","_id username")
.exec((err, posts) => {
if(err || !posts){
return res.status(400).json({
error:"No Post found"
})
}
res.json(posts)
})
}
//This is Auth controller which is middleware where I'm getting error
exports.isAuthenticated = (req, res, next) => {
let checker = req.profile && req.auth && req.profile._id == req.auth._id;
if (!checker) {
console.log(checker);
return res.status(403).json({
error: "ACCESS DENIED",
});
}
console.log(checker);
next();
};
//This is isSignedIn controller
//protected routes
exports.isSignedIn = expressJwt({
secret: process.env.SECRET,
userProperty: "auth",
});
//This is signin controller
exports.signin = (req, res) => {
const errors = validationResult(req);
const { username, password } = req.body;
if (!errors.isEmpty()) {
return res.status(422).json({
error: errors.array()[0].msg,
});
}
User.findOne({ username }, (err, user) => {
if (err || !user) {
return res.status(400).json({
error: "USER does not exists",
});
}
if (!user.autheticate(password)) {
return res.status(401).json({
error: "Username and password do not match",
});
}
//create token
const token = jwt.sign({ _id: user._id }, process.env.SECRET);
//put token in cookie
res.cookie("token", token, { expire: new Date() + 9999 });
//send response to front end
const { _id, username, role } = user;
return res.json({ token, user: { _id, username, role } });
});
};
//Only all/posts having problem remaining all are working fine even isAuthentication is also working with others routes but in this case it is giving error
I have this code for login. How do I use the current user's information from this code to another file using postman and node.js?
exports.loginUser = (req,res, next) => {
User.find({email: req.body.email})
.exec()
.then(user => {
if(user.length < 1) {
return res.status(401).json({
message: 'Auth failed'
});
}
bcrypt.compare(req.body.password, user[0].password, (err ,result) => {
if(err){
return res.status(401).json({
message: 'Auth failed'
});
}
if (result) {
const token = jwt.sign({
email: user[0].email,
userId: user[0]._id
},
process.env.JWT_KEY ,
{
//options
expiresIn: "1h"
});
You should tell exactly what you want, what you said is confusing, but If you mean how to pass the logged in user to the next middleware, you gotto assign the user to req
exports.loginUser = async (req, res, next) => {
const user = await User.find({ email: req.body.email }).exec()
if (user.length < 1) {
return res.status(401).json({
message: 'Auth failed'
});
}
bcrypt.compare(req.body.password, user[0].password, (err, result) => {
if (err) {
return res.status(401).json({
message: 'Auth failed'
});
}
if (result) {
const token = jwt.sign({
email: user[0].email,
userId: user[0]._id
},
process.env.JWT_KEY, {
//options
expiresIn: "1h"
});
req.user = user[0];
return next();
}
})
}
Then in the next middleware you have access to logged in user, using req.user.
UPDATE:
To implement the functionality that you want, according to what you described in the comment:
Before anything import these packages:
const jwt = require("jsonwebtoken");
const { promisify } = require("util");
First you implement a route that checks for credentials and sends back a signed jwt:
exports.login = CatchAsync(async(req, res, next) => {
const { email, password } = req.body;
if (!email || !password) {
return next(new Error("Please provide email and password"));
}
const user = await UserModel.findOne({email});
if (!user) {
return next(new Error("There is no user with that email"));
}
if(!(await bcrypt.compare(password, user.password))) {
// actually the pass is not correct but for security reasons we don't say that
return next(new Error("Email or password is not correct");
}
// pass the user id to jwt so later can identify user
const token = jwt.sign({ id: user._id }, 'yourJwtSecret', {
expiresIn: '90d',
});
// httpOnly prevents access to token in client's browser, so it is safe
const cookieOptions = {
expires: new Date(
Date.now() + 90 * 24 * 60 * 60 * 1000
),
httpOnly: true,
};
res.cookie("jwt", token, cookieOptions);
res.status(200).json({
status: 'success',
message: 'logged in successfully'
});
});
Then for every route that needs to check for logged In user, use this middleware:
exports.isLoggedIn = CatchAsync(async(req, res, next) => {
// Check if there is a token
// if no token was provided it means user is not logged in
let token;
if (req.cookies.jwt) {
token = req.cookies.jwt;
} else {
return next();
}
// Verify token
// decoded now has access to id of user
let decoded;
try {
decoded = await promisify(jwt.verify)(token, 'yourJwtSecret');
} catch (err) {
// if token was modified or expired or not valid
return next();
}
// get the user
const user = await UserModel.findOne({
_id: decoded.id
});
// access granted, user is logged in
req.user = user; // you can access the logged in user in the next middleware
res.locals.user = user; // you can access the logged in user in template engines
next();
});
If the user is not logged in, req.user won't be assigned. therefore in next middlewares if req.user was undefined you know user is not logged in.
for more info jwt docs.
If you have never taken any NodeJs course, I'd recommend this course
I'm trying to learn JWT authentication in express and one thing that I'm came across this code from Github
that this guy has initialised an middleware function to authenticate and check expiry of access token as per below:
app.post("/protected", auth, (req, res) => {
return res.json({ message: "Protected content!" });
})
async function auth(req, res, next) {
let token = req.headers["authorization"];
token = token.split(" ")[1]; //Access token
jwt.verify(token, "access", async (err, user) => {
if (user) {
req.user = user;
next();
} else if (err.message === "jwt expired") {
return res.json({
success: false,
message: "Access token expired"
});
} else {
console.log(err);
return res
.status(403)
.json({ err, message: "User not authenticated" });
}
});
}
and a separate route for refreshing the access token with the help of refresh token
app.post("/refresh", (req, res, next) => {
const refreshToken = req.body.token;
if (!refreshToken || !refreshTokens.includes(refreshToken)) {
return res.json({ message: "Refresh token not found, login again" });
}
// If the refresh token is valid, create a new accessToken and return it.
jwt.verify(refreshToken, "refresh", (err, user) => {
if (!err) {
const accessToken = jwt.sign({ username: user.name }, "access", {
expiresIn: "20s"
});
return res.json({ success: true, accessToken });
} else {
return res.json({
success: false,
message: "Invalid refresh token"
});
}
});
});
So, my question is how secure it is and how can I create single middleware function that could do both authentication and refreshing access token without hitting the app.post('/refresh') as in my view it wouldn't be a smooth experience to deal with it in frontend API management within react
Edit
My middleware seems to work well but it doesn't identify the wrong refresh token and then actually getting worked on protected route
app.post('/home', authenticateUser, (req, res) => {
res.send('welcome');
});
async function authenticateUser(req, res, next) {
let token = req.headers['authorization'];
token = token.split(' ')[1];
jwt.verify(token, JWT_AUTH_TOKEN, async (err, phone) => {
if (phone) {
req.phone = phone;
next();
} else if (err) {
const refreshToken = req.body.refreshToken;
if (!refreshToken || !refreshTokens.includes(refreshToken)) {
return res.json({ message: 'Refresh token not found, login again' });
} else {
jwt.verify(refreshToken, JWT_REFRESH_TOKEN, (err, phone) => {
if (!err) {
const accessToken = jwt.sign({ phone }, JWT_AUTH_TOKEN, { expiresIn: '30s' });
return res.json({ success: true, accessToken });
} else {
return res.json({
success: false,
message: 'Invalid refresh token'
});
}
next();
});
}
} else {
console.log(err);
return res.status(403).json({ err, message: 'User not authenticated' });
}
});
}