Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat | finalize code
  • Loading branch information
rkristelijn committed Jul 3, 2019
1 parent ed0d9c3 commit e8c1317
Show file tree
Hide file tree
Showing 14 changed files with 417 additions and 53 deletions.
56 changes: 46 additions & 10 deletions app.js
Expand Up @@ -2,19 +2,19 @@ var createError = require("http-errors");
var express = require("express");
var path = require("path");
var cookieParser = require("cookie-parser");
var logger = require("morgan");
var session = require("express-session");
var debug = require("debug")("app.js");

var indexRouter = require("./routes/index");
var usersRouter = require("./routes/users");
var aboutRouter = require("./routes/about");

var app = express();

// view engine setup
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "pug");

app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
Expand All @@ -29,27 +29,63 @@ app.use(
secret: "app",
name: "app",
resave: true,
saveUninitialized: true,
cookie: { maxAge: 6000 }
saveUninitialized: true
// cookie: { maxAge: 6000 } /* 6000 ms? 6 seconds -> wut? :S */
})
);

var checkUser = function(req, res, next) {
var logout = function(req, res, next) {
debug("logout()");
req.session.loggedIn = false;
res.redirect("/");
};

var login = function(req, res, next) {
var { username, password } = req.body;
if (req.body.username && checkUser(username, password)) {
debug("login()", username, password);
req.session.loggedIn = true;
res.redirect("/");
} else {
debug("login()", "Wrong credentials");
res.render("login", { title: "Login Here", error: "Wrong credentials" });
}
};

var checkUser = function(username, password) {
debug("checkUser()", username, password);
if (username === "admin" && password === "admin") return true;
return false;
};

var checkLoggedIn = function(req, res, next) {
if (req.session.loggedIn) {
debug(
"checkLoggedIn(), req.session.loggedIn:",
req.session.loggedIn,
"executing next()"
);
next();
} else {
debug(
"checkLoggedIn(), req.session.loggedIn:",
req.session.loggedIn,
"rendering login"
);
res.render("login", { title: "Login Here" });
}
};

// redirect to login form
app.use("/", function(req, res, next) {
return checkUser(req, res, indexRouter);
});
app.use("/users", usersRouter);
app.use("/users", checkLoggedIn, usersRouter);
app.use("/logout", logout, indexRouter);
app.use("/login", login, indexRouter);
app.use("/about", aboutRouter);
app.use("/", checkLoggedIn, indexRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
debug("app.use", req.path, 404);
next(createError(404));
});

Expand All @@ -58,7 +94,7 @@ app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get("env") === "development" ? err : {};

debug("app.use", "ERROR", err.message);
// render the error page
res.status(err.status || 500);
res.render("error");
Expand Down
235 changes: 235 additions & 0 deletions doc/diagrams/views.activity.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions doc/diagrams/views.activity.yuml
@@ -0,0 +1,14 @@
// {type:activity}
// {generate:true}
(start)-><a>[GET /]-><b>[logged in]->(Show homepage)->(end)
<b>[else]->(Show login)->(end)

<a>[GET /users]-><c>[logged in]->(Show users)->(end)
<c>[else]->(Show login)

<a>[GET /about]-><d>[logged in]->(Show about with bar)->(end)
<d>[else]->(Show about without bar)->(end)

