How to store a cookie sent from Express server by using fetch API in React? - node.js

I'm trying to create signup Form using React, Express and MongoDB. I succeffuly implemented the post request and saved the user data in the database.
However,though the user is saved in the database, I failed to store ( see it the browser ) the jwt token using res.cookie('jwt',token).
I have a simple form made in React:
type Props = {
children: React.ReactNode;
};
export const SignupLayout = ({ children }: Props) => {
const user = {
email: 'alexy#gmail.com',
username: 'alexladies',
password: 'pasrfsfsdfgfdsd',
securityQuestion: "father's name",
securityAnswer: 'jhon',
joinedDate: '12-12-2023',
};
const handleSignup = async (event: React.SyntheticEvent) => {
event.preventDefault();
// The problem is here
await fetch('http://localhost:3000/signup', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(user),
})
.then((reponse) => reponse.json())
.then((json) => console.log(json))
.catch((err) => console.log(err));
};
return (
<form
onSubmit={handleSignup}
method='post'
action='/signup'
className='sm:px-20 md:w-2/3 md:px-12 lg:w-1/2 lg:px-4 lg:my-4 xl:mt-16 xl:w-1/3 xl:px-16 mx-auto bg-white rounded-2xl py-10 text-center '
>
{children}
</form>
);
};
My express server:
const User = require('../../models/user');
const { handleError } = require('./error');
const { createToken } = require('../utils/token');
const getSignup = (req, res) => {
// It stores the cookie in the browser succesfully
res.cookie('name', 'value');
res.send('ok');
};
const postSignup = async (req, res) => {
// It failed to store the cookie in the browser !
const {
email,
password,
username,
securityQuestion,
securityAnswer,
joinedDate,
} = req.body;
const user = new User({
email,
password,
username,
securityQuestion,
securityAnswer,
joinedDate,
});
await user
.save()
.then(() => res.cookie('jwt', createToken(user._id)))
.then(() => res.status(200).json(user._id))
.catch((err) => {
res.status(400).json(handleError(err));
});
};
module.exports = { getSignup, postSignup };
I have tried to add:
credentials:'include'
But it does not work.
Sreenshot

Related

(ReactJS) Getting error in Login Component, while Register Compo. works perfectly

