So i have got a slight problem with the user authentication with JWT token. When i Login it returns to me "user doesnt exist", although there's clearly an user in the database. I've tried to fix by not looking for the password in the database and comparing it and it worked, but my question is how can i compare passwords in mongoose database and check if the user exists?
Login route:
//create route for sign in
router.post('/signin', async (req, res) => {
//destructure email and password from req.body
const user = await User.findOne({
email: req.body.email,
password: req.body.password,
})
if (!user) res.status(400).send({ message: 'User doesnt exist' })
else {
const valid = compare(req.body.password, user.password)
if (!valid) res.send(400).send({ message: 'Password doesnt match' })
if (valid) {
const accesstoken = createAccessToken(user._id)
const refreshtoken = createRefreshToken(user._id)
return res.send({
email: user.email,
refreshtoken: user.refreshtoken,
sendAccessToken: sendAccessToken(req, accesstoken),
sendRefreshToken: sendRefreshToken(refreshtoken),
})
}
}
})
user Model:
const mongoose = require('mongoose')
// const SALT_WORK_FACTOR = 10
// const bcrypt = require('bcryptjs')
//how a user will be stored in the mongodb schema
const userSchema = new mongoose.Schema({
name: { type: String, required: true, index: { unique: true } },
email: { type: String, required: true, unique: true, dropDups: true },
password: { type: String, required: true, minlength: 6, trim: true },
isAdmin: { type: Boolean, required: true, default: false },
refreshtoken: { type: String },
})
const userModel = mongoose.model('Users', userSchema)
module.exports = userModel
login page:
import React, { useState, useEffect, useContext } from 'react'
import { userContext } from '../../App'
// import { useDispatch, useSelector } from 'react-redux'
// import { login } from '../../actions/userActions'
function Login(props) {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [user, setUser] = useContext(userContext)
//acces the userLogin from redux store
// const userLogin = useSelector((state) => state.userLogin)
// const { loading, userInfo, error } = userLogin
// const dipsatch = useDispatch()
// useEffect(() => {
// if (userInfo) {
// props.history.push('/')
// }
// }, [userInfo])
//handlesubmit
const handleSubmit = async (e) => {
e.preventDefault()
const result = await (
await fetch('http://localhost:5000/users/signin', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email,
password,
}),
})
).json()
if (result.accesstoken) {
setUser({
accesstoken: result.accesstoken,
})
window.location = '/'
} else {
alert(result.error)
}
}
useEffect(() => {
console.log(user)
}, [user])
return (
<div>
<h1 className="login-h">Login</h1>
<form onSubmit={handleSubmit} className="login">
<div className="login-form">
<label>email</label>
<br />
<input
type="email"
placeholder="username or email"
for="email"
onChange={(e) => setEmail(e.target.value)}
></input>
<br />
<label>Password</label>
<br />
<input
type="password"
placeholder="Password"
for="password"
onChange={(e) => setPassword(e.target.value)}
></input>
<br />
<p className="forget-pw">
Forgot password?
</p>
<button>Login</button> <br />
<br />
{/* <p style={{ color: 'red' }}>{result.error}</p> */}
</div>
<p className="have-ccl">
{' '}
No account yet?Signup
</p>
</form>
</div>
)
}
export default Login
There are a few problems:
Remove the unnecessary password: req.body.password line from your mongoose query. This is basically comparing unhashed passwords with hashed passwords in a plain JavaScript object. This never returns true because the hash is always different from the unhashed password.
Is it possible that your body is never actually sent? You need to add the name attribute to inputs before they can be parsed. Are you using body-parser?
Related
I am devloping a webapp with nodejs and reactjs with redux. I am trying to make a request to register a new user but i am getting a bad request.
This is the error i am getting:
POST http://localhost:5000/api/users 400 bad request
This is my users route:
const express = require('express');
const config = require('config');
const router = express.Router();
const gravatar = require('gravatar');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const { check , validationResult } = require('express-validator/');
const User = require('../../models/User');
//#route POST api/users
//#desc Register user
//#access public
router.post('/', [
check('name', 'Name is required')
.not()
.isEmpty(),
check('email', 'Plese include a valid email').isEmail(),
check('password', 'Please enter a password with 6 or more characters').isLength({min:6})
],
async (req, res)=> {
const errors = validationResult(req);
if(!errors.isEmpty()){
return res.status(400).json({ errors:errors.array()}); //400 is for bad requests
}
const { name, email, password } = req.body;
try{
//See if user exists
let user = await User.findOne({ email });
if(user){
return res.status(400).json({ errors: [{ msg:'User already exists' }] });
}
//Get users gravatar
const avatar = gravatar.url(email,{
s:'200',
r:'pg',
d:'mm'
})
user = new User({
name,
email,
avatar,
password
});
//Encrypt password
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);
await user.save();
//Return jsonwebtoken -> this for users to be logged in right after registration
const payload = {
user:{
id: user.id
}
}
jwt.sign(
payload,
config.get('jwtSecret'),
{expiresIn: 360000}, //change to 3600 for production
(err, token)=>{
if(err) throw err;
res.json({ token });
}
)
}catch(err){
console.error('err.message');
res.status(500).send('Server Error');
}
});
module.exports = router;
The route is not the problem i think because if i register manually a user via postman it works.
This is my register.js component:
import React, { useState } from 'react';
import { connect } from 'react-redux';
import { Link, Navigate } from 'react-router-dom';
import { setAlert } from '../../actions/alert';
import { register } from '../../actions/auth';
import PropTypes from 'prop-types';
const Register = ({ setAlert, register }) => {
const [formData, setFormData] = useState({
name: '',
email: '',
password: '',
password2: ''
});
const { name, email, password, password2 } = formData;
const onChange = (e) =>
setFormData({ ...formData, [e.target.name]: e.target.value });
const onSubmit = async (e) => {
e.preventDefault();
if (password !== password2) {
setAlert('Passwords do not match', 'danger');
} else {
register({ name, email, password });
console.log('SUCCESS')
}
};
return (
<section className="container">
<h1 className="large text-primary">Sign Up</h1>
<p className="lead">
<i className="fas fa-user" /> Create Your Account
</p>
<form className="form" onSubmit={onSubmit}>
<div className="form-group">
<input
type="text"
placeholder="Name"
name="name"
value={name}
onChange={onChange}
/>
</div>
<div className="form-group">
<input
type="email"
placeholder="Email Address"
name="email"
value={email}
onChange={onChange}
/>
<small className="form-text">
This site uses Gravatar so if you want a profile image, use a
Gravatar email
</small>
</div>
<div className="form-group">
<input
type="password"
placeholder="Password"
name="password"
value={password}
onChange={onChange}
/>
</div>
<div className="form-group">
<input
type="password"
placeholder="Confirm Password"
name="password2"
value={password2}
onChange={onChange}
/>
</div>
<input type="submit" className="btn btn-primary" value="Register" />
</form>
<p className="my-1">
Already have an account? <Link to="/login">Sign In</Link>
</p>
</section>
);
};
Register.propTypes = {
setAlert: PropTypes.func.isRequired,
register: PropTypes.func.isRequired
};
export default connect( null, { setAlert , register })(Register);
and this is one of my actions auth.js where i request my api
import axios from "axios";
import { setAlert } from './alert'
import {
REGISTER_SUCCESS,
REGISTER_FAIL
} from './types'
//Register user
export const register = ( { name, email, password } ) => async dispatch => {
const config = {
headers: {
'Content-type': 'application/json'
}
}
const body = JSON.stringify({ name , email, password });
try {
const res = await axios.post('http://localhost:5000/api/users', body, config);
dispatch({
type: REGISTER_SUCCESS,
payload: res.data
});
} catch (error) {
const errors = error.response.data.errors;
if(errors){
errors.forEach(error => dispatch(setAlert(error.msg, 'danger')));
}
dispatch({
type:REGISTER_FAIL
});
}
}
I am pretty sure this might be an axios error on how i am trying to perform a request or a redux mistake.
My app works fine, if i donĀ“t enter or enter invalid data to my registration form all my validation alerts show up. The issue is when i submit the form and try to send the data to register the new user.
I am trying to store data from an option, but I'm not sure where I went wrong in the schema or the react code. Is there another I could perhaps do it?
When I run it I get this error.
POST http://localhost:8080/api/user 500 (Internal Server Error)
Here is my model
model.js
const mongoose = require('mongoose');
const jwt = require('jsonwebtoken');
const joi = require('joi');
const { optional } = require('joi');
const userSchema = new mongoose.Schema({
firstName:{type: String, required: true},
lastName:{type: String, required: true},
email:{type: String, required: true},
password:{type: String, required: true},
status:{type: String, enum: ['active', 'inactive'],required : true },
});
userSchema.methods.generateAuthToken = function(){
const token = jwt.sign({_id:this._id}, process.env.JWTPRIVATEKEY)
return token
}
const User = mongoose.model("user", userSchema);
const validate = (data) => {
const schema = joi.object({
firstName:joi.string().required().label("First Name"),
lastName:joi.string().required().label("Last Name"),
email: joi.string().email().required().label("Email"),
password: joi.string().required().label("password"),
status:joi.string().enum['active', 'inactive'].required().label(" ")
});
return schema.validate(data)
}
module.exports = {User, validate}
When I run it I get "error creating user"
routes user.js
const router =require("express").Router();
const {User, validate} = require('../models/user')
const bcrypt = require("bcrypt");
router.post("/", async(req, res) => {
try{
const {error} = validate(req.body)
if(error)
return res.status(400).send({message: error.details[0].message})
const user = await User.findOne({ email:req.body.email})
if (user)
return res.status(409).send({message: "duplicate email"})
const salt = await bcrypt.genSalt(Number(process.env.SALT))
const hashPassword = await bcrypt.hash(req.body.password, salt);
await new User({...req.body, password: hashPassword}).save();
res.status(201).send({message: "User created succesfully"})
}catch(error){
console.log(error.details)
res.status(500).send({message: "Error creating user"})
}
})
module.exports = router;
here are my react.js files, I believe my error should lie here but I maybe be mistaken.
index.jsx
import { useState, useEffect } from "react";
import axios from "axios";
import { Link, useNavigate } from "react-router-dom";
import styles from "./styles.module.css";
const Signup = () => {
const [data, setData] = useState({
firstName: "",
lastName: "",
email: "",
password: "",
status: "",
});
const [error, setError] = useState("");
const navigate = useNavigate();
const handleChange = ({ currentTarget: input }) => {
setData({ ...data, [input.name]: input.value });
};
const handleSubmit = async (e) => {
e.preventDefault();
try {
const url = "http://localhost:8080/api/user";
const { data: res } = await axios.post(url, data);
navigate("/login");
console.log(res.message);
} catch (error) {
if (
error.response &&
error.response.status >= 400 &&
error.response.status <= 500
) {
setError(error.response.data.message);
}
}
};
return (
<div className={styles.signup_container}>
<div className={styles.signup_form_container}>
<div className={styles.left}>
<h1>Welcome Back</h1>
<Link to="/login">
<button type="button" className={styles.white_btn}>
Sign in
</button>
</Link>
</div>
<div className={styles.right}>
<form className={styles.form_container} onSubmit={handleSubmit}>
<h1>Create Account</h1>
<input
type="text"
placeholder="First Name"
name="firstName"
onChange={handleChange}
value={data.firstName}
required
className={styles.input}
/>
<input
type="text"
placeholder="Last Name"
name="lastName"
onChange={handleChange}
value={data.lastName}
required
className={styles.input}
/>
<input
type="email"
placeholder="Email"
name="email"
onChange={handleChange}
value={data.email}
required
className={styles.input}
/>
<input
type="password"
placeholder="Password"
name="password"
onChange={handleChange}
value={data.password}
required
className={styles.input}
/>
<select name ="status">
<option value="1">active</option>
<option value="2">inactive</option>
</select>
{error && <div className={styles.error_msg}>{error}</div>}
<button type="submit" className={styles.green_btn}>
Sign Up
</button>
</form>
</div>
</div>
</div>
);
};
export default Signup;
I'm trying to upload a image using React for my client side, Node Js as my server and monogoDb for my database. I'm using multer to store the images and everytime I make a request to create an account I get a 500 error. How do I make a post request that includes a Image to a monogoDb database from my React frontend through a Node Js server ?
Frontend
import React, { useState } from "react";
import {useHistory } from "react-router-dom";
import FormData from 'form-data'
import axios from "axios";
const CreateAccount = () => {
const api = "http://localhost:5000/user/create-account";
const history = useHistory();
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [emailAddress, setEmailAddress] = useState("");
const [password, setPassword] = useState("");
const [gender, setGender] = useState("Male");
const [sexualPreference, setSexualPreference] = useState("Straight");
const [age, setAge] = useState("");
const [description, setDescription] = useState("");
const [picture, setPicture] = useState(null);
const [imgData, setImgData] = useState(null);
const account = {
firstName: firstName,
lastName: lastName,
emailAddress: emailAddress,
password: password,
gender: gender,
sexualPreference: sexualPreference,
age: age,
description: description,
pictures: picture
};
const onChangePicture = e => {
if (e.target.files[0]) {
console.log("picture: ", e.target.files);
setPicture(e.target.files[0]);
const reader = new FileReader();
reader.addEventListener("load", () => {
setImgData(reader.result);
});
reader.readAsDataURL(e.target.files[0]);
}
console.log(imgData);
console.log(picture);
};
const submit = () => {
let data = new FormData();
data.append('firstName', firstName);
data.append('lastName', lastName);
data.append('emailAddress', emailAddress);
data.append('password', password);
data.append('gender', gender);
data.append('sexualPreference',sexualPreference);
data.append('age', age);
data.append('description', description);
data.append('pictures',
{
uri: imgData,
name:`${picture}.jpg`,
type:'image/jpg'
});
axios.post(api, account, {
method: "post",
url: api,
data: account,
headers: { "Content-Type": "multipart/form-data" },
})
.then((res) => {
// handle success
console.log(res.data);
history.push({
pathname: "/",
});
})
.catch((error) => {
//handle error
console.log(account);
console.log(error);
});
}
const handleSubmit = (event) => {
event.preventDefault();
submit();
};
return (
<div>
<div>
<h1>Create account</h1>
</div>
<form onSubmit={handleSubmit} encType="multipart/form-data">
<p>First Name</p>
<input
id="firstName"
name="firstName"
type="firstName"
onChange={(e) => setFirstName(e.target.value)}
></input>
<p>Last Name</p>
<input
id="lastName"
name="lastName"
type="lastName"
onChange={(e) => setLastName(e.target.value)}
></input>
<p>Email Address</p>
<input
id="emailAddress"
name="emailAddress"
type="emailAddress"
onChange={(e) => setEmailAddress(e.target.value)}
></input>
<p>Password</p>
<input
id="password"
name="password"
type="password"
onChange={(e) => setPassword(e.target.value)}
></input>
<p>Gender</p>
<select
id="gender"
name="gender"
type="gender"
onChange={(e) => setGender(e.target.value)}
>
<option value="Male">Male</option>
<option value="Female">Female</option>
</select>
<p>Sexual Preference</p>
<select
id="sexualPreference"
name="sexualPreference"
type="sexualPreference"
onChange={(e) => setSexualPreference(e.target.value)}
>
<option value="Straight" >Straight</option>
<option value="Gay" >Gay</option>
<option value="Lesbian" >Lesbian</option>
<option value="Bisexual" >Bisexual</option>
</select>
<p>Age</p>
<input
id="age"
name="age"
type="age"
onChange={(e) => setAge(e.target.value)}
></input>
<p>Description</p>
<input
id="description"
name="description"
type="description"
onChange={(e) => setDescription(e.target.value)}
></input>
<input
type="file"
name="file"
id="picture"
onChange={onChangePicture}
></input>
<button type="submit">Submit</button>
</form>
</div>
);
};
export default CreateAccount;
Backend
const multer = require("multer");
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './uploads/');
},
filename: function (req, file, cb) {
cb(null, new Date().toISOString() + file.originalname);
}
})
const upload = multer({storage: storage})
router.post( "/user/create-account", upload.single("pictures"), [
check("firstName")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "firstName"'),
check("lastName")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "username"'),
check("emailAddress")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "emailAddress"'),
check("password")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "password"'),
check("gender")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "gender"'),
check("sexualPreference")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "sexualPreference"'),
check("age")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "age"'),
check("description")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "description"'),
check("pictures")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "pictures"'),
],
asyncHandler(async (req, res, next) => {
// Attempt to get the validation result from the Request object.
const errors = validationResult(req);
// If there are validation errors...
if (!errors.isEmpty()) {
// Use the Array `map()` method to get a list of error messages.
const errorMessages = errors.array().map((error) => error.msg);
// Return the validation errors to the client.
return res.status(400).json({ errors: errorMessages });
}
//new user request body using mongo model from schema
const postUser = new User({
firstName: req.body.firstName,
lastName: req.body.lastName,
emailAddress: req.body.emailAddress,
password: req.body.password,
gender: req.body.gender,
sexualPreference: req.body.sexualPreference,
age: req.body.age,
description: req.body.description,
pictures: req.file.path
});
console.log(req.file)
const userEmail = await User.findOne({
emailAddress: postUser.emailAddress,
});
if (postUser.emailAddress === userEmail) {
console.log("User with this email already exists");
return res.status(500).end();
} else if (postUser) {
//if true salts the password with bcryptjs
let salt = await bcryptjs.genSalt(10);
const hashPass = await bcryptjs.hash(postUser.password, salt);
console.log(hashPass);
postUser.password = hashPass;
postUser.save();
res.json({ postUser });
return res.status(201).end();
} else {
res.status(400).send({ error: "Error: Account not created" }).end();
}
})
);
schema
const mongoose = require('mongoose');
const userSchema = mongoose.Schema( {
firstName:{
type: String,
required: true
},
lastName: {
type: String,
require: true
},
emailAddress: {
type: String,
require: true
},
password:{
type: String,
required: true
},
gender:{
type: String,
required: true
},
sexualPreference: {
type: String,
required: true
},
age: {
type: Number,
required: true
},
description: {
type: String,
required: true
},
pictures: {
type: String,
required: true
},
matches: {
type: [{
Object
}],
},
})
module.exports = mongoose.model('User', userSchema);
I am making a MERN application with register and login functionality. When i try to submit the form in the browser the console shows me a 400 bad request. The functionality works on the backend and returns a 200 ok status but not on the front-end.
this is a picture of the error in the browser.
here is my code below
validation
const Validator = require("validator");
const isEmpty = require("is-empty");
module.exports = function validateRegisterInput(data) {
let errors = {};
data.name = !isEmpty(data.name) ? data.name : "";
data.email = !isEmpty(data.email) ? data.email: "";
data.password = !isEmpty(data.password) ? data.password: "";
data.password2 = !isEmpty(data.password2) ? data.password2: "";
if(Validator.isEmpty(data.name)) {
errors.name = "Name field is Required";
}
if(Validator.isEmpty(data.email)) {
errors.email = "Email field is Required";
} else if(!Validator.isEmail(data.email)) {
errors.email = "Email is invalid"
}
if (Validator.isEmpty(data.password)) {
errors.password = "Password field is required";
}
if (Validator.isEmpty(data.password2)) {
errors.password2 = "Confirm password field is required";
}
if(!Validator.isLength(data.password, {min: 6, max: 30})){
errors: password = "Password must be at least 6 characters";
}
if(!Validator.equals(data.password, data.password2)){
errors.password2 = "Password must match"
}
return {
errors,
isValid: isEmpty(errors)
};
};
controller
const express = require("express");
const router = express.Router();
const bcrypt = require("bcryptjs");
const jwt = require ("jsonwebtoken");
const keys = require("../../config/key");
const validateRegisterInput = require("../../validation/register");
const validateLoginInput = require("../../validation/login");
const User = require("../../models/User");
router.post("/register", (req, res) => {
//FORM VALIDATION
const {errors, isValid } = validateRegisterInput(req.body)
//CHECK VALIDATION
if(!isValid) {
return res.status(400).json(errors)
}
User.findOne({ email: req.body.email }).then( returnedUser => {
if(returnedUser) {
return res.status(400).json({email: "Email already exists"});
}
});
// saving user with request information to database
const newUser = new User({
name: req.body.name,
email: req.body.email,
password: req.body.password,
});
bcrypt.genSalt(10, (err, salt)=>{
bcrypt.hash(newUser.password, salt, (err, hash)=>{
if(err) throw err;
newUser.password = hash;
newUser
.save()
.then(user => res.json(user))
.catch(err => console.log(err));
});
});
});
router.post("/login", (req, res)=>{
const {errors, isValid} = validateLoginInput(req.body)
if(!isValid){
return res.status(400).json(errors)
}
const email = req.body.email;
const password = req.body.password
User.findOne({ email: email }).then(user =>{
if(!user){
return res.status(404).json({ emailNotFound: "Email not found"});
}
bcrypt.compare(password, user.password).then(isMatch => {
if(isMatch){
const payload = { id: user.id, name: user.name };
jwt.sign(payload,
keys.secretOrKey,
{expiresIn: 31556926},
(err, token) => {
res.json({ success: true, token: "Bearer " + token });
});
} else {
return res.status(400)
.json({passwordincorrect: "password incorrect"})
}
})
})
});
module.exports = router;
Action
import axios from "axios";
import setAuthToken from "../utils/setAuthToken";
import jwt_decode from "jwt-decode";
import {
GET_ERRORS,
SET_CURRENT_USER,
USER_LOADING
} from "./types";
// Register User
export const registerUser = (userData, history) => dispatch => {
axios.post("/api/users/register", userData)
.then(res => history.push("/login")) // re-direct to login on successful register
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
// Login - get user token
export const loginUser = userData => dispatch => {
axios
.post("/api/users/login", userData)
.then(res => {
// Save to localStorage
// Set token to localStorage
const { token } = res.data;
localStorage.setItem("jwtToken", token);
// Set token to Auth header
setAuthToken(token);
// Decode token to get user data
const decoded = jwt_decode(token);
// Set current user
dispatch(setCurrentUser(decoded));
})
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
// Set logged in user
export const setCurrentUser = decoded => {
return {
type: SET_CURRENT_USER,
payload: decoded
};
};
// User loading
export const setUserLoading = () => {
return {
type: USER_LOADING
};
};
// Log user out
export const logoutUser = () => dispatch => {
// Remove token from local storage
localStorage.removeItem("jwtToken");
// Remove auth header for future requests
setAuthToken(false);
// Set current user to empty object {} which will set isAuthenticated to false
dispatch(setCurrentUser({}));
};
component
import React, { Component } from "react";
import { Link, withRouter } from "react-router-dom";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { registerUser } from "../../actions/authActions";
import classnames from "classnames";
class Register extends Component {
constructor() {
super();
this.state = {
name: "",
email: "",
password: "",
password2: "",
errors: {},
};
}
componentDidMount() {
// If logged in and user navigates to Register page, should redirect them to dashboard
if (this.props.auth.isAuthenticated) {
this.props.history.push("/dashboard");
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.errors) {
this.setState({
errors: nextProps.errors,
});
}
}
onChange = (e) => {
this.setState({ [e.target.id]: e.target.value });
};
onSubmit = (e) => {
e.preventDefault();
const newUser = {
name: this.state.name,
email: this.state.email,
password: this.state.password,
password2: this.state.password2,
};
console.log(newUser);
this.props.registerUser(newUser, this.props.history);
};
render() {
const { errors } = this.state;
return (
<div className="container">
<div className="row">
<div className="col s8 offset-s2">
<Link to="/" className="btn-flat waves-effect">
<i className="material-icons left">keyboard_backspace</i> Back to
home
</Link>
<div className="col s12" style={{ paddingLeft: "11.250px" }}>
<h4>
<b>Register</b> below
</h4>
<p className="grey-text text-darken-1">
Already have an account? <Link to="/login">Log in</Link>
</p>
</div>
<form noValidate onSubmit={this.onSubmit}>
<div className="input-field col s12">
<input
onChange={this.onChange}
value={this.state.name}
error={errors.name}
id="name"
type="text"
className={classnames("", {
invalid: errors.name
})}
/>
<label htmlFor="name">Name</label>
<span className="red-text">{errors.name}</span>
</div>
<div className="input-field col s12">
<input
onChange={this.onChange}
value={this.state.email}
error={errors.email}
id="email"
type="email"
className={classnames("", {
invalid: errors.email
})}
/>
<label htmlFor="email">Email</label>
<span className="red-text">{errors.email}</span>
</div>
<div className="input-field col s12">
<input
onChange={this.onChange}
value={this.state.password}
error={errors.password}
id="password"
type="password"
className={classnames("", {
invalid: errors.password
})}
/>
<label htmlFor="password">Password</label>
<span className="red-text">{errors.password}</span>
</div>
<div className="input-field col s12">
<input
onChange={this.onChange}
value={this.state.password2}
error={errors.password2}
id="password2"
type="password"
className={classnames("", {
invalid: errors.password2
})}
/>
<label htmlFor="password2">Confirm Password</label>
<span className="red-text">{errors.password2}</span>
</div>
<div className="col s12" style={{ paddingLeft: "11.250px" }}>
<button
style={{
width: "150px",
borderRadius: "3px",
letterSpacing: "1.5px",
marginTop: "1rem",
}}
type="submit"
className="btn btn-large waves-effect waves-light hoverable blue accent-3"
>
Sign up
</button>
</div>
</form>
</div>
</div>
</div>
);
}
}
Register.propTypes = {
registerUser: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => ({
auth: state.auth,
errors: state.errors,
});
export default connect(mapStateToProps, { registerUser })(withRouter(Register));
Here is the root server
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const passport = require("passport");
const app = express();
const users = require("./controllers/api/users")
app.use(
bodyParser.urlencoded({
extended: false
})
);
//DATA BASE CONFIGURATION
const dbkeys = require("./config/key").mongoURI;
mongoose.connect(
dbkeys,
{useNewUrlParser: true} )
.then(()=> console.log("database connection successful"))
.catch(err => console.log(err))
app.use(passport.initialize());
require("./config/passport")(passport);
app.use("/api/users", users);
const port = 5000;
app.listen( port, () => console.log("server us up and running on port 5000!"))
Axios sends the data as JSON (application/json). You're currently only using the body parser for urlencoded, which refers to application/x-www-form-urlencoded which is the data format which <form>s use by default.
You need to also use bodyParser.json():
app.use(
bodyParser.urlencoded({
extended: false
})
);
app.use(bodyParser.json());
I am using react-redux, axios, and JWT to create a registration system to a simple web app I built. No matter what is entered, I get a "Please enter all fields error". I have programmed other errors, and even when all input is valid I get the same one. I do not see any compilation error either in dev tools or in my terminal. This is my first try at authentication/registration.
This is my registerModal:
class RegisterModal extends Component{
state = {
Modal: false,
name: '',
email: '',
password:'',
msg: null
}
static propTypes ={
isAuthenticated: PropTypes.bool,
error: PropTypes.object.isRequired,
register: PropTypes.func.isRequired,
clearErrors: PropTypes.func.isRequired
}
componentDidUpdate(prevProps){
const {error} = this.props;
if(error !== prevProps.error){
//reg error
if(error.id === 'REGISTER_FAIL'){
this.setState({msg: error.msg.msg})
}else{
this.setState({msg: null});
}
}
}
toggle = () =>{
this.props.clearErrors();
this.setState({
Modal: !this.state.Modal
});
}
onChange = (e)=>{
this.setState(
{[e.target.name]: e.target.value}
);
}
onSubmit = e =>{
e.preventDefault();
const{name, email, password} = this.state;
//create user object
const newUser = {
name,
email,
password
};
this.props.register(newUser);
}
render(){
return(
<div>
<NavLink onClick = {this.toggle} href ="#" color = "danger">
Register
</NavLink>
<Modal
isOpen={this.state.Modal}
toggle={this.toggle} >
<ModalHeader toggle={this.toggle}>Register</ModalHeader>
<ModalBody>
{this.state.msg ? <Alert color = "danger">{this.state. msg}</Alert>: null}
<Form onSubmit = {this.onSubmit}>
<FormGroup>
<Label for ="name">Name</Label>
<Input
type = "text"
name= "name"
id = "name"
placeholder="Name"
onChange = {this.onChange}>
</Input>
</FormGroup>
<FormGroup>
<Label for ="Email">Email </Label>
<Input
type = "email"
name= "email"
id = 'email'
placeholder="Enter email"
onChange = {this.onChange}>
</Input>
</FormGroup>
<FormGroup>
<Label for ="Password">Password</Label>
<Input
type = "password"
name= "password"
id = "password"
placeholder="Password"
onChange = {this.onChange}>
</Input>
</FormGroup>
<Button
color="success"
style={{marginTop:'2rem'}}
block>
Register</Button>
</Form>
</ModalBody>
</Modal>
</div>
);
}
}
const mapStateToProps = (state) => ({
isAuthenticated: state.auth.isAuthenticated,
error: state.error
});
export default connect(
mapStateToProps,
{register, clearErrors})(RegisterModal);
This is in my auth.js file in routes folder:
//POST api/auth
//#desc Auth user
router.post('/', (req, res)=> {
const { email, password } = req.body;
//validation
if(!email || !password){
return res.status(400).json({msg: 'Please enter all fields'});
}
//Check for existing user
User.findOne({email})
.then(user =>{
if(!user) return res.status(400).json({msg: 'User does not exists'})
//Validate password
bcrypt.compare(password, user.password)
.then(isMatch =>{
if(!isMatch) return res.status(400).json({ msg: 'Invalid credentials'});
jwt.sign(
{id: user.id},
config.get('jwtSecret'),
{expiresIn: 3600},
(err, token) => {
if(err) throw err;
res.json({
token,
user: {
id: user.id,
name: user.name,
email: user.email
}
});
}
)
})
});
});
authActions.js:
//check tokens and register user
export const loadUser = () => (dispatch, getState) => {
//user loading
dispatch({type: USER_LOADING});
axios.get('/api/auth/user', tokenConfig(getState))
.then(res => dispatch({
type: USER_LOADED,
payload: res.data
}))
.catch(err => {
dispatch(returnErrors(err.response.data, err.response.status));
dispatch({
type: AUTH_ERROR
});
});
}
export const register = ({name, email, password}) => dispatch =>{
//headers
const config = {
header: {
'Content-Type': 'application/json'
}
}
//request body
const body = JSON.stringify({name, email, password});
axios.post('/api/users', body, config)
.then(res => dispatch({
type: REGISTER_SUCCESS,
payload:res.data
}))
.catch(err=>{
dispatch(returnErrors(err.response.data, err.response.status, 'REGISTER_FAIL'));
dispatch({
type: REGISTER_FAIL
})
})
}
//setup config /header and token
export const tokenConfig = getState => {
//get token from local storage
const token = getState().auth.token;
//headers
const config = {
headers: {
"Content-type": "application/json"
}
}
//if token add to headers
if(token){
config.headers['x-auth-token'] = token;}
return config;
}
Please let me know if I should add anything.
Assuming your registration route handles the request correctly, changing "header" to "headers" in the config you are passing into the axios.post() of your register action should solve it.