I developing a REST API using node js and express with MongoDB.
I have developed an endpoint to insert the user into DB and works fine. Now I have added the login function with jwt and now when I run my app and try to insert my user I receive this error:
an app is listening on port 3000
(node:3650) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Expected "payload" to be a plain object.
(node:3650) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
I'm using a node js version: 8.9.3 on mac
Any help, please?
# this is the code about user add:
var router = require('express').Router();
var bcrypt = require('bcryptjs');
var User = require('../models/user');
var jwt = require('jsonwebtoken');
var constants = require('../config/constants');
router.post('/',(req, res) => {
// add user --> user = { username: 'test', email: 'test#tes.com', pwd: 'Test'}
var user = new User({
username: req.body.user.username,
email : req.body.user.email,
passhash: bcrypt.hashSync(req.body.user.pwd, 10)
});
user.save().then(
(newuser) => {
var sessionToken = jwt.sign(newuser._id, constants.JWT_SECRET, {expiresIn: 60*60*24});
res.json({
user: newuser,
message: 'success',
sessionToken: sessionToken
});
},
(err) => {
res.send(500, err.message);
}
);
});
module.exports = router;
and this is the code about login:
var router = require('express').Router();
var bcrypt = require('bcryptjs');
var jwt = require('jsonwebtoken');
var constants = require('../config/constants');
var User = require('../models/user');
router.post('/', (req, res) => {
User.findOne({ username: req.body.user.username}).then(
(user) => {
if(user){
bcrypt.compare(req.body.pwd, user.passhash, (err, matches) => {
if (matches) {
var sessionToken = jwt.sign(user._id, constants.JWT_SECRET, { expiresIn: 24*60*60 });
res.json({
user:user,
message: 'succesfully authed',
sessionToken: sessionToken
});
}else{
res.json({
user: {},
message: 'failed to auth',
sessionToken: ''
});
}
});
}else{
res.json({
user: {},
message: 'failed to auth',
sessionToken: ''
});
}
},
(err) => {
// could not find users
res.json(err);
}
);
});
module.exports = router;
I have tested the add user with postman and I have seen that when I receive the message posted, the user is inserted into db
If both API methods are declared in the same file, the problem can be because you are declaring twice same path + verb:
router.post('/', (req, res) => {
Related
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;
I'm developing a Node.JS & MongoDB app inserting articles and categories, with a signup and login users system. I need to add/fix a secret string on order to make Jsonwebtoken (JWT_KEY) work properly.
My authentication or authorization fails when I try to add an article with details (title, attached picture ect.) threw Postman, probably because I made a mistake installing or using the jsonwebtoken library. It maybe a mistake in the nodemon.json file that is should be hidden at the end (user, password, JWT_KEY), but maybe in another part of my code.
The Postman process connects with the article.js routes file, that seems to be fine. The relevant part is the createArticle POST, since the rest work fine so far:
const express = require('express');
const router = express.Router();
const upload = require('../middlewares/upload');
const checkAuth = require('../middlewares/checkAuth');
const {
getAllArticles,
createArticle,
getArticle,
updateArticle,
deleteArticle
} = require('../controllers/articles');
router.get('/', getAllArticles);
router.get('/:articleId', getArticle);
router.post('/', checkAuth, upload.single('image'), createArticle);
router.patch('/:articleId', checkAuth, updateArticle);
router.delete('/:articleId', checkAuth, deleteArticle);
module.exports = router;
Here is the authChek.js middleware that is responsible of the authorization process:
const jwt = require('jsonwebtoken');
const checkAuth = (req, res, next) => {
try {
const token = req.headers.authorization.split('')[1];
jwt.verify(token, process.env.JWT_KEY);
next();
} catch(error) {
res.status(401).json({
message: 'Auth failed'
})
}
}
module.exports = checkAuth;
The verify seems ok and should work fine connecting to nodemon. If it's all fine, Postman should return back a message that the authorization succeeded - but it returns failed auth. Here, in the article.js controller, the POST method seems fine to and should not catch an error of 500, 401 or 409:
const mongoose = require('mongoose');
const Article = require('../models/article');
const Category = require('../models/category');
module.exports = {
createArticle: (req, res) => {
const { path: image } = req.file;
const { title, description, content, categoryId } = req.body;
Category.findById(categoryId).then((category) => {
if (!category) {
return res.status(404).json({
message: 'Category not found'
})
}
const article = new Article({
_id: new mongoose.Types.ObjectId(),
title,
description,
content,
categoryId,
image: image.replace('\\','/')
});
return article.save();
}).then(() => {
res.status(200).json({
message: 'Created article'
})
}).catch(error => {
res.status(500).json({
error
})
});
}
}
Another file using the JWT_KEY is the users.js controller, in the login part. Look at the area of the if & result. It may fail to connect properly to the .env part of the nodemon.json file. See here "process.env.JWT_KEY":
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const User = require('../models/user');
module.exports = {
login: (req, res) => {
const { email, password } = req.body;
User.find({ email }).then((users) => {
if (users.length === 0) {
return res.status(401).json ({
message: 'Authentication failed'
});
}
const [ user ] = users;
bcrypt.compare(password, user.password, (error, result) => {
if (error) {
return res.status(401).json({
message: 'Authentication failed'
});
}
if (result) {
const token = jwt.sign({
id: user._id,
email: user.email,
},
process.env.JWT_KEY,
{
expiresIn: "1H"
});
return res.status(200).json({
message: 'Authentication successful',
token
})
}
res.status(401).json({
message: 'Authentication failed'
});
})
})
}
}
Is there something to fix here? Or how can I check if my JWT_KEY in nodemon.json is written properly or wrong? If the string is generated by a library or taken from somewhere else, I don't know where to search for it in my app or around the web.
During the course of testing if my user's registration route is working fine using postman as a testing machine, it throw an error that I don't know what it means.
I have tried using an async function to catch the error, but it didn't work out
const express = require('express');
const router = express.Router();
// Use to help set a default image for users
const gravatar = require('gravatar');
// Use to encrypt our password from bringing plain text
const bcrypt = require('bcryptjs');
// I add the user model from my model
// So i can be able to check create a new registration
// and also check if email exist
const User = require('../../models/User');
// #route GET api/users/register
// #desc Register user
// #access Public
router.post('/register', (req, res) => {
User.findOne({ email: req.body.email }).then(user => {
if (user) {
errors.email = 'Email already exists';
return res.status(400).json(errors);
} else {
const avatar = gravatar.url(req.body.email, {
s: '200', // Size
r: 'pg', // Rating
d: 'mm' // Default
});
const newUser = new User({
name: req.body.name,
email: req.body.email,
avatar,
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));
});
});
}
});
});
module.exports = router;
I want the user input on postman should be able to post the form so I can know if the route is actually working fine. And this is the error I got on my console
(node:14164) UnhandledPromiseRejectionWarning: ReferenceError: errors is not defined
at User.findOne.then.user (the working director/name.js:26:7)
at process._tickCallback (internal/process/next_tick.js:68:7)
(node:14164) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:14164) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Normally we should handle the error in a kind of this way:
User.findOne({ email: req.body.email }).then(user => {
// ...
}).catch(error => {
console.log('error', error)
res.json({error: error})
})
I was researching about the hapi js API basic authentication and i'm using Hapi documentation about the authentication. I believe i did everything right but i'm getting following error saying about UnhandledPromiseRejectionWarning. Please help
index.js
'use strict';
const Bcrypt = require('bcrypt');
const Hapi = require('hapi');
const Basic = require('hapi-auth-basic');
const server = new Hapi.Server({
host: 'localhost',
port: 3000
})
const users = {
john: {
username: 'john',
password: '$2a$10$iqJSHD.BGr0E2IxQwYgJmeP3NvhPrXAeLSaGCj6IR/XU5QtjVu5Tm',
name: 'John Doe',
id: '2133d32a'
}
};
const validate = function (request, username, password, callback) {
const user = users[username];
if (!user) {
return callback(null, false);
}
Bcrypt.compare(password, user.password, (err, isValid) => {
callback(err, isValid, { id: user.id, name: user.name });
});
};
server.register(Basic, (err) => {
if (err) {
throw err;
}
server.auth.strategy('simple', 'basic', { validateFunc: validate });
server.route({
method: 'GET',
path: '/',
config: {
auth: 'simple',
handler: function (request, reply) {
reply('hello, ' + request.auth.credentials.name);
}
}
});
server.start((err) => {
if (err) {
throw err;
}
console.log('server running at: ' + server.info.uri);
});
});
package.json
"bcrypt": "^1.0.3",
"hapi-auth-basic": "^5.0.0",
"hapi": "^17.1.0"
error
(node:1248) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): AssertionError [ERR_ASSERTION]: Invalid register options "value" must be an object
(node:1248) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
If you want that code to work you will have to use a version lower than 17, i.e (16.6.2) or either look for the code updated to the hapi version you are using.
const Bcrypt = require('bcrypt');
const Hapi = require('hapi');
const users = {
john: {
username: 'john',
password: '$2a$10$iqJSHD.BGr0E2IxQwYgJmeP3NvhPrXAeLSaGCj6IR/XU5QtjVu5Tm', // 'secret'
name: 'John Doe',
id: '2133d32a'
}
};
const validate = async (request, username, password, h) => {
if (username === 'help') {
return { response: h.redirect('https://hapijs.com/help') }; // custom response
}
const user = users[username];
if (!user) {
return { credentials: null, isValid: false };
}
const isValid = await Bcrypt.compare(password, user.password);
const credentials = { id: user.id, name: user.name };
return { isValid, credentials };
};
const main = async () => {
const server = Hapi.server({ port: 4000 });
await server.register(require('hapi-auth-basic'));
server.auth.strategy('simple', 'basic', { validate });
server.auth.default('simple');
server.route({
method: 'GET',
path: '/',
handler: function (request, h) {
return 'welcome';
}
});
await server.start();
return server;
};
main()
.then((server) => console.log(`Server listening on ${server.info.uri}`))
.catch((err) => {
console.error(err);
process.exit(1);
});
I have a problem with Schema.pre('save'), in my model 'user', cannot get 'this' for the hashed of my password with bcrypt.
my app.js, Simple connect in mongodb using mongoose
mongoose.connect('mongodb://localhost/gederson', {
useMongoClient: true,
});
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', () => {
console.log('Connected on mongo');
});
app.listen(process.env.PORT || 3000, () => {
console.log('listening');
});
index(app, db);
post(app, db);
admin(app, db);
module.exports = app;
my route, I created the route for create user in my application, but in pre 'save' cannot get the 'this' for hash password.
const Users = db.model('Users');
const newUser = {
username: req.body.username,
email: req.body.email,
password: req.body.password,
};
Users.create(newUser, (err) => {
if (err) throw err;
res.status = 201;
return res.send('User created');
});
my model user, the code pre 'save'
const bcrypt = require('bcrypt');
UserSchema.pre('save', (next) => {
const user = this;
bcrypt.hash(user.password, 10, (err, hash) => {
if (err) {
return next(err);
}
user.password = hash;
return next();
});
});
const Users = mongoose.model('Users', UserSchema);
module.exports = Users;
Stack Error:
events.js:183
throw er; // Unhandled 'error' event
^
Error: data and salt arguments required
at /home/gedersonchiquesi/ProjetosDev/wos/node_modules/bcrypt/bcrypt.js:114:16
at _combinedTickCallback (internal/process/next_tick.js:131:7)
at process._tickCallback (internal/process/next_tick.js:180:9)
[nodemon] app crashed - waiting for file changes before starting...
I ran into a similar issue today.
I resolved the matter by removing the ES6 syntax (arrow function).
UserSchema.pre('save', function(next) {
const user = this;
bcrypt.hash(user.password, 10, function(err, hash) {
if (err) {
return next(err);
}
user.password = hash;
next();
})
});
because you are passing the blank value of the inputs on that function
if test on postman or else data input needed
{
"name":"test",
"email":"test#test.com",
"password":"123456"
}
add see my code example for datastore
router.post("/register-user", (req, res, next) => {
bcrypt.hash(req.body.password, 10).then((hash) => {
const user = new userSchema({
name: req.body.name,
email: req.body.email,
password: hash
});
user.save().then((response) => {
res.status(201).json({
message: "User successfully created!",
result: response
});
}).catch(error => {
res.status(500).json({
error: error
});
});
});
});
My issue was my server wasn't accepting the correct data type.
Had to include the following on server.js file:
app.use(express.json()); &
app.use(express.urlencoded({extended: true}));