I can't seem to get why my Login component is getting an error when my Registration component works well while using the same POST request to the backend server. The only thing that they differ is the method of retrieving data from MongoDB in their backend script partner, which is what I am thinking is the problem, but anything I do doesn't seem to work.
Edit > * The error in the Login Component is AxiosError: Network Error. Both the Login and Register backend have been tested in Postman and works well, and responds a status. So it seems that the problem is in the Login React Component's Axios post request. It send data to the backend okay, but it catches an error after that.*
The login script of the backend server is working well and validating the credentials perfectly. But then, React gets an error.
in Login React Component (AxiosError):
async postReq() {
const loginData = JSON.stringify(
{
'email': this.state.email,
'password': this.state.password,
},
);
console.log(loginData)
let validation = await axios.post(
'http://localhost:5000/login',
loginData,
{ headers: {'Content-Type':'application/json'}
})
.then((res) => {
console.log(`Login successful. ${res}`);
let response = res;
this.props.redirect('/session');
})
.catch((error) => {
console.log(error);
console.log(`Cannot login. ${error.message}`)
console.log(error.request);
let response = error;
alert("Damn.")
});
}
in Register React Component (works smoothly):
handleSubmit() {
// POST to server
const regData = JSON.stringify(
{
'firstname': this.state.fname,
'lastname': this.state.lname,
'email': this.state.email,
'birthday': this.state.birthday,
'password': this.state.password,
'country': this.state.country,
'city': this.state.city,
'provstate': this.state.provstate,
'contactnum': this.state.contactnum,
'formpicture': this.state.img,
'disclcond': this.state.cond,
},
);
console.log(regData)
axios.post(
'http://localhost:5000/register',
regData,
{ headers: {'Content-Type':'application/json'}
})
.then((res) => {
console.log(`Registered successfully. ${res}`);
setTimeout(() => this.props.redirect('/login'), 2000)
})
.catch((res) => {
console.log(`Not registered. ${res}`)
alert("Damn.")
});
}
NodeJS, Mongoose || Login backend:
const router = require('express').Router();
let User = require('../db_models/user.model');
router.route('/').get((req, res) => {
res.sendStatus(200);
res.end();
})
// If user submits login credentials, check database
router.route('/').post((req, res) => {
const email = req.body.email;
console.log(email)
const password = req.body.password;
let accountMatched = null;
async function checkPassword() {
await User.findOne({ 'email' : email })
.then(user => {
if (user.password === password) {
console.log(`true ${user.email} :: ${user.password}`);
accountMatched = true;
res.sendStatus(200);
} else {
console.log(`damn!! ${err}`)
res.sendStatus(404);
throw err
}
})
.catch(err => console.log(err))
accountMatched === true ? console.log('Passed') : res.send('Failed');
res.end()
}
checkPassword();
})
module.exports = router;
Register backend:
const router = require('express').Router();
let User = require('../db_models/user.model');
router.route('/').get((req, res) => {
res.send('hello hello');
res.end();
})
// If user submits registration credentials, submit to database
router.route('/').post((req, res) => {
console.log(req.body)
const firstname = req.body.firstname;
const lastname = req.body.lastname;
const email = req.body.email;
const birthday = Date.parse(req.body.birthday);
const password = req.body.password;
const contactnum = req.body.contactnum;
const country = req.body.country;
const city = req.body.city;
const provstate = req.body.provstate;
// below only pass links
const formpicture = req.body.formpicture;
const disclcond = req.body.disclcond;
const newUser = new User({
firstname,
lastname,
email,
birthday,
password,
country,
city,
provstate,
contactnum,
formpicture,
disclcond,
});
newUser.save()
.then(() => {
console.log('User added.');
res.sendStatus(200);
res.end();
})
.catch(err => {
console.log(`Damn, user not added. ${err}`);
res.end();
})
});
module.exports = router;
I would really appreciate some help.
Try exchange the Login Component part to something like this if you wanna use async/await.
async postReq() {
const loginData = JSON.stringify(
{
'email': this.state.email,
'password': this.state.password,
},
);
console.log(loginData)
try {
let res = await axios.post(
'http://localhost:5000/login',
loginData,
{ headers: {'Content-Type':'application/json'}
})
console.log(`Login successful. ${res}`);
let response = res;
this.props.redirect('/session');
} catch (error) {
console.log(error);
console.log(`Cannot login. ${error.message}`)
console.log(error.request);
let response = error;
alert("Damn.")
}
}

Authorizing the admin in React

Good day,
i am trying to check if the user is an admin if the user is an admin, he/she will be able to see the content on the page. If not then he/she will be redirected back to the home page. How can i do that? Should i use the backend code for that or can i check it on the frontend.
Route for admin page (this works perfectly fine):
router.get("/adminPanel", isAuth, isAdmin, (req, res) => {
return res.json({
title: "ADMIN PANEL",
});
});
Login page:
const [user, setUser] = useContext(userContext);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [modal, setModal] = useState(false);
useEffect(() => {
localStorage.setItem("token", JSON.stringify(user));
}, [user]);
//handlesubmit
const handleSubmit = e => {
e.preventDefault();
Axios.post("http://localhost:5000/users/login", {
email,
password,
})
.then(response => {
// localStorage.setItem(
// "token",
// JSON.stringify(response.data.token),
// )((window.location = "/"));
setUser({
token: response.data.token,
isAdmin: response.data.isAdmin,
});
window.location = "/";
// console.log(response.data.token);
// console.log(response.data.isAdmin);
})
.catch(err => {
console.log(err);
});
};
setting the role and the token in the localstorage:
const getLocalStorage = () => {
let token = localStorage.getItem("token");
let admin = localStorage.getItem("admin");
if (token) {
return JSON.parse(localStorage.getItem("token"));
} else if (token && admin) {
return JSON.parse(localStorage.getItem("admin"));
} else {
return "";
}
};
const logoutCallback = () => {
// Axios.post("http://localhost:5000/users/logout");
setUser({});
localStorage.clear("token");
window.location = "/";
};
const [user, setUser] = useState(getLocalStorage());
useEffect(() => {
setUser({ token: localStorage.getItem("token") });
}, []);
console.log(user);
Admin panel:
import React, { useContext, useEffect, useState } from "react";
import Axios from "axios";
import { userContext } from "../../App";
export default function Home() {
const [user] = useContext(userContext);
const [content, setContent] = useState("login plz to display the content");
useEffect(() => {
// if (user.isAdmin) {
// alert("is Admin");
// }
// Axios.get("http://localhost:5000/users/adminPanel").then(response =>
// console.log(user),
// );
async function fetchProtected() {
const result = await (
await fetch("http://localhost:5000/users/adminPanel", {
method: "POST",
headers: {
"Content-Type": "application/json",
authorization: `Bearer ${user.isAdmin}`,
},
})
).json();
if (result.data) setContent(result.data);
}
fetchProtected();
}, [user]);
return <div>{content}</div>;
}

