Authorizing the admin in React - node.js

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

Related

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

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

React Node js Jwt Expired Control and reflesh token

Hi i am working check jwt expired and logout or reflesh token. i try a lot of thing but i didn't solve it. I use react for frontend and node for backend.
Frontend (react.js) AuthContent.js
import axios from "axios";
import { createContext, useEffect, useState } from "react";
import jwt_decode from "jwt-decode";
import { withRouter } from "../components/with-router";
export const AuthContext = createContext();
export const AuthContexProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState(
JSON.parse(localStorage.getItem("user")) || null
);
const login = async (inputs) => {
const res = await axios.post("/auth/login", inputs);
setCurrentUser(res.data);
console.log(res.data)
};
const logout = async (inputs) => {
await axios.post("/auth/logout");
setCurrentUser(null);
};
useEffect(() => {
localStorage.setItem("user", JSON.stringify(currentUser));
}, [currentUser]);
return (
<AuthContext.Provider value={{ currentUser, login, logout }}>
{children}
</AuthContext.Provider>
);
};
backend (node.js) auth.js
`
import {db} from "../db.js"
import bcrypt from "bcryptjs";
import jwt from "jsonwebtoken";
export const register = (req,res) => {
const q = "SELECT * FROM users WHERE email = ? "
db.query(q, [req.body.email], (err, data) => {
if (err) return res.status(500).json(err);
if (data.length) return res.status(409).json("User already exists!");
//Hash the password and create a user
const salt = bcrypt.genSaltSync(10);
const hash = bcrypt.hashSync(req.body.password, salt);
const q = "INSERT INTO users(`firstname`,`lastname`,`email`,`password`) VALUES (?)";
const values = [req.body.firstName, req.body.lastName, req.body.email, hash];
db.query(q, [values], (err, data) => {
if (err) return res.status(500).json(err);
return res.status(200).json("User has been created.");
});
});
};
export const login = (req,res) => {
//CHECK USER
const q = "SELECT * FROM users WHERE email = ?";
db.query(q, [req.body.email], (err, data) => {
if (err) return res.status(500).json(err);
if (data.length === 0) return res.status(404).json("User not found!");
//Check password
const isPasswordCorrect = bcrypt.compareSync(
req.body.password,
data[0].password
);
if (!isPasswordCorrect)
return res.status(400).json("Wrong username or password!");
const token = jwt.sign({ id: data[0].id, expiresIn: '24h' }, "jwtkey");
const { password, ...other } = data[0];
res
.cookie("access_token", token, {
httpOnly: true,
})
.status(200)
.json(other);
});
};
export const logout = (req, res) => {
res.clearCookie("access_token",{
sameSite:"none",
secure:true
}).status(200).json("User has been logged out.")
};
I try reach access_token in frontend but always return null. i try axios interceptors but i cant solve it.

back-end middleware cannot reach headers, but postman work fine

I'm creating new app(e-commerce) using react and react-redux with axios. For back-end using Mongo-DB(MERN stack).
There is no problem when saving data to database, but when i get that data from database middleware can not reach token from headers. But postman work fine both get and save data.
I use the same middleware when saving data to database, it work fine. But when i try to fetch data from database i get unauthorized error.
here is my code.
FOR BACK-END;
Server file;
import express from "express";
import connectDB from "./config/db.js";
import dotenv from "dotenv";
import colors from "colors";
import productRoutes from "./routes/productRoutes.js";
import userRouters from "./routes/userRoutes.js";
import shippingAddressRoutes from "./routes/shippingAddressRoutes.js";
import { notFound, errorHandler } from "./middleware/errorMiddleware.js";
dotenv.config();
connectDB();
const app = express();
// body parser
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// API route
app.get("/", (req, res) => {
res.send("API is running....");
});
// route for products
app.use("/api/products", productRoutes);
// route for users
app.use("/api/users", userRouters);
// route for shipping address
app.use("/api/shippingaddress", shippingAddressRoutes);
app.use(notFound);
app.use(errorHandler);
const PORT = process.env.PORT || 5000;
app.listen(
PORT,
console.log(
`Server running in ${process.env.NODE_ENV} mode on port ${PORT}`.yellow.bold
)
);
shippingAddressRoute file;
import express from "express";
import {
saveAddress,
changeAddress,
getAddress,
} from "../controllers/shippingAddressController.js";
import protect from "../middleware/authMiddleware.js";
const router = express.Router();
router
.route("/")
.get(protect, getAddress)
.post(protect, saveAddress)
.put(protect, changeAddress);
export default router;
shippingAddressController file;
import asyncHandler from "express-async-handler";
import ShippingAddress from "../models/shippingAddressModel.js";
import User from "../models/userModel.js";
// #desc get address
// #route GET /api/shippingaddress
// #access Private
const getAddress = asyncHandler(async (req, res) => {
const user = User.findById(req.user._id);
if (user) {
const { email } = req.body;
const shippingAddress = await ShippingAddress.findOne({
userEmail: email,
});
if (shippingAddress) {
res.status(200);
res.json({
address: shippingAddress.address,
city: shippingAddress.city,
postalCode: shippingAddress.postalCode,
county: shippingAddress.country,
email: shippingAddress.userEmail,
});
} else {
res.status(404);
throw new Error("Address not found");
}
} else {
res.status(404);
throw new Error("User not found");
}
});
// #desc save address
// #route POST /api/shippingaddress
// #access Private
const saveAddress = asyncHandler(async (req, res) => {
const user = User.findById(req.user._id);
if (user) {
const { address, city, postalCode, country, email } = req.body;
const existAddress = await ShippingAddress.findOne({ userEmail: email });
if (existAddress) {
res.status(400);
throw new Error(
"User already has an address. If you want to change address, try CHANGE ADDRESS."
);
} else {
const createdAddress = await ShippingAddress.create({
user: req.user._id,
address,
city,
postalCode,
country,
userEmail: email,
});
if (createdAddress) {
res.status(201);
res.json({
address: createdAddress.address,
city: createdAddress.city,
postalCode: createdAddress.postalCode,
county: createdAddress.country,
email: createdAddress.userEmail,
});
} else {
res.status(400);
throw new Error("Address did not save");
}
}
} else {
res.status(404);
throw new Error("User not found");
}
});
// #desc change address
// #route PUT /api/shippingaddress
// #access Private
const changeAddress = asyncHandler(async (req, res) => {
const user = await User.findById(req.user._id);
if (user) {
const { email } = req.body;
const shippingAddress = await ShippingAddress.findOne({
userEmail: email,
});
if (shippingAddress) {
shippingAddress.address = req.body.address || shippingAddress.address;
shippingAddress.city = req.body.city || shippingAddress.city;
shippingAddress.postalCode =
req.body.postalCode || shippingAddress.postalCode;
shippingAddress.country = req.body.country || shippingAddress.country;
const updatedShippingAddress = await shippingAddress.save();
res.status(200);
res.json({
address: updatedShippingAddress.address,
city: updatedShippingAddress.city,
postalCode: updatedShippingAddress.postalCode,
county: updatedShippingAddress.country,
email: updatedShippingAddress.userEmail,
});
} else {
res.status(404);
throw new Error("Address not found");
}
} else {
res.status(404);
throw new Error("User not found");
}
});
export { getAddress, saveAddress, changeAddress };
authMiddleware file;
import jwt from "jsonwebtoken";
import User from "../models/userModel.js";
import asyncHandler from "express-async-handler";
const protect = asyncHandler(async (req, res, next) => {
let token = req.headers.token;
if (
req.headers.authorization &&
req.headers.authorization.startsWith("Bearer")
) {
try {
// get token from req
token = req.headers.authorization.split(" ")[1];
// verify token
const decode = jwt.verify(token, process.env.JWT_SECRET);
// get user from token
req.user = await User.findById(decode.id).select("-password");
next();
} catch (error) {
res.status(401);
throw new Error("Not1 Authorized");
}
}
if (!token) {
res.status(401);
throw new Error("Not2 Authorized");
}
});
export default protect;
FOR FRONT-END;
shippingAction file;
import { createAsyncThunk } from "#reduxjs/toolkit";
import shippingServices from "./shippingServices";
// get address
export const getAddress = createAsyncThunk(
"shipping/get",
async (userInfo, thunkAPI) => {
try {
return await shippingServices.get(userInfo);
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
// save address
export const saveAddress = createAsyncThunk(
"shipping/save",
async (addressInfo, thunkAPI) => {
try {
return await shippingServices.save(addressInfo);
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
// update address
export const updateAddress = createAsyncThunk(
"shipping/update",
async (addressInfo, thunkAPI) => {
try {
return await shippingServices.update(addressInfo);
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
shippingServices file;
import axios from "axios";
const SHIPPING_ADDRESS_URL = "/api/shippingaddress";
// save address
const save = async (addressInfo) => {
const config = {
headers: {
Authorization: `Bearer ${addressInfo.token}`,
},
};
const { data } = await axios.post(SHIPPING_ADDRESS_URL, addressInfo, config);
return data;
};
// get address
const get = async (userInfo) => {
const config = {
headers: {
Authorization: `Bearer ${userInfo.token}`,
},
};
const { data } = await axios.get(SHIPPING_ADDRESS_URL, userInfo, config);
return data;
};
// update address
const update = async (addressInfo) => {
const config = {
headers: {
Authorization: `Bearer ${addressInfo.token}`,
},
};
const { data } = await axios.put(SHIPPING_ADDRESS_URL, addressInfo, config);
return data;
};
const shippingServices = {
save,
get,
update,
};
export default shippingServices;
shippigPage file;
import React, { useState, useEffect } from "react";
import { Link, useNavigate } from "react-router-dom";
import { Form, Button } from "react-bootstrap";
import { useDispatch, useSelector } from "react-redux";
import FormContainer from "../components/FormContainer";
import Message from "../components/Message";
import Loader from "../components/Loader";
import { shippingSliceAction } from "../features/shipping/shippingSlice";
import { saveAddress, getAddress } from "../features/shipping/shippingAction";
function ShippingPage() {
const { user } = useSelector((state) => state.auth);
const { shippingAddress, isLoading, isSuccess, isError, message } =
useSelector((state) => state.shipping);
const [address, setAddress] = useState("");
const [city, setCity] = useState("");
const [postalCode, setPostalCode] = useState("");
const [country, setCountry] = useState("");
const [comMessage, setComMessage] = useState(null);
const dispatch = useDispatch();
const navigate = useNavigate();
useEffect(() => {
if (shippingAddress) {
setAddress(shippingAddress.address);
setCity(shippingAddress.city);
setPostalCode(shippingAddress.postalCode);
setCountry(shippingAddress.country);
} else {
const userInfo = {
token: user.token,
email: user.email,
};
dispatch(getAddress(userInfo));
}
}, []);
const submitHandler = (e) => {
e.preventDefault();
if (!user) {
navigate("/login");
} else {
if (!address || !city || !postalCode || !country) {
setComMessage("Please fill all fields");
setTimeout(() => setComMessage(null), 3000);
} else {
dispatch(
shippingSliceAction.takeAddress({
email: user.email,
address,
city,
postalCode,
country,
})
);
localStorage.setItem(
"userAddress",
JSON.stringify({
email: user.email,
address,
city,
postalCode,
country,
})
);
}
}
};
const saveContinue = () => {
if (!user) {
navigate("/login");
} else {
dispatch(
saveAddress({
email: user.email,
address,
city,
postalCode,
country,
token: user.token,
})
);
if (isSuccess) {
setComMessage("Your Address Saved");
setTimeout(() => dispatch(shippingSliceAction.reset()), 3000);
setTimeout(() => setComMessage(null), 3000);
setTimeout(() => navigate("/payment"), 4000);
}
}
};
return (
<FormContainer>
<h1>Shipping</h1>
{comMessage && <Message variant="danger">{comMessage}</Message>}
<Form onSubmit={submitHandler}>
<Form.Group controlId="address">
<Form.Label>Address</Form.Label>
<Form.Control
type="text"
placeholder="Enter Address"
value={address}
onChange={(e) => setAddress(e.target.value)}
></Form.Control>
</Form.Group>
<Form.Group controlId="city">
<Form.Label>City</Form.Label>
<Form.Control
type="text"
placeholder="Enter City"
value={city}
onChange={(e) => setCity(e.target.value)}
></Form.Control>
</Form.Group>
<Form.Group controlId="postalCode">
<Form.Label>Postal Code</Form.Label>
<Form.Control
type="number"
placeholder="Enter Postal Code"
value={postalCode}
onChange={(e) => setPostalCode(e.target.value)}
></Form.Control>
</Form.Group>
<Form.Group controlId="country">
<Form.Label>Country</Form.Label>
<Form.Control
type="text"
placeholder="Enter Country"
value={country}
onChange={(e) => setCountry(e.target.value)}
></Form.Control>
</Form.Group>
<Button className="shippingButton" type="submit" variant="primary">
Continue without saving
</Button>
{isLoading && <Loader />}
{isSuccess && <Message variant="success"></Message>}
{isError && <Message variant="danger">{message}</Message>}
<Button
className="shippingButton save"
type="button"
variant="secondary"
onClick={saveContinue}
>
Save my address and continue
</Button>
</Form>
</FormContainer>
);
}
export default ShippingPage;
// shippingServices.js
const { data } = await axios.get(SHIPPING_ADDRESS_URL, userInfo, config);
this should be
const { data } = await axios.get(SHIPPING_ADDRESS_URL, config);
Also stop using unnecessary packages like express-async-handler,
just cover entire middle-ware function body with try/catch block, call next(error) in catch block & it will work just fine.

JWT Authentication Express Js and React Js for specific user email

I have e problem in JWT with the express and react project. User add products and items page show product , Specific User see his added item not all item, I have use filter method.
Here is my API
app.get('/items', verifyJWT, async (req, res) => {
const decodedEmail = req.decoded.email;
const email = req.query.email;
if (email === decodedEmail) {
const query = { email: email };
const cursor = ItemsCollection.find(query);
const Items = await cursor.toArray();
res.send(Items);
}
else {
res.status(403).send({ message: 'forbidden access, Try again' })
}
})
my JWT function
function verifyJWT(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).send({ message: 'unauthorized access' });
}
const token = authHeader.split(' ')[1];
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, decoded) => {
if (err) {
return res.status(403).send({ message: 'Forbidden access, Please dont try' });
}
console.log('decoded', decoded);
req.decoded = decoded;
next();
})
}
My frontend react code is
const [user] = useAuthState(auth);
const [Items, setItems] = useState([]);
const navigate = useNavigate();
const [shouldRemount, setShouldRemount] = useState(false)
useEffect(() => {
const fetchData = async () => {
const email = user.email;
const url = `https://protected-tor-63915.herokuapp.com/items?email=${email}`;
try {
const response = await fetch(url)
if (response.status === 200) {
let data = await response.json();
setItems(data);
}
} catch (error) {
if (error.response.status === 401 || error.response.status === 403) {
signOut(auth);
navigate('/login')
}
}
}
fetchData();
}, [user, shouldRemount, navigate]);

