I'm trying to register users and then authenticate them using passport-local-mongoose. When I try to register a user I get the error 'User validation failed: password: Path password
is required.' I got rid of the error by changing required
to false
for password
field of my userSchema in 'userModel.js' but I assume that password has to be required. Does anyone know of a better solution?
And moreover I don't understand why it's giving me the error when I've included a password field for the user.
And when I do change required
to false
for password
, the registration works and I end up with the user stored in the Users collection, but then the authentication gives me a Status 400 Bad Request error and over there I'm completely stuck.
This is my server.js
import express from "express";
import mongoose from "mongoose";
import userRouter from "./routers/userRouter.js";
import session from "express-session";
import bodyParser from "body-parser";
import passport from "passport";
import User from "./models/userModel.js";
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true}));
app.use(session({
secret: "some secret sentence",
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
mongoose.connect(process.env.MONGODB_URL || "mongodb://localhost/somedatabase", {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
});
passport.use(User.createStrategy());
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
app.use("/api/users", userRouter);
app.get("/", (req, res) => {
res.send("Server is ready");
});
app.use((err, req, res, next) => {
res.status(500).send({ message: err.message });
});
app.listen(5000, () => {
console.log("Serve at http://localhost:5000");
});
userRouter.js:
import express from "express";
import expressAsyncHandler from "express-async-handler";
import User from "../models/userModel.js";
import passport from "passport";
import passportLocalMongoose from "passport-local-mongoose";
userRouter.post("/register", expressAsyncHandler(async (req, res) =>{
const createdUser = new User({name: req.body.name, username: req.body.email})
User.register(createdUser, req.body.password, async function(err, user){
if(err){
console.log(err);
res.redirect("http://localhost:3000/register");
}else{
passport.authenticate("local")(req ,res , function(){
console.log("authenticate successful");
res.send({
_id: createdUser._id,
user: createdUser.name,
email: createdUser.username,
isAdmin: createdUser.isAdmin,
token: generateToken(user)
})
})
}
})
userModel.js
import mongoose from "mongoose";
import passport from "passport";
import passportLocalMongoose from "passport-local-mongoose";
const userSchema = new mongoose.Schema(
{
name: {type: String, required: true},
username: {type: String, required: true, unique: true},
password: {type: String, required: true},
isAdmin: {type: Boolean, default: false, required: true},
},
{
timestamps: true,
}
);
userSchema.plugin(passportLocalMongoose);
const User = mongoose.model("User", userSchema);
export default User;
RegisterScreen.jsx
import React,{ useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { register } from "../actions/userActions.js"
export default function RegisterScreen(props){
const[email,setEmail] = useState("");
const[name,setName] = useState("");
const[password, setPassword] = useState("");
const[confirmPassword, setConfirmPassword] = useState("");
const redirect = props.location.search? props.location.search.split("=")[1] : "/";
const userRegister = useSelector((state) => state.userRegister);
const {userInfo , loading, error} = userRegister;
const dispatch = useDispatch();
const submitHandler = (e) =>{
e.preventDefault();//prevents refresh
if(password !== confirmPassword){
alert("Password and confirm password do not match")
}else{
dispatch(register(name, email, password));
}
};
useEffect(() =>{
if(userInfo){
props.history.push(redirect);
}
}, [userInfo, redirect, props.history]);
return (
<div className="container mt-5">
<h1>Register</h1>
{
loading && <h2>Loading</h2>
}
{ error && <h1>{error}</h1>}
<div className="row">
<div className="col-sm-8">
<div className="card">
<div className="card-body">
<form onSubmit={submitHandler}>
<div className="form-group">
<label htmlFor="email">Name</label>
<input
type="text"
id="name"
className="form-control"
placeholder="Enter name"
required
onChange={(e) => setName(e.target.value)}
></input>
</div>
<div className="form-group">
<label htmlFor="email">Email</label>
<input
type="email"
id="email"
className="form-control"
placeholder="Enter email"
required
onChange={(e) => setEmail(e.target.value)}
></input>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
placeholder="Enter password"
className="form-control"
required
onChange={(e) => setPassword(e.target.value)}
></input>
</div>
<div className="form-group">
<label htmlFor="confirmPassword">Confirm Password</label>
<input
type="password"
id="confirmPassword"
placeholder="Enter password"
className="form-control"
required
onChange={(e) => setConfirmPassword(e.target.value)}
></input>
</div>
<button type="submit" className="btn btn-dark">Register</button>
<div>
Already have an account? {' '}
<Link to={`/login?redirect=${redirect}`}>Log into your account</Link>
</div>
</form>
</div>
</div>
</div>
<div className="col-sm-4">
<div className="card">
<div className="card-body">
<a className="btn btn-block btn-social btn-google" href="/auth/google" role="button">
<i className="fab fa-google"></i>
Sign In with Google
</a>
</div>
</div>
</div>
</div>
</div>)
}
remove the password
field from the model. Passport takes care of that