Token undefined in put and patch request. MERN stack application - reactjs

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 });
}
};

Related

Axios error 401 message pops up in app before trying refresh token in axios interceptor

AxiosConfig.js
import axios from "axios";
import { store } from "./redux/store";
import { login, logout } from "./redux/slices/user";
const baseURL = process.env.NEXT_PUBLIC_API_URL;
axios.defaults.baseURL = baseURL;
export const axiosInstance = axios.create({
withCredentials: true,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
accept: "application/json",
},
});
axiosInstance.interceptors.request.use(
(config) => {
return config;
},
(error) => {
return Promise.reject(error);
}
);
axiosInstance.interceptors.response.use(
(response) => {
return response;
},
async (error) => {
const originalRequest = error?.config;
if (error?.response?.status === 401 && !originalRequest.sent) {
originalRequest.sent = true;
const response = await axios.get("/auth/refresh", {
withCredentials: true,
});
if (response.error.status === 403) {
store.dispatch(logout());
}
store.dispatch(login({ user: response.data.user }));
return axiosInstance(originalRequest);
}
return Promise.reject(error);
}
);
VerifyJwt.js
export const verifyToken = (req, res, next) => {
try {
const accessToken = req.cookies.accessToken;
if (!accessToken)
return res.status(403).json({
message: "Not authenticated, no token provided",
});
jwt.verify(accessToken, process.env.ACCESS_SECRET_KEY, (err, decoded) => {
if (err)
return res.status(401).json({
message: "Not logged in, invalid token",
});
req.user = decoded.id;
next();
});
} catch (e) {
res
.status(500)
.json({ message: "Something went wrong while verifying jwt" });
}
};
routes/video.js
import express from "express";
import { unAuthVerify, verifyToken } from "../middlewares/verifyJwt.js";
import {
deleteVideo,
dislikeVideo,
getVideoDetails,
getVideos,
likeVideo,
updateVideo,
uploadVideo,
} from "../controllers/videos.js";
const router = express.Router();
router.get("/", getVideos);
router.post("/", verifyToken, uploadVideo);
router.get("/:id", unAuthVerify, getVideoDetails);
router.put("/:id", verifyToken, updateVideo);
router.delete("/:id", verifyToken, deleteVideo);
router.put("/like/:id", verifyToken, likeVideo);
router.put("/dislike/:id", verifyToken, dislikeVideo);
export default router;
RefreshToken.js
export const refreshToken = async (req, res) => {
const refreshToken = req.cookies.refreshToken;
try {
if (refreshToken) {
jwt.verify(
refreshToken,
process.env.REFRESH_SECRET_KEY,
async (err, decoded) => {
if (err) {
return res.status(403).json({
message: "Invalid token/not logged in",
});
}
const accessToken = jwt.sign(
{ id: decoded.id },
process.env.ACCESS_SECRET_KEY,
{
expiresIn: "1m",
}
);
const foundUser = await db.query(
"SELECT * FROM channels WHERE id = $1",
[decoded.id]
);
if (foundUser.rows.length <= 0) {
return res.status(400).json({
message: "User/Channel not found",
});
}
const { password, ...others } = foundUser.rows[0];
if (foundUser.rows.length > 0) {
res.cookie("accessToken", accessToken, {
httpOnly: true,
secure: true,
});
return res.status(200).json({
user: others,
});
}
}
);
} else {
return res.status(403).json({
message: "Invalid token/not logged in",
});
}
} catch (e) {
console.log(e);
res.status(500).json({
message: "Something went wrong.Please try again",
});
}
};
Video.jsx
const handleLike = async (id) => {
if (!isLoggedIn) {
toast.error("You need to login to like a video");
return;
}
if (likedByMe) {
toast.success("You have already liked this video");
return;
}
try {
await axiosInstance.put(`/videos/like/${id}`);
setLikes((prev) => prev + 1);
if (likedByMe === false) {
setDisLikes((prev) => prev - 1);
}
setLikedByMe(true);
} catch (e) {
console.log("Something went wrong while liking video", e);
toast.error(e.response.data.message); //I am getting this error first and the app is
crashing and only then axios interceptors call refresh token endpoint)
}
};
I am trying to refresh token in axios interceptor and get new access token. For example, if suppose I try to dislike or like any video and my access token expires at that time, I will get 401 message that token has expired, but I thought before getting this response in client side that is happening inside catch block, axios interceptor will call refresh token since it is 404 message and the client will get new access token and then the dislike or like api request will happen again. But in my app, I am getting that error message and it's crashing the app, shouldn't axios interceptor handle it silently? That 404 aunthorized message is getting catched by catch block in client side and errors pops up
I can notice that your route is named video.js but you're sending a PUT request to videos/ (plural). Is that a typo, or that's the cause of your issue?

