diff --git a/app.js b/app.js index 2ecc9f2220..62b9dd6d3e 100644 --- a/app.js +++ b/app.js @@ -18,6 +18,23 @@ const app = express(); // ℹ️ This function is getting exported from the config folder. It runs most middlewares require('./config')(app); +// Import session management packages +const session = require('express-session'); +const MongoStore = require('connect-mongo'); + +// Configure session middleware +app.use( + session({ + secret: process.env.SESSION_SECRET || 'defaultSecret', // Replace with an actual secret key + resave: false, + saveUninitialized: false, + cookie: { maxAge: 24 * 60 * 60 * 1000 }, // 1 day in milliseconds + store: MongoStore.create({ + mongoUrl: process.env.MONGODB_URI || 'mongodb://localhost/lab-express-basic-auth', // Replace with your MongoDB URI + }), + }) +); + // default value for title local const projectName = 'lab-express-basic-auth'; const capitalized = string => string[0].toUpperCase() + string.slice(1).toLowerCase(); @@ -28,8 +45,16 @@ app.locals.title = `${capitalized(projectName)}- Generated with Ironlauncher`; const index = require('./routes/index'); app.use('/', index); +const authRoutes = require('./routes/auth.routes'); +const protectedRoutes = require('./routes/protected.routes'); + +app.use(authRoutes); +app.use(protectedRoutes); + + // ❗ To handle errors. Routes that don't exist or errors that you handle in specific routes require('./error-handling')(app); module.exports = app; + diff --git a/middleware/auth.middelware.js b/middleware/auth.middelware.js new file mode 100644 index 0000000000..0a2b7cee94 --- /dev/null +++ b/middleware/auth.middelware.js @@ -0,0 +1,8 @@ +// middleware/auth.middleware.js +module.exports = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/login'); + } + next(); + }; + \ No newline at end of file diff --git a/models/User.model.js b/models/User.model.js index 9cdd3a3ce4..2e22218124 100644 --- a/models/User.model.js +++ b/models/User.model.js @@ -1,14 +1,18 @@ -const { Schema, model } = require("mongoose"); +// models/User.model.js +const mongoose = require('mongoose'); +const { Schema } = mongoose; -// TODO: Please make sure you edit the user model to whatever makes sense in this case const userSchema = new Schema({ username: { type: String, - unique: true + unique: true, + required: true }, - password: String + password: { + type: String, + required: true + } }); -const User = model("User", userSchema); +module.exports = mongoose.model('User', userSchema); -module.exports = User; diff --git a/package.json b/package.json index 19489d9695..c4b6cf2d13 100644 --- a/package.json +++ b/package.json @@ -7,11 +7,15 @@ "dev": "nodemon server.js" }, "dependencies": { + "bcryptjs": "^2.4.3", + "body-parser": "^1.20.3", + "connect-mongo": "^5.1.0", "cookie-parser": "^1.4.5", "dotenv": "^8.2.0", - "express": "^4.17.1", + "express": "^4.21.1", + "express-session": "^1.18.1", "hbs": "^4.1.1", - "mongoose": "^6.1.2", + "mongoose": "^6.13.4", "morgan": "^1.10.0", "serve-favicon": "^2.5.0" }, diff --git a/routes/auth.routes.js b/routes/auth.routes.js new file mode 100644 index 0000000000..1ab6e02470 --- /dev/null +++ b/routes/auth.routes.js @@ -0,0 +1,63 @@ +const express = require('express'); +const bcrypt = require('bcryptjs'); +const User = require('../models/User.model'); +const router = express.Router(); + +// GET sign-up form +router.get('/signup', (req, res) => { + res.render('auth/signup'); +}); + +// POST sign-up form +router.post('/signup', async (req, res) => { + const { username, password } = req.body; + + if (!username || !password) { + return res.render('auth/signup', { error: 'All fields are required.' }); + } + + try { + const salt = await bcrypt.genSalt(10); + const hashedPassword = await bcrypt.hash(password, salt); + + await User.create({ username, password: hashedPassword }); + res.redirect('/login'); + } catch (error) { + res.render('auth/signup', { error: 'Error during sign-up.' }); + } +}); + +// GET login form +router.get('/login', (req, res) => { + res.render('auth/login'); +}); + +// POST login form +router.post('/login', async (req, res) => { + const { username, password } = req.body; + + if (!username || !password) { + return res.render('auth/login', { error: 'All fields are required.' }); + } + + try { + const user = await User.findOne({ username }); + if (!user) { + return res.render('auth/login', { error: 'Invalid username or password.' }); + } + + const passwordMatch = await bcrypt.compare(password, user.password); + if (!passwordMatch) { + return res.render('auth/login', { error: 'Invalid username or password.' }); + } + + // Save the user in the session + req.session.user = user; + res.redirect('/main'); + } catch (error) { + res.render('auth/login', { error: 'Error during login.' }); + } +}); + +module.exports = router; + diff --git a/routes/protected.routes.js b/routes/protected.routes.js new file mode 100644 index 0000000000..c03741f443 --- /dev/null +++ b/routes/protected.routes.js @@ -0,0 +1,16 @@ +// routes/protected.routes.js +const express = require('express'); +const router = express.Router(); +const authMiddleware = require('../middleware/auth.middleware'); + +// GET /main +router.get('/main', authMiddleware, (req, res) => { + res.render('main', { user: req.session.user }); +}); + +// GET /private +router.get('/private', authMiddleware, (req, res) => { + res.render('private', { user: req.session.user }); +}); + +module.exports = router; diff --git a/views/auth/login.hbs b/views/auth/login.hbs new file mode 100644 index 0000000000..052caca153 --- /dev/null +++ b/views/auth/login.hbs @@ -0,0 +1,10 @@ + +
+ + + + + + + +
diff --git a/views/auth/signup.hbs b/views/auth/signup.hbs new file mode 100644 index 0000000000..e2e407328c --- /dev/null +++ b/views/auth/signup.hbs @@ -0,0 +1,10 @@ + +
+ + + + + + + +
diff --git a/views/main.hbs b/views/main.hbs new file mode 100644 index 0000000000..33cea3b75b --- /dev/null +++ b/views/main.hbs @@ -0,0 +1,9 @@ + +

Welcome to the Main Page!

+Funny Cat +Go Back + + +

This is a Private Page

+Favorite GIF +Go Back