How to find array elements in npm? - node.js

I am trying to find user with the entered email and password from this users dummy array but I get undefined in the return value and hence I am not able to login. If I run this code in console it fetches correct output. Can somebody help why this find function (in the post req) not working in Node?
Dummy array -
const users = [
{ id: 1, name: 'Sherry', email:'sherry#gmail.com', password: 1234 },
{ id: 2, name: 'harry', email:'harry#gmail.com', password: 1234 },
{ id: 3, name: 'cherry', email:'cherry#gmail.com', password: 1234 }
]
Get Request -
app.get('/login', (req, res) => {
res.send(`<form method='POST' action='/login'>
<input type="email" name="email" placeholder="Enter email" />
<input type="password" name="password" placeholder="Enter pass" />
<input type="submit" />
</form>`);
})
Post Request -
app.post('/login', (req,res) => {
const { email, password } = req.body;
console.log(email)
console.log(password)
console.log(users)
let user1 = users.find((user) => user.email === email && user.password === password);
console.log(user1);
if(user1) {
req.session.userId = user.id
return res.redirect('/home')
}
res.redirect('/')
})
Issue - User1 is undefined.

I would say the password value received in req.body is a string . Your === comparator checks also a variables type, and string and number won't match.
Option 1
I recommend to change your users data password prop to string:
const users = [
{ id: 1, name: 'Sherry', email:'sherry#gmail.com', password: '1234' },
{ id: 2, name: 'harry', email:'harry#gmail.com', password: '1234' },
{ id: 3, name: 'cherry', email:'cherry#gmail.com', password: '1234' }
]
Option 2
If you want to only have numeric passwords you can also change your find function to compare numbers correctly:
let user1 = users.find((user) => user.email === email && user.password === parseInt(password));
Please let me know if it worked.

// Parse URL-encoded bodies (as sent by HTML forms)
app.use(express.urlencoded());
// Parse JSON bodies (as sent by API clients)
app.use(express.json());
This two line will make sure you sever-side will receive the data from req.body.
First of all you were sending the data as a form data so you need to include
// Parse URL-encoded bodies (as sent by HTML forms)
app.use(express.urlencoded());
which will allow you to read the data from req.body.
app.post('/login', (req,res) => {
const { email, password } = req.body;
console.log('req',req.body)
console.log("email",email)
console.log("password",password)
let index = users.findIndex((user) => user.email === email && user.password == password);
console.log("index",index)
let user1= users[index]
console.log("user1",user1);
if(user1) {
console.log("HERE=======================")
req.session.userId = user1.id
return res.redirect('/home')
}
res.redirect('/')
})
Now try the above code snap, hopefull it will help you.

Related

Express - How to exclude a field from the response returned by Model.findOne() and Model.create() mongoose

I want to exclude the user's password field when sending a response to my front end. of course, I don't want anyone to access the user's encrypted password.
I have looked at this SO question, but the solution does not look good for models with many fields.
I'm using .select('-password') with Model.find(), Model.findById(), and Model.findByIdAndUpdate()
but It's not working for Model.findOne() and Model.create()
How can I exclude some fields when returning a response ?
/**
* #desc Authenticate User
* #route /api/acccounts/signin
* #access Public
*/
export const authenticateUser = asyncHandler(async (req, res) => {
const values = await loginSchema.validateAsync(req.body);
const { email, password, rememberMe } = values;
const account = await Account.findOne({ email });
if (account && (await account.matchPassword(password))) {
return res.json({
id: account.id,
firstName: account.firstName,
lastName: account.lastName,
email: account.email,
isAdmin: account.isAdmin,
...other fields,
token: generateToken(account.id, rememberMe),
});
}
return res.status(400).json({ message: 'Invalid email or password' });
});
Here you can use projection to exclude the field in your response like this
db.collection.findOne({
email: "sample#sample.com"
},
{
password: 0
})
Here is like of playground to test it: MongoPlayground
For more details check out findOne official documentation

Redirect response into a dynamic url

I am using node js with:
express framework
hbs framework
mongodb
My task is to redirect my response to a url with a format /users/:name/course/:courseId.
After running the code with the parameters name=James and courseId=1234, I get /users/James/course/1234 in the browser url. However, I am not able to post to /users/James/course/1234.
Edit: the data is being successfully posted, but I am getting a message Cannot GET /users/James/course/1234.
I have the following code:
app.js:
app.get('/', (req, res) => {
res.render('form.hbs')
})
app.post('/saveUsers', [
// firstname must contain letters only
check('firstname', 'First name should contain only letters').isAlpha(),
// email must be in a email format
check('email', 'Email field must be in a email format').isEmail(),
// courseId must be numerical and exactly 4 digits
check('courseId', 'course ID should contain only numbers').isNumeric(),
check('courseId', 'course ID should be exactly 4 digits').isLength({ min: 4, max: 4 })
], (req, res) => {
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array(), data:req.body})
}
var db = utils.getDb()
db.collection('users').insertOne({
name: req.body.firstname,
email: req.body.email,
courseId: req.body.courseId
}, (err, result) => {
if (err) {
Response.send('Unable to insert a student')
}
console.log(result.ops)
nameUrl = result.ops[0].name
courseIdUrl = result.ops[0].courseId
res.redirect(`/users/${nameUrl}/course/${courseIdUrl}`)
})
})
app.post(`/users/${nameUrl}/course/${courseIdUrl}`, (req, res) => {
res.json(result.ops)
})
form.hbs:
<!DOCTYPE html>
<html>
<body>
<h1>Welcome</h1>
<p>Enter your name, email and course ID:</p>
<form action="/saveUsers" method="POST" id="myForm">
<input type="text" placeholder="firstname" name="firstname">
<input type="email" placeholder="email" name="email">
<input type="text" placeholder="courseId" name="courseId">
<input type="submit" value="Submit">
</form>
</body>
</html>
It looks like you are using template literal string interpolation where you should be using the Express syntax for route parameters. See the "route parameters" section in the docs.
Instead of:
app.post(`/users/${nameUrl}/course/${courseIdUrl}`, (req, res) => {
res.json(result.ops)
})
Try:
app.post(`/users/:name/course/:courseId`, (req, res) => {
res.json(result.ops)
})

cognito pool - failed to signup a user

I am trying to build a user management based on cognito user pool.
I am using nodesjs + express for the backend, all the operation to cognito will be done from the nodejs.
my signup route is
app.post('/signup', (req, res) => {
let userName = req.body.userName;
let email = req.body.email;
let familyName = req.body.familyName;
let password = req.body.password;
let attributeList = [];
let dataEmail = {
Name: 'email',
Value: email
};
let name = {
Name: 'name',
Value: userName
};
let familyname = {
Name: 'family_name',
Value: familyName
};
let attributeEmail = new AmazonCognitoIdentity.CognitoUserAttribute(dataEmail);
let attributeName = new AmazonCognitoIdentity.CognitoUserAttribute(name);
let attributeFamilyName = new AmazonCognitoIdentity.CognitoUserAttribute(familyname);
attributeList.push(attributeEmail);
attributeList.push(attributeName);
attributeList.push(familyname);
let userPool = new CognitoUserPool(poolData);
userPool.signUp(email, password, attributeList, null, (err, result) => {
if (err) {
console.log(`got error during signup ${err}`);
res.sendStatus(500);
} else {
let cognitoUser = result.user;
console.log('user name is ' + cognitoUser.getUsername());
res.sendStatus(200);
}
});
});
however I am getting a failure and cannot find how to over come it
the error is missing parameter:
got error during signup MissingRequiredParameter: Missing required key 'Username' in params
A 'username' attribute is always required to register a user. So you'll need to add one to the attributes list.
If you're not asking the users for a username or don't need one for your use case, you can just use whatever value your using to uniquely identify the user. Thats a simple way to handle it.
So for example, on top of adding their email address to the email address attribute, submit it as the username attribute too.
Reference:
http://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html

Why is 'encryptedPassword' parameter being interpreted as undefined in sails.js project?

I am working on a web app following irlnathan/ponzicoder's Sailscasts series as a guide. I have reached Ep. 5 on creating entries for the 'User' model using a signup action. At this stage in the tutorial (precise time linked to), we see that the response page he receives is the JSON object of the User that was just created using the signup. I, however, get the following for my response page:
{
"error": "E_VALIDATION",
"status": 400,
"summary": "1 attribute is invalid",
"model": "User",
"invalidAttributes": {
"encryptedPassword": [
{
"rule": "string",
"message": "`undefined` should be a string (instead of \"null\", which is a object)"
},
{
"rule": "required",
"message": "\"required\" validation rule failed for input: null"
}
]
}
}
This states that my 'encryptedPassword' parameter is somehow undefined by the time I am returning a response. However, when I log the json object I am returning to the response in my console, I get the following (which is what I expect to see instead of the E_VALIDATION response):
{ firstName: 'First',
lastName: 'Last',
email: 'email#domain.com',
encryptedPassword: '$2a$10$q0TfKWnN47mucsZLybN8WeygPPMpYxp9VMGUgIjA./ipZBn.POuOG',
lastLoggedIn: '2015-08-03T23:45:22.520Z',
gravatarUrl: 'http://www.gravatar.com/avatar/7328fddefd53de471baeb6e2b764f78a?',
createdAt: '2015-08-03T23:45:22.523Z',
updatedAt: '2015-08-03T23:45:22.523Z',
id: 39 }
So encryptedPassword is definitely being correctly created and defined.
This is a problem similar to the one faced by THIS user, except my problem does not lie in missing HTML name attributes. Stranger still, my problem specifically lies in the 'encryptedPassword' parameter and none of the others.
I must admit that since I am not using the exact same fields for my signup form as the tutorial, there may be some discrepancies which are causing the issue. However, I still haven't been able to figure it out after a few hours of debugging. Here's what I'm using
Relevant code:
Signup action (UserController.js <-- following sails.js conventions)
And as a note for each of the console.log statements littered in the signup function, I get a non-null value for them all. I did this to verify that indeed encryptedPassword was not null/undefined
/**
* UserController
*
* #description :: Server-side logic for managing Users
* #help :: See http://sailsjs.org/#!/documentation/concepts/Controllers
*/
module.exports = {
signup: function(req, res) {
var Passwords = require('machinepack-passwords');
// Encrypt a string using the BCrypt algorithm.
Passwords.encryptPassword({
password: req.param('password')
// difficulty: 10,
}).exec({
// An unexpected error occurred.
error: function (err){
return res.negotiate(err);
},
// OK.
success: function (encryptedPassword){
console.log("1", encryptedPassword);
require('machinepack-gravatar').getImageUrl({
emailAddress: req.param('email')
}).exec({
error: function(err) {
return res.negotiate(err);
},
success: function(gravatarUrl) {
console.log("2", encryptedPassword);
console.log("2", gravatarUrl);
User.create({
firstName: req.param('firstName'),
lastName: req.param('lastName'),
email: req.param('email'),
encryptedPassword: encryptedPassword,
lastLoggedIn: new Date(),
gravatarUrl: gravatarUrl
}, function userCreated(err, newUser) {
console.log("NEWUSER", newUser);
console.log("3", encryptedPassword);
console.log("3", gravatarUrl);
if (err) {
console.log("err: ", err);
console.log("err.invalidAttributes: ", err.invalidAttributes)
// If this is a uniqueness error about the email attribute,
// send back an easily parseable status code.
if (err.invalidAttributes && err.invalidAttributes.email && err.invalidAttributes.email[0]
&& err.invalidAttributes.email[0].rule === 'unique') {
return res.emailAddressInUse(); //some error that I have already defined somewhere else in my project
}
return res.negotiate(err);
}
return res.json({
id: newUser.id
});
});
}
});
}
});
}
};
Attributes of the User model (User.js <-- following sails.js conventions)
/**
* User.js
*
* #description :: TODO: You might write a short summary of how this model works and what it represents here.
* #docs :: http://sailsjs.org/#!documentation/models
*/
module.exports = {
schema: true,
attributes: {
//all the other attributes
// The encrypted password for the user
encryptedPassword: {
type: 'string',
required: true
}
//more attributes
}
};
HTML of the signup form
<form ng-submit="submitSignupForm()" id="sign-up-form" name="signup" class="form" role="form">
<!-- irrelevant html -->
<!-- P A S S W O R D -->
<div class="control-group form-group" ng-class="{'has-error':signup.password.$invalid && signup.password.$dirty}">
<input type="password" name="password" value="" class="form-control input-lg" placeholder="Password" ng-model="signupForm.password" id="password" required ng-minlength="12"/>
<span class="help-block has-error" ng-if="signup.password.$dirty">
<span ng-show="signup.password.$error.required">Password is required.</span>
<span ng-show="signup.password.$error.minlength">Password must be at least 12 characters.</span>
</span>
</div>
<!-- more misc html -->
</form>
And the signup controller
angular.module('SignupModule').controller('SignupController', ['$scope', '$http', 'toastr', function($scope, $http, toastr) {
$scope.signupForm = {
loading: false
}
$scope.submitSignupForm = function() {
$scope.signupForm.loading = true;
console.log("clicked!");
$http.post('/signup', {
firstName: $scope.signupForm.firstName,
lastName: $scope.signupForm.lastName,
email: $scope.signupForm.email,
password: $scope.signupForm.password
})
.then(function onSuccess() {
window.location = '/user';
})
.catch(function onError(sailsResponse) {
var emailAddressAlreadyInUse = (sailsResponse.status == 409);
if (emailAddressAlreadyInUse) {
toastr.error('That email address has already been taken, please try again.', 'Error');
return;
}
})
.finally(function eitherWay() {
$scope.signupForm.loading = false;
});
}
}]);
So to reiterate, why on earth is encryptedPassword being interpreted as null?

