saintedlama / passport-local-mongoose

Passport-Local Mongoose is a Mongoose plugin that simplifies building username and password login with Passport
MIT License
1.17k stars 295 forks source link

TypeError: passportLocalMongoose.changePassword is not a function #268

Open icecreamsandwich opened 6 years ago

icecreamsandwich commented 6 years ago

I am using passport and passport-local-mongoose for authentication in my react app . The login and registration works without any

problem . But the reset password / change password is not working properly . I have the reset password component as follows ResetPassword.js

var React = require("react");
var helpers = require("../utils/helpers");

class ResetPassword extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      oldpassword: "",
      newpassword: "",
      confirmpassword: "",
      usertype: "",
    }
    this.handleUserChange = this.handleUserChange.bind(this);
    this.handleLogin = this.handleLogin.bind(this);
  }

componentDidMount() {
    helpers.getCurrentUser().then(function(response) {
      if (response !== this.state.username) {
        this.setState({ 
            usertype: response.data.userType
        });
      }
    }.bind(this));
}

  handleUserChange(event) {
     this.setState({ [event.target.name]: event.target.value});
  }

  handleLogin(event) {
      // just in case we need it
      // event.preventDefault();
  }

    render() {
      return (
        <div className="container">
            <div className="row" id="loginForm">
                <div className="col m6 offset-m3 s12">
                    <div className="card-panel">
                        <div className="row grey lighten-5">
                            <div className="col s12 center">
                                <h4 className="blue-text text-darken-1"><img id="logo" src="/assets/images/logo.png"/><span className="hide-on-med-and-down">Reset Password</span></h4>
                            </div>
                        </div>
                        <form action="/manager/reset-password" method="POST" onSubmit={this.handleLogin}>
                            <div className="row">
                                <div className="col s12">
                                    <input
                                        placeholder="Old Password"
                                        type="password"
                                        className="validate"
                                        value={this.state.oldpassword}
                                        name="oldpassword"
                                        onChange={this.handleUserChange}
                                        required />
                                </div>
                            </div>
                            <div className="row">
                                <div className="col s12">
                                    <input
                                        placeholder="New Password"
                                        type="password"
                                        className="validate"
                                        value={this.state.newpassword}
                                        name="newpassword"
                                        onChange={this.handleUserChange}
                                        required />
                                </div>
                            </div>
                            <div className="row">
                                <div className="col s12">
                                    <input
                                        placeholder="Confirm New Password"
                                        type="password"
                                        className="validate"
                                        value={this.state.confirmnewpassword}
                                        name="confirmpassword"
                                        onChange={this.handleUserChange}
                                        required />
                                </div>
                            </div>

                             <div className="row">
                                <div className="col s12">
                                    <input
                                        placeholder="userType"
                                        type="hidden"
                                        className="validate"
                                        value={this.state.usertype}
                                        name="usertype"
                                        onChange={this.handleUserChange}
                                        required />
                                </div>
                            </div>
                            <div className="row">
                                <div className="col s12">
                                    <button className="btn waves-effect waves-light btn-large blue accent-3 loginButtons" type="submit" value="Submit" name="action">Submit<i className="material-icons right">send</i></button>
                                </div>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
      );
    }
};

module.exports = ResetPassword;

and in server.js file

var passport = require("passport");
  var LocalStrategy= require("passport-local");
  var passportLocalMongoose = require("passport-local-mongoose");

//change password (passport-local-mongoose)
  app.post('/manager/reset-password', function(req, res, next){
    passportLocalMongoose .changePassword(req.body.oldpassword,req.body.newpassword, function(err) {
            if (err){
                return next(err) 
            }
            else {
              if (req.body.userType === "manager" || req.body.userType === "su") {
                res.redirect("/manager");
              } else {
                res.redirect("/employee");
              }
            }
    }); 
  });

When submitting it throws me error :

TypeError: passportLocalMongoose.changePassword is not a function at C:\Users\admin\Documents\react_lms\server.js:212:14 at Layer.handle [as handle_request] (C:\Users\admin\Documents\react_lms\node_modules\express\lib\router\layer.js:95:5) at next (C:\Users\admin\Documents\react_lms\node_modules\express\lib\router\route.js:137:13) at Route.dispatch (C:\Users\admin\Documents\react_lms\node_modules\express\lib\router\route.js:112:3) at Layer.handle [as handle_request] (C:\Users\admin\Documents\react_lms\node_modules\express\lib\router\layer.js:95:5) at C:\Users\admin\Documents\react_lms\node_modules\express\lib\router\index.js:281:22 at Function.process_params (C:\Users\admin\Documents\react_lms\node_modules\express\lib\router\index.js:335:12) at next (C:\Users\admin\Documents\react_lms\node_modules\express\lib\router\index.js:275:10) at serveStatic (C:\Users\admin\Documents\react_lms\node_modules\serve-static\index.js:75:16) at Layer.handle [as handle_request] (C:\Users\admin\Documents\react_lms\node_modules\express\lib\router\layer.js:95:5) at trim_prefix (C:\Users\admin\Documents\react_lms\node_modules\express\lib\router\index.js:317:13) at C:\Users\admin\Documents\react_lms\node_modules\express\lib\router\index.js:284:7 at Function.process_params (C:\Users\admin\Documents\react_lms\node_modules\express\lib\router\index.js:335:12) at next (C:\Users\admin\Documents\react_lms\node_modules\express\lib\router\index.js:275:10) at jsonParser (C:\Users\admin\Documents\react_lms\node_modules\body-parser\lib\types\json.js:101:7) at Layer.handle [as handle_request] (C:\Users\admin\Documents\react_lms\node_modules\express\lib\router\layer.js:95:5) at trim_prefix (C:\Users\admin\Documents\react_lms\node_modules\express\lib\router\index.js:317:13) at C:\Users\admin\Documents\react_lms\node_modules\express\lib\router\index.js:284:7 at Function.process_params (C:\Users\admin\Documents\react_lms\node_modules\express\lib\router\index.js:335:12) at next (C:\Users\admin\Documents\react_lms\node_modules\express\lib\router\index.js:275:10) at textParser (C:\Users\admin\Documents\react_lms\node_modules\body-parser\lib\types\text.js:60:7) at Layer.handle [as handle_request] (C:\Users\admin\Documents\react_lms\node_modules\express\lib\router\layer.js:95:5)