Send POST data using Send Grid API for verification email

I am having issues posting to send a verification email. I am using ReactJS front end NodeJS backend and MongoDB database. I am running npm run dev to make sure my backend server is running when testing in Postman and gives me an error Cannot Post /api/verify-email I am not getting any other error code so having a hard time trouble shooting. I am using Send Grid API to send the verification email.
verifyEmailRoute.js
import { ObjectId } from 'mongodb';
import jwt from 'jsonwebtoken';
import { getDbConnection } from '../src/db';
export const verifyEmailRoute = {
path: 'http://localhost:8080/api/verify-email',
method: 'put',
handler: async (req, res) => {
const { verificationString } = req.body;
const db = await getDbConnection('react-auth-db');
const result = await db.collection('users').findOne({ verificationString });
if (!result) return res.status(401).json({ message: 'The email verification code is incorrect' });
const { _id: id, email, info } = result;
await db.collection('users').updateOne({ _id: ObjectId(id) }, { $set: { isVerified: true } });
jwt.sign({ id, email, isVerified: true, info }, process.env.JWT_SECRET, { expiresIn: '2d' }, (err, token) => {
if (err) {
return res.status(500).json(err);
}
res.status(200).json({ token });
});
},
};
emailVerificationLandingPage.js
import { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { useToken } from '../src/auth/useToken';
import { EmailVerificationSuccess } from './EmailVerificationSuccess';
import { EmailVerificationFail } from './EmailVerificationFail';
export const EmailVerificationLandingPage = () => {
const [isLoading, setIsLoading] = useState(true);
const [isSuccess, setIsSuccess] = useState(false);
const [user, setUser] = useState(null);
const { verificationString } = useParams();
const [token, setToken] = useToken();
useEffect(() => {
const loadVerification = async () => {
try {
const response = await fetch.put('http://localhost:8080/api/verify-email', { verificationString });
const { token } = response.data;
setToken(token);
console.log(token);
setIsSuccess(true);
setIsLoading(false);
} catch (error) {
setIsSuccess(false);
setIsLoading(false);
}
}
loadVerification();
}, [setToken, verificationString]);
console.log(token);
if (isLoading) return <p>Loading...</p>;
if (!isSuccess) return <EmailVerificationFail />
return <EmailVerificationSuccess />
}
sendEmail.js
import sendgrid from '#sendgrid/mail';
sendgrid.setApiKey(process.env.SENDGRID_API_KEY);
export const sendEmail = ({ to, from, subject, text, html }) => {
const msg = { to, from, subject, text, html };
return sendgrid.send(msg);
}
signUpRoute.js
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { v4 as uuid } from 'uuid';
import { getDbConnection } from '../src/db';
import { sendEmail } from '../src/util/sendEmail';
export const signUpRoute = {
path: '/api/signup',
method: 'post',
handler: async (req, res) => {
const { email, password } = req.body;
// Make sure there's no existing user with that email - no verification yet
const db = getDbConnection('react-auth-db');
const user = await db.collection('users').findOne({ email });
if (user) {
console.log("found user", user)
return res.sendStatus(409).json(user); // 409 is the "conflict" error code
}
// Generate the salt first
const salt = uuid();
const pepper = process.env.PEPPER_STRING;
// Encrypt the password
const passwordHash = await bcrypt.hash(salt + password + pepper, 10);
const verificationString = uuid();
// Store email and password hash in database (i.e. create user) - you'll also want to get the id
const startingInfo = {
firstName: '',
lastName: '',
dob: '',
}
const result = await db.collection('users').insertOne({
email,
isVerified: false,
verificationString,
passwordHash,
salt,
info: startingInfo,
});
const { insertedId } = result;
try {
await sendEmail({
to: email,
from: 'mdolly01#gmail.com',
subject: 'Welcome to Tool Right Please verify your email',
text: `
Thanks for signing up! To verify your email, you just need to click the link below:
http://localhost:3000/verify-email/${verificationString}
`
});
} catch (e) {
console.log(e);
throw new Error(e);
}
jwt.sign({
id: insertedId,
isVerified: false,
email, // Do NOT send the salt back to the user
info: startingInfo,
},
process.env.JWT_SECRET,
{
expiresIn: '2d',
},
(err, token) => {
if (err) {
return res.status(500).send(err);
}
return res.status(200).json({token});
});
},
};

Resources