From aeece84d230324597b79c4a1da9f995fa1b68ce4 Mon Sep 17 00:00:00 2001 From: Aditya Raikar Date: Wed, 3 Jul 2024 12:40:58 +0200 Subject: [PATCH 1/4] iteration 1 done --- app.js | 3 + models/User.model.js | 37 +++++++++---- package.json | 1 + public/stylesheets/style.css | 104 ++++++++++++++++++++++++++++++++++- routes/index.js | 2 + routes/signup.js | 36 ++++++++++++ views/auth/signup.hbs | 23 ++++++++ views/auth/user.hbs | 1 + views/layout.hbs | 26 +++++---- 9 files changed, 209 insertions(+), 24 deletions(-) create mode 100644 routes/signup.js create mode 100644 views/auth/signup.hbs create mode 100644 views/auth/user.hbs diff --git a/app.js b/app.js index 2ecc9f2220..88b93e2c89 100644 --- a/app.js +++ b/app.js @@ -28,6 +28,9 @@ app.locals.title = `${capitalized(projectName)}- Generated with Ironlauncher`; const index = require('./routes/index'); app.use('/', index); +const signup = require('./routes/signup') +app.use('/', signup) + // ❗ To handle errors. Routes that don't exist or errors that you handle in specific routes require('./error-handling')(app); diff --git a/models/User.model.js b/models/User.model.js index 9cdd3a3ce4..a307bb3bbd 100644 --- a/models/User.model.js +++ b/models/User.model.js @@ -1,14 +1,29 @@ -const { Schema, model } = require("mongoose"); +// models/User.model.js +const { Schema, model } = require('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 +const userSchema = new Schema( + { + username: { + type: String, + trim: true, + required: [true, 'Username is required.'], + unique: true + }, + email: { + type: String, + required: [true, 'Email is required.'], + unique: true, + lowercase: true, + trim: true + }, + passwordHash: { + type: String, + required: [true, 'Password is required.'] + } }, - password: String -}); + { + timestamps: true + } +); -const User = model("User", userSchema); - -module.exports = User; +module.exports = model('User', userSchema); diff --git a/package.json b/package.json index 19489d9695..76f9de9da4 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "dev": "nodemon server.js" }, "dependencies": { + "bcryptjs": "^2.4.3", "cookie-parser": "^1.4.5", "dotenv": "^8.2.0", "express": "^4.17.1", diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 9453385b99..301783d551 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -1,8 +1,108 @@ +/* public/stylesheets/style.css */ body { padding: 50px; - font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; + font: 14px 'Lucida Grande', Helvetica, Arial, sans-serif; } a { - color: #00B7FF; + color: #00b7ff; + text-decoration: none; + font-size: 1.5em; +} + +form { + margin: 30px auto; + width: 380px; +} + +form label { + color: #666; + display: inline-block; + margin-bottom: 10px; + text-align: center; + width: 100%; +} + +form input, +form button { + box-sizing: border-box; + font-size: 14px; + outline: 0; + padding: 4px; + width: 100%; + margin-bottom: 20px; +} + +form button { + background: #43a3e6; + border: 1px solid #43a3e6; + border-radius: 4px; + color: #fff; + cursor: pointer; + display: inline-block; + font-size: 14px; + padding: 8px 16px; + text-decoration: none; + text-transform: uppercase; + transition: 0.3s ease background; + width: 100%; +} + +form button:hover { + background: #fff; + color: #43a3e6; + transition: 0.3s ease background; +} + +.error { + background: #f02b63; + box-sizing: border-box; + color: #fff; + margin: 20px auto; + padding: 20px; + width: 100%; +} + +ul { + border-bottom: 2px solid #43a3e6; + height: 30px; + padding: 1em 0; + list-style-type: none; +} + +ul li { + line-height: 20px; +} + +#to-right { + padding: 1em; + float: right; +} + +#to-right span { + color: #43a3e6; + font-size: 1.5em; +} + +#form h2 { + display: flex; + justify-content: center; +} + +#to-left { + float: left; +} + +#to-left p a { + color: #000; + font-weight: bold; + font-size: 2em; +} + +#logout-form { + display: inline; +} + +#logout-form button { + width: 120px; } diff --git a/routes/index.js b/routes/index.js index 81c2396ceb..e0b83e350e 100644 --- a/routes/index.js +++ b/routes/index.js @@ -5,4 +5,6 @@ router.get("/", (req, res, next) => { res.render("index"); }); + + module.exports = router; diff --git a/routes/signup.js b/routes/signup.js new file mode 100644 index 0000000000..92d4847195 --- /dev/null +++ b/routes/signup.js @@ -0,0 +1,36 @@ +const router = require("express").Router(); +const bcryptjs = require('bcryptjs'); +const User = require('../models/User.model') +const saltRounds = 10; + + +/* GET home page */ +router.get("/signup", (req, res, next) => { + res.render("auth/signup"); +}); + +router.post('/signup', (req, res, next) => { + const { username, email, password } = req.body; + + bcryptjs + .genSalt(saltRounds) + .then(salt => bcryptjs.hash(password, salt)) + .then(hashedPassword => { + return User.create({ + username, + email, + passwordHash: hashedPassword + }); + }) + .then(userFromDB => { + /* console.log('Newly created user is: ', userFromDB); */ + res.redirect('/userProfile'); + }) + .catch(error => next(error)); +}); + +router.get('/userProfile', (req, res) => res.render('auth/user')); + + + +module.exports = router; \ No newline at end of file diff --git a/views/auth/signup.hbs b/views/auth/signup.hbs new file mode 100644 index 0000000000..0433dc3e87 --- /dev/null +++ b/views/auth/signup.hbs @@ -0,0 +1,23 @@ +{{!-- views/auth/signup.hbs --}} + +
+