What is the reason ? how to solve it ?

package.json

{
  "name": "react-shift-scheduler",
  "version": "1.0.0",
  "description": "Employee management system",
  "main": "server.js",
  "scripts": {
    "build": "webpack -p --progress --config webpack.config.js",
    "start": "node server",
    "test": "echo \"Error: no test specified\" && exit 1",
    "watch": "webpack --progress -d --config webpack.config.js --watch"
  },
  "author": "HB,NC,AR,CS",
  "license": "ISC",
  "dependencies": {
    "axios": "^0.15.3",
    "bluebird": "^3.4.6",
    "body-parser": "^1.15.2",
    "dotenv": "^4.0.0",
    "express": "^4.14.0",
    "express-session": "^1.14.2",
    "json-loader": "^0.5.7",
    "loader-utils": "^1.1.0",
    "moment-timezone": "^0.5.21",
    "mongoose": "^4.7.3",
    "morgan": "^1.7.0",
    "nodemailer": "^4.6.8",
    "passport": "^0.4.0",
    "passport-google-auth": "^1.0.1",
    "passport-google-oauth": "^1.0.0",
    "passport-linkedin-oauth2": "^1.4.1",
    "passport-local": "^1.0.0",
    "passport-local-mongoose": "^4.5.0",
    "path": "^0.12.7",
    "react": "^15.6.2",
    "react-dom": "^15.6.2",
    "react-live-clock": "^2.0.3",
    "react-router": "^3.0.0"
  },
  "devDependencies": {
    "babel-core": "^6.3.13",
    "babel-loader": "^7.0.0-alpha.3",
    "babel-preset-es2015": "^6.3.13",
    "babel-preset-react": "^6.3.13",
    "react-hot-loader": "^4.3.6",
    "webpack": "2.1.0-beta.25"
  }
}
JosephmBassey commented 6 years ago

I'm having the issue here, please let me know if you solve this, and I will let you know too, if I come up with a solution. Thanks

JosephmBassey commented 6 years ago

Hi, I figure it out Here is what i did in my controller handling the reset password , exports.editPassword = async (req, res) => { const user = await User.findOne({ username: req.user.username }); await user.setPassword(req.body.password); const updatedUser = await user.save(); req.login(updatedUser); req.flash('success', 'Password Changed Successfully') res.redirect('back') }

from the Documentation Passport-local-mongoose , you first need to get the specific user to update the password , here in my case the current login user , which is available on the req.user which we are exposed to , you can use any of the return property to query your collection, using async await i made a variable to hold the return object, in my case 'user', thereafter i chained the setProperty on it passing in the new password(req.body.password) since it return a promise i await it and assign a variable to it. from here you are good ... Note: since it is a promise it either resolved or reject, handling error can be done by rapping your code in a safe blanket, try..catch . You can read more Here

icecreamsandwich commented 6 years ago

OK , i resolved the problem . I did the same way . earlier the User model was not available to reset the password . I inherited user from the model and checked the user is logged in and then used it

User.findById(userid).then(function(sanitizedUser){
if (sanitizedUser){
    sanitizedUser.setPassword(newpassword, function(){
        sanitizedUser.save();
        // res.status(200).json({message: 'password reset successful'});
        res.redirect("/reset-password");
    });
} else {
    res.status(500).json({message: 'This user does not exist'});
}
},function(err){
    console.error(err);
})
JosephmBassey commented 6 years ago

Alright we are all good then..

Arootin commented 5 years ago

Since changePassword is a schema method, it must be used on an instance of a model, not the model itself or the imported passportLocalMongoose.

UserModel.findById(req.user._id)
        .then(foundUser => {
            foundUser.changePassword(req.body.old, req.body.new)
                .then(() => {
                    console.log('password changed');
                })
                .catch((error) => {
                    console.log(error);
                })
        })
        .catch((error) => {
            console.log(error);
        });
seasmurph2k commented 4 years ago

I know this is an old issue, but seeing as the last reply was 6 months ago - I thought I'd update it with a little syntax sugar..

try {
    //your validation/sainitization
    const { currentPass, newPass } = req.body;
    const { user, error } = await User.authenticate()(
        req.user.email,
        currentPass
      );

      if (!user || error) {
        throw error;
      }
      await user.changePassword(currentPass, newPass);
     //token/session invalidation & redirect to login on front-end
    res.sendStatus(200);
  } catch (error) {
    //your error handling
  }

I'm reauthing the user because I'm using JWTs, but as mentioned above you could do something like the following:

const user = await UserModel.findById(req.user._id)
user.changePassword('oldpass','newPass')