Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
PORT=3000
PORT=3000
SESSION_SECRET=thisIsSoBoringThatIcouldDie
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ npm-debug.log

# Environment Variables should NEVER be published
# We are submitting it for learning purposes
# .env
.env

# for maintenance purposes - not best practices
package-lock.json
Expand Down
43 changes: 31 additions & 12 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,54 @@
// app.js
// ℹ️ Gets access to environment variables/settings
// https://www.npmjs.com/package/dotenv
require('dotenv/config');
require("dotenv").config();

// ℹ️ Connects to the database
require('./db');
require("./db");

// Handles http requests (express is node js framework)
// https://www.npmjs.com/package/express
const express = require('express');
const express = require("express");

// Handles the handlebars
// https://www.npmjs.com/package/hbs
const hbs = require('hbs');
const hbs = require("hbs");

const app = express();

// ℹ️ This function is getting exported from the config folder. It runs most middlewares
require('./config')(app);

// Place session configuration here, right after initializing Express and before other middleware/route setups
const session = require('express-session');
const MongoStore = require('connect-mongo');

app.use(
session({
secret: process.env.SESSION_SECRET || 'superSecret', // Remember to set SESSION_SECRET in your .env
resave: false,
saveUninitialized: false,
store: MongoStore.create({ mongoUrl: process.env.MONGODB_URI || 'mongodb://localhost/basic-auth' })
})
);

// ℹ️ This function is getting exported from the config folder. It runs most pieces of middleware
require("./config")(app);

// default value for title local
const projectName = 'lab-express-basic-auth';
const capitalized = string => string[0].toUpperCase() + string.slice(1).toLowerCase();
const capitalize = require("./utils/capitalize");
const projectName = "basic-auth";

app.locals.title = `${capitalized(projectName)}- Generated with Ironlauncher`;
app.locals.appTitle = `${capitalize(projectName)} created with IronLauncher`;

// 👇 Start handling routes here
const index = require('./routes/index');
app.use('/', index);
const indexRoutes = require("./routes/index.routes");
app.use("/", indexRoutes);

// authRouter
const authRouter = require('./routes/auth.routes');
app.use('/', authRouter);

// ❗ To handle errors. Routes that don't exist or errors that you handle in specific routes
require('./error-handling')(app);
require("./error-handling")(app);

module.exports = app;

11 changes: 11 additions & 0 deletions middleware/isAuthenticated.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const isAuthenticated = (req, res, next) => {
if (req.session.currentUser) {
// User is logged in, proceed to the next middleware/route handler
next();
} else {
// User is not logged in, redirect to the login page
res.redirect('/login');
}
};