Signup

+
+ + + + + + + + + + {{!-- error message will be added here --}} +
+
diff --git a/views/auth/user.hbs b/views/auth/user.hbs new file mode 100644 index 0000000000..aae145a080 --- /dev/null +++ b/views/auth/user.hbs @@ -0,0 +1 @@ +

welcome to ur profile friend

\ No newline at end of file diff --git a/views/layout.hbs b/views/layout.hbs index 73199c166b..782cc0b290 100644 --- a/views/layout.hbs +++ b/views/layout.hbs @@ -1,19 +1,23 @@ - - - - - - - - {{title}} - - + + + + + {{{body}}} - \ No newline at end of file + From aae852a8a9df8c9a798f53b4e61f94754fda0bf0 Mon Sep 17 00:00:00 2001 From: Aditya Raikar Date: Wed, 3 Jul 2024 13:01:32 +0200 Subject: [PATCH 2/4] iteration 1 complete --- routes/signup.js | 10 ++++++++-- views/auth/user.hbs | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/routes/signup.js b/routes/signup.js index 92d4847195..32089827e1 100644 --- a/routes/signup.js +++ b/routes/signup.js @@ -24,12 +24,18 @@ router.post('/signup', (req, res, next) => { }) .then(userFromDB => { /* console.log('Newly created user is: ', userFromDB); */ - res.redirect('/userProfile'); + res.redirect(`/userProfile/${userFromDB.username}`); }) .catch(error => next(error)); }); -router.get('/userProfile', (req, res) => res.render('auth/user')); +router.get('/userProfile/:username', (req, res) => { + User.findOne({username: req.params.username}) + .then(founduser => { + res.render('auth/user', {user: founduser}) + }) + +}); diff --git a/views/auth/user.hbs b/views/auth/user.hbs index aae145a080..93c2f231c8 100644 --- a/views/auth/user.hbs +++ b/views/auth/user.hbs @@ -1 +1 @@ -

welcome to ur profile friend

\ No newline at end of file +

welcome to ur profile {{user.username}}

