Why is my Node validation failing in this POST? - node.js

I'm following along in the MEAN machine Node authentication tutorial.
Here is their source code: https://github.com/scotch-io/mean-machine-code/blob/master/10-node-authentication/server.js I basically have everything except for the apiRouter.post('/authenticate', part
The Express APIs are working:
http://localhost:8615/api/users will return a list of users from scotch.io's MongoDB
The following is the API for /api/users:
apiRouter.route('/users')
// create a user (accessed at POST http://localhost:8615/api/users)
.post(function(req, res) {
// create a new instance of the User model
var user = new User();
// set the users information (comes from the request)
user.name = req.body.name;
user.username = req.body.username;
user.password = req.body.password;
// save the user and check for errors
user.save(function(err) {
if (err) {
// duplicate entry
if (err.code == 11000)
return res.json({ success: false, message: 'A user with that username already exists. '});
else
return res.send(err);
}
// return a message
res.json({ message: 'User created!' });
});
})
// get all users (access at GET http://localhost:8615/api/users)
.get(function(req, res) {
User.find(function(err, users) {
if (err) return res.send(err);
// return the users
res.json(users);
})
});
Here is my user.js User Schema
// SCHEMAS ------------------------------------
// user schema
var UserSchema = new Schema({
name: String,
username: { type: String, required: true, index: { unique: true }},
password: { type: String, required: true, select: false }
// ^ select false will not return passwords
});
// hash the password before the user is saved
UserSchema.pre('save', function(next) {
var user = this;
// PUT username
if (!user.isModified('username')) return next();
// PUT name
if (!user.isModified('name')) return next();
// hash the password only if the password has been changed or user is new
if (!user.isModifited('password')) return next();
// generate the salt
bcrypt.hash(user.password, null, null, function(err, hash) {
if (err) return next(err);
// change the password to the hashed version
user.password = hash;
next();
});
});
FROM THE BOOK:
Create a Sample User
First, we need to make sure that we even have a user to authenticate
since towards the end of last chapter, we deleted everyone. Let’s
create the user using the POST http://localhost:8080/api/users route
we created in our API to add a user to our database.
We will send a POST request with the following information: Name Chris
Username chris Password supersecret
I'm using Postman to add a new user, as you can see I have put in key/value pairs for username and password, however getting an error saying "Validation failed" "username is required" "password is required":
UPDATE, I just tried x-www-form-urlencoded and got the following error
GET /api/users 200 66.141 ms - 655
••• API CRUD hit •••
/Users/leongaban/NodeDallas/projects/awesome-test/app/models/user.js:27
if (!user.isModifited('password')) return next();
^
TypeError: Object { password: 'supersecret',
username: 'Chris',
name: 'chris',
_id: 54c001dc4ee4028c18e61334 } has no method 'isModifited'
at model.UserSchema.methods.comparePassword.user (/Users/leongaban/NodeDallas/projects/awesome-test/app/models/user.js:27:12)
Screenshot of error in Postman: https://s3.amazonaws.com/f.cl.ly/items/1M0M392M0E3b2i430I1n/Image%202015-01-21%20at%201.45.51%20PM.png

try x-www-form-urlencoded in postman, that would do.

You want to push a json to your server. Select raw and set the data type to JSON.
Then you just have to write your user in JSON format with all his fields here.
{
"name": "Chris",
"username": "chris",
"password": "supersecret"
}

Related

Dynamically adding new property: value to a model from mongoose findOne

I'm trying to implement forgotten password recovery logic in my application.
Here is my Express router:
router.post('/passwordreset', function(req, res) {
User.findOne({'local.email' : req.body.email}, function(err, user) {
if(err) throw err;
if(user) {
// creating a token for this user's password reset
let token = Math.random().toString(36).substring(2);
// creating a password reset link
let url = req.protocol + '://' + req.get('host') + '/passwordreset/' + token;
// fetching recivier email
let email = user.local.email;
// setting reset token to user and saving it's modified model
user.add({resetToken: token});
user.save(function(err) {
if(err) throw err;
mailer.passwordReset(email, url);
});
}else {
res.render('login', {message: 'Email is not found in database!'});
}
});
});
I don't have a "resetToken" property in my mongoose User schema, but I want to add it dynamically so I can later on search for a user by this token and assign new password for it. I know that it is possible to delete unused properties by assigning "undifined" to them before saving, but is there a wise versa solution to add properties to the object I get from findOne function?
user.add keep saying that it is not a function. Any suggestions?
PS. I know that I can create an empty "resetToken" property beforehand and use it for storing and overriding token, but i don't know if it's the best and only one solution. Any suggestions? Thanx in advance.
Disable strict option in your users schema.
let UsersSchema = new Schema(
{
login: String,
sha256: String,
group: Number,
//And so on
},
{
strict: false
});
Link to the documentation

