Commit 186b4e79 authored by Tharaka it19975696's avatar Tharaka it19975696 :headphones:

Authentication module improvements and logout function

parent bc901804
...@@ -4,8 +4,9 @@ const cors = require('cors'); ...@@ -4,8 +4,9 @@ const cors = require('cors');
const { PrismaClient } = require('@prisma/client'); const { PrismaClient } = require('@prisma/client');
const { hashPassword, verifyPassword } = require('./utils/authHelpers'); const { hashPassword, verifyPassword } = require('./utils/authHelpers');
const jwt = require('jsonwebtoken'); const jwt = require('jsonwebtoken');
// Uncomment the line below if you're using the authenticateToken middleware
// const { authenticateToken } = require('./middleware/authenticateToken'); // Uncomment if we decide to use the authenticateToken middleware
//const { authenticateToken } = require('./middleware/authenticateToken');
const app = express(); const app = express();
const prisma = new PrismaClient(); const prisma = new PrismaClient();
...@@ -21,99 +22,95 @@ if (!JWT_SECRET) { ...@@ -21,99 +22,95 @@ if (!JWT_SECRET) {
process.exit(1); process.exit(1);
} }
// Helper function to create a JWT token
const createToken = (userId, role) => {
return jwt.sign(
{ userId, role },
JWT_SECRET,
{ expiresIn: '1h' }
);
};
// Registration endpoint for a student // Registration endpoint for a student
app.post('/register/student', async (req, res) => { app.post('/register/student', async (req, res) => {
try { try {
const { username, password } = req.body; const { username, password } = req.body;
// Check if username is already taken
const existingStudent = await prisma.student.findUnique({ const existingStudent = await prisma.student.findUnique({
where: { username: username } where: { username }
}); });
if (existingStudent) { if (existingStudent) {
return res.status(409).json({ message: 'Username already exists' }); return res.status(409).send('Username already exists');
} }
const hashedPassword = await hashPassword(password); const hashedPassword = await hashPassword(password);
const student = await prisma.student.create({ const student = await prisma.student.create({
data: { username, password: hashedPassword } data: { username, password: hashedPassword }
}); });
// Exclude password from the response for security reasons
const { password: _, ...studentWithoutPassword } = student; const token = createToken(student.id, 'student');
res.status(201).json(studentWithoutPassword); res.status(201).json({ token }); // Send the token in the response
} catch (error) { } catch (error) {
res.status(500).json({ message: 'Error registering student', error: error.message }); res.status(500).send('Error registering student');
} }
}); });
// Registration endpoint for a teacher // Registration endpoint for a teacher
app.post('/register/teacher', async (req, res) => { app.post('/register/teacher', async (req, res) => {
try { try {
const { username, password } = req.body; const { username, password } = req.body;
// Check if username is already taken
const existingTeacher = await prisma.teacher.findUnique({ const existingTeacher = await prisma.teacher.findUnique({
where: { username } where: { username }
}); });
if (existingTeacher) { if (existingTeacher) {
return res.status(409).json({ message: 'Username already exists' }); return res.status(409).send('Username already exists');
} }
const hashedPassword = await hashPassword(password); const hashedPassword = await hashPassword(password);
const teacher = await prisma.teacher.create({ const teacher = await prisma.teacher.create({
data: { username, password: hashedPassword } data: { username, password: hashedPassword }
}); });
// Exclude password from the response for security reasons
const { password: _, ...teacherWithoutPassword } = teacher; const token = createToken(teacher.id, 'teacher');
res.status(201).json(teacherWithoutPassword); res.status(201).json({ token }); // Send the token in the response
} catch (error) { } catch (error) {
res.status(500).json({ message: 'Error registering teacher', error: error.message }); res.status(500).send('Error registering teacher');
} }
}); });
// Login endpoint for a student // Login endpoint for a student
app.post('/login/student', async (req, res) => { app.post('/login/student', async (req, res) => {
try { try {
const { username, password } = req.body; const { username, password } = req.body;
const student = await prisma.student.findUnique({ const student = await prisma.student.findUnique({
where: { username } where: { username }
}); });
if (!student || !(await verifyPassword(password, student.password))) { if (!student || !(await verifyPassword(password, student.password))) {
return res.status(401).json({ message: 'Invalid credentials' }); return res.status(401).send('Invalid credentials');
} }
const token = jwt.sign({ studentId: student.studentID }, 'YOUR_SECRET_KEY', { expiresIn: '1h' }); const token = createToken(student.id, 'student');
res.json({ token }); res.json({ token }); // Send the token in the response
} catch (error) { } catch (error) {
res.status(500).json({ message: 'Error logging in', error: error.message }); res.status(500).send('Error logging in');
}
});
// Similar login endpoint for teachers
app.post('/login/teacher', async (req, res) => {
const { username, password } = req.body;
try {
const teacher = await prisma.teacher.findUnique({ where: { username } });
if (!teacher) {
return res.status(401).json({ message: 'Invalid username or password' });
} }
});
const isPasswordValid = await verifyPassword(password, teacher.password); // Login endpoint for teachers
if (!isPasswordValid) { app.post('/login/teacher', async (req, res) => {
return res.status(401).json({ message: 'Invalid username or password' }); try {
const { username, password } = req.body;
const teacher = await prisma.teacher.findUnique({
where: { username }
});
if (!teacher || !(await verifyPassword(password, teacher.password))) {
return res.status(401).send('Invalid credentials');
} }
const token = createToken(teacher.id, 'teacher');
const token = jwt.sign({ teacherId: teacher.teacherID }, process.env.JWT_SECRET, { expiresIn: '1h' }); res.json({ token }); // Send the token in the response
res.json({ token }); } catch (error) {
} catch (err) { res.status(500).send('Error logging in');
res.status(500).json({ message: 'Something went wrong', error: err.message });
} }
}); });
/* Commented out due to repeated Logic Conflicts // Start the server
// Example of a protected route const PORT = process.env.PORT || 5000;
app.get('/protected-route', authenticateToken, (req, res) => { app.listen(PORT, () => {
res.json({ message: 'This is a protected route' }); console.log(`Server running on port ${PORT}`);
}); });
*/ \ No newline at end of file
// Start the server
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
...@@ -2,14 +2,13 @@ ...@@ -2,14 +2,13 @@
const jwt = require('jsonwebtoken'); const jwt = require('jsonwebtoken');
function authenticateToken(req, res, next) { function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization']; const token = req.headers['authorization']?.split(' ')[1]; // Using optional chaining
const token = authHeader && authHeader.split(' ')[1]; if (!token) return res.sendStatus(401); // Using negation (!) for clarity
if (token == null) return res.sendStatus(401);
jwt.verify(token, process.env.JWT_SECRET, (err, user) => { jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) return res.sendStatus(403); if (err) return res.sendStatus(403); // Forbidden if there's an error
req.user = user; req.user = user;
next(); next(); // Token is valid, proceed to the next middleware
}); });
} }
......
// React core
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
// React Router DOM for routing
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'; import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
// Components
import Home from './Home'; import Home from './Home';
import About from './About'; import About from './About';
import Insights from './Insights'; import Insights from './Insights';
...@@ -11,13 +16,15 @@ function App() { ...@@ -11,13 +16,15 @@ function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false); const [isLoggedIn, setIsLoggedIn] = useState(false);
useEffect(() => { useEffect(() => {
// Assume token is stored in localStorage after successful login/registration
const token = localStorage.getItem('token'); const token = localStorage.getItem('token');
if (token) { setIsLoggedIn(!!token);
setIsLoggedIn(true);
}
}, []); }, []);
const handleLogout = () => {
localStorage.removeItem('token');
setIsLoggedIn(false);
};
const handleLoginSuccess = () => { const handleLoginSuccess = () => {
setIsLoggedIn(true); setIsLoggedIn(true);
}; };
...@@ -28,20 +35,27 @@ function App() { ...@@ -28,20 +35,27 @@ function App() {
return ( return (
<Router> <Router>
<Navbar /> <Navbar isLoggedIn={isLoggedIn} onLogout={handleLogout} />
<Routes> <Routes>
{/* Redirect to home if user is already logged in */} <Route path="/login" element={
<Route path="/login" element={!isLoggedIn ? <LoginPage onLoginSuccess={handleLoginSuccess} /> : <Navigate replace to="/home" />} /> !isLoggedIn ? (
<Route path="/register" element={!isLoggedIn ? <RegisterPage onRegistrationSuccess={handleRegistrationSuccess} /> : <Navigate replace to="/home" />} /> <LoginPage onLoginSuccess={handleLoginSuccess} />
) : (
{/* Protected routes that require user to be logged in */} <Navigate replace to="/home" />
)
} />
<Route path="/register" element={
!isLoggedIn ? (
<RegisterPage onRegistrationSuccess={handleRegistrationSuccess} />
) : (
<Navigate replace to="/home" />
)
} />
<Route path="/about" element={isLoggedIn ? <About /> : <Navigate replace to="/login" />} /> <Route path="/about" element={isLoggedIn ? <About /> : <Navigate replace to="/login" />} />
<Route path="/insights" element={isLoggedIn ? <Insights /> : <Navigate replace to="/login" />} /> <Route path="/insights" element={isLoggedIn ? <Insights /> : <Navigate replace to="/login" />} />
<Route path="/home" element={isLoggedIn ? <Home /> : <Navigate replace to="/login" />} /> <Route path="/home" element={isLoggedIn ? <Home /> : <Navigate replace to="/login" />} />
<Route path="/" element={<Navigate replace to={isLoggedIn ? "/home" : "/login"} />} />
{/* Default route */} <Route path="*" element={<Navigate replace to={isLoggedIn ? "/home" : "/login"} />} />
<Route path="/" element={<Navigate replace to={isLoggedIn ? "/home" : "/register"} />} />
<Route path="*" element={<Navigate replace to={isLoggedIn ? "/home" : "/register"} />} />
</Routes> </Routes>
</Router> </Router>
); );
......
// Navbar.js // Navbar.js
import React from 'react'; import React from 'react';
import { NavLink } from 'react-router-dom'; import { NavLink, useNavigate } from 'react-router-dom';
const Navbar = ({ isLoggedIn, onLogout }) => {
const navigate = useNavigate();
const Navbar = () => {
const activeLinkStyle = ({ isActive }) => const activeLinkStyle = ({ isActive }) =>
isActive ? 'text-blue-500' : 'text-white'; isActive ? 'text-blue-500' : 'text-white';
const logout = () => {
// Clear the user token and any other relevant data
localStorage.removeItem('token');
// Call the onLogout prop to update the App state
onLogout();
// Redirect to the login page
navigate('/login');
};
return ( return (
<nav className="bg-gray-800 p-4"> <nav className="bg-gray-800 p-4">
<ul className="flex justify-start items-center space-x-4"> <ul className="flex justify-between items-center space-x-4">
<li> <div className="flex space-x-4">
<NavLink <li>
to="/" <NavLink to="/" className={activeLinkStyle} aria-label="Home">
className={activeLinkStyle} Home
aria-label="Home" </NavLink>
> </li>
Home <li>
</NavLink> <NavLink to="/about" className={activeLinkStyle} aria-label="About">
</li> About
<li> </NavLink>
<NavLink </li>
to="/about" <li>
className={activeLinkStyle} <NavLink to="/insights" className={activeLinkStyle} aria-label="Insights">
aria-label="About" Insights
> </NavLink>
About </li>
</NavLink> </div>
</li> {isLoggedIn && (
<li> <li>
<NavLink <button onClick={logout} className="text-white hover:text-red-500 transition duration-300">
to="/insights" Logout
className={activeLinkStyle} </button>
aria-label="Insights" </li>
> )}
Insights
</NavLink>
</li>
</ul> </ul>
</nav> </nav>
); );
......
// src/components/RegisterPage.js // src/components/RegisterPage.js
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Link } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import { registerStudent, registerTeacher } from '../api/api'; // Adjusted import path import { registerStudent, registerTeacher } from '../api/api'; // Assuming these are API calls returning promises
const RegisterPage = () => { const RegisterPage = () => {
const [userData, setUserData] = useState({ const [userData, setUserData] = useState({
username: '', username: '',
password: '', password: '',
role: 'student', // default role role: 'student', // default role
}); });
const navigate = useNavigate();
const handleChange = (e) => { const handleChange = (e) => {
setUserData({ ...userData, [e.target.name]: e.target.value }); setUserData({ ...userData, [e.target.name]: e.target.value });
}; };
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
try { try {
if (userData.role === 'student') { let token;
await registerStudent({ username: userData.username, password: userData.password }); if (userData.role === 'student') {
} else { const response = await registerStudent({ username: userData.username, password: userData.password });
await registerTeacher({ username: userData.username, password: userData.password }); token = response.token;
} } else {
alert('Registration successful'); const response = await registerTeacher({ username: userData.username, password: userData.password });
} catch (error) { token = response.token;
alert(error.message); }
}
}; if (token) {
console.log("Token received:", token); // Log for debugging
localStorage.setItem('token', token); // Store the token
navigate('/'); // Redirect to the homepage
} else {
console.error("No token received");
alert('Registration successful but no token received');
}
} catch (error) {
console.error("Registration failed:", error);
alert(error.message);
}
};
return ( return (
<div className="flex items-center justify-center h-screen bg-gray-200"> <div className="flex items-center justify-center h-screen bg-gray-200">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment