19

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

1
  • you are using user.password !! where is user.password coming from? Commented Mar 6, 2020 at 5:50

12 Answers 12

6

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
}

Sign up to request clarification or add additional context in comments.

Comments

3

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.

Comments

3

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.

Comments

1

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);

Comments

1

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.

Comments

1

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.

Comments

0

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.

Comments

0

Do like this:

UserSchema.pre('save', async function (next) {
  const hash = await bcrypt.hash(this.password, 10);

  this.password = hash;
  next()
})

Comments

0

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.

Comments

0

In my case it was a simple spelling mistake before sending to bcrypt.hash :

enter image description here

Comments

0

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.

Comments

0

In my case, the same error was coming so I tried to add await keyword because the function was asynchronous. I don't know how it is working but this worked for me.

async login(req,res,next) {

    const {email,password} = req.body;
    try{
        //first check if user exists or not
        const existinguser = await users.findOne({email:email});//add await here
        if(!existinguser){
            return res.send(404).json({message:`User not found`});
        }
        // console.log(password);
        // console.log(existinguser);
        // console.log("Db password" + existinguser.password);
        const matchedpassword = bcrypt.compare(password,existinguser.password);
        if(!matchedpassword){
            return res.send(401).json({message:`Invalid Credential`});
        }
        const token = jwt.sign({email:existinguser.email,id:existinguser._id},process.env.SECRET_KEY);
        res.status(200).json({
            message:`User is Logged In`,
            token:token
        })
    }
    catch(err){
        console.log(err); 
        next(new CustomError(err.message,500,"Unable to Login"))
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.