Cannot read property of "undefined": POST request from React to Express - reactjs

I have this controller to authenticate a user by their email and password
const authUser = asyncHandler(async (req, res, next) => {
console.log('Hit');
const { email, password } = req.body;
await User.findOne({ email }, async (err1, foundUser) => {
if (err1) {
next(err1);
} else if (foundUser && (await foundUser.matchPasswords(password))) {
res.json({
_id: foundUser._id,
name: foundUser.name,
email: foundUser.email,
token: generateWebToken(foundUser._id),
});
} else {
res.status(401);
next(new Error('Invalid credentials'));
}
});
});
where the mongoose instance method matchPasswords is defined as
userSchema.methods.matchPasswords = async function (enteredPassword) {
return await bcrypt.compare(
enteredPassword,
this.password,
(err, success) => {
console.log(success);
return success;
}
);
};
In the React frontend I have created an action creator loginUser like so
export const loginUser = ({ email, password }) => async (dispatch) => {
try {
dispatch({ type: USER_LOGIN_REQUEST });
const config = {
headers: {
'Content-Type': 'application/json',
},
};
const { data } = await axios.post(
'/api/v1/users/login',
{ email, password },
config
);
dispatch({
type: USER_LOGIN_SUCCESS,
payload: data,
});
} catch (error) {
dispatch({
type: USER_LOGIN_FAIL,
payload:
error.response && error.response.data.message
? error.response.message.data
: error.message,
});
}
};
However, when I try to login with the wrong credentials or the correct ones I receive this message in the screen and console Uncaught (in promise) TypeError: Cannot read property 'data' of undefined. Above that there is this error main.js?attr=bsXXNSP9r2dL_fbuBOkoev2GjgusyPgY7MC7K-twziLtf_MItBzQdXJ4l_HgsPQw:2699 POST http://localhost:3000/api/v1/users/login 401 (Unauthorized) which made me think maybe it's proxy error because my server is running on port 5000, but the console.log('Hit'); inside the authUser controller is fired when I make the request, which means the request reaches the backend, but there is some uncaught error. Where is my mistake?

Related

Token undefined in put and patch request. MERN stack application

Working with a mern application. I am passing token in authorization header. The issue is whenever I use put or patch method from frontend, token is undefined. Get, Post, Delete requests are working fine. Api is working fine with postman too.
frontend action ->
export const approveClient = (id) => async (dispatch) => {
try {
const config = {
headers: {
Authorization: `${localStorage.getItem("token")}`,
},
};
dispatch({ type: adminConstants.APPROVECLIENT_REQUEST });
const res = await axios.put(`/admin/approveClient/${id}`, config);
dispatch({
type: adminConstants.APPROVECLIENT_SUCCESS,
payload: res.data,
});
} catch (error) {
dispatch({
type: adminConstants.APPROVECLIENT_FAIL,
payload: error.response.data,
});
}
};
Backend middleware function ->
const isAuthenticated = async (req, res, next) => {
try {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ success: false, message: "Not logged in" });
}
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const user = await User.findById(decoded._id);
const client = await Client.findById(decoded._id);
const admin = await Admin.findById(decoded._id);
if (user) {
req.user = user;
}
if (client) {
req.user = client;
}
if (admin) {
req.user = admin;
}
next();
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};

Error Cannot set headers after they are sent to the client

I got this error whenever I try to log In using Google Login API
In my console I get this error:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent
to the client
and in the screen I get white page with this error:
InternalOAuthError: Failed to fetch user profile
I'm using two Login method, one is normal and one using Passport JS
Login.jsx Login Page
import { useEffect, useContext, useRef } from "react";
import { Context } from "../../context/Context";
import axios from "axios";
import { useState } from "react"
export default function Login() {
const userRef = useRef();
const passwordRef = useRef();
const { dispatch, isFetching } = useContext(Context);
const [error, setError] = useState(false);
// FOR LOGIN
const handleSubmit = async (e) => {
e.preventDefault();
dispatch({ type: "LOGIN_START" });
try {
const res = await axios.post("/login", {
email: userRef.current.value,
password: passwordRef.current.value,
});
dispatch({ type: "LOGIN_SUCCESS", payload: res.data });
} catch (err) {
dispatch({ type: "LOGIN_FAILURE" });
setError(true)
}
};
// FOR GOOGLE LOGIN
useEffect(() => {
fetch(`http://localhost:4000/login/success`, {
method: 'GET',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'Access-Control-Allow-Credentials': true,
},
})
.then((response) => {
dispatch({ type: "LOGIN_START" });
if (response.status === 200) return response.json();
throw new Error('failed to authenticate user');
})
.then((responseJson) => {
dispatch({ type: "LOGIN_SUCCESS", payload: responseJson.data });
})
.catch((error) => {
dispatch({ type: "LOGIN_FAILURE" });
// eslint-disable-next-line no-console
console.error("Failed to authenticate user", error)
});
}, []);
const google = () => {
window.open("http://localhost:4000/auth/google/callback", "_self");
};
return()
}
auth.js Route:
router.get("/login/success", (req, res) => {
if (req.user) {
res.status(200).json({
error: false,
message: "succesfull",
user: req.user,
cookies: req.cookies
});
} else {
res.status(403).json({ error: true, message: "Not Authorized" });
}
});

How to send error messages from express to react/redux

I have a MERN app using redux. My actions look like this:
export const logIn = (logInData) => async (dispatch) => {
try {
const { data } = await api.logIn(logInData);
localStorage.setItem('auth', JSON.stringify(data))
dispatch({
type: LOG_IN,
payload: data
});
} catch (error) {
dispatch({
type: ADD_FLASH_MESSAGE,
payload: error
})
}
}
And my server looks like this
export const logIn = async (req, res) => {
const logInParams = req.body;
const user = await User.findOne({ email: logInParams.email });
if (!user) {
console.log("USER NOT FOUND");
res.status(400).json({
message: "Invalid credentials."
});
}
const passwordMatches = await bcrypt.compare(logInParams.password, user.password);
if (!passwordMatches) {
console.log("WRONG PASSWORD")
return res.status(400).json({
message: "Invalid credentials."
})
}
// Sign in user with jwt
const payload = {
user: {
id: user.id
}
}
jwt.sign(payload, config.get('jwtSecret'), (error, token) => {
if (error) throw error;
console.log('Successfully logged in');
return res.status(200).json({
token: token,
user: user,
loggedIn: true
});
})
}
I'm not able to access my error messages in my actions. I just get error messages like so
POST http://localhost:5000/auth/login 400 (Bad Request)
And my console.log looks like this:
Error: Request failed with status code 400
at createError (createError.js:16)
at settle (settle.js:17)
at XMLHttpRequest.handleLoad (xhr.js:62)
How do I access my custom error messages from my server?
try to get the status as error.status and custom message as error.data.message
your custom error message contain in error.response.data
try change this in your action:
export const logIn = (logInData) => async (dispatch) => {
try {
const { data } = await api.logIn(logInData);
localStorage.setItem('auth', JSON.stringify(data))
dispatch({
type: LOG_IN,
payload: data
});
}
catch (error) {
dispatch({
type: ADD_FLASH_MESSAGE,
payload: **error.response.data**
})
}
}

Returning response from Dispatch

