-
Notifications
You must be signed in to change notification settings - Fork 99
SECURITY + AUTH: Secure auth system #236
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| // Middleware to check if user is authenticated | ||
| const isAuthenticated = (req, res, next) => { | ||
| if (req.isAuthenticated()) { | ||
| return next(); | ||
| } | ||
| res.status(401).json({ message: 'Unauthorized - Please log in' }); | ||
| }; | ||
|
|
||
| module.exports = { isAuthenticated }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,42 +1,67 @@ | ||
| const express = require("express"); | ||
| const passport = require("passport"); | ||
| const User = require("../models/User"); | ||
| const { isAuthenticated } = require("../middleware/authMiddleware"); // I'm calling the Middleware I just created here | ||
| const router = express.Router(); | ||
|
|
||
| // Signup route | ||
| router.post("/signup", async (req, res) => { | ||
| const { username, email, password } = req.body; | ||
|
|
||
| const { username, email, password } = req.body; | ||
| try { | ||
| const existingUser = await User.findOne({ email }); | ||
|
|
||
| try { | ||
| const existingUser = await User.findOne( {email} ); | ||
| if (existingUser) | ||
| return res.status(400).json({ message: "User already exists" }); | ||
|
|
||
| if (existingUser) | ||
| return res.status(400).json( {message: 'User already exists'} ); | ||
| const newUser = new User({ username, email, password }); | ||
| await newUser.save(); | ||
| res.status(201).json({ message: "User created successfully" }); | ||
| } catch (err) { | ||
| res | ||
| .status(500) | ||
| .json({ message: "Error creating user", error: err.message }); | ||
| } | ||
|
Comment on lines
+21
to
+24
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid returning raw internal error messages to clients. These responses expose Suggested change- res
- .status(500)
- .json({ message: "Error creating user", error: err.message });
+ console.error("Signup error:", err);
+ res.status(500).json({ message: "Error creating user" });- return res
- .status(500)
- .json({ message: "Login error", error: err.message });
+ console.error("Login error:", err);
+ return res.status(500).json({ message: "Login error" });- return res
- .status(500)
- .json({ message: "Session error", error: err.message });
+ console.error("Session error:", err);
+ return res.status(500).json({ message: "Session error" });- return res
- .status(500)
- .json({ message: "Logout failed", error: err.message });
+ console.error("Logout error:", err);
+ return res.status(500).json({ message: "Logout failed" });Also applies to: 31-34, 42-45, 60-63 🤖 Prompt for AI Agents |
||
| }); | ||
|
|
||
| const newUser = new User( {username, email, password} ); | ||
| await newUser.save(); | ||
| res.status(201).json( {message: 'User created successfully'} ); | ||
| } catch (err) { | ||
| res.status(500).json({ message: 'Error creating user', error: err.message }); | ||
| // I'm writing a much more complex login route here because I want to handle all the possible errors that can happen during the login process, and I also want to return the user data if the login is successful. This way, the frontend can easily access the user information after logging in without needing to make an additional request to get the current user. | ||
| router.post("/login", (req, res, next) => { | ||
| passport.authenticate("local", (err, user, info) => { | ||
| if (err) { | ||
| return res | ||
| .status(500) | ||
| .json({ message: "Login error", error: err.message }); | ||
| } | ||
| if (!user) { | ||
| return res | ||
| .status(401) | ||
| .json({ message: info.message || "Invalid credentials" }); | ||
| } | ||
| req.logIn(user, (err) => { | ||
| if (err) { | ||
| return res | ||
| .status(500) | ||
| .json({ message: "Session error", error: err.message }); | ||
| } | ||
| res.status(200).json({ message: "Login successful", user: req.user }); | ||
| }); | ||
| })(req, res, next); | ||
| }); | ||
|
|
||
| // Login route | ||
| router.post("/login", passport.authenticate('local'), (req, res) => { | ||
| res.status(200).json( { message: 'Login successful', user: req.user } ); | ||
| // Get current authenticated user | ||
| router.get("/me", isAuthenticated, (req, res) => { | ||
| res.status(200).json({ user: req.user }); | ||
| }); | ||
|
|
||
| // Logout route | ||
| router.get("/logout", (req, res) => { | ||
|
|
||
| req.logout((err) => { | ||
|
|
||
| if (err) | ||
| return res.status(500).json({ message: 'Logout failed', error: err.message }); | ||
| else | ||
| res.status(200).json({ message: 'Logged out successfully' }); | ||
| }); | ||
| req.logout((err) => { | ||
| if (err) | ||
| return res | ||
| .status(500) | ||
| .json({ message: "Logout failed", error: err.message }); | ||
| else res.status(200).json({ message: "Logged out successfully" }); | ||
| }); | ||
|
Comment on lines
57
to
+64
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use Logout mutates server session state and should not be a GET endpoint. Keeping it as GET makes cross-site triggering easier and weakens CSRF posture. Suggested change-router.get("/logout", (req, res) => {
+router.post("/logout", (req, res) => {
req.logout((err) => {🤖 Prompt for AI Agents |
||
| }); | ||
|
|
||
| module.exports = router; | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,39 +1,51 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| const express = require('express'); | ||||||||||||||||||||||||||||||||||||||||||||||
| const mongoose = require('mongoose'); | ||||||||||||||||||||||||||||||||||||||||||||||
| const session = require('express-session'); | ||||||||||||||||||||||||||||||||||||||||||||||
| const passport = require('passport'); | ||||||||||||||||||||||||||||||||||||||||||||||
| const bodyParser = require('body-parser'); | ||||||||||||||||||||||||||||||||||||||||||||||
| require('dotenv').config(); | ||||||||||||||||||||||||||||||||||||||||||||||
| const cors = require('cors'); | ||||||||||||||||||||||||||||||||||||||||||||||
| const express = require("express"); | ||||||||||||||||||||||||||||||||||||||||||||||
| const mongoose = require("mongoose"); | ||||||||||||||||||||||||||||||||||||||||||||||
| const session = require("express-session"); | ||||||||||||||||||||||||||||||||||||||||||||||
| const passport = require("passport"); | ||||||||||||||||||||||||||||||||||||||||||||||
| const bodyParser = require("body-parser"); | ||||||||||||||||||||||||||||||||||||||||||||||
| require("dotenv").config(); | ||||||||||||||||||||||||||||||||||||||||||||||
| const cors = require("cors"); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Passport configuration | ||||||||||||||||||||||||||||||||||||||||||||||
| require('./config/passportConfig'); | ||||||||||||||||||||||||||||||||||||||||||||||
| require("./config/passportConfig"); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const app = express(); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // CORS configuration | ||||||||||||||||||||||||||||||||||||||||||||||
| app.use(cors('*')); | ||||||||||||||||||||||||||||||||||||||||||||||
| //Ok so this needs to be taken care of, Cannot expose the BE like that, only FE can call it, else you'll face issues | ||||||||||||||||||||||||||||||||||||||||||||||
| // For testing, if anyone else uses any other port, pls change the default vite port to that port, and also change the FRONTEND_URL in .env to that port, else you'll face CORS issues, and you won't be able to call the BE from the FE. | ||||||||||||||||||||||||||||||||||||||||||||||
| app.use( | ||||||||||||||||||||||||||||||||||||||||||||||
| cors({ | ||||||||||||||||||||||||||||||||||||||||||||||
| origin: process.env.FRONTEND_URL || "http://localhost:5173", | ||||||||||||||||||||||||||||||||||||||||||||||
| credentials: true, | ||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Middleware | ||||||||||||||||||||||||||||||||||||||||||||||
| app.use(bodyParser.json()); | ||||||||||||||||||||||||||||||||||||||||||||||
| app.use(session({ | ||||||||||||||||||||||||||||||||||||||||||||||
| app.use( | ||||||||||||||||||||||||||||||||||||||||||||||
| session({ | ||||||||||||||||||||||||||||||||||||||||||||||
| secret: process.env.SESSION_SECRET, | ||||||||||||||||||||||||||||||||||||||||||||||
| resave: false, | ||||||||||||||||||||||||||||||||||||||||||||||
| saveUninitialized: false, | ||||||||||||||||||||||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+27
to
+31
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Harden session configuration for authenticated deployments. Add explicit cookie security settings and fail fast when Suggested change+if (!process.env.SESSION_SECRET) {
+ throw new Error("SESSION_SECRET is required");
+}
+
app.use(
session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
+ cookie: {
+ httpOnly: true,
+ sameSite: "lax",
+ secure: process.env.NODE_ENV === "production",
+ },
}),
);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||
| app.use(passport.initialize()); | ||||||||||||||||||||||||||||||||||||||||||||||
| app.use(passport.session()); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Routes | ||||||||||||||||||||||||||||||||||||||||||||||
| const authRoutes = require('./routes/auth'); | ||||||||||||||||||||||||||||||||||||||||||||||
| app.use('/api/auth', authRoutes); | ||||||||||||||||||||||||||||||||||||||||||||||
| const authRoutes = require("./routes/auth"); | ||||||||||||||||||||||||||||||||||||||||||||||
| app.use("/api/auth", authRoutes); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Connect to MongoDB | ||||||||||||||||||||||||||||||||||||||||||||||
| mongoose.connect(process.env.MONGO_URI, {}).then(() => { | ||||||||||||||||||||||||||||||||||||||||||||||
| console.log('Connected to MongoDB'); | ||||||||||||||||||||||||||||||||||||||||||||||
| mongoose | ||||||||||||||||||||||||||||||||||||||||||||||
| .connect(process.env.MONGO_URI, {}) | ||||||||||||||||||||||||||||||||||||||||||||||
| .then(() => { | ||||||||||||||||||||||||||||||||||||||||||||||
| console.log("Connected to MongoDB"); | ||||||||||||||||||||||||||||||||||||||||||||||
| app.listen(process.env.PORT, () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| console.log(`Server running on port ${process.env.PORT}`); | ||||||||||||||||||||||||||||||||||||||||||||||
| console.log(`Server running on port ${process.env.PORT}`); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
| }).catch((err) => { | ||||||||||||||||||||||||||||||||||||||||||||||
| console.log('MongoDB connection error:', err); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||
| .catch((err) => { | ||||||||||||||||||||||||||||||||||||||||||||||
| console.log("MongoDB connection error:", err); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add alt text to the contributors image.
The image is missing alt text (MD045), which is an accessibility gap in documentation.
Proposed fix
📝 Committable suggestion
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 143-143: Images should have alternate text (alt text)
(MD045, no-alt-text)
🤖 Prompt for AI Agents