Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into task/229/int-create…
Browse files Browse the repository at this point in the history
…-issue-db
nadamels committed Nov 24, 2023
2 parents 09edac2 + 0685744 commit 01d882e
Showing 10 changed files with 169 additions and 78 deletions.
19 changes: 18 additions & 1 deletion back-end/app.js
Original file line number Diff line number Diff line change
@@ -63,7 +63,24 @@ app.use(cookieParser());
app.use(passport.initialize());

// protected routes setup
app.use(checkJWT);
// app.use(checkJWT);

// Logout endpoint to remove token from cookie
app.get('/api/logout', (req, res) => {
res.cookie('jwt', '', { maxAge: 0 }); // Clear the cookie
console.log('Logged out successfully');
res.json({ message: 'Logged out successfully' });
});

app.get("/api/check-auth", checkJWT, (req, res) => {
if (req.user) {
console.log("User authenticated!!!!!!!");
res.status(200).json({ authenticated: true, user: req.user });
} else {
// User is not authenticated
res.status(401).json({ authenticated: false, message: "User not authenticated" });
}
});

// ROUTE HANDLERS

19 changes: 11 additions & 8 deletions back-end/src/middlewares/checkJWT.js
Original file line number Diff line number Diff line change
@@ -2,20 +2,23 @@ import passport from "../../config/passportConfig.js";

export default function checkJWT(req, res, next) {
passport.authenticate("jwt", { session: false }, (err, user, info) => {
if (err || !user) {
if (err) {
console.error("Error during authentication:", err);
return res.status(500).json({ message: "Internal server error" });
}

if (!user) {
if (req.path !== "/") {
console.log("User not authenticated. Redirecting to login page.");
next();
// return res.redirect("/");
// redirecting won't work as the routing is managed by react in the frontend, not express
// we would need to send some json data here rather that indicates a redirect in the frontend
console.log("User not authenticated. Sending response.");
// Send a 401 Unauthorized response with a message
return res.status(401).json({ authenticated: false, message: "User not authenticated" });
} else {
return next();
}
} else {
req.user = user; // Forward user information to the next middleware
req.user = user;
console.log("User authenticated.");
next();
}
})(req, res, next);
}
}
50 changes: 31 additions & 19 deletions front-end/src/App.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,40 @@
// import { useState, useEffect } from "react";

import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import "./App.css";
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
import { useContext } from 'react';
import { AuthProvider, AuthContext } from "./components/general/AuthContext/AuthContext"; // Import AuthProvider and AuthContext
import StudentDashboard from "./layouts/StudentDashboard/StudentDashboard";
import IssueDetails from "./components/student/StudentIssueOverlay/DesktopIssueDetails";
import LoginPage from "./layouts/LoginPage/LoginPage.js";
import LoginPage from "./layouts/LoginPage/LoginPage";
import AdminDashboard from "./layouts/AdminDashboard/AdminDashboard";
import AdminIssueDetails from "./components/admin/AdminIssueDetails/AdminIssueDetails";