react-admin authorization permissions

I try to make authorization and permissions availlable with react-admin and a Node server:https://github.com/hagopj13/node-express-mongoose-boilerplate
For react-admin there is an exemple of code: https://marmelab.com/react-admin/Authorization.html#configuring-the-auth-provider
// in src/authProvider.js
import decodeJwt from 'jwt-decode';
export default {
login: ({ username, password }) => {
const request = new Request('https://example.com/authenticate', {
method: 'POST',
body: JSON.stringify({ username, password }),
headers: new Headers({ 'Content-Type': 'application/json' }),
});
return fetch(request)
.then(response => {
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
return response.json();
})
.then(({ token }) => {
const decodedToken = decodeJwt(token);
localStorage.setItem('token', token);
localStorage.setItem('permissions', decodedToken.permissions);
});
},
logout: () => {
localStorage.removeItem('token');
localStorage.removeItem('permissions');
return Promise.resolve();
},
checkError: error => {
// ...
},
checkAuth: () => {
return localStorage.getItem('token') ? Promise.resolve() : Promise.reject();
},
getPermissions: () => {
const role = localStorage.getItem('permissions');
return role ? Promise.resolve(role) : Promise.reject();
}
};
But i don't understand how it work and on login the server return an user object like this:
{user: {id: "5e429d562910776587c567a2", email: "admin#test.com", firstname: "Ad", lastname: "Min",…},…}
tokens: {access: {,…}, refresh: {,…}}
access: {,…}
expires: "2020-03-03T06:45:10.851Z"
token: "eyJhbGciOi..."
refresh: {,…}
expires: "2020-04-02T06:15:10.851Z"
token: "eyJhbGciOi..."
user: {id: "5e429d562910776587c567a2", email: "admin#test.com", firstname: "Ad", lastname: "Min",…}
createdAt: "2020-02-11T12:25:58.760Z"
email: "admin#test.com"
firstname: "Ad"
id: "5e429d562910776587c567a2"
lastname: "Min"
role: "admin"
updatedAt: "2020-02-11T12:25:58.760Z"
There are already tokens and role and in the server, it seems to have a permission control:
role.js
const roles = ['user', 'admin'];
const roleRights = new Map();
roleRights.set(roles[0], []);
roleRights.set(roles[1], ['getUsers', 'manageUsers']);
module.exports = {
roles,
roleRights,
};
And the auth.js
const passport = require('passport');
const httpStatus = require('http-status');
const AppError = require('../utils/AppError');
const { roleRights } = require('../config/roles');
const verifyCallback = (req, resolve, reject, requiredRights) => async (err, user, info) => {
if (err || info || !user) {
return reject(new AppError(httpStatus.UNAUTHORIZED, 'Please authenticate'));
}
req.user = user;
if (requiredRights.length) {
const userRights = roleRights.get(user.role);
const hasRequiredRights = requiredRights.every(requiredRight => userRights.includes(requiredRight));
if (!hasRequiredRights && req.params.userId !== user.id) {
return reject(new AppError(httpStatus.FORBIDDEN, 'Forbidden'));
}
}
resolve();
};
const auth = (...requiredRights) => async (req, res, next) => {
return new Promise((resolve, reject) => {
passport.authenticate('jwt', { session: true }, verifyCallback(req, resolve, reject, requiredRights))(req, res, next);
})
.then(() => next())
.catch(err => next(err));
};
module.exports = auth;
But how to get authorization and permission works from the react-admin?
Thanks & Regards
Ludo
In react-admin there is a usePermissions() hook, which calls the authProvider.getPermissions() method on mount. This method return to you single string value like 'admin' or string array of permissions like ['admin','crm'...]. This strings are up to you how to set. In example below it's storing in localStorage, but in real life need to extract it from JWT token or retrieve it from backend.
getPermissions: () => {
const role = localStorage.getItem('permissions');
return role ? Promise.resolve(role) : Promise.reject();
}
import { usePermissions } from 'react-admin';
in function
const { permissions } = usePermissions();
return (
{ permissions == 'admin' &&
<DashboardMenuItem primaryText="Dashboard" onClick={onMenuClick} sidebarIsOpen={open} />
}
...
);