<a>[POST /login]->(Check login)
(Check login)-><e>[success]->(Login and redirect /)->(Show homepage)
<e>[else]->(Show Login with error)->(end)
Binary file added doc/images/demo.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 0 additions & 20 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions package.json
Expand Up @@ -8,11 +8,9 @@
"dependencies": {
"bootstrap": "^4.3.1",
"cookie-parser": "~1.4.4",
"debug": "~2.6.9",
"express": "~4.16.1",
"express-session": "^1.16.2",
"http-errors": "~1.6.3",
"morgan": "~1.9.1",
"pug": "^2.0.4"
},
"devDependencies": {
Expand Down
42 changes: 40 additions & 2 deletions readme.md
Expand Up @@ -4,22 +4,60 @@ Clone this repo or follow the steps below to learn about setting up a Node/expre

| step | instruction |
| ------------- | ------------------------------------------- |
| **boilerplating** | These steps will set up an initial project. |
| **0. boilerplating** | These steps will set up an initial project. |
| 0.1 | Install [express-generator](https://www.npmjs.com/package/express-generator) globally `npm install -g express-generator`|
| 0.2 | Create a new project named **login** in a new folder **login** and add pug support (instead of Jade). `express -css=less --view=pug --git login` |
| 0.3 | Install dependencies by executing `npm i` after moving into the directory using `cd login` |
| 0.4 | You will see warnings, depending on how many vulnerabilities are found.<br> `npm notice created a lockfile as package-lock.json. You should commit this file. `<br>`added 118 packages from 174 contributors and audited 247 packages in 10.793s `<br>`found 1 low severity vulnerability run npm audit fix to fix them, or npm audit for details`<br> after running `npm audit` you will see recommendations to fix, you might want to run `npm ls` to see the dependency tree.<br>In this case you can fix it by running `npm i pug@latest`<br><br>After running this no vulnerabilities are reported and we can go ahead and start the app |
| 0.5 | start the app by one of the following commands: <br> 1. `npm start` - *to start app on default port 3000*<br>2. `PORT=8080 npm start` - *to start on port 8080*<br>3. `DEBUG=login:* npm start` - *start with debug information*|
| 0.6 | Point your browser to `localhost:3000` ![](./doc/images/boilerplate.png) <br> In our console we see:<br>`~/login$ DEBUG=login:* npm start`<br><br>`> login@0.0.0 start /home/gius/login`<br>`> node ./bin/www`<br><br>` login:server Listening on port 3000 +0ms`<br>`GET / 304 719.979 ms - -`<br>`GET /stylesheets/style.css 304 7.148 ms - -`
| create login form | After completing these steps we have a new page and route.|
| **1. create login form** | After completing these steps we have a new page and route.|
| 1.1 | run `npm i --save express-session bootstrap` to add the dependency<br>Note that bootstrap has 2 peer dependencies: jquery and popper.js. We don't need these, because we are just going to use the css. This is a list of ways to handle the `npm WARN`:<br>1. Ignore the warnings; *not desired because the team will ignore all npm output*<br>2. Install peer deps: `npm i --save jquery popper.js`; *not desired; packers will include jquery and popper.js and let the codebase grow significantly*<br>3. Install as dev deps: `npm i -D jquery popper.js`; *Unsure yet if it solves 2, but it shuts up the WARN*<br>4. Use [ignore-warings](https://github.com/codejamninja/ignore-warnings): *Unclear [how to use](https://github.com/codejamninja/ignore-warnings/issues/2) yet, but it seems like a legit way of avoiding 2 and still keep npm output clean*<br>5. use bootstrap cdn; *Preferred to install locally to allow offline dev*<br>6. manual install bootstrap; *deps should be in package.json for keeping all updatable and visable for npm audit*|
| 1.2 | create `/views/login.pug` [see change](https://github.com/rkristelijn/login/commit/e3c94bb22d43140f0a18054c793572fca60ce7ae#diff-6d5d452b8670045112ed889367008056) |
| 1.3 | add the route to `app.js` for reroute to login, bootstrap and include session [see change](https://github.com/rkristelijn/login/commit/e3c94bb22d43140f0a18054c793572fca60ce7ae#diff-0364f57fbff2fabbe941ed20c328ef1a)<br> |
| 1.4 | update styles `public/style.css` [see change](https://github.com/rkristelijn/login/commit/e3c94bb22d43140f0a18054c793572fca60ce7ae#diff-0aaa9d35a8082eda23139c53348d2e51) |
| 1.5 | update layout.pug to include bootstrap `views/layout.pug` [see change](https://github.com/rkristelijn/login/commit/e3c94bb22d43140f0a18054c793572fca60ce7ae#diff-5c5792469bc79f8d2ab44b4192b02a20) |
| 1.6 | current result routes default route to login<br>![](./doc/images/login.png)<br>`~/login $ DEBUG=login:* npm start -s`<br>` login:server Listening on port 3000 +0ms`<br>`GET / 304 737.652 ms - -`<br>`GET /css/bootstrap.min.css 304 `<br>`.766 ms - -`<br>`GET /stylesheets/style.css 304 1.070 ms - -` |
| **2. add about-page and header** | Follow below steps to add about page and header |
| 2.1 | Before we continue, I like to clean up some logging. `morgan` is creating too much noise. Remove the following lines from `app.js`:<br>`var logger = require("morgan");`<br>`app.use(logger("dev"));`<br><br>You can remove morgan from `package.json` by: <br>`npm rm --save morgan`.<br><br>While we are at it; `debug` is a direct dependency for `express` and `express-session` so we can remove it from our `package.json` by: <br>`npm rm --save debug`.<br><br>To see if `debug` still works, we can use:<br>`DEBUG=app.js,login:server,express-session npm start -s`. Note that we can use `DEBUG=*` for all output.<br><br>Now we can add debug info like so (in app.js): <br>`var debug = require('debug')('app.js');` <br> `debug('hello world');`<br><br>Our output:<br>`login:server Listening on port 3001 +0ms`<br>` express-session no SID sent, generating session +26s`<br>` app.js checkLoggedIn(), req.session.loggedIn: undefined rendering login +5ms`<br>` express-session saving PybLKV4TpsaSMx_PzZx_Mj5Is4X_0U9g +748ms`<br>` express-session set-cookie app=s%3APybLKV4TpsaSMx_PzZx_Mj5Is4X_0U9g.Bd1dSB8w4kfcL9DPonfLBXFRLtZBdqHThCNMTsv0Ixo; Path=/; HttpOnly +4ms`<br>` express-session fetching PybLKV4TpsaSMx_PzZx_Mj5Is4X_0U9g +736ms`<br>` express-session session found +1ms`<br>` app.js checkLoggedIn(), req.session.loggedIn: undefined rendering login +1ms`<br>` express-session saving PybLKV4TpsaSMx_PzZx_Mj5Is4X_0U9g +93ms`<br>` express-session split response +1ms`
| 2.2 | add `routes/about.js` and `views/about.pug` with [this content]() and update `app.js`; add: <br>`app.use("/about", aboutRouter);`<br>`var aboutRouter = require("./routes/about");`|
| 2.3 | add `views/header.pug` with [this content]() and include it in `index.pug` and `users.pug` and conditionally in `views/about.pug`:<br>`if loggedIn`<br>&nbsp;&nbsp;&nbsp;&nbsp;`include header` |
| 2.4 | then there are some bits and pieces to fix in `views/login.pug`:<br>- add error placeholder<br>- add link to about page<br>- change button to input, so the enter key works |
| 2.5 | add `views/users.pug` with contents from [here]() and pass a list of users [like so]() |
| **3. finalize login** | There still stuff left to do. The [examples](#sources) just support login and logout, and the session is killed after 6000 ms (6sec) |
|3.1| We can remove the line `cookie: {maxAge: 6000}` so the session isn't just 6sec from `app.js` |
|3.2| In `app.js` we create a `login()`- and a `logout()` function that only care about logging in and logging out.<br><br>Logout removes the session.loggedIn flag, Login sets it and calls checking the credentials. A separate function is created to check the credentials called `checkUser()` |

# Final design
![](./doc/diagrams/views.activity.svg)

# Final Demo
- clean start `/`
- get `/about`
- get `/users`
- login
- homepage
- get `/users`
- get `/about`
- logout
- wrong login

![](./doc/images/demo.gif)
# Questions / evaluation

These are answers that I seek answer to before starting this document, raised during creation and reviewing the code.

| Question | Answer |
|---|---|
| Do I need passport and passport local for logging in? | no. |
| What is the simplest way of creating login/logout? | Cookies? Server-side session?|
| Can I identify the user? | |
| Is this the simplest example? | |
| is creating `req.session.loggedIn` a legit way to manage the session? | |
| What are generic security recommendations? | |

# Sources

1. [original tutorial](http://projectsplaza.com/login-logout-nodejs-express/)
2. [another tutor from Nima HKH](https://medium.com/@nima.2004hkh/create-your-first-login-page-with-exprerssjs-pug-f42250229486)
3. [node logging done right](http://www.jyotman.xyz/post/logging-in-node.js-done-right)
9 changes: 9 additions & 0 deletions routes/about.js
@@ -0,0 +1,9 @@
var express = require("express");
var router = express.Router();

/* GET about page. */
router.get("/", function(req, res, next) {
res.render("about", { title: "About", loggedIn: req.session.loggedIn });
});

module.exports = router;
9 changes: 6 additions & 3 deletions routes/users.js
@@ -1,9 +1,12 @@
var express = require('express');
var express = require("express");
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
router.get("/", function(req, res, next) {
res.render("users", {
title: "User listing",
users: [{ username: "admin", password: "admin" }]
});
});

module.exports = router;
12 changes: 12 additions & 0 deletions views/about.pug
@@ -0,0 +1,12 @@
extends layout

block content
if loggedIn
include header
div(class="wrapper")
div(class="form-signin")
h1 About this page
p See code at #[a(href="https://github.com/rkristelijn/login") github.com]. Log in using admin/admin
if !loggedIn
p
a(href="/") Log in
15 changes: 15 additions & 0 deletions views/header.pug
@@ -0,0 +1,15 @@
nav(class="navbar navbar-expand-md navbar-dark bg-dark")
div(class="navbar-collapse collapse w-100 order-1 order-md-0 dual-collapse2")
ul(class="navbar-nav mr-auto")
li(class="nav-item active")
a(class="nav-link" href="/users") Users
li(class="nav-item")
a(class="nav-link" href="/about") About
div(class="mx-auto order-0")
a(class="navbar-brand mx-auto" href="/") Home
button(class="navbar-toggler" type="button" data-toggle="collapse" data-target=".dual-collapse2")
span(class="navbar-toggler-icon")
div(class="navbar-collapse collapse w-100 order-3 dual-collapse2")
ul(class="navbar-nav ml-auto")
li(class="nav-item")
a(class="nav-link" href="/logout") Logout
7 changes: 5 additions & 2 deletions views/index.pug
@@ -1,5 +1,8 @@
extends layout

block content
h1= title
p Welcome to #{title}
include header
div(class="wrapper")
div(class="form-signin")
h1= title
p Welcome to #{title}
32 changes: 18 additions & 14 deletions views/login.pug
Expand Up @@ -4,17 +4,21 @@ block content
div(class="wrapper")
div(class="form-signin")
h1= title
form(action="post",method="post")
div(class="form-group")
label(for="username") Username
input(class="form-control" type="text",name="username" placeholder="Username" autofocus)
div(class="form-group")
label(for="password") Password
input(class="form-control" type="password",name="password" placeholder="Password")
div(class="form-group")
div(class="text-right")
label(class="form-check-label") Remember me
input(type="checkbox")
div(class="form-group")
div(class="text-right")
button(class="btn btn-primary" type="submit") Sign in
if error
div(class="alert alert-danger" role='alert') #{error}
form(action="/login" method="post")
div(class="form-group")
label(for="username") Username
input(class="form-control" type="text",name="username" placeholder="Username" autofocus)
div(class="form-group")
label(for="password") Password
input(class="form-control" type="password",name="password" placeholder="Password")
div(class="form-group")
div
a(href="/about") About this page
div(class="text-right")
label(class="form-check-label") Remember me
input(type="checkbox")
div(class="form-group")
div(class="text-right")
input(class="btn btn-primary" type="submit" value="Sign in")
17 changes: 17 additions & 0 deletions views/users.pug
@@ -0,0 +1,17 @@
extends layout

block content
include header
div(class="wrapper")
div(class="form-signin")
h1 A list of users
table(class="table")
thead
tr
th(scope="col") Username
th(scope="col") Password
tbody
each user in users
tr
td(scope="row") #{user.username}
td #{user.password}

0 comments on commit e8c1317

Please sign in to comment.