Aiming for elegance, one thought at a time

Basic Angular front-end for authentication (SPA Part 3)

Posted: May 7th, 2013 | Author: | Filed under: Uncategorized | No Comments »

This is the third part of a series of blog posts that will walk step-by-step through the process of creating a secure Single Page App (SPA) using node.js, passport.js, and Angular.

If you’re new here, you should start at Part 1, where we set up Passport.js. Today, we’re going to get started with Angular. We’re just going to ease in to it: this is deliberately simplified. We’ll cover creating a simple security service and some of the directives we’ll need. Later, we’ll wire the service up to the backend, and make some improvements to how the front end works.

The complete series of posts will cover:

  • Getting started with passport.js
  • Adding in users
  • Handling login, logout, and registration from a single page app
  • Adding in robust remember-me functionality
  • Protecting against CSRF
  • Protecting against XSS
  • Adding a reasonably robust remember-me to passport js
  • Enhancing security by adding some simple HTTP headers
  • Setting up to always redirect to SSL
  • Pushing our app to Heroku
  • Throttling login attempts

Let’s get started

The code for this part is at:

git clone https://github.com/studds/secure-spa-part-3.git

Unlike previous examples, for today we won’t be hitting the database, so you can start the server right away:

node app.js

New home page:
Secure-SPA-Part-3-Home

Login page:
Secure-SPA-Part-3-Login

Signup page:
Secure-SPA-Part-3-Signup

Home page after login:
Secure-SPA-Part-3-Logged-In

Right now, these pages aren’t wired up to the backend. We’ll handle that later in the week. So feel free to try it out and enter whatever information you choose.

The code

The action is all happening in public/scripts/app.js, which is the main file for the new Angular js app.

We’ve only got a few routes here, as the login and signup pages will be “popups”. You’ll have your own preferred way to implement popups, so for this example, I’ve just used simple directives and ng-show. (If I can work out a more elegant way of doing this without assuming what libraries will be used, I’ll build that in to future posts. Suggestions welcome!) The MainCtrl simply maps in the Security service, which we’ll look at next.

'use strict';

var javascriptApp = angular.module('javascriptApp', [])
    .config(function ($routeProvider, $locationProvider) {
        $routeProvider
            .when('/', {
                templateUrl: 'views/main.html',
                controller: 'MainCtrl'
            })
            .when('/account', {
                templateUrl: 'views/account.html',
                controller: 'MainCtrl'
            })
            .otherwise({
                redirectTo: '/'
            });
        $locationProvider.html5Mode(true);
    })
    .controller('MainCtrl', function ($scope, Security) {
        $scope.security = Security;
    })

This simple Security service is just an outline to get us started. It allows us to show and hide the login and signup forms, as well as managing the current logged in user. Ideally, we’d be deferring to a dialog service to display the login popup, but again, I didn’t want to assume too many libraries at this point. For now, it should be pretty simple to modify this to suit your needs.

javascriptApp.factory('Security', function ($location) {
    return {
        showLogin: function () {
            this.isSignupShown = false;
            this.isLoginShown = true;
        },
        isLoginShown: false,
        showSignup: function () {
            this.isLoginShown = false;
            this.isSignupShown = true;
        },
        isSignupShown: false,
        login: function (username, password) {
            this.currentUser = {username: username, email: username+"@example.com" };
            this.isLoginShown = false;
        },
        signup: function (username, email, password1, password2) {
            this.currentUser = {username: username, email: email};
            this.isSignupShown = false;
        },
        logout: function () {
            delete this.currentUser;
        },
        isAuthenticated: function () {
            return !!this.currentUser;
        }
    };
});

The login directive is for the login form, and is very simple: it simply makes the Security service available to the login template. To keep everything tight, we’re using replace.

javascriptApp.directive("login", function () {
    return {
        restrict: "E",
        scope: {},
        replace: true,
        templateUrl: "views/login.html",
        controller: function ($scope, Security) {
            $scope.security = Security;
        },
        link: function (scope) {
        }
    }
});