module.exports = isAuthenticated;
30 changes: 19 additions & 11 deletions models/User.model.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
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
},
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);
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@
"dev": "nodemon server.js"
},
"dependencies": {
"bcryptjs": "^2.4.3",
"connect-mongo": "^5.1.0",
"cookie-parser": "^1.4.5",
"dotenv": "^8.2.0",
"dotenv": "^8.6.0",
"express": "^4.17.1",
"express-session": "^1.18.0",
"hbs": "^4.1.1",
"mongoose": "^6.1.2",
"morgan": "^1.10.0",
Expand Down
99 changes: 99 additions & 0 deletions routes/auth.routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// routes/auth.routes.js

const { Router } = require("express");
const router = new Router();

const bcryptjs = require("bcryptjs");
const saltRounds = 10;

const User = require("../models/User.model");

const isAuthenticated = require('../middleware/isAuthenticated');

// GET route ==> to display the signup form to users
router.get("/signup", (req, res) => res.render("auth/signup"));

// POST route ==> to process form data
router.post("/signup", (req, res, next) => {
// console.log("The form data: ", req.body);

const { username, password } = req.body;

bcryptjs
.genSalt(saltRounds)
.then((salt) => bcryptjs.hash(password, salt))
.then((hashedPassword) => {
// console.log(`Password hash: ${hashedPassword}`);
return User.create({
// username: username
username,
email,
// passwordHash => this is the key from the User model
// ^
// | |--> this is placeholder (how we named returning value from the previous method (.hash()))
passwordHash: hashedPassword,
});
})
.then((userFromDB) => {
// console.log("Newly created user is: ", userFromDB);
res.redirect('/userProfile');
})
.catch((error) => next(error));
});



// GET route to display the login form
router.get("/login", (req, res) => {
res.render("auth/login"); // Make sure you create this view
});

// POST route to process the login form
router.post("/login", (req, res, next) => {
const { username, password } = req.body;

User.findOne({ username })
.then(user => {
if (!user) {
// Username not found
res.render("auth/login", { errorMessage: "Username not found." });
return;
}
// User found, now compare passwords
if (bcryptjs.compareSync(password, user.passwordHash)) {
// Password matches, login successful
req.session.currentUser = user; // Save user info in session
res.redirect("/userProfile"); // Redirect to the user profile or another protected route
} else {
// Password does not match
res.render("auth/login", { errorMessage: "Incorrect password." });
}
})
.catch(error => next(error));
});


router.get('/userProfile', (req, res) => {
if (!req.session.currentUser) {
// If no user is logged in, redirect to the login page
res.redirect('/login');
} else {
// If a user is logged in, render the user profile page
res.render('users/user-profile', { user: req.session.currentUser });
}
});


// Use isAuthenticated middleware to protect the /main and /private routes
// Place these AFTER your signup and login routes to keep a logical order

router.get('/main', isAuthenticated, (req, res) => {
res.render('protected/main'); // Make sure you have a main.hbs view
});

router.get('/private', isAuthenticated, (req, res) => {
res.render('protected/private'); // Make sure you have a private.hbs view
});


module.exports = router;
3 changes: 2 additions & 1 deletion routes/index.js → routes/index.routes.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const router = require("express").Router();
const express = require('express');
const router = express.Router();

/* GET home page */
router.get("/", (req, res, next) => {
Expand Down
5 changes: 5 additions & 0 deletions utils/capitalize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function capitalize (string) {
return string[0].toUpperCase() + string.slice(1).toLowerCase();
}

module.exports = capitalize;
12 changes: 12 additions & 0 deletions views/auth/login.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<h2>Login</h2>
<form action="/login" method="POST">
<label for="username">Username:</label>
<input type="text" name="username" id="username" required>

<label for="password">Password:</label>
<input type="password" name="password" id="password" required>

<button type="submit">Log In</button>
</form>

<a href="/signup">Don't have an account? Sign up</a>
22 changes: 22 additions & 0 deletions views/auth/signup.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{{! views/auth/signup.hbs }}

<div id="form">
<h2>Signup</h2>
{{! The HTML form’s action attribute defines where to send the form data when a form is submitted. This and the method="POST" tell us that we should create a post route that will handle the form data. }}
<form action="/signup" method="POST">

<label>
Username
<input type="text" name="username" />
</label>

<label>
Password
<input type="password" name="password" placeholder="********" />
</label>

<button type="submit">Create account</button>

{{! error message will be added here }}
</form>
</div>
34 changes: 23 additions & 11 deletions views/layout.hbs
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{{title}}</title>
<link rel="stylesheet" href="/stylesheets/style.css" />
</head>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{{appTitle}}</title>
<link rel="stylesheet" href="/stylesheets/style.css" />
</head>

<body>
<body>

{{{body}}}
<!-- add the navbar code below -->
<nav>
<ul>
<li id="to-left">
<p><a href="/"> Users - demo </a></p>
</li>
<li id="to-right">
<a href="/signup">Signup</a>
</li>
</ul>
</nav>

<script src="/js/script.js"></script>
</body>
{{{body}}}

<script src="/js/script.js"></script>
</body>

</html>
4 changes: 4 additions & 0 deletions views/protected/main.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<h1>Main Page</h1>
<img src="https://www.istockphoto.com/de/foto/stern-traurige-katze-isoliert-auf-wei%C3%9Fem-hintergrund-gm1434414228-476036446?searchscope=image%2Cfilm" alt="Funny Cat">
<p>This is a protected page. Only logged-in users can see this.</p>
<a href="/">Back to Home</a>
4 changes: 4 additions & 0 deletions views/protected/private.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<h1>Private Page</h1>
<img src="https://giphy.com/gifs/moodman-YRtLgsajXrz1FNJ6oy" alt="Favorite GIF">
<p>Welcome to the private page! It's for your eyes only.</p>
<a href="/">Back to Home</a>
1 change: 1 addition & 0 deletions views/users/user-profile.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<p>This is your profile page my friend!</p>