\ No newline at end of file From bb20392a7c322608f8543b0decaabfa6430bb668 Mon Sep 17 00:00:00 2001 From: Aditya Raikar Date: Thu, 4 Jul 2024 17:29:28 +0200 Subject: [PATCH 3/4] iteration 2 complete --- .env | 3 +- app.js | 3 +- config/sessions.config.js | 32 +++++++++++++++++ package.json | 2 ++ routes/auth.routes.js | 76 +++++++++++++++++++++++++++++++++++++++ routes/signup.js | 42 ---------------------- views/auth/login.hbs | 22 ++++++++++++ views/auth/user.hbs | 15 +++++++- views/layout.hbs | 3 ++ 9 files changed, 153 insertions(+), 45 deletions(-) create mode 100644 config/sessions.config.js create mode 100644 routes/auth.routes.js delete mode 100644 routes/signup.js create mode 100644 views/auth/login.hbs diff --git a/.env b/.env index c0c68b1ca0..fe43d031c2 100644 --- a/.env +++ b/.env @@ -1 +1,2 @@ -PORT=3000 \ No newline at end of file +PORT=3000 +SESS_SECRET = 'super session secret' \ No newline at end of file diff --git a/app.js b/app.js index 88b93e2c89..cd84b9483b 100644 --- a/app.js +++ b/app.js @@ -17,6 +17,7 @@ const app = express(); // â„šī¸ This function is getting exported from the config folder. It runs most middlewares require('./config')(app); +require('./config/sessions.config')(app); // default value for title local const projectName = 'lab-express-basic-auth'; @@ -28,7 +29,7 @@ app.locals.title = `${capitalized(projectName)}- Generated with Ironlauncher`; const index = require('./routes/index'); app.use('/', index); -const signup = require('./routes/signup') +const signup = require('./routes/auth.routes') app.use('/', signup) // ❗ To handle errors. Routes that don't exist or errors that you handle in specific routes diff --git a/config/sessions.config.js b/config/sessions.config.js new file mode 100644 index 0000000000..1098636ae1 --- /dev/null +++ b/config/sessions.config.js @@ -0,0 +1,32 @@ +// config/session.config.js + +// require session +const session = require('express-session'); +const MongoStore = require('connect-mongo'); + +// since we are going to USE this middleware in the app.js, +// let's export it and have it receive a parameter +module.exports = app => { + // <== app is just a placeholder here + // but will become a real "app" in the app.js + // when this file gets imported/required there + + // required for the app when deployed to Heroku (in production) + app.set('trust proxy', 1); + + // use session + app.use( + session({ + secret: process.env.SESS_SECRET, + resave: true, + saveUninitialized: true, + store: MongoStore.create({ mongoUrl: process.env.MONGODB_URI || "mongodb://127.0.0.1:27017/lab-express-basic-auth" }), + cookie: { + sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 600000 // 600 * 1000 ms === 10 min + } + }) + ); +}; diff --git a/package.json b/package.json index 76f9de9da4..4f918a0152 100644 --- a/package.json +++ b/package.json @@ -8,9 +8,11 @@ }, "dependencies": { "bcryptjs": "^2.4.3", + "connect-mongo": "^5.1.0", "cookie-parser": "^1.4.5", "dotenv": "^8.2.0", "express": "^4.17.1", + "express-session": "^1.18.0", "hbs": "^4.1.1", "mongoose": "^6.1.2", "morgan": "^1.10.0", diff --git a/routes/auth.routes.js b/routes/auth.routes.js new file mode 100644 index 0000000000..15d19b00e6 --- /dev/null +++ b/routes/auth.routes.js @@ -0,0 +1,76 @@ +const router = require("express").Router(); +const bcryptjs = require('bcryptjs'); +const User = require('../models/User.model') +const saltRounds = 10; + + +/* GET home page */ +router.get("/signup", (req, res, next) => { + res.render("auth/signup"); +}); + +router.post('/signup', (req, res, next) => { + const { username, email, password } = req.body; + + bcryptjs + .genSalt(saltRounds) + .then(salt => bcryptjs.hash(password, salt)) + .then(hashedPassword => { + return User.create({ + username, + email, + passwordHash: hashedPassword + }); + }) + .then(userFromDB => { + /* console.log('Newly created user is: ', userFromDB); */ + res.redirect(`/userProfile`); + }) + .catch(error => next(error)); +}); + +router.get('/userProfile', (req, res) => { + console.log('req.session', req.session) + res.render('auth/user', {user: req.session.currentUser}) +}); + +router.get('/login', (req, res, next) => { + res.render("auth/login") +}); + +router.post('/login', (req, res, next) => { + console.log('SESSION =====> ', req.session); + const { email, password } = req.body; + if (email === '' || password === '') { + res.render('auth/login', { + errorMessage: 'Please enter both, email and password to login.' + }); + return; + } + User.findOne({ email }) + .then(user => { + if (!user) { + console.log("Email not registered. "); + res.render('auth/login', { errorMessage: 'User not found and/or incorrect password.' }); + return; + } else if (bcryptjs.compareSync(password, user.passwordHash)) { + req.session.currentUser = user; + res.redirect('/userProfile'); + } else { + console.log("Incorrect password. "); + res.render('auth/login', { errorMessage: 'User not found and/or incorrect password.' }); + } + }) + .catch(error => next(error)); +}); + +router.post('/logout', (req, res, next) => { + req.session.destroy(err => { + if (err) next(err); + res.redirect('/'); + }); +}); + + + +module.exports = router; \ No newline at end of file diff --git a/routes/signup.js b/routes/signup.js deleted file mode 100644 index 32089827e1..0000000000 --- a/routes/signup.js +++ /dev/null @@ -1,42 +0,0 @@ -const router = require("express").Router(); -const bcryptjs = require('bcryptjs'); -const User = require('../models/User.model') -const saltRounds = 10; - - -/* GET home page */ -router.get("/signup", (req, res, next) => { - res.render("auth/signup"); -}); - -router.post('/signup', (req, res, next) => { - const { username, email, password } = req.body; - - bcryptjs - .genSalt(saltRounds) - .then(salt => bcryptjs.hash(password, salt)) - .then(hashedPassword => { - return User.create({ - username, - email, - passwordHash: hashedPassword - }); - }) - .then(userFromDB => { - /* console.log('Newly created user is: ', userFromDB); */ - res.redirect(`/userProfile/${userFromDB.username}`); - }) - .catch(error => next(error)); -}); - -router.get('/userProfile/:username', (req, res) => { - User.findOne({username: req.params.username}) - .then(founduser => { - res.render('auth/user', {user: founduser}) - }) - -}); - - - -module.exports = router; \ No newline at end of file diff --git a/views/auth/login.hbs b/views/auth/login.hbs new file mode 100644 index 0000000000..42ef554557 --- /dev/null +++ b/views/auth/login.hbs @@ -0,0 +1,22 @@ +{{!-- views/auth/login.hbs --}} + +
+

