Book Image

Building Scalable Apps with Redis and Node.js

By : Joshua Johanan
Book Image

Building Scalable Apps with Redis and Node.js

By: Joshua Johanan

Overview of this book

Table of Contents (17 chapters)
Building Scalable Apps with Redis and Node.js
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Very simple authentication


We want everyone who goes to /chat to be authenticated. We will build a very simple, yet very insecure, authentication system. We will then gut this and put in a real authentication system later, but this exercise will show us how all the pieces we have talked about work together. The first thing is to check if a user is logged in during a request. We will use middleware for this. Open up our utilities.js from the middleware folder, and add two new functions.

The first function is to add an isAuthenticated variable for our templates, which we will use shortly. The following is our function:

module.exports.authenticated = function authenticated(req, res, next){
  res.locals.isAuthenticated = req.session.isAuthenticated;
  if (req.session.isAuthenticated) {
    res.locals.user = req.session.user;
  }
  next();
};

We will store whether or not someone is authenticated in the session. We are just adding it to the response so that a template can check the isAuthenticated value. We also add a user object if the user is logged in.

Next, we will create middleware to check to see if someone is authenticated. If not, we redirect them to the login page. The following is the function:

module.exports.requireAuthentication = function requireAuthentication(req, res, next){
  if (req.session.isAuthenticated) {
    next();
  }else {
    res.redirect('/login');
  }
};

This middleware is pretty straightforward. If you are authenticated, run the next middleware, if not, redirect to /login.

Now, we need to add these to our middleware stack. Add the authenticated function right after our CSRF and before our routes, as you can see in the following code:

app.use(util.csrf);
app.use(util.authenticated);

Our other middleware is going to go on the chat route. So far, all middleware has been added by using app.use. The app.use function will apply the middleware to every request coming in. For a lot of middleware, this is the correct place. For some though, the middleware should only be executed on certain routes. To do this, add them as the second parameter on a route definition. You can also chain multiple middleware together by using an array. Here is how our chat route looks now:

app.get('/chat', [util.requireAuthentication], routes.chat);

You can just pass the function by itself as the second parameter, but I wanted to demonstrate this syntax. Each middleware passed in the array will be executed in order.

If you load this up, you will see that you cannot get to http://localhost:3000/chat; it will always redirect you to /login.

We need to build an authentication function so that we can log users in. Open up utilities.js from middleware, and add the following function:

module.exports.auth = function auth(username, password, session){
  var isAuth = username === 'joshua' || username === 'brian';
  if (isAuth) {
    session.isAuthenticated = isAuth;
    session.user = {username: username};
  }
  return isAuth;
};

This is a very simple username check as it will only authenticate if you enter joshua or brian as the username.

Note

I will reiterate—do not use anything like this in production. We will cover how to do proper and secure local authentication in Chapter 3, Authenticating Users.

We now have to execute this inside our login post route. Open up index.js from routes and edit the loginProcess function:

//add a reference to util at the top of the file
var util = require('../middleware/utilities');
//then modify loginProcess
function loginProcess(req, res){
  var isAuth = util.auth(req.body.username, req.body.password, req.session);
  if (isAuth) {
    res.redirect('/chat');
  }else {
    res.redirect('/login');
  }
};

We pass in the username, password, and session so that auth can do its job. Depending on whether the user is authenticated or not, we will send them to /chat or redirect them back to /login. If the authentication was successful, our auth function will set isAuthenticated on the session, which means that our requireAuthentication function will not redirect you. Our little app works, well kind of. It is still a little clunky and missing some polish, and in addition to that, there is no way to log out.

This leads us right into writing a logout function, so in our utilities.js file, add the following function:

module.exports.logOut = function logOut(session){
  session.isAuthenticated = false;
  delete session.user;
};

A simple logOut function for a simple auth system. We have set isAuthenticated on the session back to false and got rid of the user in the session. Now, we have to put this in a route, so let's add that route in index.js present in the routes folder.

function logOut(req, res){
  util.logOut(req.session);
  res.redirect('/');
};

We log the user out, and then redirect to root. Finally, it needs to be added to our routes. Open up app.js.

app.get('/logout', routes.logOut);

After logging in, we can log ourselves out by going to http://localhost:3000/logout. We are still missing a little polish on our app, so let's add links to log in and out of our app.

We will do this by using partials. We will have a loggedin and loggedout partial. Create a directory in views called partials, before adding two files called user-loggedin.ejs and user-loggedout.ejs. The files should look like the following:

  • user -loggedin.ejs: Hello <%= user.username %> <a href="/logout">Logout</a>

  • user-l oggedout.ejs: <a href="/login">Login</a>

We can use the user object in our templates because of our middleware. We know the logged-in template will only be run when a user has successfully authenticated.

We will now update our layout to use these partials. Remember this functionality is provided by express-partials as of Express 3 and 4. Express 2 had this built in, so you can run into issues with code from the Internet. Here is what our layout.ejs should look like now:

<!DOCTYPE html>
<html>
<head>
    <title><%= title %></title>
    <link rel="stylesheet" href="css/cosmo.min.css">
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="container">
  <div class="row">
      <div class="col-sm-4"><h1 class="pull-left">PacktChat</h1></div>
      <div class="col-sm-4 col-sm-offset-4 top-margin">
        <div class="pull-right">
      <% if (isAuthenticated) { %>
        <%- partial('partials/user-loggedin') %>
      <% } else { %>
        <%- partial('partials/user-loggedout') %>
      <% } %>
        </div>
    </div>
  </div>
  <div class="row">
  <%- body %>
  </div>
</div>
</body>
</html>

Our authentication middleware sets req.locals.isAuthenticated, which means that any request can run a Boolean check on it. It also sets the req.locals.user object for the template. The partial function will search for the path that is passed to it beginning at the views directory. The following is a screenshot of what our site should look like now:

The following screenshot highlights our other partial that uses the logged in user's username and changes the link to Logout:

The final part of our app that we will add is flash messages. Flash messaging is when we have something to tell the user from one request to the next. It is called flash because we only want to show it once. A great example of this is when someone enters a wrong username or password, which is in fact what we are going to implement. Right now, our app just takes you back to the login page without letting you know why, which is a very bad user experience.

We will use connect-flash to let the user know when something has happened. Connect-flash uses the session, so it must be after the session middleware. Let's initialize it and add it to our middleware stack:

//variable declarations
Var flash = require('connect-flash');
//middleware stack after session, but before the routes
app.use(flash());

This gives us access to req.flash to get and set flash messages. The first message we will set is our login failed message. Change the loginProcess function in index.js present in the routes folder to include our message, as follows:

function loginProcess(req, res){
  var isAuth = util.auth(req.body.username, req.body.password, req.session);
  if (isAuth) {
    res.redirect('/chat');
  }else {
    req.flash('error', 'Wrong Username or Password');
    res.redirect('/login');
  }
};

The message is now in the session. To display this, we just have to get it out. The act of getting it out will also delete it from the session, so it is time to edit our login function in index.js present in the routes folder.

function login(req, res){
  res.render('login', {title: 'Login', message: req.flash('error')});
};

The message is now passed to the template, but the template is not ready to display it. Edit login.ejs, and add this code right under the form declaration:

<form method="post">
<% if (message.length > 0) { %>
    <div class="alert alert-danger"><%= message %></div>
  <% } %>

The message will come out in an array. We do a quick check to see if there is at least one message, and then display it. Our users will now see that authentication has failed, as seen in the following screenshot: