From bc14c82a893f6863410e9538341a6472c8cd61fb Mon Sep 17 00:00:00 2001 From: biswarup-naha Date: Thu, 23 May 2024 01:27:05 +0530 Subject: [PATCH] Secure schema-based Session Handling with expiration & persistence --- server/controllers/Auth.js | 115 ++++++++++++++++++++++++++----------- server/models/session.js | 22 +++++++ src/pages/Login.jsx | 1 + src/pages/Signup.jsx | 4 +- 4 files changed, 107 insertions(+), 35 deletions(-) create mode 100644 server/models/session.js diff --git a/server/controllers/Auth.js b/server/controllers/Auth.js index ec8d842..31e876c 100644 --- a/server/controllers/Auth.js +++ b/server/controllers/Auth.js @@ -2,6 +2,7 @@ const bcrypt = require("bcrypt"); const User = require("../models/studentLoginInfo"); const jwt = require("jsonwebtoken"); const Canteen = require("../models/canteenLoginInfo"); +const Session = require("../models/session"); require("dotenv").config(); @@ -38,6 +39,8 @@ exports.studentSignup = async (req, res) => { password: hashedPassword, }); + await user.save(); + return res.status(200).json({ success: true, message: "User created succesfully", @@ -81,22 +84,35 @@ exports.studentLogin = async (req, res) => { let token = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: "2h", }); + + // creating a session + const session = new Session({ userId: user._id, token }); + await session.save(); + user = user.toObject(); user.token = token; user.password = undefined; console.log(user); - const options = { - expires: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000), - httpOnly: true, - }; + // const options = { + // expires: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000), + // httpOnly: true, + // }; + + // res.cookie("token", token, options).status(200).json({ + // success: true, + // token, + // user, + // message: "User logged in succesfully", + // }); - res.cookie("token", token, options).status(200).json({ - success: true, - token, - user, - message: "User logged in succesfully", + // Setting cookie + res.cookie("token", token, { + httpOnly: true, + secure: true, + maxAge: 3600000, }); + res.json({ success: true, message: "Logged in successfully", token, user }); } else { return res.status(403).json({ success: false, @@ -127,14 +143,24 @@ exports.studentLogout = async (req, res) => { } ); - const options = { - httpOnly: true, - }; + // const options = { + // httpOnly: true, + // }; - return res.status(200).clearCookie("token", options).json({ - success: true, - message: "User Logged off successfully.", - }); + // return res.status(200).clearCookie("token", options).json({ + // success: true, + // message: "User Logged off successfully.", + // }); + + const token = + req.cookies?.token || + req?.header("Authorization")?.replace("Bearer ", ""); + + if (token) { + await Session.findOneAndDelete({ token }); + res.clearCookie("token"); + } + res.status(200).json({ success: true, message: "Logged out successfully" }); } catch (error) { console.log(error); return res.status(500).json({ @@ -251,18 +277,31 @@ exports.canteenLogin = async (req, res) => { canteen.password = undefined; console.log(canteen); - const options = { - expires: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000), + // const options = { + // expires: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000), + // httpOnly: true, + // }; + + // res.cookie("token", token, options).status(200).json({ + // success: true, + // token, + // canteen, + // message: "Canteen logged in succesfully", + // cantId: canteen._id, + // }); + + // Create session + const session = new Session({ userId: canteen._id, token }); + await session.save(); + + // Set cookie + res.cookie("token", token, { httpOnly: true, - }; - - res.cookie("token", token, options).status(200).json({ - success: true, - token, - canteen, - message: "Canteen logged in succesfully", - cantId: canteen._id, + secure: true, + maxAge: 3600000, }); + res.json({ success: true, message: "Logged in successfully", token, canteen, cantId: canteen._id }); + } else { return res.status(403).json({ success: false, @@ -293,14 +332,24 @@ exports.canteenLogout = async (req, res) => { } ); - const options = { - httpOnly: true, - }; + // const options = { + // httpOnly: true, + // }; - return res.status(200).clearCookie("token", options).json({ - success: true, - message: "Canteen User Logged off successfully.", - }); + // return res.status(200).clearCookie("token", options).json({ + // success: true, + // message: "Canteen User Logged off successfully.", + // }); + + const token = + req.cookies?.token || + req?.header("Authorization")?.replace("Bearer ", ""); + + if (token) { + await Session.findOneAndDelete({ token }); + res.clearCookie("token"); + } + res.status(200).json({ success: true, message: "Logged out successfully" }); } catch (error) { console.log(error); return res.status(500).json({ diff --git a/server/models/session.js b/server/models/session.js new file mode 100644 index 0000000..2f92ba1 --- /dev/null +++ b/server/models/session.js @@ -0,0 +1,22 @@ +const mongoose = require('mongoose'); + +const sessionSchema = new mongoose.Schema({ + userId: { + type: mongoose.Schema.Types.ObjectId, + required: true, + ref: 'Student' + }, + token: { + type: String, + required: true + }, + createdAt: { + type: Date, + default: Date.now, + expires: 3600 + } +}); + +const Session = mongoose.model('Session', sessionSchema); + +module.exports = Session; diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx index 55ddf95..b359ecd 100644 --- a/src/pages/Login.jsx +++ b/src/pages/Login.jsx @@ -33,6 +33,7 @@ function Login() { if(formData.accountType === "User"){ const apiUrl = `${process.env.REACT_APP_BASE_URL}/studentLogin`; + // const apiUrl = `http://localhost:4000/api/v1/studentLogin`; axios.post(apiUrl , formData) .then((response)=>{ diff --git a/src/pages/Signup.jsx b/src/pages/Signup.jsx index ef7705e..e6a6005 100644 --- a/src/pages/Signup.jsx +++ b/src/pages/Signup.jsx @@ -84,8 +84,8 @@ function Signup() { if (lowerValidated && upperValidated && numberValidated && specialValidated && lengthValidated) { if (formData.accountType === "User") { - const apiUrl = `${process.env.REACT_APP_BASE_URL}/studentSignup`; - // const apiUrl = `http://localhost:4000/api/v1/studentSignup`; + // const apiUrl = `${process.env.REACT_APP_BASE_URL}/studentSignup`; + const apiUrl = `http://localhost:4000/api/v1/studentSignup`; axios .post(apiUrl, formData)