Login

+ +
+ + + + + + + + {{#if errorMessage}} +

{{errorMessage}}

+ {{/if}} +
+
diff --git a/views/auth/user.hbs b/views/auth/user.hbs index 93c2f231c8..d90a9a3434 100644 --- a/views/auth/user.hbs +++ b/views/auth/user.hbs @@ -1 +1,14 @@ -

welcome to ur profile {{user.username}}

\ No newline at end of file +{{!-- views/users/user-profile.hbs --}} + +{{#if user}} +

Welcome, {{ user.username }}!

+

This is your profile page my friend!

+ +
+ +
+{{else}} +

Still no logged in user, sorry!

+{{/if}} diff --git a/views/layout.hbs b/views/layout.hbs index 782cc0b290..e35408e5c1 100644 --- a/views/layout.hbs +++ b/views/layout.hbs @@ -12,6 +12,9 @@
  • Signup
  • +
  • + Login +
  • From dfc76d7a1f7ea9e6ddcafea9fe62b2c8f42cebfd Mon Sep 17 00:00:00 2001 From: Aditya Raikar Date: Thu, 4 Jul 2024 18:02:05 +0200 Subject: [PATCH 4/4] done --- .env | 2 +- middleware/route-guard.js | 24 ++++++++++++++++++++++++ routes/auth.routes.js | 13 +++++++------ views/layout.hbs | 3 +++ 4 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 middleware/route-guard.js diff --git a/.env b/.env index fe43d031c2..c4d0754581 100644 --- a/.env +++ b/.env @@ -1,2 +1,2 @@ PORT=3000 -SESS_SECRET = 'super session secret' \ No newline at end of file +SESS_SECRET = 'super saiyan 3' \ No newline at end of file diff --git a/middleware/route-guard.js b/middleware/route-guard.js new file mode 100644 index 0000000000..0dac9e4dc0 --- /dev/null +++ b/middleware/route-guard.js @@ -0,0 +1,24 @@ +// middleware/route-guard.js + +// checks if the user is logged in when trying to access a specific page +const isLoggedIn = (req, res, next) => { + if (!req.session.currentUser) { + return res.redirect('/login'); + } + next(); + }; + + // if an already logged in user tries to access the login page it + // redirects the user to the home page + const isLoggedOut = (req, res, next) => { + if (req.session.currentUser) { + return res.redirect('/'); + } + next(); + }; + + module.exports = { + isLoggedIn, + isLoggedOut + }; + \ No newline at end of file diff --git a/routes/auth.routes.js b/routes/auth.routes.js index 15d19b00e6..55c57188c5 100644 --- a/routes/auth.routes.js +++ b/routes/auth.routes.js @@ -1,15 +1,16 @@ const router = require("express").Router(); const bcryptjs = require('bcryptjs'); const User = require('../models/User.model') +const { isLoggedIn, isLoggedOut } = require('../middleware/route-guard.js'); const saltRounds = 10; /* GET home page */ -router.get("/signup", (req, res, next) => { +router.get("/signup",isLoggedOut, (req, res, next) => { res.render("auth/signup"); }); -router.post('/signup', (req, res, next) => { +router.post('/signup',isLoggedOut, (req, res, next) => { const { username, email, password } = req.body; bcryptjs @@ -29,16 +30,16 @@ router.post('/signup', (req, res, next) => { .catch(error => next(error)); }); -router.get('/userProfile', (req, res) => { +router.get('/userProfile',isLoggedIn, (req, res) => { console.log('req.session', req.session) res.render('auth/user', {user: req.session.currentUser}) }); -router.get('/login', (req, res, next) => { +router.get('/login',isLoggedOut, (req, res, next) => { res.render("auth/login") }); -router.post('/login', (req, res, next) => { +router.post('/login',isLoggedOut, (req, res, next) => { console.log('SESSION =====> ', req.session); const { email, password } = req.body; if (email === '' || password === '') { @@ -64,7 +65,7 @@ router.post('/login', (req, res, next) => { .catch(error => next(error)); }); -router.post('/logout', (req, res, next) => { +router.post('/logout',isLoggedIn, (req, res, next) => { req.session.destroy(err => { if (err) next(err); res.redirect('/'); diff --git a/views/layout.hbs b/views/layout.hbs index e35408e5c1..a72a558876 100644 --- a/views/layout.hbs +++ b/views/layout.hbs @@ -15,6 +15,9 @@
  • Login
  • +
    + +