Node.js (Express) Form Clears on Submission

I am working on a really basic registration form in Node.js (with Express), and I am trying to find the easiest way to provide basic form validation. I've gone with "Express-Validator", which seems to do a fine job. However, my goal is to simply show any validation messages that are required and to leave the values entered by the user alone.
It seems that the request information is not making it back into the res.render, which I guess makes sense. However, I've looked everywhere I can think of and I can't find any reference that discusses how to keep form fields populated after showing error messages.
Below is a small snippet describing my approach:
post: function(req, res){
var userName = req.body.username;
var password = req.body.password;
//Validate input
req.assert("username", 'Invalid email address.').isEmail();
req.assert("password", 'Password cannot be empty.').notEmpty();
req.assert("passwordConfirm", 'Passwords entered do not match!').equals(password);
//Make sure we have no validation errors
var pageErrors = req.validationErrors();
if(!pageErrors)
{
userModel.CreateUser(userName, password, function(err){
if(err)
{
//there was a problem inserting new user... probably already exists
//will need to check the error to confirm
var dbErrorMessage = "Could not insert record into database!";
if(err.code === 11000)
{
//this is a duplicate entry
dbErrorMessage = "A user with that email address already exists!";
}
res.render('register.html', { pageErrors: [{msg: dbErrorMessage }]});
}
else
{
res.render('register.html', { successMessage: successMessage });
}
});
}
else
{
res.render('register.html', { pageErrors: pageErrors });
}
Unfortunately, you have to repopulate the form manually. If you get any page errors, you will pass back the form values to the view.
if(!pageErrors)
{
// ...
}
else
{
res.render('register.html', {
pageErrors: pageErrors,
userName: userName
});
}
And in your view, you would do a simple check to see if their are any errors and repopulate accordingly. You would have to keep track of what errors are produced for each form field.
<% if (userNameError) { %>
<input type="text" name="userName" value="<%- userName %>" />
<% } else { %>
<input type="text" name="userName" />
<% } %>
Another popular way is to send your form via ajax to to the server, and do all your validations. If there is an error, the entered form data remains and you would show the error, otherwise redirect after the successful login. Below is an example of how to submit a form with javascript.
$("#login-button").live("submit", function (e) {
// this will prevent the form from being uploaded to the server the conventioanl way
e.preventDefault();
// the form data
var data = $(this).serialize();
// this logs the user in
$.ajax({
type: 'POST',
url: BASE_URL + '/login',
data: data,
dataType: 'json',
success: function (data, status) {
// successful
},
});
// superfluous fallback
return false;
});
There is an easy way is you are using
app.use(express.bodyParser()) and app.use(expressValidator());
You can use req.body
res.render('register.html', {
pageErrors: pageErrors,
validated: req.body
});
And I'm not sure which templating language you are using but you could do something like..
<input type="text" name="userName" value="<%= pageErrors.userName.value || validated.userName %>" />
This then gives back the good input if ok or the bad input if it needs correcting.
You can get this done using connect-flash
Below are the code snippets in different files in order to get the values entered by the user back in form when validations fail while doing signup with passport.
Run this below command to add new package into package.json
npm install connect-flash --save
app.js
var flash = require('connect-flash');
app.use(flash()); // add this above passport initialize
app.use(passport.initialize());
app.use(passport.session());
config/passport.js (Please focus on form data loading into flash)
passport.use('local.signup', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
}, function (req, email, password, done) {
req.checkBody('first_name', 'Firstname is missing').notEmpty();
req.checkBody('last_name', 'Lastname is missing').notEmpty();
req.checkBody('email', 'Invalid email').notEmpty().isEmail();
req.checkBody('password', 'Password is too short. Minimum size is 6.').notEmpty().isLength({min:6});
req.checkBody('confirm_password', 'Password and confirm password didn\'t not match').equals(req.body.password);
var errors = req.validationErrors();
if (errors) {
var messages = [];
errors.forEach(function(error) {
messages.push(error.msg);
});
req.flash('formdata', req.body); // load form data into flash
return done(null, false, req.flash('error', messages));
}
User.findOne({'email': email}, function (err, user) {
if (err) {
req.flash('formdata', req.body); // load form data into flash
return done(err);
}
if (user) {
req.flash('formdata', req.body); // load form data into flash
return done(null, false, {message: 'Email is already in use.'});
}
var newUser = new User();
newUser.first_name = req.body.first_name;
newUser.last_name = req.body.last_name;
newUser.email = email;
newUser.password = newUser.encryptPassword(password);
newUser.save(function(err, result) {
if (err) {
return done(err);
}
return done(null, newUser);
});
});
}));
routes/index.js (Please focus on form data in flash loaded back into a variable)
router.get('/signup', function (req, res, next) {
var messages = req.flash('error');
var formdata = req.flash('formdata'); // Get formdata back into a variable
res.render('user/signup', {csrfToken: req.csrfToken(),
messages: messages, // pass it here to access in view file
hasErrors: messages.length > 0,
formData: formdata[0]
});
});
router.post('/signup', passport.authenticate('local.signup', {
badRequestMessage: 'Please fill the form with all details',
failureRedirect: '/user/signup',
failureFlash: true
}), function (req, res, next) {
if (req.session.oldUrl) {
var oldUrl = req.session.oldUrl;
req.session.oldUrl = null;
res.redirect(oldUrl);
} else {
res.redirect('/user/profile');
}
});
views/signup.hbs (Please focus on values in input elements)
<form class="wow fadeInUp animated" data-wow-delay=".7s" action="/user/signup" method="post" >
<input type="text" placeholder="First Name" name="first_name" value="{{ formData.first_name }}">
<input type="text" placeholder="Last Name" name="last_name" value="{{ formData.last_name }}">
<input type="text" class="email" placeholder="Email Address" name="email" value="{{ formData.email }}">
<input type="password" name="password" value="" class="lock" placeholder="Password">
<input type="password" name="confirm_password" value="" class="lock" placeholder="Confirm Password">
<input type="hidden" name="_csrf" value="{{ csrfToken }}">
<input type="submit" name="Register" value="Register"></form>
Hope this helps.
set a variable for all the input, for example
var inputData = {
firstname : req.body.firstname,
lastname : req.body.lastname,
email : req.body.email,
username : req.body.username,
password : req.body.password,
password_confirmation : req.body.password_confirmation,
agreetoterms: req.body.agreetoterms
}
and then pass that variable to the view
res.render('register.html', { pageErrors: [{msg: dbErrorMessage }], inputData: inputData });
then in your view
value="<%= inputData.userName %>"
if you are using jade and form Validator from npm , the best part is that you can an if statement in jade and then you simply check if error then with res.render we send the objects also . See This
if(errors){
res.render('register',{
errors : errors,
name : name,
email : email,
username : username,
password : password,
password2 : password2
});
And in jade you do this
input.form-control(name='name',type='text',placeholder='Enter Name',value = (errors ? '#{name}':''))
so if there is errors value will set to variable in name which will rendered when we send back
I think you can also be done in Angular2/Angular.js
Well, there is a simple way to achieve this. Do following in controller:
else{
res.render('register.html', { pageErrors: pageErrors, inputData: req.body});
}
Now, in your ejs (or do it according to your view engine) file do following:
<input type="text" name="name" value="<% if(typeof pageErrors !== "undefined"){ %> <%- inputData.name %> <% } %>" class="form-control">
check http://www.quietless.com/kitchen/building-a-login-system-in-node-js-and-mongodb/
on register.html make this
var data = {};
data.user = $('#user-input').val();
data.email = $('#email-input').val();
data.pass = $('#pass-input').val();
$.ajax({ url: '/signup'
, type: 'POST'
, data: JSON.stringify(data)
, contentType: 'application/json'
, dataType: 'html'
})
.done(function(data) {
if (data == 'ok') {
$('#content').html('You are registered!');
}
else $('#account-form-container').append('<br>error:' + data);
});
there might be error like: CANNOT POST /
in this case the author of tutorial on link above uses lib $.ajaxForm
you can also use https://github.com/felixge/node-formidable
or $('#myform').submit() replace to $('#submit-a-link').click()

Resources