Pass data from react component to proxy(node server)

I set up a proxy to bypass CORS for the intended api in this react application. I'm having trouble to pass data from react component to proxy(nodeJS server). I've read a few links such as here and here but still have no clues.
/*React component*/
import React, { useState } from "react";
import axios from "axios";
export default function Mail() {
const [emailInput, setEmailInput] = useState('')
const getMail = () => {
axios.get("/list/members").then(json => {
console.log(json.data);
});
};
const subscribe = (email) => {
console.log('inside subscribe')
axios.post("/member", email)
.then(data => console.log('post succeeds'))
.catch(err => console.log(err))
}
const handleSubmit = e => {
e.preventDefault();
const email = {
email_address: `${emailInput}`,
status: "subscribed"
};
subscribe(email)
};
const handleChange = (e) => {
setEmailInput(e.target.value)
}
return (
<form onSubmit={handleSubmit}>
<input type="text" name="email" placeholder="enter your email" value={emailInput} onChange={handleChange}/>
<input type="submit" value="subscribe" />{" "}
</form>
);
}
In node server, I have
app.post("/member", (req, res) => {
const email = {
email_address: "axios1234#gmail.com",
status: "subscribed"
};
axios.post(
"https://<apilink>",
email,
{
withCredentials: true,
auth: {
username: "anystring",
password: "<apikey>"
}
}
).then(json => {
res.send(json.data)
}).catch(err => {
console.log(err);
})
});
I've checked that my conduit between react front end app and proxy server is working. I also examined both req and res in app.post("/member", (req, res) but found thatreq.body is undefined and couldn't find the email object that was passed in from react function component. Did I miss anything here?
Are you using a body-parser? If not, install body-parser and then change your code to this:
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.post("/member", (req, res) => {
const email = req.body.email_address;
axios.post(
"https://<apilink>",
email,
{
withCredentials: true,
auth: {
username: "anystring",
password: "<apikey>"
}
}
).then(json => {
res.send(json.data)
}).catch(err => {
console.log(err);
})
});

Multer + React + Nodejs Axios request

Axios Post request
// Create profile
export const createProfile = (profileData, avatar, history) => dispatch => {
dispatch(clearErrors());
const image = new FormData();
image.append("avatar", avatar, avatar.name);
axios
.post("/api/profile", image, profileData)
.then(res => history.push("/dashboard"))
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
Edit ---> Axios post request second attempt
// Create profile
export const createProfile = (profileData, avatar, history) => dispatch => {
dispatch(clearErrors());
const image = new FormData();
image.append("avatar", avatar, avatar.name);
image.append("user", profileData, profileData.username);
axios
.post("/api/profile", image)
.then(res => history.push("/dashboard"))
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
profileData is what i want in the req.body and avatar is what i receive in req.file in my back-end with multer, but what i receive is the req.file with the image but nothing in my req.body(Just an empty object)
This is my router in node
router.post(
"/",
upload.single("avatar"),
passport.authenticate("jwt", { session: false }),
(req, res) => {
console.log(req.body);
}
);
Try to implement in following way using FormData
handleSubmit(e)
{
e.preventDefault();
const err = this.validate();
if (!err) {
var formData = {
category: this.state.category,
course: this.state.course,
};
const { category, course } = this.state;
let fd = new FormData();
fd.append('Test', this.state.testFile, this.state.testFile.name);
fd.append('category', category);
fd.append('course', course);
console.log(fd);
axios({
method: 'post',
url: 'http://localhost:7777/api/uploadTest',
data: fd,
})
.then((response) => {
if (response.data == 'Success') {
alert('Test has been Added..!!');
}
else {
alert('Something went wrong');
this.setState({ category: '' });
}
// this.setState({success:'Alert: '+response.data});
})
.catch((e) => {
console.error(e);
this.setState({ success: 'Alert: Something went wrong' });
});
}
}
I consider your route as /api/profile in route file.
You don't show your header profileData.
It should be like this
const profileData = {
headers: { 'content-type': 'multipart/form-data' }
}
Then you can request to the server as you already did.

Resources