I am writing system for login and registration. When I post correct details in my register Post method, my request is pending and I am unable to find error. The one error it is giving data and salt arguments required and I am getting status 500 and pending my fetch method in ejs file is producing error "Error: data and salt arguments required"
const users = [];
let status = "";
router
.route("/register")
.get((req, res) => {
res.render('register', {message: ''})
})
.post((req, res) => {
console.log([req.body.password, req.body.email]);
const hashPassword = bcrypt.hashSync(req.body.password, 10);
const checkUser = {
email: req.body.email,
password: req.body.password,
};
const { value, error } = validateUser(checkUser);
if (error) {
status = "error"
res.status(404).render('register', {message : error.message});
} else {
const user = users.find((u)=>{
if(u.email === req.body.email){
status = "user exist"
res.status(404).render('register',{ message : "user exist"})
}else{
const newUser = {
email : req.body.email,
password: hashPassword
}
users.push(newUser)
console.log("users is" + users)
res.status(200).render('ok')
}
})
}
});
the errors i am getting in console
[ undefined, undefined ]
Error: data and salt arguments required
at Object.hashSync (C:\Users\USER\Desktop\nodejs\login\node_modules\bcrypt\bcrypt.js:91:15)
at C:\Users\USER\Desktop\nodejs\login\routes\login.js:24:33
at Layer.handle [as handle_request] (C:\Users\USER\Desktop\nodejs\login\node_modules\express\lib\router\layer.js:95:5)
at next (C:\Users\USER\Desktop\nodejs\login\node_modules\express\lib\router\route.js:137:13)
at next (C:\Users\USER\Desktop\nodejs\login\node_modules\express\lib\router\route.js:131:14)
at Route.dispatch (C:\Users\USER\Desktop\nodejs\login\node_modules\express\lib\router\route.js:112:3)
at Layer.handle [as handle_request] (C:\Users\USER\Desktop\nodejs\login\node_modules\express\lib\router\layer.js:95:5)
at C:\Users\USER\Desktop\nodejs\login\node_modules\express\lib\router\index.js:281:22
at Function.process_params (C:\Users\USER\Desktop\nodejs\login\node_modules\express\lib\router\index.js:335:12)
at next (C:\Users\USER\Desktop\nodejs\login\node_modules\express\lib\router\index.js:275:10)
[ '113245', 'sambhav#gmail.com' ]
this is the frontend system i am using
<body>
<h1>register</h1>
<p><%=message%></p>
<form action="/api/register" method="POST">
<input type="text" name="email" id="email" placeholder="enter email" /><br />
<input
type="password"
name="password"
id="password"
placeholder="enter password"
/><br />
<button type="submit">register</button>
</form>
login
<script>
const form = document.querySelector('form')
const email = document.querySelector('#email')
const password = document.querySelector('#password')
form.addEventListener("submit", function (e) {
e.preventDefault()
fetch("/api/register", {
// Adding method type
method: "POST",
// Adding body or contents to send
body: JSON.stringify({
email: email.value,
password: password.value,
}),
})
// Converting to JSON
.then(response => response.json())
// Displaying results to console
.then(json => console.log(json));
})
</script>
</body>
Values req.body.password and req.body.email are clearly undefined, so bcrypt.hashSync is complaining that it cannot hash a null/undefined value.
It looks like the values are not being POSTed correctly. Check your code to make sure you are using body-parsing middleware (e.g. router.route("/register").use(bodyParser.json())).
You should also look at your frontend code or in devtools for a clue as to why they are not being passed.
I can't tell if your request is hanging, but additionally, you may want to define an error handling route in Express if you do not want the requests to hang when there is a server error (if you have not already).
Remember also that any sync function on a server will block incoming requests until it completes
Related
I have the following route setup in my node js api app:
const { body } = require("express-validator");
router.post(
"/user/signup",
[
body("firstName").not().isEmpty().withMessage("First name is required"),
body("lastName").not().isEmpty().withMessage("Last name is required"),
body("email")
.isEmail()
.withMessage("Email is required")
.custom((value, { req }) => {
return User.findOne({ email: value }).then(userDoc => {
if (userDoc) {
return Promise.reject('E-Mail address already exists!');
}
});
}),
body("mobile").not().isEmpty().withMessage("Mobile is required"),
body("password").not().isEmpty().withMessage("Password is required"),
body("confirmPassword")
.not()
.isEmpty()
.withMessage("Confirm password is required"),
],
UserController.signup
);
signup method in UserController
const { validationResult } = require("express-validator");
exports.signup = async (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
const error = new Error('Validation failed.');
error.statusCode = 422;
error.data = errors.array();
throw error;
}
const {
firstName,
lastName,
email,
mobile,
password,
confirmPassword
} = req.body;
try {
if (password !== confirmPassword) {
res
.status(422)
.json({ message: "Password and confirm password must be same" });
}
//save user and return response to front end
} catch (err) {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
}
};
Code block at the end of app.js to catch error:
/** Catch and return custom errors */
app.use((error, req, res, next) => {
const status = error.statusCode || 500;
const message = error.message;
const data = error.data;
res.status(status).json({ message: message, data: data });
});
In this route I'm checking if user has already registered with same email or not. If user has been registered with same email return error message.
Error message returned by server before crash:
/storage/node/Jeevan-Api/controllers/UserController.js:10
const error = new Error('Validation failed.'); ^
Error: Validation failed.
at exports.signup (/storage/node/Jeevan-Api/controllers/UserController.js:10:19)
at Layer.handle [as handle_request] (/storage/node/Jeevan-Api/node_modules/express/lib/router/layer.js:95:5)
at next (/storage/node/Jeevan-Api/node_modules/express/lib/router/route.js:144:13)
at middleware (/storage/node/Jeevan-Api/node_modules/express-validator/src/middlewares/check.js:16:13)
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
statusCode: 422,
data: [
{
value: 'user#user.com',
msg: 'E-Mail address already exists!',
param: 'email',
location: 'body'
}
]
}
[nodemon] app crashed - waiting for file changes before starting...
The above code does the job but the server crashes after it returns the error message. This is happening in both local server and my development server.
How can I return validation message and
You are throwing error which makes the app to stop processing to the next request or response / middleware. What you could do is doing next(error) so it will catch in the last catch block.
Or you could also look into this to set up error handling in express; https://expressjs.com/en/guide/error-handling.html#:~:text=Writing%20error%20handlers
This is happening because your middleware is throwing an async error and your node app has no way of handling it.
Even if you have an error handler in place you need to explicitly call the next function with the error object.
E.g.
try{
// Your code...
}catch(error){
console.log(error)
next(error)
}
When express sees next(error) i.e. next function being called with an argument it passes it to the error handler that you have written at the end of app.js
/** Catch and return custom errors */
app.use((error, req, res, next) => {
const status = error.statusCode || 500;
const message = error.message;
const data = error.data;
res.status(status).json({ message: message, data: data });
});
Solution:
You can make use of an npm package express-async-errors
Link for the npm package: https://www.npmjs.com/package/express-async-errors
And in your app.js file just add require('express-async-errors') on the very top. This will direct all the async errors to the error handler middleware in your application.
For context, I'm trying to send a one time link to the user's email as a reset password link that will take them to the reset password page if the jwt token is successfully verified. I followed a tutorial and created a dummy version where user info was stored locally and it worked perfectly. But when I try to implement into my main project which pulls user data from mySQL I keep getting a malformed error, I am checking all the values and everything matches including checking the token on the jwt website to see if it return the correct info which it does so I'm very confused as to what I've done wrong. The only thing that changes between the test and main project is where the data is pulled from. Here is my code for this part of the project:
// Create and send link
router.post('/forgot-password', (req, res, next) => {
var email = req.body.email
db.query('SELECT * FROM users_test WHERE email = ?', [ email ], (error, results) => {
if (results.length < 1) {
res.send('no user')
return
}
const user = results[0]
const secret = process.env.JWT_SECRET + user.password
const payload = {
email: email,
id: user.id
}
const token = jwt.sign(payload, secret)
const link = `http://localhost:5000/auth/reset-password/${user.id}/${token}`
console.log(link)
res.send('sent')
})
})
// verify token and display password reset page
router.get('/reset-password/:id/:token')= (req, res, next) => {
const { id, token } = req.params
db.query('SELECT * FROM users_test WHERE id = ?', [ id ], (error, results) => {
if (error) {
console.log(error)
}
const user = results[0]
const secret = process.env.JWT_SECRET + user.password
res.json({secret})
try {
var payload = jwt.verify(token, secret)
res.render('reset-password.hbs')
}
catch (e) {
console.log(e)
}
})
}
The line the error is point at: var payload = jwt.verify(token, secret)
The error I'm getting:
throw err; // Rethrow non-MySQL errors
^
JsonWebTokenError: jwt malformed
at Object.module.exports [as verify] (C:\Users\winba\Desktop\SecureSoftware\Secure-Software-02\node_modules\jsonwebtoken\verify.js:63:17)
at Query.<anonymous> (C:\Users\winba\Desktop\SecureSoftware\Secure-Software-02\controllers\auth.js:497:29)
at Query.<anonymous> (C:\Users\winba\Desktop\SecureSoftware\Secure-Software-02\node_modules\mysql\lib\Connection.js:526:10)
at Query._callback (C:\Users\winba\Desktop\SecureSoftware\Secure-Software-02\node_modules\mysql\lib\Connection.js:488:16)
at Query.Sequence.end (C:\Users\winba\Desktop\SecureSoftware\Secure-Software-02\node_modules\mysql\lib\protocol\sequences\Sequence.js:83:24)
at Query._handleFinalResultPacket (C:\Users\winba\Desktop\SecureSoftware\Secure-Software-02\node_modules\mysql\lib\protocol\sequences\Query.js:149:8)
at Query.EofPacket (C:\Users\winba\Desktop\SecureSoftware\Secure-Software-02\node_modules\mysql\lib\protocol\sequences\Query.js:133:8)
at Protocol._parsePacket (C:\Users\winba\Desktop\SecureSoftware\Secure-Software-02\node_modules\mysql\lib\protocol\Protocol.js:291:23)
at Parser._parsePacket (C:\Users\winba\Desktop\SecureSoftware\Secure-Software-02\node_modules\mysql\lib\protocol\Parser.js:433:10)
at Parser.write (C:\Users\winba\Desktop\SecureSoftware\Secure-Software-02\node_modules\mysql\lib\protocol\Parser.js:43:10)
Any help or ideas as to where the error is coming from would be appreciated, thank you.
Try with following changes
const link = `http://localhost:5000/auth/reset-password/${user.id}/${JSON.stringify(token)}`
var payload = jwt.verify(JSON.parse(token), secret)
I'm implementing a password reset following a tutorial but running into a problem. My req.body.email is coming back undefined. I have body-parser installed and my other routes are running perfectly.
Here is my code summary:
var express = require('express');
var router = express.Router({ mergeParams: true });
var Kids = require('../models/kid');
var User = require('../models/user');
var async = require('async');
var nodemailer = require('nodemailer');
var crypto = require('crypto');
var middleware = require('../middleware');
router.post('/password_reset', function(req, res, next) {
function(token, done) {
User.findOne({ email: req.body.email }, function(err, user) {
console.log(req.body.email); <====== Returning and undefined
console.log(user); <====== Returning as null
if (!user) {
req.flash('error', 'No account with that email address exists.');
return res.redirect('/password_reset');
}
user.resetPasswordToken = token;
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
user.save(function(err) {
done(err, token, user);
});
});
}
});
and my form
<form action="/password_reset" method="POST" >
<div class="form-group">
<label for="exampleInputEmail1">Enter your email address</label>
<input type="email" class="form-control" id="email" aria-describedby="emailHelp" placeholder="Enter email" required>
</div>
<button type="submit" class="btn btn-warning">Submit</button>
</form>
You have two problems:
You aren't submitting any data
Your <input> has no name attribute, so it can't be a successful control.
If you want req.body.email to have data in it then you need to say name="email".
Related to this, you said <label for="exampleInputEmail1"> but id="email". The for attribute needs to match the id of the element it is labelling. Then aria-describedby="emailHelp" needs to match the ID of the element that is labelling the current element … and isn't needed when you have a real <label>.
You aren't parsing the submitted data
See the documentation for req.body:
Contains key-value pairs of data submitted in the request body. By default, it is undefined, and is populated when you use body-parsing middleware such as express.json() or express.urlencoded().
You haven't used any body-parsing middleware.
Your form is submitting urlencoded data (the default) so use express.urlencoded():
router.use(express.urlencoded())
I am trying to get the token value from the following URL http://localhost:3000/users/reset/e3b40d3e3550b35bc916a361d8487aefa30147c8. I have a get request that checks if the token is valid and redirects the user to a reset password screen. I also have a post request but when I console req.params.token, it outputs :token instead of e3b40d3e3550b35bc916a361d8487aefa30147c8. I am wondering if the form action is correct but don't know how to get the token value from it.
Reset Password Get Request
router.get('/reset/:token', (req, res) => {
console.log(req.params.token) // e3b40d3e3550b35bc916a361d8487aefa30147c8
User.findOne({
resetPasswordToken: req.params.token,
resetPasswordExpires: {
$gt: Date.now()
}
}, (err, user) => {
if (!user) {
req.flash('error_msg', 'The password reset token is invalid or has expired.')
return res.redirect('/users/forgot')
}
res.render('reset')
})
})
reset.ejs
<% include ./partials/messages %>
<form action="/users/reset/:token" method="POST">
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" class="form-control" placeholder="Please enter a password."
value="<%= typeof password != 'undefined' ? password : '' %>" />
</div>
<button type="submit" class="btn btn-primary btn-block">Register</button>
</form>
Reset Password Post Request
router.post('/reset/:token', (req, res) => {
console.log(req.params.token) // :token
User.findOne({
resetPasswordToken: req.params.token,
resetPasswordExpires: {
$gt: Date.now()
}
}, (err, user) => {
if (!user) {
req.flash('error_msg', 'The password reset token is invalid or has expired.')
return res.redirect('/users/forgot')
}
user.password = req.body.password;
user.resetPasswordToken = undefined;
user.resetPasswordExpires = undefined;
user.save(function (err) {
req.flash('success_msg', 'Working.')
return res.redirect('/users/login')
})
})
})
In your form in your HTML, you have this:
<form action="/users/reset/:token" method="POST">
That's going to make the actual URL that gets requested when the form is posted be:
/users/reset/:token
There's no code doing any substitution for the :token here. That's just getting sent directly to the server as the URL.
So, when you then have:
router.post('/reset/:token', (req, res) => {
console.log(req.url); // "/user/reset/:token"
console.log(req.params.token); // ":token"
});
What req.params.token is showing you is whatever is in the URL that's after /users/reset. In your case, that is the literal string ":token". For req.params.token to actually have to token in it, you would have to insert the actual token into the URL so your form tag looks like this:
<form action="/users/reset/e3b40d3e3550b35bc916a361d8487aefa30147c8" method="POST">
Or, you will have to get access to the token some other way such as from the express session, from a cookie, from a field in the form, etc...
To get a URL parameter's value
app.get('/reset/:token', function(req, res) {
res.send("token is " + req.params.token);
});
To get a query parameter ?token=Adhgd5645
app.get('/reset/?token=Adhgd5645', function(req, res) {
res.send("token is " + req.query.token);
});
I have a form being submitted, but I am unable to assign assign the form 'req.body' username and password properties to the database (no hash on the password, just trying to get a MEAN CRUD app working for starters).
My form is as follows:
form
<form action="/signup" method="post">
<input type="text" name="username" placeholder="Username" />
<input type="password" name="password" placeholder="Password" />
<button type="submit" value="Sign Up" />
</form>
user schema (./models/user.js)
var mongoose = require("mongoose");
var UserSchema = new mongoose.Schema({
username: String,
password: String
});
module.exports = mongoose.model('User', UserSchema);
app.js (only showing reference to User model and "/signup" post function
var User = require("./models/user");
app.post("/signup", function(req, res) {
var user = new User();
user.username = req.body.username;
user.password = req.body.password;
user.save(function(err) {
if (err) {
res.send(err);
}
res.redirect("/profile", { message: 'User successfully registered!', data: user });
});
});
When I run the application:
node app.js
Everything runs fine. All endpoints, routes are working correctly.
However, when I click the submit button of the above form, I receive the following error in my Terminal:
TypeError: Cannot read property 'username' of undefined
at Layer.handle [as handle_request](/node_modules/express/lib/router/layer.js:95:5)
at next (/node_modules/express/lib/router/route.js:131:13)
at Route.dispatch (/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/node_modules/express/lib/router/layer.js:95:5)
How can I ensure that the req.body.username and the req.body.password successfully pass to the mongodb database?
Thanks for any help.
Here is what you need to do.
at first, install the body-parser
$ npm install --save body-parser
then in your aap.js file
app.post('/', function(req, res){
var username= req.param('username', null);
var username= req.param('password', null);
});
did you enable body-parser in your app? Make sure you have this line:
app.use(bodyParser.urlencoded({extended: false}));