The login template will only show if security.isLoginShown is true (our poor-mans popup.) On submit, the username and password entered will be passed to the login function.

<div ng-show="security.isLoginShown"><form ng-submit="security.login(user.username, user.password)">
	<div>
	<label>Username:</label>
	<input type="text" name="username" ng-model="user.username" required/><br/>
	</div>
	<div>
	<label>Password:</label>
	<input type="password" name="password" ng-model="user.password" required/>
	</div>
	<div>
	<input type="submit" value="Log in"/>
	</div>
</form>
<p><small>Hint - bob:secret</small></p></div>

The login toolbar directive is to display the buttons for logging in and logging out, and is also very simple: as above, replace is true, and the controller simply makes the Security service available.

javascriptApp.directive("loginToolbar", function () {
    return {
        restrict: "E",
        scope: {},
        replace: true,
        templateUrl: "views/login-toolbar.html",
        controller: function ($scope, Security) {
            $scope.security = Security;
        },
        link: function (scope) {
        }
    }
});

The toolbar template uses the security service to determine what should (and shouldn’t) be shown to the user.

<ul>
    <li><a href="/">Home</a></li>
    <li ng-show="security.isAuthenticated()"><a href="/account">Account</a></li>
    <li ng-show="!security.isAuthenticated()"><a ng-click="security.showLogin()">Log In</a></li>
    <li ng-show="!security.isAuthenticated()"><a ng-click="security.showSignup()">Sign up</a></li>
    <li ng-show="security.isAuthenticated()"><a ng-click="security.logout()">Log out</a></li>
</ul>

The signup directive does a little bit more that the login and toolbar directives. We’ve wired it up to make sure our password has sufficient complexity, although it’s a bit clunky, and we’re not checking that passwords match. We’ll improve the validation on this page in the next instalment.

javascriptApp.directive("signup", function () {
    return {
        restrict: "E",
        scope: {},
        replace: true,
        templateUrl: "views/signup.html",
        controller: function ($scope, Security) {
            $scope.user = {};
            $scope.security = Security;
        },
        link: function (scope) {
            scope.$watch("user.password", function (value) {
                scope.user.passwordStrength = !value || value.length === 0 ? 0 : typeof zxcvbn !== "undefined" ? zxcvbn(value).score : 0;
            })
        }
    }
});

The signup template will only be shown is isSignupShown is true (again, our poor-mans popup). In the next instalment we’ll be making some improvements to the validation in place here.

<div ng-show="security.isSignupShown">
    <form ng-submit="security.signup(user.username, user.email, user.password, user.password2)">
        <div>
            <label>Username:</label>
            <input type="text" required name="username" ng-model="user.username"/><br/>
        </div>
        <div>
            <label>Email:</label>
            <input type="email" required name="email" ng-model="user.email"/><br/>
        </div>
        <div>
            <label>Password:</label>
            <input type="password" required name="password" ng-model="user.password"/>
            <input type="text" class="password-strength-{{user.passwordStrength}}" disabled/>
        </div>
        <div>
            <label>Repeat password:</label>
            <input type="password" required name="password2" ng-model="user.password2"/>
        </div>
        <div>
            <input id="submit" type="submit" value="Sign up" ng-disabled="user.passwordStrength < 2"/>
        </div>
    </form>
    <p>
        <small>Hint - some strong password are correcthorsebatterystaple, rWibMFACxAUGZmxhVncy,
            Ba9ZyWABu99[BK#6MBgbH88Tofv)vs$w
        </small>
    </p>
</div>

Putting it all together

By using directives for the toolbar, login, and signup, we can easily insert the authentication components where they’re needed.

<login-toolbar></login-toolbar>
<login></login>
<signup></signup>
<h2 ng-show="!security.isAuthenticated()">Welcome. Please log in.</h2>
<h2 ng-show="security.isAuthenticated()">Hello {{security.currentUser.username}}</h2>

Wrapping up

Today we’ve just put the very basics in place for authentication using Angular. Next up, we’ll improve the validation and wire it together with the back end.


Sign up for danielstudds.com
* = required field

Leave a Reply