How to decode and verify password in feathers js

I am new to feathersJs and trying to learn how to perform authentication using hooks and services. I am using Couchdb database and cradle.
This is the post method to encrypt password in hashPassword using "users" hooks service. The post method is as below:
app.post('/dev',function(req,res,next){
var username = req.body.username;
var password = req.body.password;
app.service('database').create({username,password}).then(user => {
db.save(user, function (err, docs) {
// Handle response
res.json(docs);
});
console.log('User Created Successfully.', user);
}).catch(console.error);
})
and service is:
app.service('authentication').hooks({
before: {
create: [
// You can chain multiple strategies
auth.hooks.authenticate(['jwt', 'local'])
],
remove: [
auth.hooks.authenticate('jwt')
]
}
});
app.service('database').hooks({
before: {
find: [
auth.hooks.authenticate('jwt')
],
create: [
local.hooks.hashPassword({ passwordField: 'password' })
]
}
});
now i am using this to retrive data :
app.post('/devget',function(req,res,next){
var User = {
username: req.body.username,
password: req.body.password
};
app.service('dataget').find(User).then(user => {
db.view('byuser/user',{key: User.username}, function (err, docs) {
// Handle response
res.json(docs);
});
console.log('User Get Successfully.', user);
}).catch(console.error);
})
this will give me data in response as:
Response [
{ id: '060ab48a4826da7125d8ae45350037ee',
key: 'w',
value:
{ _id: '060ab48a4826da7125d8ae45350037ee',
_rev: '1-ea9a18d3724ce4542019dc5752c1fd4d',
username: 'w',
password: '$2a$10$yBJVJTmVXfTk0V4CCiWkd.GvAZZB9dF2pckKJ9wb/lJcAK8Ou.v06',
id: 0 } } ]
this works fine and password is encrypted but i am not getting how to decrypt password and authenticate user.
Note: i just want o do it with hooks and services or custom service or class but not using passport.
You do not decrypt the password; you compare the encrypted password to a function that will encrypt the password (after you've found the user to make a password comparison to).
const bcrypt = require('bcryptjs');
var hash = bcrypt.hashSync("bacon");
bcrypt.compareSync("bacon", hash); // true
bcrypt.compareSync("veggies", hash); // false
I would use the comparePassword function in this file
https://github.com/feathersjs/feathers/blob/be5d8df3b7afa39852ff1b5643676d9140ba8203/packages/authentication-local/src/strategy.ts#L94
Based on that you could write a hook and consider contributing it to the feathersJS project.

How to to access first and last name for the following user model (based on mongoose)?

I am looking at some code, with a user schema, similar to the following.
var UserSchema = new mongoose.Schema(
{
email: {
type: String,
lowercase: true,
unique: true,
required: true
},
password: {
type: String,
required: true
},
profile: {
firstName: {
type: String
},
lastName: {
type: String
}
}
}
);
Now, as far as I can understand, the top-level properties are email, password and profile.... firstName and lastName should only be accessible from within profile. However, the details are being accessed with something like the following.
exports.register = function(req, res, next) {
// Check for registration errors
const email = req.body.email;
const password = req.body.password;
const firstName = req.body.firstName;
const lastName = req.body.lastName;
// Return error if no email provided
if (!email) {
return res.status(422).send({ error: 'You must enter an email address.'});
}
// Return error if no password provided
if (!password) {
return res.status(422).send({ error: 'You must enter a password.' });
}
// Return error if full name not provided
if (!firstName || !lastName) {
return res.status(422).send({ error: 'You must enter your full name.'});
}
...
I don't seem to understand why the firstName and lastName are being accessed directly with req.body.firstName instead of req.body.profile.firstName. There don't seem to be any virtual attributes in place either. So what's going on!?
req.body added by body-parser and this is not related to your mongoose model. You will get data in req.body sent from front-end(client side). Apart from this issue, I would like to recommend you to use following format that may help you
You may like to use schema for sub-document
var profileSchema = new Schema({
firstName: String,
lastName: String
});
var UserSchema = new mongoose.Schema({
email: {
type: String,
lowercase: true,
unique: true,
required: true
},
password: {
type: String,
required: true
},
profile: profileSchema
});
and may use like
exports.register = function(req, res, next) {
if(!req.body)
return res.status(500).send({ error: 'Unable to parse data'});
// Check for registration errors
const userData = {
email: req.body.email,
password: req.body.password,
profile: {
firstName: req.body.firstName,
lastName: req.body.lastName
}
}
// Return error if no email provided
if (!userData.email) {
return res.status(422).send({ error: 'You must enter an email address.'});
}
// Return error if no password provided
if (!userData.password) {
return res.status(422).send({ error: 'You must enter a password.' });
}
// Return error if full name not provided
if (!userData.profile.firstName || !userData.profile.lastName) {
return res.status(422).send({ error: 'You must enter your full name.'});
}
var newUser = new User(userData);// you must import user schema before using User
In an express application request parameters are passed in as first parameter of a route (req, res, next). The sample code posted shows the result of a POST request to a route called /register.
This data does not relate to the model posted with the question.
To be able to work with the model, the data needs to be stored into a new Mongoose object.
So within the route one would write:
exports.register = function(req, res, next) {
const User = new User();
User.profile.firstName = req.body.firstName;
// ... and so on
User.save((err, savedUser) => {
if(err) return next(err);
// end request
});
}
Please note that some kind of sanity check is recommended when dealing with user provided variables. Using it like in my example above may enable an attacker to store a string of arbitrary length inside the database which is most probably not desired.
As pointed out by #DanielKhan, within the above comments, mongoose is only being used to model the data. However, at this point, it has nothing to do with the data coming in directly from the client. Hence, all the fields, including email, password, and first name, and last name will be retrieved at the same level... using req.body.

How to access Mongoose Schema Attributes?

I am fairly new to using Mongoose and MongoDB. I am working on a register / login page. The register function works fine, storing the user account onto the database, but my problem is with logging in. What I am trying to do is get the 'password' attribute from the matching user off of the database, to match against the password that the user enters. This is my login function.
router.post('/logSubmit', function(req, res, next) {
var gusername = req.body.username;
var gpassword = req.body.password;
User.count({
'credentials.username': gusername
}, function(err, count) {
if (err) throw err;
console.log(count);
if (count > 0) {
// Where I need to pull password attribute from the database
} else {
// Wrong username or password
}
});
});
I have looked all over the internet on how to read an attribute from a database entry, but I can't find anything. I feel like it is something very simple, but I guess I don't know the syntax. The name of my model is User. I figured it would be something like:
User.find({ username: gusername }, function(err, user) {
if (err) throw err;
var getpassword = user.password;
console.log(getpassword);
});
I half-thought that would work, but it didn't. How do I access the password attribute from the database?? Thanks.
EDIT:
This is what my user accounts look like stored in my database:
{
"_id": {
"$oid": "569e5344d4355010b63734b7"
},
"credentials": {
"username": "testuser",
"password": "password1234"
},
"__v": 0
}
A find query is sufficient for your purposes. If a non-null user object is retrieved from the find query, you have a guarantee that it is a user with a password.
User.find({ 'credentials.username': gusername }, function(err, users) {
if (err) throw err;
// 'users' is an array of the User objects retrieved.
users.forEach(function(user) {
// Do something with the password.
// The password is stored in user.credentials.password
console.log(user.credentials.password);
});
});

Bcrypt error: illegal arguments String unidentified

here is my complete code
var express = require('express'),
app = express(),
mongoose = require('mongoose'),
bodyParser = require('body-parser'),
morgan = require('morgan'),
webToken = require('jsonwebtoken'),
bcrypt = require('bcryptjs'),
assert = require('assert');
Schema = mongoose.Schema,
secretKey = "omjdiuwkslxmshsoepdukslsj";
//User Schema
var userSchema = new Schema({
username: {type: String, required: true, index: {unique:true}},
password: {type: String, required: true, select: false}
})
userSchema.pre('save', function(next){
var user = this;
if(!user.isModified('password')) return next();
bcrypt.hash(user.password, null, null, function(err, hash){
if(err) return next(err);
user.password = hash;
next();
})
});
userSchema.methods.comparePassword = function(password){
var user = this;
bcrypt.compare(password, user.password, function(err, result){
if(err){
console.log(err);
}
else {
console.log("passwords match!");
return;
}
})
}
var userModel = mongoose.model('users', userSchema);
//Connecting to Mongo
mongoose.connect("mongodb://localhost/userstories", function(err){
if(err) {
console.log(err);
}
else {
console.log("Connected to database!");
}
});
//Creating Token
function createToken(user){
var token = webToken.sign({
_id: user.id,
username: user.username
}, secretKey,{
expiresIn: 30 * 60 * 1000
})
return token;
}
//Middlewares
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(morgan('dev'));
//Api's
app.post('/signup', function(req, res){
var user = new userModel({
username: req.body.username,
password: req.body.password
})
user.save(function(err){
if(err){
console.log(err);
}
else{
res.json({message: "User created!"});
}
})
})
app.post('/login', function(req, res){
userModel.findOne({username: req.body.username}, function(err, user){
if(err) console.log(err);
if(!user){
res.send("User not found!");
}
else if(user){
var validPassword = user.comparePassword(req.body.password);
if(validPassword){
var tokens = createToken(user);
res.json({
success: true,
message: "Successfully logged In",
token: tokens
});
}
else {
res.send("Invalid password");
}
}
})
});
//Running the server
app.listen(3000, function(err){
if(err) console.log("port not working");
else{
console.log("Everything went just fine");
}
})
I've tried every approaches and saw all the answers here. But no one seem to come across this illegal argument error. Please figure this one out for me Im sure there is a bug I cant see
In your User Schema, you are setting select to false for the password field. This means that anytime you look for a user in the schema as you're trying to do in the login request, you won't get the value of the password field or any other field that has select false defined in the schema.
What you need to do is to specify you need the password when the user is found:
app.post('/login', function(req, res){
userModel.findOne({username: req.body.username}, 'password', function(err, user){
// continue
}
This will return only the _id and the password from the DB. If you want to return other fields, you'd have to add them in:
app.post('/login', function(req, res){
userModel.findOne({username: req.body.username}, 'password firstName lastName email', function(err, user){
// continue
}
I have tried the same code for authentication once and got the same error Error: Illegal arguments: string, function.
But I did not see any issue with the code. The thing is, I had registered two users with the same user name and a different password. Then, when I tried to log in with the user name and one password this error occurred and stopped the server.
So it seems that you are also facing the same problem. Make sure there are no errors with this stuff if you do not want to have a bug in your code.
Check the value of user.password before sending it to bcrypt.compare().
Chances are, you've fetched the user without including the password property, resulting in a value of undefined. This can happen in Sequelize if you set custom attributes or if you're using a scope that excludes props.
Your code is wrong in this place. You may see it.
var validPassword = user.comparePassword(req.body.password);
If you use bcryptjs thrid party plugins, like that
let validPassword = bcrypt.compare(req.body.password, user.password);
bcrypt.compare(password, hashedPassword);
In my particular case, I was dealing with this error, checking out all the code up and down unsuccessfully for almost two days.
Finally, realized that the column PASSWORD in MariaDB was in uppercase. Theoretically that shouldn't affect at all, but I decided to rename it to lowercase and bum! problem solved.
For those using async/await for database calls, make sure you don't forget the await keyword on the User.findOne() call.
In my case, I had forgotten the await keyword while fetching the user. This as a result, was giving me a Promise object instead of User object and hence the password property on it was undefined.
I also encountered the same error when I was using
bcrypt.compareSync("input to be compared with the hash", hash).
Later on I discovered that I was supposed to pass the actual value in the first input parameter i.e (The actual value from which the hash was generated) and the hash in the second input parameter, but I was passing hashed values in both the input parameters.
After correcting the same it was giving me the desired output as true or false.
You can also run and check your code here.
Do like this:
UserSchema.pre('save', async function (next) {
const hash = await bcrypt.hash(this.password, 10);
this.password = hash;
next()
})
You need to specify that you also want the password because you have set the select property to false on password.
So when you are fetching the user, just make sure to explicitly specify that you also want the password. Add .select('+password') on the user object when you are querying a user.
In my case it was a simple spelling mistake before sending to bcrypt.hash :
If you're testing this with Postman, I just found an issue where the default Content-Type header is set to text/plain. If you untick the default header (as it doesn't allow you to change it) and add another Content-Type header with a value of application/json, it works.

Resources