Unable to verify user token stored in cookie in MERN app

I am trying to verify a logged In user token in my MERN. However, I am getting "undefined" on client-side as API response.
When I hit the endpoint http://127.0.0.1:1000/api/v1/users/login in postman to login a user and this endpoint http://127.0.0.1:1000/api/v1/users/verify to verify a user's token, the token is returned.
But when I do the same in my react app, the token returns undefined. I have spent hours debugging it but unable to figure it out.
Please I need help.
Here is my React (client side) code:
axios.defaults.withCredentials = true;
function Welcome() {
const [user, setUser] = useState("");
const sendRequest = async () => {
const res = await axios
.get("http://127.0.0.1:1000/api/v1/users/verify", {
withCredentials: true,
})
.catch((err) => console.log(err));
const data = await res.data;
return data;
};
React.useEffect(() => {
sendRequest().then((response) => console.log(response.data));
}, []);
return <div>Welcome</div>;
}
Here is my login endpoint.
exports.loginUser = async (req, res) => {
const { name, password } = req.body;
let existingUser;
try {
existingUser = await User.findOne({ name: name });
} catch (err) {
return new Error(err);
}
if (!existingUser) {
return res.status(400).json({ status: "fail", message: "user not found" });
}
const isPasswordCorrect = bcrypt.compareSync(password, existingUser.password);
if (!isPasswordCorrect) {
return res
.status(400)
.json({ status: "fail", message: "invalid email or password" });
}
const token = jwt.sign({ id: existingUser._id }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRES_IN,
});
res.cookie("jwt", token, {
path: "/",
expires: new Date(Date.now() + 1000 * 70),
httpOnly: true,
sameSite: "lax",
});
return res
.status(200)
.json({ message: "Succesfully loggedIn", user: existingUser, token });
};
Here is my verifyToken endpoint
exports.verifyToken = (req, res, next) => {
const cookies = req.headers.cookie;
const token = cookies.split("=")[1];
if (!token) {
res.status(404).json({ message: "no token found" });
}
jwt.verify(String(token), process.env.JWT_SECRET, (err, user) => {
if (err) {
return res.status(400).json({ message: "Invalid token" });
}
req.id = user.id;
});
next();
};
router.route("/verify").get(verifyToken, getUser);//endpoint
exports.getUser = async (req, res, next) => {
const userId = req.id;
let user;
try {
user = await User.findById(userId, "-password");
} catch (err) {
return new Error(err);
}
if (!user) {
return res.status(404).json({ status: "fail", message: "user not found" });
}
return res.status(200).json({ user });
};

Cookie is generated in API but not Cookies not being stored in the browser

Cookie is generated in API but not Cookies not being stored in the browser,
I fing some solution but it did not work for me . Can anybody answer my query .Please
This is Login route from API ROUTE
const express=require("express");
const router = express.Router();
const { body } = require('express-validator');
const { register ,login} = require("../controllers/auth");
router.post("/login",[
body('email',"Email is not valid").isEmail(),
body('password',"password must contain at least 5 characters"),
],login)
This is Controller of Login route FROM BACKEND and it is connected to login route
const cookie = require('cookie-parser')
const express = require("express");
const User = require("../models/users");
const { validationResult } = require('express-validator');
const bcryptjs = require("bcryptjs")
const jwt = require("jsonwebtoken")
const login = async (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { password, email } = req.body
try {
let user = await User.findOne({ email });
if (!user) {
success = false
return res.status(400).json({ error: "Please try to login with correct credentials" });
}
let comparePassword = await bcryptjs.compare(password, user.password)
if (!comparePassword) {
return res.status(400).json("Incorrect Login credentials")
}
let data = {
user: {
id: user.id
}
}
let token = await jwt.sign({ data, isAdmin: user.isAdmin }, process.env.SECRET_KEY)
res.cookie("access_token", token, {
expires: new Date(Date.now() + 2589000),
httpOnly: true
});
res.status(201).json({user,token});
} catch (error) {
res.status(500).json({ status: "server error", message: error.message })
}
}
This is the middlewear of and and its for authentication and autherization of user
const jwt = require("jsonwebtoken");
const verifyToken = (req, res, next) => {
const token = req.cookies.access_token;
if (!token) {
return res.status(403).json({
status: "user not authenticated",
});
}
try {
jwt.verify(token,process.env.SECRET_KEY, (err, user) => {
if (err)
return res.json({ status: "Invalid Token", message: err.message });
req.user = user;
next();
});
} catch (error) {
res
.status(500)
.send({ status: "Internal server error", message: error.message });
}
};

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

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?

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