I have a react applicaton running on localhost:3000 and I have a backend Server running on localhost:4000,
If I try to navigate or access a protected route when I dont login, it should not allow me to see that page.
And When I login my backend is giving me a token in cookie which is httpOnly:true, known how do I do the auth flow properly an in secure way
Backend Api:
exports.login = async (req, resp) => {
try {
// fetch data
const { email, password } = req.body
// validation for fields required
if (!email || !password) {
return resp.status(200).json({
status: "Failed",
msg: "All fields are required in login !",
})
}
// check if email exists in database
// We also populate additionDetails
const user = await User.findOne({ email: email })
.populate("additionalDetails")
.exec()
console.log("Data fetched about User from Db:", user)
// If user not found with provided email
if (!user) {
return resp.status(200).json({
status: "Failed",
msg: "User with given email dont exists. try again !"
})
}
// user exists check password with hashed password
const checkHashedPassword = await bcrypt.compare(password, user.password)
// password is correct generate Token
if (checkHashedPassword) {
// In token we send user id, email and accountType
const payload = {
id: user._id,
email: user.email,
accountType: user.accountType,
}
const tokenOptions = {
expiresIn: "24h"
}
//Token Expires in 2 hours
const token = jwtToken.sign(payload, process.env.JWT_SECRET, tokenOptions)
// we update the object
// send token and password
user.password = undefined;
user.token = token;
console.log("User Modified", user)
// generate cookie
const cookieOptions = {
// expires in 3days
//expiresIn:"3hr, 3days"
expires: new Date(Date.now() + 3 * 24 * 60 * 1000),
httpOnly: true,
}
resp.cookie("token", token, cookieOptions).
// Req body
status(200).json({
success: "Success",
token,
user,
message: 'User Logged in successfully !',
})
}
else {
return resp.status(200).json({
status: "Failed",
msg: "Password dont match ! Try Again !"
})
}
} catch (error) {
console.log(error);
return resp.status(200).json({
status: "Failed",
message: 'Login Failure, please try again',
errormsg: error
});
}
}
FrontEnd :
import { Routes, Route } from "react-router-dom";
import "./App.css";
import Navbar from "./components/core/Navbar";
import { HomePage } from "./pages/HomePage";
import CoursePage from "./pages/CoursePage";
import AboutUsPage from "./pages/AboutUsPage";
import LoginPage from "./pages/LoginPage";
import SignUpPage from "./pages/SignUpPage";
import ScrollToTop from "./ScrollToTop";
import ContactPage from "./pages/ContactPage";
import { Toaster } from "react-hot-toast";
import ProfilePage from "./pages/ProfilePage";
import ProtectedRoute from "./components/protectedRoute/ProtectedRoute";
import Test from "./pages/Test";
import {useSelector} from "react-redux"
function App() {
const toastconfiguration={
position:'top-center',
}
return (
<div className="tw-font-inter tw-bg-richblack-900 tw-h-full tw-relative">
<Toaster position="top-center" toastOptions={toastconfiguration} />
<Navbar />
<ScrollToTop />
<Routes>
<Route path='/' element={<HomePage />} />
<Route path='/courses' element={<CoursePage />} />
<Route path='/about' element={<AboutUsPage />} />
<Route path='/login' element={<LoginPage />} />
<Route path='/signup' element={<SignUpPage />} />
<Route path='/contact' element={<ContactPage />} />
<Route element={<ProtectedRoute />}>
<Route element={<ProfilePage />} path="/profile" />
<Route element={<Test />} path="/test" />
</Route>
</Routes>
</div >
);
}
export default App;
import React from 'react'
import { Outlet,Navigate } from 'react-router-dom'
function ProtectedRoute() {
//* Here add the logic of access the global auth token from redux
let auth={'token':false} // This I have saved globally using redux but for now I want to known the solution
return (
auth.token?<Outlet/>:<Navigate to='/login'/>
)
}
export default ProtectedRoute
I didnt find any article to properly understand it, I can send the token in resp and save it browser localStorage. But I dont known what would be the correct way. PLEASE HELP
2
Answers
JWTs often have identifiable information in them, such as a user’s uuid as the most basic example. In general, it’s a fairly secure way to be able to identify your user, without giving up what the uuid is, and making it extremely difficult to generate a a uuid that decrypts to an actual user in your database.
Some things that you might want to consider is embedding data that can’t change within the user’s "session" and validating that on every request. It’s not uncommon to include an expiration as well.
An example of authentication might look like this: ( untested 🙂 )
We’re using different middleware to handle different user types accordingly.
On the front-end, you’ll want to save the token to local storage or equivalent and attach the token to the
Authorization
header on all of your requests that require auth.Some things to note: It’s best practice to always check the iss, and uad claims. This helps to mitigate attack vectors where one resource server would obtain a genuine access token intended for it, and then use it to gain access to resources on a different resource server, which would not normally be available to the original server.
I removed the cookie, you don’t need it. When you login, the server just responds with 200, and the token. The front-end should manage it from there.
You don’t need to update a user object with the token, in fact it’s best not to store those tokens in the DB.
Lastly, I would have a wrapper around fetch. whenever a 401 is encountered, remove the old token, and redirect to the login screen unless of course, you’re already on the login screen.
for better caching