token refresher
This commit is contained in:
@@ -13,9 +13,12 @@ import CourseTable from "./Pages/CourseTable";
|
|||||||
import ConsolidatedTable from "./Pages/ConsolidatedTable";
|
import ConsolidatedTable from "./Pages/ConsolidatedTable";
|
||||||
import CourseConsolidated from "./Pages/courseConsolidated";
|
import CourseConsolidated from "./Pages/courseConsolidated";
|
||||||
import PrivateRoute from "./components/PrivateRoute";
|
import PrivateRoute from "./components/PrivateRoute";
|
||||||
|
import TokenRefresher from "./components/TokenRefresher";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<TokenRefresher />
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<AuthPage />}></Route>
|
<Route path="/" element={<AuthPage />}></Route>
|
||||||
<Route path="/course-form/:id" element={<PrivateRoute element={<CourseForm />} />} />
|
<Route path="/course-form/:id" element={<PrivateRoute element={<CourseForm />} />} />
|
||||||
@@ -28,6 +31,7 @@ function App() {
|
|||||||
<Route path="/consolidated" element={<PrivateRoute element={<ConsolidatedTable />} />} />
|
<Route path="/consolidated" element={<PrivateRoute element={<ConsolidatedTable />} />} />
|
||||||
<Route path="/courseConsolidated" element={<PrivateRoute element={<CourseConsolidated />} />} />
|
<Route path="/courseConsolidated" element={<PrivateRoute element={<CourseConsolidated />} />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,39 +6,39 @@ import Navbar from "./Navbar";
|
|||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
header: {
|
header: {
|
||||||
background: '#003366',
|
background: "#003366",
|
||||||
color: 'white',
|
color: "white",
|
||||||
padding: '20px 0',
|
padding: "20px 0",
|
||||||
textAlign: 'center',
|
textAlign: "center",
|
||||||
fontSize: '24px',
|
fontSize: "24px",
|
||||||
marginBottom: '0',
|
marginBottom: "0",
|
||||||
},
|
},
|
||||||
buttonRow: {
|
buttonRow: {
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
justifyContent: 'space-around',
|
justifyContent: "space-around",
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
padding: '10px 0',
|
padding: "10px 0",
|
||||||
margin: '0',
|
margin: "0",
|
||||||
backgroundColor: '#003366',
|
backgroundColor: "#003366",
|
||||||
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)'
|
boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)",
|
||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
padding: '10px 20px',
|
padding: "10px 20px",
|
||||||
borderRadius: '5px',
|
borderRadius: "5px",
|
||||||
color: 'white',
|
color: "white",
|
||||||
border: 'none',
|
border: "none",
|
||||||
cursor: 'pointer',
|
cursor: "pointer",
|
||||||
fontSize: '16px',
|
fontSize: "16px",
|
||||||
flex: '1',
|
flex: "1",
|
||||||
},
|
},
|
||||||
bulkDownloadButton: {
|
bulkDownloadButton: {
|
||||||
backgroundColor: '#3fb5a3',
|
backgroundColor: "#3fb5a3",
|
||||||
},
|
},
|
||||||
downloadButton: {
|
downloadButton: {
|
||||||
backgroundColor: '#28a745',
|
backgroundColor: "#28a745",
|
||||||
},
|
},
|
||||||
emailButton: {
|
emailButton: {
|
||||||
backgroundColor: '#ff6f61',
|
backgroundColor: "#ff6f61",
|
||||||
},
|
},
|
||||||
tableContainer: {
|
tableContainer: {
|
||||||
maxHeight: "70vh",
|
maxHeight: "70vh",
|
||||||
@@ -77,11 +77,9 @@ const styles = {
|
|||||||
},
|
},
|
||||||
main: {
|
main: {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const ConsolidatedTable = () => {
|
const ConsolidatedTable = () => {
|
||||||
const [searchQuery, setSearchQuery] = useState(""); // State for search input
|
const [searchQuery, setSearchQuery] = useState(""); // State for search input
|
||||||
const [data, setData] = useState([]);
|
const [data, setData] = useState([]);
|
||||||
@@ -94,7 +92,8 @@ const ConsolidatedTable = () => {
|
|||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
"http://localhost:8080/api/table/consolidated-table"
|
"http://localhost:8080/api/table/consolidated-table",
|
||||||
|
{ withCredentials: true }
|
||||||
);
|
);
|
||||||
setData(response.data);
|
setData(response.data);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@@ -113,8 +112,7 @@ const ConsolidatedTable = () => {
|
|||||||
|
|
||||||
// Extract unique faculty names and filter based on the search query
|
// Extract unique faculty names and filter based on the search query
|
||||||
const filteredTeachers = [...new Set(data.map((row) => row.Name))].filter(
|
const filteredTeachers = [...new Set(data.map((row) => row.Name))].filter(
|
||||||
(teacher) =>
|
(teacher) => teacher.toLowerCase().includes(searchQuery.toLowerCase()) // Filter by search query
|
||||||
teacher.toLowerCase().includes(searchQuery.toLowerCase()) // Filter by search query
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Pagination logic applied to filtered teachers
|
// Pagination logic applied to filtered teachers
|
||||||
@@ -151,7 +149,8 @@ const ConsolidatedTable = () => {
|
|||||||
const facultyId = teacherData[0].facultyId;
|
const facultyId = teacherData[0].facultyId;
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
`http://localhost:8080/api/faculty/${facultyId}`
|
`http://localhost:8080/api/faculty/${facultyId}`,
|
||||||
|
{ withCredentials: true }
|
||||||
);
|
);
|
||||||
const facultyEmail = response.data.email;
|
const facultyEmail = response.data.email;
|
||||||
const workbook = createExcelBook(teacherData, teacher);
|
const workbook = createExcelBook(teacherData, teacher);
|
||||||
@@ -259,9 +258,7 @@ const ConsolidatedTable = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<h2
|
<h2 style={{ color: "black", margin: 0, fontSize: "1.5rem" }}>
|
||||||
style={{ color: "black", margin: 0, fontSize: "1.5rem" }}
|
|
||||||
>
|
|
||||||
{teacher}'s Table
|
{teacher}'s Table
|
||||||
</h2>
|
</h2>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ function AuthPage() {
|
|||||||
toast.error(message);
|
toast.error(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function ToggleSign(event) {
|
function ToggleSign(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
setSignin(!signin);
|
setSignin(!signin);
|
||||||
@@ -58,11 +57,12 @@ function AuthPage() {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
`http://localhost:8080/api/${
|
`http://localhost:8080/api/${!signin ? "login" : "register"}`,
|
||||||
!signin ? "login" : "register"
|
formData,
|
||||||
}`,
|
{ withCredentials: true }
|
||||||
formData
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (response.status === 200) {
|
||||||
const { user } = response.data;
|
const { user } = response.data;
|
||||||
delete user.password;
|
delete user.password;
|
||||||
const gravatarUrl = `https://www.gravatar.com/avatar/${md5(
|
const gravatarUrl = `https://www.gravatar.com/avatar/${md5(
|
||||||
@@ -70,8 +70,8 @@ function AuthPage() {
|
|||||||
)}?d=identicon`;
|
)}?d=identicon`;
|
||||||
user.profilePicture = gravatarUrl;
|
user.profilePicture = gravatarUrl;
|
||||||
|
|
||||||
localStorage.setItem("user", JSON.stringify(user));
|
window.location.href = "/Welcome";
|
||||||
window.location.href = "/Welcom";
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Authentication error:", error);
|
console.error("Authentication error:", error);
|
||||||
if (
|
if (
|
||||||
@@ -88,11 +88,9 @@ function AuthPage() {
|
|||||||
|
|
||||||
const handleGoogleLogin = (event) => {
|
const handleGoogleLogin = (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
window.location.href =
|
window.location.href = "http://localhost:8080/auth/google";
|
||||||
"http://localhost:8080/auth/google";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ToastContainer />
|
<ToastContainer />
|
||||||
|
|||||||
@@ -1,9 +1,26 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { FaUserCircle } from "react-icons/fa";
|
import { FaUserCircle } from "react-icons/fa";
|
||||||
import { NavLink } from "react-router-dom"; // Import NavLink for navigation
|
import { NavLink, useNavigate } from "react-router-dom"; // Import NavLink for navigation
|
||||||
import "./Navbar.css"; // Navbar-specific styles
|
import "./Navbar.css"; // Navbar-specific styles
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
const Navbar = () => {
|
const Navbar = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
// Handle logout functionality
|
||||||
|
const handleLogout = async () => {
|
||||||
|
try {
|
||||||
|
// Call the logout API
|
||||||
|
await axios.get("http://localhost:8080/auth/logout", { withCredentials: true });
|
||||||
|
|
||||||
|
// Redirect to the login page after successful logout
|
||||||
|
navigate("/");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error during logout:", error);
|
||||||
|
alert("Failed to log out. Please try again.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="navbar">
|
<header className="navbar">
|
||||||
<div className="navbar-container">
|
<div className="navbar-container">
|
||||||
@@ -21,6 +38,11 @@ const Navbar = () => {
|
|||||||
Course Consolidated
|
Course Consolidated
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<button className="logout-button" onClick={handleLogout}>
|
||||||
|
Logout
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* User icon at the right */}
|
{/* User icon at the right */}
|
||||||
<NavLink to="/accounts" className="user-icon-link">
|
<NavLink to="/accounts" className="user-icon-link">
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ const CourseConsolidated = () => {
|
|||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
"http://localhost:8080/api/table/course-consolidated"
|
"http://localhost:8080/api/table/course-consolidated",
|
||||||
|
{ withCredentials: true }
|
||||||
);
|
);
|
||||||
setData(response.data);
|
setData(response.data);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|||||||
@@ -1,12 +1,36 @@
|
|||||||
import React from 'react';
|
import React, { useEffect, useState } from "react";
|
||||||
import { Navigate } from 'react-router-dom'; // Use Navigate for redirect
|
import { Navigate } from "react-router-dom";
|
||||||
import Cookies from "js-cookie";
|
import axios from "axios";
|
||||||
|
|
||||||
const PrivateRoute = ({ element: Element, ...rest }) => {
|
const PrivateRoute = ({ element: Element, ...rest }) => {
|
||||||
const token = Cookies.get("token");
|
const [isAuthenticated, setIsAuthenticated] = useState(null); // Track authentication status
|
||||||
|
|
||||||
// If token exists, render the element. Otherwise, redirect to the login page
|
useEffect(() => {
|
||||||
return token ? Element : <Navigate to="/" />;
|
// Call the server to validate the token
|
||||||
|
const checkAuth = async () => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get("http://localhost:8080/api/auth-check", {
|
||||||
|
withCredentials: true, // Include cookies in the request
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.status === 200) {
|
||||||
|
setIsAuthenticated(true); // User is authenticated
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setIsAuthenticated(false); // User is not authenticated
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
checkAuth();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// If still checking authentication, render nothing or a loading spinner
|
||||||
|
if (isAuthenticated === null) {
|
||||||
|
return <div>Loading...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If authenticated, render the component. Otherwise, redirect to the login page
|
||||||
|
return isAuthenticated ? Element : <Navigate to="/" />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PrivateRoute;
|
export default PrivateRoute;
|
||||||
|
|||||||
37
client/src/components/TokenRefresher.js
Normal file
37
client/src/components/TokenRefresher.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
|
import axios from "axios";
|
||||||
|
import Cookies from "js-cookie";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
const TokenRefresher = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Function to refresh the token
|
||||||
|
const refreshToken = async () => {
|
||||||
|
try {
|
||||||
|
const response = await axios.post("http://localhost:8080/api/refresh", {}, {
|
||||||
|
withCredentials: true, // Make sure cookies are sent
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to refresh token", error);
|
||||||
|
|
||||||
|
// If refreshing the token fails (e.g., due to expiration), log out the user
|
||||||
|
Cookies.remove("token"); // Optionally, clear any existing cookies
|
||||||
|
navigate("/"); // Redirect to login page
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the interval to call refresh every 55 minutes (3300000 ms)
|
||||||
|
const refreshInterval = setInterval(() => {
|
||||||
|
refreshToken();
|
||||||
|
}, 3300000);
|
||||||
|
|
||||||
|
// Clean up the interval on component unmount
|
||||||
|
return () => clearInterval(refreshInterval);
|
||||||
|
}, [navigate]); // Empty dependency array to run once when component mounts
|
||||||
|
|
||||||
|
return null; // This component doesn't render anything
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TokenRefresher;
|
||||||
23
server/package-lock.json
generated
23
server/package-lock.json
generated
@@ -12,6 +12,7 @@
|
|||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"connect-mongo": "^5.1.0",
|
"connect-mongo": "^5.1.0",
|
||||||
|
"cookie-parser": "^1.4.7",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"crypto": "^1.0.1",
|
"crypto": "^1.0.1",
|
||||||
"csv-writer": "^1.6.0",
|
"csv-writer": "^1.6.0",
|
||||||
@@ -436,6 +437,28 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cookie-parser": {
|
||||||
|
"version": "1.4.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz",
|
||||||
|
"integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cookie": "0.7.2",
|
||||||
|
"cookie-signature": "1.0.6"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cookie-parser/node_modules/cookie": {
|
||||||
|
"version": "0.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||||
|
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cookie-signature": {
|
"node_modules/cookie-signature": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"connect-mongo": "^5.1.0",
|
"connect-mongo": "^5.1.0",
|
||||||
|
"cookie-parser": "^1.4.7",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"crypto": "^1.0.1",
|
"crypto": "^1.0.1",
|
||||||
"csv-writer": "^1.6.0",
|
"csv-writer": "^1.6.0",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const bodyParser = require("body-parser");
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
const bcrypt = require("bcryptjs");
|
const bcrypt = require("bcryptjs");
|
||||||
const jwt = require("jsonwebtoken");
|
const jwt = require("jsonwebtoken");
|
||||||
|
const cookieparser = require("cookie-parser");
|
||||||
require("dotenv").config();
|
require("dotenv").config();
|
||||||
|
|
||||||
// Import Routes
|
// Import Routes
|
||||||
@@ -34,6 +35,7 @@ const PORT = 8080;
|
|||||||
|
|
||||||
// Middleware
|
// Middleware
|
||||||
app.use(cors({ origin: "http://localhost:3000", credentials: true }));
|
app.use(cors({ origin: "http://localhost:3000", credentials: true }));
|
||||||
|
app.use(cookieparser());
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(bodyParser.urlencoded({ extended: true }));
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
app.use(
|
app.use(
|
||||||
@@ -73,7 +75,7 @@ app.get(
|
|||||||
(req, res) => {
|
(req, res) => {
|
||||||
const token = jwt.sign({ userId: req.user._id }, process.env.JWT_SECRET, { expiresIn: "1h" });
|
const token = jwt.sign({ userId: req.user._id }, process.env.JWT_SECRET, { expiresIn: "1h" });
|
||||||
// Set token as a cookie or send it in the response
|
// Set token as a cookie or send it in the response
|
||||||
res.cookie("token", token, { httpOnly: false, secure: false });
|
res.cookie("token", token, { httpOnly: true, secure: false, maxAge: 3600000 });
|
||||||
res.redirect("http://localhost:3000/Welcome"); // Redirect to a frontend route after successful login
|
res.redirect("http://localhost:3000/Welcome"); // Redirect to a frontend route after successful login
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -101,16 +103,16 @@ app.post("/api/register", async (req, res) => {
|
|||||||
await user.save();
|
await user.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
req.login(user, (err) => {
|
// Generate a JWT token using the user's ID
|
||||||
if (err) {
|
const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: "1h" });
|
||||||
console.error("Error logging in user after registration:", err);
|
|
||||||
return res.status(500).send("Internal server error");
|
// Set the token as a cookie
|
||||||
}
|
res.cookie("token", token, { httpOnly: true, secure: false, maxAge: 3600000 }); // 1 hour expiry
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
message: "Registered and logged in successfully",
|
message: "Registered and logged in successfully",
|
||||||
user,
|
user,
|
||||||
});
|
});
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error registering user:", error);
|
console.error("Error registering user:", error);
|
||||||
res.status(400).send("Registration failed");
|
res.status(400).send("Registration failed");
|
||||||
@@ -129,27 +131,83 @@ app.post("/api/login", (req, res, next) => {
|
|||||||
if (err) {
|
if (err) {
|
||||||
return res.status(500).json({ message: "Internal server error" });
|
return res.status(500).json({ message: "Internal server error" });
|
||||||
}
|
}
|
||||||
|
// Generate a JWT token using the user's ID
|
||||||
|
const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: "1h" });
|
||||||
|
|
||||||
|
// Set the token as a cookie
|
||||||
|
res.cookie("token", token, { httpOnly: true, secure: false, maxAge: 3600000 }); // 1 hour expiry
|
||||||
return res.status(200).json({ message: "Login successful", user });
|
return res.status(200).json({ message: "Login successful", user });
|
||||||
});
|
});
|
||||||
})(req, res, next);
|
})(req, res, next);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/auth/logout", function (req, res) {
|
app.get("/auth/logout", function (req, res) {
|
||||||
req.logout((err) => {
|
try {
|
||||||
|
// Clear the token cookie
|
||||||
|
res.clearCookie("token", {
|
||||||
|
httpOnly: true, // Ensure it matches the cookie options you set earlier
|
||||||
|
secure: false, // Match the "secure" option from cookie settings
|
||||||
|
sameSite: "lax", // Ensure this matches the original cookie configuration
|
||||||
|
});
|
||||||
|
|
||||||
|
// Destroy the session if used (optional, if sessions are implemented)
|
||||||
|
if (req.session) {
|
||||||
|
req.session.destroy((err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.error("Error destroying session:", err);
|
||||||
return res.status(500).json({ message: "Error logging out" });
|
return res.status(500).json({ message: "Error logging out" });
|
||||||
}
|
}
|
||||||
req.session.destroy(function (err) {
|
res.status(200).json({ message: "Logout successful, session destroyed" });
|
||||||
if (err) {
|
});
|
||||||
console.log(err);
|
} else {
|
||||||
return res.status(500).json({ message: "Error destroying session" });
|
// If no session, simply respond with success
|
||||||
|
res.status(200).json({ message: "Logout successful, cookie cleared" });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error logging out:", err);
|
||||||
|
res.status(500).json({ message: "Error logging out" });
|
||||||
}
|
}
|
||||||
res.json({ message: "Logout successful" });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Refresh Token Endpoint
|
||||||
|
app.post("/api/refresh", (req, res) => {
|
||||||
|
const refreshToken = req.cookies.token;
|
||||||
|
console.log(refreshToken);
|
||||||
|
|
||||||
|
if (!refreshToken) {
|
||||||
|
return res.status(401).json({ message: "No refresh token, authorization denied" });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const decoded = jwt.verify(refreshToken, process.env.JWT_SECRET);
|
||||||
|
const newToken = jwt.sign({ userId: decoded.userId }, process.env.JWT_SECRET, { expiresIn: "1h" });
|
||||||
|
|
||||||
|
return res
|
||||||
|
.cookie("token", newToken, { httpOnly: true, maxAge: 3600000 }) // Set new access token
|
||||||
|
.status(200)
|
||||||
|
.json({ message: "Token refreshed" });
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error refreshing token:", err);
|
||||||
|
res.status(401).json({ message: "Invalid or expired refresh token" });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get("/api/auth-check", (req, res) => {
|
||||||
|
const token = req.cookies.token; // Retrieve the httpOnly cookie
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
return res.status(401).json({ message: "Unauthorized" });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
jwt.verify(token, process.env.JWT_SECRET); // Verify the token
|
||||||
|
res.status(200).json({ authenticated: true }); // Valid token
|
||||||
|
} catch (err) {
|
||||||
|
res.status(401).json({ message: "Unauthorized" }); // Invalid token
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// User Profile Route
|
// User Profile Route
|
||||||
app.get("/api/user/profile", async (req, res) => {
|
app.get("/api/user/profile", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user