const App = (props) => {
// ProtectedRoute component
const ProtectedRoute = ({ component: Component }) => {
const { isAuthenticated } = useContext(AuthContext); // Use AuthContext to get isAuthenticated

return (
isAuthenticated
? <Component />
: <Navigate to="/" />
);
};

const App = () => {
return (
<div className="App">
<Router>
<main className="App-main">
<Routes>
<Route path="/" element={<LoginPage />} />
<Route path="/student/dashboard" element={<StudentDashboard />} />
<Route path="/issue/:index" element={<IssueDetails />} />
<Route path="admin/dashboard" element={<AdminDashboard />} />
<Route path="admin/dashboard/:index" element={<AdminIssueDetails />} />
</Routes>
</main>
</Router>
</div>
<AuthProvider> {/* Wrap the application in AuthProvider */}
<div className="App">
<Router>
<main className="App-main">
<Routes>
<Route path="/" element={<LoginPage />} />
<Route path="/student/dashboard" element={<ProtectedRoute component={StudentDashboard} />} />
<Route path="/issue/:index" element={<ProtectedRoute component={IssueDetails} />} />
<Route path="/admin/dashboard" element={<ProtectedRoute component={AdminDashboard} />} />
<Route path="/admin/dashboard/:index" element={<ProtectedRoute component={AdminIssueDetails} />} />
</Routes>
</main>
</Router>
</div>
</AuthProvider>
);
};

18 changes: 15 additions & 3 deletions front-end/src/components/admin/AdminNavbar/AdminNavbar.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
import { useContext } from 'react'; // Import useContext
import { useNavigate } from 'react-router-dom';
import { AuthContext } from "../../general/AuthContext/AuthContext"; // Import AuthContext
import logoutImage from "../../../assets/images/logout-icon.png";
import "./AdminNavbar.css";
import axios from "axios";

export default function AdminNavbar({ adminName, unresolvedIssues }) {
// const [showNotification, setShowNotification] = useState(false);
const navigate = useNavigate();
const { setIsAuthenticated } = useContext(AuthContext); // Use AuthContext
const BASE_URL = process.env.REACT_APP_BACKEND_URL;

const handleLogout = () => {
navigate('/');
axios.get(`${BASE_URL}/api/logout`, { withCredentials: true })
.then(() => {
setIsAuthenticated(false); // Update state to reflect that user is logged out
localStorage.removeItem('isAuthenticated'); // Clear the authentication flag from browser storage
navigate('/'); // Redirect to login page
})
.catch(error => {
console.error("Logout error:", error);
// Handle error
});
};

return (
@@ -25,6 +38,5 @@ export default function AdminNavbar({ adminName, unresolvedIssues }) {
/>
</div>
</div>

);
}
34 changes: 34 additions & 0 deletions front-end/src/components/general/AuthContext/AuthContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { createContext, useState, useEffect } from 'react';
import axios from 'axios';

export const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
const [isAuthenticated, setIsAuthenticated] = useState(
localStorage.getItem('isAuthenticated') === 'true'
);

const BASE_URL = process.env.REACT_APP_BACKEND_URL;
// Function to check authentication
const checkAuthentication = () => {
axios.get(`${BASE_URL}/api/check-auth`, { withCredentials: true })
.then((response) => {
setIsAuthenticated(response.data.authenticated);
localStorage.setItem('isAuthenticated', 'true');
})
.catch((error) => {
console.error("Authentication error:", error);
setIsAuthenticated(false);
localStorage.setItem('isAuthenticated', 'false');
});
};

useEffect(() => {
checkAuthentication();
}, []);

return (
<AuthContext.Provider value={{ isAuthenticated, setIsAuthenticated, checkAuthentication }}>
{children}
</AuthContext.Provider>
);
};
13 changes: 13 additions & 0 deletions front-end/src/components/general/SiteWideFooter/SiteWideFooter.css
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
/* .footer-container {
padding: 10px;
background-color: var(--primary);
color: white;
font-family: var(--primary-font);
margin-top: auto;
} */

.footer-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 10px;
background-color: var(--primary);
color: white;
font-family: var(--primary-font);
margin-top: auto;
text-align: center;
}

