I am having an issue with my React Redux and Node app. I am trying to Log in an existing user but in my console i am getting the following error:
Uncaught (in promise) TypeError: error.response.data is undefined
The console points to the following block of code:
export const login = (email, password) => async (dispatch) => {
const body = { email, password };
try {
const res = await axios.post('http://localhost:5000/api/auth', body);
dispatch({
type: LOGIN_SUCCESS,
payload: res.data
});
dispatch(loadUSer());
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, 'danger')));
}
dispatch({
type: LOGIN_FAIL
});
}
};
This is my server side for user auth auth.js:
const express = require('express');
const router = express.Router();
const User = require('../../models/User');
const auth = require('../../middleware/auth');
const config = require('config');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const { check , validationResult } = require('express-validator/');
//#route GET api/auth
//#desc Test route
//#access public
router.get('/',auth, async(req, res)=> {
try{
const user = await User.findById(req.user.id).select('-password');
res.json(user);
}catch(err){
console.error(err.message);
res.status(500).send('Server Error');
}
});
//#route POST api/auth
//#desc Authenticate user & get token
//#access public
router.post('/', [
check('email', 'Plese include a valid email').isEmail(),
check('password', 'Password is required').exists()
],
async (req, res)=> {
const errors = validationResult(req);
if(!errors.isEmpty()){
return res.status(400).json({ errors:errors.array()}); //400 is for bad requests
}
const { email, password } = req.body;
try{
//See if user exists
let user = await User.findOne({ email });
if(!user){
return res.status(400).json({ errors: [{ msg:'Invalid credentials' }] });
}
//Compare the input password, plane text, to the encrypted password.
const isMatch = await bcrypt.compare(password, user.password);
if(!isMatch){
return res.status(400).json({ errors: [{ msg:'Invalid credentials' }] });
}
//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;
And this is my Login component:
import React, { Fragment, useState } from 'react'
/* import axios, { Axios } from 'axios'; */
import { Link, Navigate } from 'react-router-dom'
import { connect } from 'react-redux';
import { PropTypes } from 'prop-types';
import { login } from '../../actions/auth';
const Login = ({ login, isAuthenticated }) => {
const [formData, setFormData] = useState({
email: '',
password: ''
});
const { email, password } = formData;
const onChange = e => setFormData({
...formData, [e.target.name]:e.target.value
});
const onSubmit = async e => {
e.preventDefault();
login(email, password);
}
//Redirect if logged in
if(isAuthenticated){
return <Navigate to="/dashboard"/>;
}
return (
<Fragment>
<section className='container'>
<h1 className="large text-primary">Sign Up</h1>
<p className="lead"><i className="fas fa-user"></i> Sign Into Your Account</p>
<form className="form" action="create-profile.html" onSubmit={e => onSubmit(e)}>
<div className="form-group">
<input type="email"
placeholder="Email Address"
name="email" value={email}
onChange={e => onChange(e)}
required/>
</div>
<div className="form-group">
<input
type="password"
placeholder="Password"
name="password"
minLength="6"
value={password}
onChange={e => onChange(e)}
required
/>
</div>
<input type="submit" className="btn btn-primary" value="Login" />
</form>
<p className="my-1">
Don´t have an account? <Link to="/register">Sign up</Link>
</p>
</section>
</Fragment>
)
}
Login.propTypes = {
login: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool
}
const mappedStateToProps = state => ({
isAuthenticated: state.auth.isAuthenticated
})
export default connect(mappedStateToProps , { login })(Login)
For some reason there are sometimes i am able to login and sometimes i encounter this issue but i cannot figure out what am i doing wrong.
My redux devtools also show the AUTH_ERROR action type:
This is my auth.js in my actions directory.
export const loadUSer = () => async dispatch => {
if(localStorage.token){
setAuthToken(localStorage.token);
}
try {
const res = await axios.get('http://localhost:5000/api/auth');
dispatch({
type: USER_LOADED,
payload: res.data
})
} catch (error) {
dispatch({
type:AUTH_ERROR
})
}
}
auth.js (reducers directory):
import{
REGISTER_FAIL,
REGISTER_SUCCESS,
USER_LOADED,
AUTH_ERROR,
LOGIN_SUCCESS,
LOGIN_FAIL,
LOGOUT
} from '../actions/types'
const initialState = {
token: localStorage.getItem('token'),
isAuthenticated: null,
loading: true,
user: null
}
function authReducer(state = initialState, action){
const { type, payload } = action
switch (type) {
case USER_LOADED:
return {
...state,
isAuthenticated:true,
loading: false,
user:payload
}
case LOGIN_SUCCESS:
case REGISTER_SUCCESS:
localStorage.setItem('token', payload.token);
return {
...state,
...payload,
isAuthenticated: true,
loading: false
}
case LOGOUT:
case LOGIN_FAIL:
case REGISTER_FAIL:
case AUTH_ERROR:
localStorage.removeItem('token');
return {
...state,
toke: null,
isAuthenticated: false,
loading: false
}
default:
return state;
}
}
export default authReducer
So i first log in with an user everything works fine, i logout and log in with a different user and also works, i now logout and want to login to the first user again and the error shows up. The only difference between both users is that one has a profile and the other one doesn´t.
When i try to log in with the user with no profile my app crashes and my vscode terminal shows a different errors:
Can't set headers after they are sent to the client
Related
I have made sure the backend works pretty fine with postman but when I try running the code on the client side, it fails with response: Failed to load resource: the server responded with a status of 400 (Bad Request)
Below are the codes controlling the functionality of the user signup and verification
/backend/routes/userRoutes.js
userRouter.post(
'/signup',
expressAsyncHandler(async (req, res) => {
try {
const newUser = new User({
name: req.body.name,
email: req.body.email,
password: bcrypt.hashSync(req.body.password),
});
const user = await newUser.save();
const verifyToken = await new VerificationToken({
userId: user._id,
token: crypto.randomBytes(32).toString('hex'),
}).save();
const url = `${process.env.BASE_URL}/${verifyToken._id}/verify/${verifyToken.token}`;
await mailTransport(user.email, 'Verify Email', url);
res.status(201).send({
message:
'A verification link has been sent to your account, please verify ',
});
} catch (error) {
res.status(500).send({ message: 'Internal Server Error' });
}
})
);
userRouter.get('/:id/verify/:token', async (req, res) => {
try {
const user = await User.findOne({ _id: req.params.id });
if (!user) return res.status(400).send({ message: 'Invalid Link' });
const verificationToken = await VerificationToken.findOne({
userId: user._id,
token: req.params.token,
});
if (!verificationToken)
return res.status(400).send({ message: 'Invalid link' });
await User.updateOne({ verified: true });
res.status(200).send({
_id: user._id,
name: user.name,
email: user.email,
isAdmin: user.isAdmin,
verified: user.verified,
token: generateToken(user),
message: 'Email verified successfully',
});
await verificationToken.remove();
// res.status(200).send({ message: 'Email verified successfully' });
} catch (error) {
res.status(500).send({ message: 'Internal Server Error' });
console.log(error);
}
});
/frontend/src/screens/SignupScreen.js
import React, { useContext, useState, useEffect } from 'react';
import Container from 'react-bootstrap/Container';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import { Helmet } from 'react-helmet-async';
import { Link, useLocation, useNavigate } from 'react-router-dom';
import axios from 'axios';
import { Store } from '../Store';
import { toast } from 'react-toastify';
import { getError} from '../utils';
export default function SignupScreen() {
const navigate = useNavigate();
const { search } = useLocation();
const redirectInUrl = new URLSearchParams(search).get('redirect');
const redirect = redirectInUrl ? redirectInUrl : '/signup';
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [error, setError] = useState('');
const [msg, setMsg] = useState('');
const { state } = useContext(Store);
const { userInfo } = state;
const submitHandler = async (e) => {
e.preventDefault();
if (password !== confirmPassword) {
toast.error('Passwords do not match');
return;
}
try {
const { data } = await axios.post(`/api/users/signup`, {
name,
email,
password,
confirmPassword,
});
setMsg(data.message);
setError(data.error);
} catch (err) {
toast.error(getError(err));
}
};
useEffect(() => {
if (userInfo) {
navigate(redirect);
}
}, [navigate, redirect, userInfo]);
return (
<Container className="small-container">
<Helmet>
<title>Sign Up</title>
</Helmet>
<h1 className="my-3">Sign Up</h1>
<Form onSubmit={submitHandler}>
<Form.Group className="mb-3" controlId="name">
<Form.Label>Name</Form.Label>
<Form.Control
type="name"
onChange={(e) => setName(e.target.value)}
required
/>
</Form.Group>
<Form.Group className="mb-3" controlId="email">
<Form.Label>Email</Form.Label>
<Form.Control
type="email"
onChange={(e) => setEmail(e.target.value)}
required
/>
</Form.Group>
<Form.Group
className="mb-3"
onChange={(e) => setPassword(e.target.value)}
controlId="password"
>
<Form.Label>Password</Form.Label>
<Form.Control type="password" required />
</Form.Group>
<Form.Group className="mb-3" controlId="confirmPassword">
<Form.Label>Confirm Password</Form.Label>
<Form.Control
type="password"
onChange={(e) => setConfirmPassword(e.target.value)}
required
/>
</Form.Group>
{msg && <div className="mb-3">{msg}</div>}
{error && <div className="mb-3">{error}</div>}
<div className="mb-3">
<Button type="submit">Sign Up</Button>
</div>
<div className="mb-3">
New customer? <Link to={`/signin?redirect=${redirect}`}>Sign In</Link>
</div>
</Form>
</Container>
);
}
/frontend/src/screens/VerifyEmailScreen.js
import React, { useContext, useState, useEffect } from 'react';
import Container from 'react-bootstrap/Container';
import Button from 'react-bootstrap/Button';
import { Helmet } from 'react-helmet-async';
import { Link, useLocation, useNavigate, useParams } from 'react-router-dom';
import axios from 'axios';
import { Store } from '../Store';
import { toast } from 'react-toastify';
import { getError, API_URL } from '../utils';
axios.defaults.withCredentials = true;
export default function VerifyEmailScreen() {
const navigate = useNavigate();
const { search } = useLocation();
const params = useParams();
const redirectInUrl = new URLSearchParams(search).get('redirect');
const redirect = redirectInUrl ? redirectInUrl : '/:id/verify/:token';
const [validUrl, setValidUrl] = useState('');
const { state, dispatch: ctxDispatch } = useContext(Store);
const { userInfo } = state;
const [msg, setMsg] = useState('');
useEffect(() => {
const verifyEmailUrl = async () => {
try {
const url = await axios.get(
`/api/users/${params.id}/verify/${params.token}`
);
console.log(url);
ctxDispatch({ type: 'USER_SIGNIN', payload: url });
localStorage.setItem('userInfo', JSON.stringify(url));
setMsg(url.message);
setValidUrl(true);
} catch (error) {
getError(error);
setValidUrl(false);
}
};
verifyEmailUrl();
});
return (
<Container className="small-container">
<Helmet>
<title>Sign Up</title>
</Helmet>
{validUrl ? (
<div className="d-flex justify-content-center flex-column text-center">
<h1 className="mb-5">Email Verified successfully</h1>
{msg && <div className="mb-3">{msg}. Please Login below</div>}
<Link to="/login">
<Button>Login</Button>
</Link>
</div>
) : (
<h1>404 Not Found</h1>
)}
</Container>
);
}
App.js //it is at this point I get a bad request response
<Route
path="/:id/verify/:token"
element={<VerifyEmailScreen />}
/>
backend server is running at port 5111 while frontend server is on port 3000 and I have set proxy in frontend package.json to port 5111. Please help pls
I solve this issue by replacing _id with userId in the url link that was being sent to the user email in the backend. I was already passing the user's id to the userId in the verifyToken function and then passing the verifyToken._id to the url link to sent.
Replace:
const verifyToken = await new VerificationToken({
userId: user._id,
token: crypto.randomBytes(32).toString('hex'),
}).save();
const url = `${process.env.BASE_URL}/${verifyToken._id}/verify/${verifyToken.token}`;
With:
const verifyToken = await new VerificationToken({
userId: user._id,
token: crypto.randomBytes(32).toString('hex'),
}).save();
const url = `${process.env.BASE_URL}/${verifyToken.userId}/verify/${verifyToken.token}`;
I solved the problem and should add an error message "Uncaught (in promise) SyntaxError: Unexpected end of JSON input"
When creating a new user, got the error 400
It was fine locally but doesn't work after deployed
**Note: The database in Heroku is different from Railway, there's no varchar in Railway, use text instead
I deployed react in Netlify and Express & Postgresql db in Railway
I doubted it was from the setUser part, but I have no clue what the problem is
Does it relate to useContext, cuz setUser is from there?
**Note: setUser is not the problem
// front-end sign up
import React, { useState, useContext } from "react";
import { useNavigate } from "react-router-dom";
import Button from "../Button";
import { Wrapper } from "./Signup.styles";
import { Context } from "../../context";
const Signup = () => {
const [username, setUsername] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState(false);
const [user, setUser] = useContext(Context);
const navigate = useNavigate();
const handleSubmit = () => {
if (!username || !password || !email) return setError(true);
try {
fetch("https://react-rmdb-backend-production.up.railway.app/signup", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
username: username,
email: email,
password: password
})
})
.then(res => res.json())
.then(user => {
if (user.id) {
setUser({
id: user.id,
username: user.username,
email: user.email
});
navigate("/");
}
});
} catch (error) {
setError(true);
}
};
const handleInput = e => {
const name = e.target.name;
const value = e.target.value;
if (name === "username") setUsername(value);
if (name === "password") setPassword(value);
if (name === "email") setEmail(value);
};
return (
<Wrapper>
{error && <div className="error">Oops! Something went wrong!</div>}
<h2>Sign Up</h2>
<label htmlFor="username">Username:</label>
<input
type="text"
value={username}
name="username"
onChange={handleInput}
/>
<label htmlFor="email">Email:</label>
<input type="email" value={email} name="email" onChange={handleInput} />
<label htmlFor="password">Password:</label>
<input
type="password"
value={password}
name="password"
onChange={handleInput}
/>
<Button text="Signup" callback={handleSubmit} />
</Wrapper>
);
};
export default Signup;
// back-end sign up
const handleSignup = (req, res, db, bcrypt) => {
const { username, email, password } = req.body;
if (!email || !username || !password) {
return res.status(400).json("Something went wrong");
}
const saltRounds = 10;
const hash = bcrypt.hashSync(password, saltRounds);
db.transaction((trx) => {
trx
.insert({
hash: hash,
email: email,
})
.into("login")
.returning("email")
.then((loginEmail) => {
return trx("users")
.returning("*")
.insert({
username: username,
email: loginEmail[0].email,
})
.then((user) => {
// should use res.json(user[0])
res.send(user[0]);
});
})
.then(trx.commit)
.catch(trx.rollback);
}).catch((err) => res.status(400).json("unable to sign up"));
};
module.exports = {
handleSignup,
};
Any advice would be appreciated.
This webpage is in a perpetual state of loading and I don't know why. It's almost as if it's awaiting the data. I tested the backend and the user exists and should be being sent but it's just waiting. I also tried isolating the the code so that it's just the bare minimum needed and there's still the perpetual loading. The This is what it looks like: Edit user Error Below is the code:
UserEditScreens.js
import axios from "axios";
import React, { useContext, useEffect, useReducer, useState } from "react";
import Container from "react-bootstrap/Container";
import Button from "react-bootstrap/Button";
import Form from "react-bootstrap/Form";
import { Helmet } from "react-helmet-async";
import { useNavigate, useParams } from "react-router-dom";
import { toast } from "react-toastify";
import LoadingBox from "../components/LoadingBox";
import MessageBox from "../components/MessageBox";
import { getError } from "../utils";
import { Store } from "./Store";
const reducer = (state, action) => {
switch (action.value) {
case "FETCH_REQUEST":
return { ...state, loading: true };
case "FETCH_SUCCESS":
return { ...state, loading: false };
case "FETCH_FAIL":
return { ...state, loading: false, error: action.payload };
case "UPDATE_REQUEST":
return { ...state, loadingUpdate: true };
case "UPDATE_SUCCESS":
return { ...state, loadingUpdate: false };
case "UPDATE_FAIL":
return { ...state, loadingUpdate: false };
default:
return state;
}
};
export default function UserEditScreen() {
const [{ loading, error, loadingUpdate }, dispatch] = useReducer(reducer, {
loading: true,
error: "",
});
const { state } = useContext(Store);
const { userInfo } = state;
const params = useParams();
const { id: userId } = params;
const navigate = useNavigate();
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [email, setEmail] = useState("");
const [isAdmin, setIsAdmin] = useState(false);
useEffect(() => {
const fetchData = async () => {
try {
dispatch({ type: "FETCH_REQUEST" });
const { data } = await axios.get(`/api/users/${userId}`, {
headers: { authorization: `Bearer ${userInfo.token}` },
});
dispatch({ type: "FETCH_SUCCESS", payload: data });
} catch (err) {
dispatch({ type: "FETCH_FAIL", payload: getError(err) });
}
};
fetchData();
}, [userInfo, userId]);
const submitHandler = async (e) => {
e.preventDefault();
try {
dispatch({ type: "UPDATE_REQUEST" });
await axios.put(
`/api/users/${userId}`,
{ _id: userId, firstName, lastName, email, isAdmin },
{ headers: { Authorization: `Bearer ${userInfo.token}` } }
);
dispatch({ type: "UPDATE_SUCCESS" });
toast.success("User updated successfully");
navigate("/admin/users");
} catch (error) {
toast.error(getError(error));
dispatch({ type: "UPDATE_FAIL" });
}
};
return (
<Container>
<Helmet>Edit User #{userId}</Helmet>
<h1>Edit User #{userId}</h1>
{loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
<Form onSubmit={submitHandler}>
<Form.Group className="mb-3" controlId="firstName">
<Form.Label>First Name</Form.Label>
<Form.Control
value={firstName}
type="name"
onChange={(e) => setFirstName(e.target.value)}
required
></Form.Control>
</Form.Group>
<Form.Group>
<Form.Label>Last Name</Form.Label>
<Form.Control
value={lastName}
type="name"
onChange={(e) => setLastName(e.target.value)}
required
></Form.Control>
</Form.Group>
<Form.Group className="mb-3" controlId="lastName">
<Form.Label>Email</Form.Label>
<Form.Control
value={email}
type="email"
onChange={(e) => setEmail(e.target.value)}
required
></Form.Control>
</Form.Group>
<Form.Check
className="mb-3"
type="checkbox"
id="isAdmin"
label="isAdmin"
checked={isAdmin}
onChange={(e) => setIsAdmin(e.target.checked)}
></Form.Check>
<div>
<Button disabled={loadingUpdate} type="submit">
Update
</Button>
{loadingUpdate && <LoadingBox></LoadingBox>}
</div>
</Form>
)}
</Container>
);
}
UserRoutes.js
import express from "express";
import User from "../models/userModel.js";
import bcrypt from "bcryptjs";
import { generateToken, isAuth } from "../utils.js";
import expressAsyncHandler from "express-async-handler";
const userRouter = express.Router();
userRouter.get(
"/",
isAuth,
expressAsyncHandler(async (req, res) => {
const users = await User.find();
res.send(users);
})
);
userRouter.get(
"/:id",
isAuth,
expressAsyncHandler(async (req, res) => {
const user = await User.findById(req.params.id);
if (user) {
console.log(user);
res.send(user);
} else {
res.status(404).send({ message: "User Not Found" });
}
})
);
userRouter.put(
"/:id",
isAuth,
expressAsyncHandler(async (req, res) => {
if (user) {
const user = await User.findById(req.params.id);
user.firstName = req.body.firstName || user.firstName;
user.lastName = req.body.lastName || user.lastName;
user.email = req.body.email || user.email;
user.isAdmin = Boolean(req.body.isAdmin);
const updatedUser = await user.save();
res.send({ message: "User Updated", user: updatedUser });
} else {
res.status(404).send({ message: "User Not Found" });
}
})
);
userRouter.post(
"/signin",
expressAsyncHandler(async (req, res) => {
const user = await User.findOne({ email: req.body.email });
if (user) {
if (bcrypt.compareSync(req.body.password, user.password)) {
res.send({
_id: user._id,
name: user.firstName,
email: user.email,
token: generateToken(user),
});
return;
}
}
res.status(401).send({ message: "Invalid email or password" });
})
);
userRouter.post(
"/signup",
expressAsyncHandler(async (req, res) => {
const newUser = new User({
firstName: req.body.firstName,
lastName: req.body.firstName,
email: req.body.email,
password: bcrypt.hashSync(req.body.password),
});
const user = await newUser.save();
res.send({
_id: user._id,
firstName: user.firstName,
email: user.email,
isAdmin: user.isAdmin,
token: generateToken(user),
});
})
);
userRouter.put(
"/profile",
isAuth,
expressAsyncHandler(async (req, res) => {
const user = await User.findById(req.user._id);
if (user) {
user.name = req.body.name || user.name;
user.email = req.body.email || user.email;
if (req.body.password) {
user.password = bcrypt.hashSync(req.body.password, 8);
}
const updatedUser = await user.save();
res.send({
_id: updatedUser._id,
name: updatedUser.name,
email: updatedUser.email,
isAdmin: updatedUser.isAdmin,
token: generateToken(updatedUser),
});
} else {
res.status(404).send({ message: "User not found" });
}
})
);
export default userRouter;
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.