The first thing we’re going to do is create a service for the Node server. This will perform our authentication of a user, expose our logged-in user information to the application, and handle the invalidation of the user once they log out of the system.
Now, remember, we’re calling this a simple security model, so be aware that there is no database involved. There are no hashed passwords. There are no tokens passed around. We’re going simple just to demonstrate the idea of security. Now, let’s take a look at the user.service.js
module:
module.exports = {
status: "",
user: {
userName: "admin",
password: "admin",
authenticated: false,
status: this.status
},
validateUser: function(login) {
console.log("this.user: ", this.user, " and login: ", login);
if (
login.username === this.user.userName &&
login.password === this.user.password
) {
this.user.authenticated = true;
this.status = "You have successfully signed in.";
return true;
} else {
this.status = "Invalid useranme or password.";
this.user.authenticated = false;
return false;
}
},
getUserInfo: function() {
if (this.user.authenticated) {
return {
username: this.user.userName,
authenticated: this.user.authenticated,
status: this.status
};
}
},
logoutUser: function() {
this.status = "You are now signed out.";
this.user.authenticated = false;
}
};
From top to bottom, I’ll walk you through this code file. First, we’re creating a simple module exports that expose a handful of functions and properties. We are exposing both a status and a user at this time. Of the two, we’ll be using the User object the most throughout the execution of logging in and logging out.
The first function is validateUser, which is looking for a simple parameter called login. This parameter will be an object that contains the username and the password being passed from the user interface. If both parameters match the static user information that we have in the userobject, we will set a user.authenticated property to true and set the status to a success message.
The second function of the module is getUserInfo. This function will return a simple user object back to the calling code. Because of the need to be authenticated, we first check our internal user object to determine if it has been authenticated. If it has, we then create a literal object on-the-fly containing the username, the current status of the user, and a boolean flag of authenticated.
Our last function is a simple logoutUser function which accepts no parameters. The function will set the current status to a signed out status and flips the authenticated status flag to false.
Now that we have created our security model, let’s go on and use it for logging in and enabling a secure page for our user.
In our routes directory and inside the index.js file, we have all of the routes for our application. At this point, we’ve added in several more to enable logging in and logging out and for our secure page.
The first route that we have created has been a simple GET route to direct the user to a login page.
app.get("/login/", function(req, res, next) {
let data = {
title: "Sign In"
};
res.render("static/login", { data });
});
The markup for that page looks like this:
{{> header }}
{{> navigation }}
<div class="row justify-content-center align-items-center" style="height:60vh">
<div class="col-4">
<h2><i class="fa fa-sign-in" aria-hidden="true"></i> Sign In</h2>
<div class="card">
<div class="card-body">
<form action="/checklogin" method="POST" autocomplete="off">
<div class="form-group">
<label>Username</label>
<input type="text" class="form-control" name="username">
</div>
<div class="form-group">
<label>Pasword</label>
<input type="password" class="form-control" name="password">
</div>
<button type="submit" id="sendlogin" class="btn btn-primary">login</button>
<a href="/" class="btn btn-outline-secondary">cancel</a>
</form>
</div>
</div>
</div>
</div>
{{> footer }}
This page has a simple Bootstrap 4 form centered in the page. There are two text fields on the form along with a submit button. The fields are looking for the username and for the password. Upon clicking the submit button, the form will POST back to the server using this code:
<form action="/checklogin" method="POST" autocomplete="off">
Notice that we’re posting to the /checklogin
route with the information from the form. Now for the checklogin route code:
app.post("/checklogin", function(req, res, next) {
let login = {
username: req.body.username,
password: req.body.password
};
let authenticated = userService.validateUser(login);
if (authenticated) {
// simulating a database call
let timer = setTimeout(function() {
user = userService.getUserInfo();
clearInterval(timer);
res.redirect(302,"/secure");
}, 1000);
} else {
res.redirect(302, "/login/");
res.end();
}
});
From the request, the code is going to look for two parameters sent back to the server. We’re going to assign those two values to an object literal called login’ with the properties of username and password. We then create a local variable called authenticated and capture the return value from our userService.validateUser function after we pass in the login object.
Assuming that we have a valid login object,authenticated
will pass the if test and now sets a global user object with the contents that we requested from the userService
.getUserInfo function. We’ve surrounded the setting of the user object and the redirect with a time out to simulate hitting a database. Once the timeout finishes, we use the response object’s redirect function to set a response status of 302 (found) and redirects the user to the /secure route
. If we fail to authenticate, we are going to simply redirect the user back to the login form and kill the response.
We’ve logged in and we’re being redirected to another route. This is great, but what does this new route do for us? Let’s take a look at the code:
app.get("/secure/", function(req, res, next){
if(user.authenticated){
let data = {
title: "Secure Page",
links: fileListing.CreatedFileList(enums.RouteEnums().unknown),
contents: "",
user: user,
active: true
};
res.render('static/secure', { data });
} else {
res.redirect(302, "/login/");
res.end();
}
});
Once we hit the/secure/
route, the first thing we’re going to do is check to see if the user can be here by checking the user.authenticated
property. Remember, if we successfully logged in, this property should be true.
Upon passing through the if statement, we can now set the actual page data as we have been doing for most of our other pages. Notice that on the links property we’re still using the fileListing.CreatedFileList
function. This time, though, we’re going to be passing a value of unknown to the listing because the file is not part of the view/pages
directory. This was done so that we can still have global navigation across the top of the application. We also have a user property where we will assign the global user object to it. This way, we can utilize the information about the user on the views we access.
Once our data object is set, we now will render the view static/secure view to the browser. If at any time we are not authenticated, this route will redirect the user back to the /login/ route for the user to try again.
Our secured page doesn’t have much going on with it right now, but we do utilize the information about the user in it. Here is the Handlebars view:
{{> header }}
{{> navigation }}
<h3><i class="fa fa-lock" aria-hidden="true"></i> Signed In</h3>
<hr />
<p>{{data.user.status}}</p>
{{> footer }}
Remember that we set the status of the user object during login? Now, we’re going to display that information using the Handlebars double curly brace helper and display the data.user.status to the user.
Now for the navigation bar. To begin with, the navigation has a sign in link, but we want to be able to change that depending on the status of the user. Let’s take a look at the markup for the navigation:
<div class="navbar-nav">
{{#if data.user.authenticated}}
<span class="text-light navbar-text">Welcome {{data.user.username}} </span>
<a href="/logout" class="nav-link">Sign Out</a>
{{else}}
<a href="/login" class="nav-link">Sign In</a>
{{/if}}
</div>
We’re going to use one of Handlebars view helpers and do an if conditional on the data.user.authenticated property which we set back in the /secure/ route. If this passes, the markup allows the user name to be displayed using the curly braces and it will display a new hyperlink which directs to the /logout/ route. If the conditional fails, the user of the application only sees the link for signing in.
Now that we’ve gone through signing into the application and getting redirected to a secure page that checks our authentication, let’s go ahead and sign back out. Clicking the link found in the navigation bar will send us to a simple GET route called /logout which contains the following code:
app.get("/logout", function(req, res) {
userService.logoutUser();
user = {};
res.redirect(302, "/");
});
This route does three simple things. We set the authentication of the users authenticated flag to false using the userService.logoutUser
function. We set the global user object to an empty object literal. And we now redirect the user to the index of the site.
There we have it. Our simple security model has been implemented, which allows a user to login and authenticate via a service. The valid user is then redirected to a secure page and the navigation bar is updated with the status and changes from Sign In to Sign Out. This model can definitely be expanded by adding database access to store additional users. Passwords should be stored in some non-human readable form when in the database. JSON web tokens should be generated and passed to the front end to validate any request made back and forth from the front end and the back end.
Thanks for reading : Let’s share it!
#node-js #javascript