I'm implementing login functionality to my application and am trying to return the response from a dispatched thunk action. I'm purposely entering the incorrect password because i'd like for the errors that are set in my backend to display within an antd notification on the frontend. My expected response should be:
return res.status(400).json({
success: false,
message: 'Invalid email address or password.',
});
but instead i'm getting the following in the console:
Error: Request failed with status code 400
Route:
const loginUser = async (req, res) => {
// Validate Login Input
const { error } = validateLoginInput(req.body);
if (error)
return res
.status(400)
.json({ success: false, message: error.details[0].message });
req.body.email = req.body.email.toLowerCase();
req.body = sanitize(req.body);
const { email, password } = req.body;
try {
// See if user exists
let user = await User.findOne({ email });
if (!user) {
return res.status(400).json({
success: false,
message: 'Invalid email address or password.',
});
}
// Compare passwords
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(400).json({
success: false,
message: 'Invalid email address or password.',
});
}
// Return jsonwebtoken
const payload = {
user: {
id: user.id,
},
};
jwt.sign(
payload,
process.env.JWT_SECRET,
{ expiresIn: 3600 },
(error, token) => {
if (error) throw error;
res.json({ token });
}
);
} catch (error) {
res.status(500).json({ success: false, messsage: 'Server error' });
}
};
Actions:
export const loginBegin = () => ({
type: LOGIN_BEGIN,
});
export const loginSuccess = (user) => ({
type: LOGIN_SUCCESS,
payload: user,
});
export const loginFail = (error) => ({
type: LOGIN_FAIL,
payload: error,
});
Thunk:
export const attemptLogin = (formData) => async (dispatch) => {
dispatch(loginBegin());
return await postLogin(formData)
.then((res) => {
dispatch(loginSuccess(res.data));
console.log(res.data); <-- This doesn't even show in console
})
// .then(() => {
// setTimeout(() => {
// dispatch(push('/app'));
// }, 2000);
// })
.catch((error) => {
dispatch(loginFail(error));
});
};
onSubmit:
const onSubmit = async (values) => {
const { email, password } = values;
setLoading(true);
try {
const response = await dispatch(attemptLogin({ email, password }));
console.log(response); <-- returns undefined
if (response.data.success) {
setLoading(false);
notification['success']({
message: 'Congrats!',
description: response.message,
});
} else {
notification['error']({
message: 'Uh-oh!',
description: response.message,
});
}
} catch (error) {
console.log(error);
}
};
The problem is that you are not returning the response in the promise. Also, if you are getting a 400 response, it probably rejects the promise or throws an error.
export const attemptLogin = (formData) => async (dispatch) => {
dispatch(loginBegin());
return await postLogin(formData)
.then((res) => {
dispatch(loginSuccess(res.data));
return res;
})
.catch((error) => {
dispatch(loginFail(error));
return error; // this will only work if the error is the response object.
});
};

How to Resolve 401 unAuthorized error even though Tokens and everything is set? and postman returns no issue at all while testing -MERN Stack

