I use JWT for auth.
The auth will be a middleware:
const jwt = require('jsonwebtoken');
require('dotenv').config();
module.exports = (req, res, next) => {
const token = req.header("auth-token");
if (!token) return res.status(401).send("Missing token. Access denied");
try {
const decoded = jwt.verify(token, process.env.jwtKey);
req.user = decoded;
next();
} catch (err) {
console.log(err);
res.status(400).send('Invalid token.');
}
};
now when I get an token and use it in header "auth-token" and making a get or post request with the auth middleware its allways gives me "JsonWebTokenError: invalid signature"
I use the middleware like this:
router.get('/:id', auth, async (req, res) => {
const user = await User.findOne({
_id: req.params.id,
});
if (!user) return res.status(404).send('User not found');
res.send(user);
})
here is where the token is generated
router.post('/', async (req, res) => {
//check for validation errors
const { error } = validate(req.body);
if (error) return res.status(400).send(error.details[0].message);
let user = await User.findOne({ email: req.body.email });
if (!user) return res.status(404).send("Invalid email or password");
const validPassword = await bcrypt.compare(req.body.password, user.password);
if (!validPassword) return res.status(400).send("Invalid email or password");
res.json({ token: user.generateAuthToken(), status: "User is logged in" })
});
const validate = (req) => {
const schema = Joi.object({
email: Joi.string().min(6).max(255).email().required(),
password: Joi.string().min(6).max(255).required(),
});
return schema.validate(req);
};
module.exports = router;
You can see the auth middleware is used when I make the request GET
At JWT.io debugger when I put the token its says its ok... so whats worng?
So I sloved the issue, I had mistake with the key I decoded and key I veryfied.
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
I am quite new to Node.js / Express and development of web apps. I try to do a simple user registration where I hash the password with bcrypt before saving the hash to mongodb. The login form, which should allow a user to login, does subsequently lookup a user in the db and then compares the two passwords.
Certain routes in my web app I do want to protect so that only authenticated user have access to them. So when successfully login in I do send a Json Web Token (jwt) along the response header which should then be used - when redirected to the protected '/lobby' route - to authenticate the user and allow him / her to proceed to that route.
However, I always get the following error:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
So it looks like it already sends back a response to the client before trying to set the header which of course is then not possible anymore.
I would highly appreciate your help here!
I do use the following code:
Register function
async function register(req, res) {
//Check with user already exists
const emailExists = await User.findOne({email: req.body.email});
if(emailExists) {
return res.status(400).send('User already exists!');
};
//Hash the password and create new user from request data
bcrypt.hash(req.body.password, 10, async function (err, hashedPass){
if(err){
res.json({
error: err
});
}
let user = new User({
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: hashedPass,
password2: hashedPass
});
try {
await user.save();
}catch (err) {
res.status(400).send(err);
};
});
res.render('index');
};
Login function
async function login(req, res) {
const user = await User.findOne({email: req.body.email});
if(!user) {
return res.status(400).json({message: 'User not found!'}).render('index');
};
bcrypt.compare(req.body.password, user.password).then((result)=> {
if(result){
const token = jwt.sign({_id: user._id}, process.env.TOKEN_SECRET);
res.setHeader('auth-token', token.toString());
res.redirect('/lobby');
}else {
return res.status(400).json({message: 'Passwords do not match!'}).render('index');
}
}).catch((err)=> {
console.log(err);
});
};
As a middleware to the '/lobby' route (i.e. when someone does a get request to '/lobby') I use a "verifyToken" function which should ensure correct authentication of the user via jwt.
verifyToken function
const jwt = require('jsonwebtoken');
module.exports = function(req, res, next) {
console.log('verify function started');
const token = req.header('auth-token');
console.log(token);
if(!token) {
res.status(401).json({
message: 'Access denied!'
});
};
try {
const verified = jwt.verify(token, process.env.TOKEN_SECRET);
req.user = verified;
next();
}catch (err) {
res.status(400).json({
message: 'Invalid token!'
});
};
};
As said, I would very much appreciate your help here! I assume the problem is much simpler than I think it is :-).
Cheers
You forgot to return the response in few cases. So it continues to execute other code aswell, that's where server trying to send the response again, which is why you're getting that error.
Change your response like the following.
verifyToken function
const jwt = require('jsonwebtoken');
module.exports = function(req, res, next) {
console.log('verify function started');
const token = req.header('auth-token');
console.log(token);
if(!token) {
return res.status(401).json({ // <-- here you need to `return`
message: 'Access denied!'
});
};
try {
const verified = jwt.verify(token, process.env.TOKEN_SECRET);
req.user = verified;
next();
}catch (err) {
return res.status(400).json({
message: 'Invalid token!'
});
};
};
Register function
async function register(req, res) {
//Check with user already exists
const emailExists = await User.findOne({email: req.body.email});
if(emailExists) {
return res.status(400).send('User already exists!');
};
//Hash the password and create new user from request data
bcrypt.hash(req.body.password, 10, async function (err, hashedPass){
if(err) {
return res.json({ // <-- here as well
error: err
});
}
let user = new User({
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: hashedPass,
password2: hashedPass
});
try {
await user.save();
return res.render('index'); // <-- assuming this is your success response
}catch (err) {
return res.status(400).send(err); <-- here too
};
});
};
Looks like in the Login function the header gets set. I can see this via console.log(res.header('auth-token'));. Subsequently the redirect to "/lobby" gets called because the verifyToken function does start.
However, in the verifyToken function the respective header is then undefined. Because I always also get a 'Access denied!' message.
As said, I do call the verifyToken function as middleware when doing a get request to the /lobby route. The route for '/lobby' looks as follows:
const express = require('express');
const router = express.Router();
const lobbyCtrl = require('../controllers/lobby');
const verify = require('./verifyToken');
router.get('/', verify, lobbyCtrl.display);
module.exports = router;
In my project, when a user logs in, I generate an auth token like so:
// For my User model
userSchema.methods.generateAuthToken = () => {
const token = jwt.sign({ _id: this._id }, config.get('jwtPrivateKey'))
return token
}
I then apply an auth middleware on a route that allows a user to create posts.
auth middleware:
module.exports = (req, res, next) => {
const token = req.header('x-auth-token')
if (!token) return res.status(401).send('No token provided')
try {
const decoded = jwt.verify(token, config.get('jwtPrivateKey'))
req.user = decoded
next()
}
catch (ex) {
res.status(400).send('Invalid token')
}
}
My route:
router.post('/', auth, upload.single('postImage'), async (req, res) => {
console.log(req.user._id)
const post = new Post({
postImage: `http://localhost:3000/${req.file.path}`,
caption: req.body.caption,
user: req.user._id
})
await post.save()
res.send(post)
})
As you can see, I log the req.user._id in the route, and when I call this endpoint, I get undefined logged to the console. Instead I expect the _id of the user who generated the auth token.
Below is my login route, where a user gets their auth token.
Login route:
router.post('/', validateLogin, async (req, res) => {
const errors = validationResult(req)
if (!errors.isEmpty()) return res.status(422).send(errors.array())
let user = await User.findOne({ $or: [{ username: req.body.usernameEmail }, { email: req.body.usernameEmail }]})
if (!user) return res.status(400).send('Invalid username/email or password')
const validPassword = await bcrypt.compare(req.body.password, user.password)
if (!validPassword) return res.status(400).send('Invalid username/email or password')
const token = user.generateAuthToken()
res.send(token)
})
Does anybody know the issue here? Thanks.
I am new in Node.JS API creation. I have successfully implemented login with JWT token and also I have created a file to verify and pass token in header.
This is my login route :-
router.post('/login' , (req, res, next) => {
User.find({email: req.body.email})
.exec()
.then(user => {
if(user.length < 1) {
return res.status(401).json({
message: "Auth failed. User not found."
})
}
bcrypt.compare(req.body.password, user[0].password, (err, result) =>{
if (err) {
return res.status(401).json({
message: "Auth failed. Check email and password"
});
}
if (result){
const adminEmail = "tom#xyz.com";
const role = user[0].email===adminEmail? "admin" : "user";
const token = jwt.sign(
{
email: user[0].email,
userId: user[0]._id,
role
},
process.env.JWT_KEY,
{
expiresIn : "1h"
});
return res.status(200).json({
message: "Auth Successful",
token : token
});
}
});
})
.catch(err =>{
if (err.code == 500)
res.status(500).send(["Something went wrong in login"]);
else
return next(err);
});
});
With this I am creating a JSON response in login:-
{
"message": "Auth Successful",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InRvbUB4eXouY29tIiwidXNlcklkIjoiNWMzMzI0MTgyNTg4M2I0Yzc0ODNkOGJjIiwicm9sZSI6InVzZXIiLCJpYXQiOjE1NDY5MzMwODYsImV4cCI60.zfRUQmg5K_mBU0Dkq72c0sgzFtCXvu8kObyvEflhg"
}
Now, I have also created a file to check authentication of user's. check-auth.js
const jwt = require('jsonwebtoken');
module.exports = (req, rest, next) =>{
try{
const token = req.headers.authorization.split(" ")[1];
console.log(token);
const decoded = jwt.verify(token, process.env.JWT_KEY)
req.userData = decoded;
next();
}
catch(error){
return res.status(401).json({
message: "Auth failed"
});
}
};
I need user's to login and can only see the details which they have provided during the registration.
registration route :-
router.post('/register' , ctrlUser.register);
user controller:-
const mongoose = require ('mongoose');
const User = mongoose.model('User');
module.exports.register = (req, res, next) =>{
var user = new User();
user.fullName = req.body.fullName;
user.email = req.body.email;
user.password = req.body.password;
user.phoneNumber = req.body.phoneNumber;
user.save((err, doc) =>{
if(!err)
res.send(doc);
else{
if (err.code == 11000)
res.status(422).send(["Entered duplicate email address. Please check"]);
else
return next(err);
}
});
}
Here in profile route i need to show registration details after successful login :-
router.get('/profile', checkAuth, (req, res) =>{
//something to do.
});
I am stuck here for a while.
You might want to get the logged in user information.
Try the following code
router.get('/profile', checkAuth, (req, res) =>{
//something to do.
// printing the user data
console.log(req.userData);
// sending the user data
res.send(req.userData)
});
In your check-auth middleware you already put the user data
req.userData = decoded;
So your userData should be available on the routes.
And since you used the user mail and id to get jwt token in sin in section,
they will be available in the userData object.