Server-Side Scripting/Cookies and Sessions/Node.js (Express)

app.js

edit
// Demonstrates a complete server-side website using:
//   * static HTML and CSS
//   * a template
//   * a code module
//
// NOTE: Static pages (html, css, images, etc.) are placed in
// a folder named "static". Template pages are placed in a folder
// named "templates".
//
// Folder structure:
// app.js
// hello.js
// static
//   index.html
//   static.html
//   styles.css
// templates
//   template.html
//
// References:
//  https://repl.it/languages/express
//  https://expressjs.com/en/guide/routing.html
//  https://www.npmjs.com/package/express-fileupload
//  https://www.tutorialspoint.com/expressjs/expressjs_cookies.htm
//  https://www.geeksforgeeks.org/session-cookies-in-nodejs/
//  https://www.geeksforgeeks.org/node-js-crypto-randombytes-method/

const express = require("express");
const fileUpload = require('express-fileupload');
const cookieParser = require("cookie-parser");
const session = require("express-session");
const crypto = require("crypto");

const app = express();

app.use(express.static(__dirname + '/static'));
app.use(express.urlencoded({
        extended: true
}));

app.use(fileUpload({
    limits: { fileSize: 1 * 1024 * 1024 },
}));
app.use(cookieParser());
app.use(session({
    resave: false,
    saveUninitialized: false,
    secret: crypto.randomBytes(16).toString("hex")
}));

const fs = require("fs");
fs.readdirSync("./routes").map((filename) => {
    const module = require("./routes/" + filename);
    const route = filename.replace(".js", "")
    app.use("/" + route, module);
});

app.listen(3000, () => console.log('server started'));

routes/lesson12.js

edit
// Demonstrates session and cookie processing. The username is stored
// as a cookie and an internal userid is saved in a session variable.
// Also demonstrates secure password authentication using bcrypt salt
// and hash.
//
// References:
//  https://en.wikibooks.org/wiki/JavaScript
//  https://www.geeksforgeeks.org/http-cookies-in-node-js/
//  https://www.geeksforgeeks.org/session-cookies-in-nodejs/
//  https://www.npmjs.com/package/bcrypt

const express = require("express");
const bcrypt = require("bcrypt");
const fs = require("fs");
const handlebars = require('handlebars');
const router = express.Router();

users = [
    // Password is the same as the username, just salted and hashed.
    // Don't do this in a production application! Use custom passwords.
    { "userid": 1, "username": "admin", 
        "password": "$2b$10$l20wKFNqyzWl9NgeexjQ9el9KY7HzbTAPefSyntaZE.jqJlHZI0Ba" },
    { "userid": 2, "username": "test", 
        "password": "$2b$10$T.7DuAdfGVzq8uP1.xYZLe8rbPrOE6/DtMqbT5.O/bYwTFMZDC6ru" }
]

router.get("/", function (request, response) {
    let username = request.cookies.username;
    let userid = request.session.userid;
    result = build_form(username, userid);
    response.send(result);
});

router.post("/", function (request, response) {
    if (request.body["reload"]) {
        response.redirect(request.originalUrl);
    }
    else if (request.body["log-out"]) {
        request.session.destroy();
        let username = request.cookies.username;
        let userid = null;
        result = build_form(username, userid);
        response.send(result);
    }
    else if (request.body["forget-me"]) {
        request.session.destroy();
        result = build_form(null, null);
        response.cookie("username", "", { expires: 0 });
        response.send(result);
    }
    else {
        let username = request.body.username;
        let password = request.body.password;
        let userid = authenticateUser(username, password);
        if (userid) {
            request.session.userid = userid;
            result = build_form(username, userid);
            response.cookie("username", username);
            response.send(result);    
        }
        else {
            response.redirect(303, request.originalUrl);
        }
    }
});

function build_form(username, userid) {
    let cookie = !!username;
    let session = !!userid;
    if (username && userid) {
        welcome = "Welcome back " + username + "! You are logged in.";
    }
    else if (username) {
        welcome = "Welcome back " + username + "! Please log in.";
    }
    else {
        welcome = "Welcome! Please log in.";
    }

    let source = fs.readFileSync("./templates/lesson12.html");
    let template = handlebars.compile(source.toString());
    let data = {
        cookie: cookie,
        session: session,
        welcome: welcome,
        username: username
    }
    result = template(data);
    return result
}

function authenticateUser(username, password) {
    for (let index = 0; index < users.length; index++) {
        let user = users[index];
        if (user.username == username) {
            if (bcrypt.compareSync(password, user.password)) {
                // Should track successful logins
                return user.userid;
            } else {
                // Should track failed attempts, lock account, etc.
                return null;
            }
        }        
    }
    return null;
}

function generateHashedPassword(password) {
    // Use this function to generate hashed passwords to save in 
    // the users list or a database.
    let salt = bcrypt.genSaltSync();
    let hashed = bcrypt.hashSync(password, salt);
    return hashed    
}    

module.exports = router;

templates/lesson12.html

edit
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Lesson 12</title>
    <link rel="stylesheet" href="styles.css">
</head>

<body>
    <h1>Cookies and Sessions</h1>
    <p>Cookie: {{cookie}}<br>Session: {{session}}</p>
    <p>{{welcome}}</p>
    <hr>
    <form method="POST">
        <p><label for="username">Username:</label>
            <input type="text" id="username" name="username" value="{{username}}">
        </p>
        <p><label for="password">Password:</label>
            <input type="password" id="password" name="password">
        </p>
        <input type="submit" id="log-in" name="log-in" value="Log In">&nbsp;
        <input type="submit" id="log-out" name="log-out" value="Log Out">&nbsp;
        <input type="submit" id="forget-me" name="forget-me" value="Forget Me">&nbsp;
        <input type="submit" id="reload" name="Reload" value="Reload">
    </form>
</body>

</html>

Try It

edit

See Server-Side Scripting/Routes and Templates/Node.js (Express) to create a test environment.