i am new to react and I've been trying to make this api work,(following you tube tutorial), when i test the routes via postman the response i receive is 'OK-working' but then when i try to login from my react app, it turns to unauthorized, i don't know how to deal with this *** i have done everything i could possibily upto my understanding ***, I've explored as much stack Overflow as i could as well as Googled it but still not found something understandable enough.
p.s this is very critical issue for me as this is part of my web Class at college and it holds a great deal of grades!
Attaching the link to github directory as well in case you need it :
https://github.com/nescafestar/twitter-clone
this is my AuthActions.js file
```
//authAction.js
import axios from 'axios';
import {GET_ERRORS, SET_CURRENT_USER} from '../constants';
import setAuthHeader from '../utils/setAuthHeader'
export const registerUser = (userData, history) => dispatch => {
axios.post('http://localhost:5000/api/users/register', userData,{
headers: {
'Authorization': 'Bearer'+ localStorage.getItem('jwtToken')
}
})
.then(res => {
console.log('----> registering')
const { token } = res.data
localStorage.setItem('jwtToken', token)
if(token){
axios.defaults.headers.common['Authorization']='Bearer : '+ token
}
history.push('/')})
.catch(err => dispatch({
type: GET_ERRORS,
payload: err.response.data
}))
}
export const loginUser = (userData) => dispatch => {
axios.post('http://localhost:5000/api/users/login',userData,{
headers: {
'Authorization': 'Bearer : ' +localStorage.getItem('jwtToken')
}
})
.then(res => {
// console.log(userData)
const { token } = res.data
localStorage.setItem('jwtToken', token)
if(token){
axios.defaults.headers.common['Authorization']='Bearer : ' +token
}
console.log('---> hit dispatch')
dispatch(getCurrentUser())
})
.catch(err => {
// console.log(err);
// dispatch({
// type: GET_ERRORS,
// payload: err.response.data
// })
})
}
export const getCurrentUser = () => dispatch => {
axios.get('http://localhost:5000/api/users',{
headers: {
'Authorization': 'Bearer : ' +localStorage.getItem('jwtToken')
}
})
.then(res => dispatch(setCurrentUser(res.data)))
.catch(err => dispatch({
type: GET_ERRORS,
payload: err.response.data
}))
}
export const setCurrentUser = (data) => {
console.log('----> setting user!')
return {
type: SET_CURRENT_USER,
payload: data
}
}
export const logoutUser=()=>dispatch=>{
localStorage.removeItem('jwtToken')
setAuthHeader()
dispatch(setCurrentUser())
}
```
this is the set Auth Header one:
```
import axios from 'axios'
// const jsonwebtoken=require('jsonwebtoken')
export default function(token){
console.log(token)
if(token){
console.log('Token has been set-properly')
return axios.defaults.headers.common['Authorization']='Bearer'+ token
}else{
return axios.defaults.headers.common['Authorization']=null
}
}
```
and lastly this is my POST.js file which is also returning the same issue of being unAuthorized
import axios from 'axios'
// const jsonwebtoken=require('jsonwebtoken')
export default function(token){
console.log(token)
if(token){
console.log('Token has been set-properly')
return axios.defaults.headers.common['Authorization']='Bearer'+ token
}else{
return axios.defaults.headers.common['Authorization']=null
}
}
``` ```
////////////////////////*** ROUTER ***
``` ```
// also the Routes/User.js file in case someone wants to know about it as well
const router = require("express").Router();
const User = require("../models/User");
const jwt=require('jsonwebtoken');
const passport = require("passport");
//validation
const validateRegisterInput = require("../validation/register");
const validateLoginInput = require("../validation/login");
//handling password hasing
const bcrypt = require("bcryptjs");
// const utils = require("../utils/utils");
//routing requests
//handling post request
router.route("/register").post((req, res) => {
const { isValid, errors } = validateRegisterInput(req.body);
if (!isValid) {
return res.status(404).json(errors);
}
//test arguments
// return res.send('OKKKK!!');
//register user is it does not exist in db
User.findOne({ email: req.body.email }).then((user) => {
if (user) {
errors.email = "Email already Registered!";
return res.status(404).json(errors);
}
bcrypt.genSalt(10, function (err, salt) {
bcrypt.hash(req.body.password, salt, function (err, hash) {
//adding new User to DB is Not Exist
const newMember = new User({
username: req.body.username,
email: req.body.email,
password: hash,
});
newMember
.save()
.then((newMember) => res.json(newMember))
.catch((err) => console.log(err));
});
});
});
}); //register route ends
//Login Route
router.route('/login')
.post((req, res) => {
const { errors, isValid } = validateLoginInput(req.body)
if (!isValid) {
return res.status(404).json(errors)
}
User.findOne({ email: req.body.email })
.then(user => {
if (!user) {
errors.email = 'User not found/not exist'
return res.status(404).json(errors)
} else {
bcrypt.compare(req.body.password, user.password)
.then(isMatch => {
if (isMatch) {
const token = jwt.sign({ id: user._id }, process.env.SECRET, { expiresIn: '1d' }, function (err, token) {
return res.json({
success: true,
token: token
})
})
} else {
errors.password = 'Password is incorrect'
return res.status(404).json(errors)
}
})
}
})
})
//returning route for logged in user
router.route('/')
.get( passport.authenticate('jwt', { session: false }),(req, res) => {
console.log('here! in home redirect')
res.json({
_id: req.user._id,
email: req.user.email,
username: req.user.username,
followers: req.user.followers,
following: req.user.following
})
})
module.exports = router;
You are not populating the Authorization header consistently or correctly. Assume your jwtToken value is AAAA for simplicity. In some cases you send BearerAAAAAA, in others you send Bearer : AAAA. The standard should be to use the string "Bearer", followed by one space, followed by the base64 encoding of the token (Bearer AAAA). See the RFC for more information: https://www.rfc-editor.org/rfc/rfc6750
When setting the header, you could use something like this:
const token = localStorage.getItem('jwtToken') //Or however you choose to get it
const headers = {
Authorization: `Bearer ${token}`
}

Resources