.footer-container .info>div {
16 changes: 14 additions & 2 deletions front-end/src/components/student/StudentNavbar/StudentNavbar.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { useState } from "react";
import { useContext, useState } from "react";
import { useNavigate } from 'react-router-dom';
import axios from "axios";
import logoutImage from "../../../assets/images/logout-icon.png";
import notificationIcon from "../../../assets/images/notification-icon.png";
import { AuthContext } from "../../general/AuthContext/AuthContext";
import "./StudentNavbar.css";

export default function StudentNavbar({ studentName }) {
const [notificationTimer, setNotificationTimer] = useState(null);
const [showNotificationOverlay, setShowNotificationOverlay] = useState(false);
// const [showNotification, setShowNotification] = useState(false);
const { setIsAuthenticated } = useContext(AuthContext); // Use AuthContext
const navigate = useNavigate();
const BASE_URL = process.env.REACT_APP_BACKEND_URL;

// Handles notification click to display a notification overlay
const handleNotificationClick = () => {
@@ -37,7 +41,15 @@ export default function StudentNavbar({ studentName }) {
};

const handleLogout = () => {
navigate('/');
axios.get(`${BASE_URL}/api/logout`, { withCredentials: true })
.then(() => {
setIsAuthenticated(false);
localStorage.removeItem('isAuthenticated'); // Clear the authentication flag from browser storage
navigate('/');
})
.catch(error => {
console.error("Logout error:", error);
});
};

return (
2 changes: 1 addition & 1 deletion front-end/src/layouts/AdminDashboard/AdminDashboard.js
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ import SiteWideFooter from '../../components/general/SiteWideFooter/SiteWideFoot
import axios from "axios";
export const currentSetDepartment = "IT";

function AdminDashboard() {
function AdminDashboard({ setIsAuthenticated }) {
const [searchText, setSearchText] = useState('');
const [issues, setIssues] = useState([]);
const [activeOptionsOverlay, setActiveOptionsOverlay] = useState(null);
74 changes: 31 additions & 43 deletions front-end/src/layouts/LoginPage/LoginPage.js
Original file line number Diff line number Diff line change
@@ -1,85 +1,73 @@
import { useState } from "react";
import axios from "axios";
import { useNavigate } from "react-router-dom";
import "./LoginPage.css";
import logo from "../../assets/images/nyu-logo.png";
import LoginPageNavbar from "../../components/general/LoginPageNavbar/LoginPageNavbar";
import { useState, useContext } from 'react';
import axios from 'axios';
import { useNavigate } from 'react-router-dom';
import { AuthContext } from '../../components/general/AuthContext/AuthContext'; // Import AuthContext
import './LoginPage.css';
import logo from '../../assets/images/nyu-logo.png';
import LoginPageNavbar from '../../components/general/LoginPageNavbar/LoginPageNavbar';

const LoginPage = () => {
// state variable to keep track of the user type
const [userType, setUserType] = useState("student");
const BASE_URL = process.env.REACT_APP_BACKEND_URL; // base url for the backend

// useNavigate hook later to be used to redirect to the student dashboard
const [userType, setUserType] = useState('student');
const { setIsAuthenticated } = useContext(AuthContext); // Use useContext to access AuthContext
const navigate = useNavigate();
const BASE_URL = process.env.REACT_APP_BACKEND_URL;

// handleFormSubmit function to handle form submission
const handleFormSubmit = async (event) => {
// preventing reload of the page
event.preventDefault();

// creating a new FormData object(key-value pairs representing form fields and values
const formData = new FormData(event.target);
const urlEncodedData = new URLSearchParams(formData);
let auth = false;

try {
// making a POST request to the backend
const response = await axios.post(
`${BASE_URL}/api/login/${userType}`,
urlEncodedData,
{
withCredentials: true
}
{ withCredentials: true }
);
// The response data from the server
auth = response.data.authenticated;

if (auth) {
setIsAuthenticated(true); // Update the authentication state
localStorage.setItem('isAuthenticated', 'true');
if (userType === 'student') {
navigate('/student/dashboard');
} else if (userType === 'admin') {
navigate('/admin/dashboard');
}
}
} catch (error) {
console.error("Error during form submission:", error);
// In case of error, if you need to access the response provided by the server (if any)
console.error('Error during form submission:', error);
if (error.response) {
const errorData = error.response.data;
console.error("Error data from server:", errorData);
console.error('Error data from server:', errorData);
}
}

if (userType === "student" && auth) {
// Redirect to the student dashboard
navigate("/student/dashboard");
}

if (userType === "admin" && auth) {
navigate("/admin/dashboard");
setIsAuthenticated(false);
localStorage.setItem('isAuthenticated', 'false');
}
};

return (
<div className="login-page">
<LoginPageNavbar />
{/* <p className="login-header">NYU Abu Dhabi Issue Resolution Portal</p> */}

<section className="login-box">
<div className="toggle-container">
<button
// setting the button active if the user type is student
className={userType === "student" ? "active" : ""}
onClick={() => setUserType("student")}
className={userType === 'student' ? 'active' : ''}
onClick={() => setUserType('student')}
>
Student
</button>
<button
// setting the button active if the user type is admin
className={userType === "admin" ? "active" : ""}
onClick={() => setUserType("admin")}
className={userType === 'admin' ? 'active' : ''}
onClick={() => setUserType('admin')}
>
Admin
</button>
</div>
<img src={logo} alt="Logo" className="logo" />
<h3>Log In to Your NYU Account</h3>

{/* Form to take the username and password as input - calls the handleFormSubmit function on form submission */}
<form className="login-form" onSubmit={handleFormSubmit}>
{/* Form fields */}
<label>
<strong>NetID </strong>(e.g., aqe123)
<input type="text" name="username" required />
@@ -91,7 +79,7 @@ const LoginPage = () => {
<em>
<p>
By logging in you agree to <br />
abide by the{" "}
abide by the{' '}
<a
href="https://www.nyu.edu/about/policies-guidelines-compliance/policies-and-guidelines/responsible-use-of-nyu-computers-and-data-policy-on.html"
target="_blank"
2 changes: 1 addition & 1 deletion front-end/src/layouts/StudentDashboard/StudentDashboard.js
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ import SiteWideFooter from "../../components/general/SiteWideFooter/SiteWideFoot
import { CreateRequest } from "../../components/student/CreateRequest/CreateRequest.js";
import axios from "axios";

const StudentDashboard = () => {
const StudentDashboard = ({ setIsAuthenticated }) => {
// State initialization for holding requests and their display variant
const [allRequests, setAllRequests] = useState([]);
const [displayedRequests, setDisplayedRequests] = useState([]);

0 comments on commit 01d882e

Please sign in to comment.