Votr Part 4: AngularJS and Authentication with CouchDB

carter-rabasa-seattle-shirt-lo-res (1)

This is the fourth part in a series of blog posts about building a real-time SMS and voice voting application using Node.js. In part one, we created the Node.js application and captured incoming votes over SMS and stored them in a CouchDB. In part two, we created a real-time visualization of the voting using Socket.io and Highcharts. In part three, we tweaked our app to scale to thousands of votes per second and millions of total votes.

Through the first 3 parts of this series we now have a scalable voting application that can process votes for events via SMS or voice and display the real-time progress of voting. However, there is currently no web interface for an administrator. Creating events or modifying them require making changes directly to the documents in CouchDB. In this blog post, I will walk through the process of creating a simple web interface for administrators using AngularJS.

This tutorial uses the Twilio Node.JS Module and the Twilio Rest Message API

Why Angular JS?

There are quite a few client-side MVC frameworks out there that a developer can use to build web apps. This is both a blessing and a curse. On the one hand, competition between all of these frameworks for your attention ensures that they will get better and better. On the other hand, it can be bewildering trying to figure out which one is the best fit for you and your project.

That being said, I needed to choose one. I played around with Backbone and Ember but ultimately settled on Angular for the for the following reasons:

AngularJS + Node.js + CouchDB

In this blog post, we’re going to cover:

  1. Creating the beginning of a simple AngularJS app
  2. Routing web requests to it through Node.js
  3. Authentication using CouchDB credentials

This is less of an exhaustive AngularJS 101 tutorial and more of a pragmatic walk-through on how to get AngularJS running, connect it to RESTful services being powered by Node.js and handle authentication using CouchDB. If that sounds good to you, let’s go!

Route Admin Requests to Angular JS

The first thing we need to do is take browser requests for /admin/ and route them to our AngularJS application. We’ll need to add a new Express route in app.js to handle this. In addition, we’re going to do a little bit of extra work to make sure that all requests are sent over HTTPS since we will be dealing with sensitive log-in credentials:

Now let’s add an admin method to our routes module to render a template called admin:

Create A View For The App

All requests for /admin/ now cause a template called admin to get rendered. Let’s create a file called admin.hjs and place it in our views directory. Think of this file as a template that contains the frame for the web page (header, footer, etc) and a portion inside of the frame that AngularJS will populate with dynamic content.

As I mentioned, AngularJS builds on the declarative nature of HTML and CSS. There are several custom attributes that we will use to declare our application’s intentions. Let’s walk through admin.hjs and discuss what some of these declarations do:

The ng-app declaration tells AngularJS that this this element and everything inside of it are part of the AngularJS application called votr.

Here we include the core AngularJS library, the ngResource library (more on that later) and our own votr.js file which defines our application.

Lastly, we use the ng-view declaration to specify that this is the <div> that we will populate with the content of our application.

Create The AngularJS App

The file votr.js will house the AngularJS application, so let’s look at some of the first few lines:

This declares our application, names it votr and specifies that it will rely on the ngResource module. Next, let’s define the routes that make up our admin application. In our application we’re only going to define three: logging-in, listing events and logging-out:

Remember, these are just the client-side routes for AngularJS. The server-side route for our Node.js application is /admin/. So the fully qualified URIs will look like this:

  • Login = /admin/#/login
  • Event List = /admin/#/
  • Logout = /admin/#/logout

Create A Login Controller and View

Now that most of the bootstrapping is behind us, we can start to focus on the HTML and JS that will power our application. Let’s start by going back and reviewing our route declarations and look for the one corresponding to the path /login:

So, the path /login is associated with a template called login.html and a controller called LoginCtrl. Let’s check out our login template:

This is a pretty simple form. The input tags include ng-model directives for the username and password. This directive is used to bind the value of what you type into those fields with an object called user that has username and password attributes.

Attached to the form tag is an AngularJS directive called ng-submit that points to a function called login. Since our /login path is associated with the LoginCtrl, you should expect a login function to be defined to handle the form submission, which it is:

Let’s Pause: $scope and Dependency Injection

Let’s not gloss over the parameters being passed-in to the LoginCtrl function. AngularJS makes extensive use of dependency injection. You’ll start to notice that none of the functions in an AngularJS application reference variables that aren’t passed-in to or declared within the function. No global variables are used.

There are numerous benefits to this approach that I don’t have time to cover here. From the perspective of a developer building an AngularJS application, please be aware that you’ll need to pass in any objects that your functions need to work properly. Some objects (such as $scope and $rootScope) don’t require any set-up on your part, they are pre-defined by the framework.

Other dependencies, such as the SessionService you see in LoginCtrl, will need to be defined and registered with AngularJS:

Finally, let’s cover $scope and $rootScope. The $scope object is a container for all functions and objects that are needed for a single controller. In the case of LoginCtrl, $scope contains the object user (which hold username and password data) and the login function (which logs a user in to the web app). The $rootScope object, as the name implies, is a container for values and functionality needed across all controllers. In our app we use $rootScope to store the logged-in state of the user.

Authenticate Using CouchDB and Cookies

Now, before we can show the user a list of events we need to ask them to log-in. Rather than create an additional username/password system, we’ll piggyback on CouchDB’s authentication scheme. After all, if you can log-in to CouchDB directly and edit the data, you’re probably allowed the log-in to the web application to do the same.

When a user submits their username and password on the login form, these credentials are passed all the way down and used to log-in to CouchDB. CouchDB returns a cookie to use for subsequent authenticated requests to the database. When the cookie expires (or hasn’t been issued yet) CouchDB will return an HTTP 401 error.

The AngularJS application interacts with the server (i.e. the fetch a list of events) via AJAX calls. Errors are passed back, so we can keep an eye out for 401 errors to let us know that the user needs to log-in. Below is how you configure AngularJS to intercept and inspect HTTP requests:

It’s worth going over that code again. The first block (request) covers all HTTP requests. The AngularJS application has a global variable ( $rootScope.loggedIn) that we use to store the login state. If this variable is undefined or false, the user is redirected to the login view.

The second block (responseError) covers the event of an HTTP error happening during an AJAX call. If the error is 401 (not logged in) the user is redirected to the login page.

Build A RESTful Login API

Getting back to the login function, you’ll notice a call to SessionService.save(). SessionService is a service class. In AngularJS you specify the unique id of the service and the code for how it should be instantiated by the service factory.

In our case SessionService is a thin wrapper around a $resource object. A $resource object provides a programmatic interface between your app and a RESTful web service. When you instantiate a $resource object, you specify the root endpoint for that RESTful service and AngularJS will convert your calls of get(), save(), etc into the equivalent RESTful HTTP requests to that endpoint.

In our case, when a user attempts to login, we execute save on the SessionService. This initiates a POST to the API endpoint that we defined. Let’s walk all the way through this form submission to the log-in to CouchDB itself starting with app.js:

In the routes module we have a handle to the sessions module. This is the module that we use to house all functionality related to logging-in. We call the login method, passing in the username and password:

Inside of the sessions module, we have a handle to our CouchDB client and we attempt to authenticate:

If the login fails, we return the error. This errors bubbles its way back up and and HTTP error is returned to the client. The error handler of the call to SessionService.save is triggered and we set $scope.loginError to true.

In the markup for login.html you’ll notice a <div> tag that contains the error message. That <div> also includes the ng-show directive. This directive selective shows or hides an element based on the value specified. In our case, setting $scope.loginError to true caused the div to display. If it was set to false or undefined, it would be hidden.

Now, if authentication succeeds, we are given a cookie that we can use to make subsequent requests. We store this cookie in memory on the server and associate it with the username of the person who logged-in. This enables us to delete the cookie from memory when the user logs out. The success of the log-in bubbles up through our Node.js application is returned to the client as an HTTP 200. We then forward the user to the Event List view.

Seeing This In Action

Fire up the node process and point your browser at http://localhost:3000/admin/. Initially, the root of our AngularJS application will load ( /admin/#/) but we will soon get re-directed to /admin/#/login:


At this point, go ahead and enter some bogus credentials. These will get passed all the way through to the CouchDB instance where authentication will fail and our server will return a 401 error. This will set loginError to true which will trigger the error message:


Finally, go ahead and authenticate using your valid CouchDB credentials. This should succeed and the LoginCtrl will then redirect to the root path ( /admin/#/) which will load the…


To Be Continued…

And that’s where we’re going to stop! We’ve covered a ton of ground in this blog post, let’s quickly recapped the things we’ve learned:

  1. Setting up Node.js and Express to route requests to the AngularJS application
  2. Creating the HTML page to house the AngularJS app
  3. Defining the routes
  4. Building the controller and associated view the login experience
  5. Authentication using CouchDB and Cookies
  6. Handling login with a RESTful API and the $http interceptor

All of the code we’ve gone over here is posted on Github. In the next blog post, we will finish building this AngularJS application and add all of the functionality necessary to load a list of events, edit an event, create new events and delete events.

Join the conversation on Reddit!

  • Nico2706

    Really good post, thx for the details.
    Can’t wait for the next one :)

  • malix

    Great series!

    You should probably use res.json(…) to send JSON back


    • http://carter.rabasa.com crabasa

      Where do you think I should be doing this? Could you submit a PR?

      • Guest

        maybe i missed something. But you are reading it wiht Node here

        var cookie = headers[‘set-cookie’][0];
        var authSession = cookie.split(‘;’)[0].split(‘=’)[1];
        addLoggedInUser(authSession, username);
        callback(null, cookie);

        and u save it inside authSession var to share it inside the app.

        From what i read around the web is not possible to get the cookie with javascript if httponly flag is true

  • Julian Jelfs

    I think the client side stuff could be improved a bit by remembering what the user was trying to do when they got a 401 and replaying after they authenticate. Something like this https://github.com/witoldsz/angular-http-auth which I have used successfully. The principle is the same otherwise I think.

    • http://carter.rabasa.com crabasa

      Julian, that’s a spot-on point. My code is heavily inspired Witold’s plugin. I’m going to add some attribution right now, big oversight on my part.

      That being said, I didn’t use the plugin as-is because I wanted to minimize dependencies. I modified the code because I was trying to reduce the complexity of this tutorial as much as possible. For more complex apps, you’ll definitely want something more powerful.

  • ryan

    it looks like you made SessionService a factory instead of a service in the code example.

  • psahni

    Nicely put up, thanks

  • http://eddiemonge.com Eddie Monge

    Why do you set the loggedin status on the $rootScope instead of a in the SessionService? this is kind of why SessionService should be a service instead of a factory

    • http://carter.rabasa.com crabasa

      You could certainly do that, but you still would have to expose a true/false value to the $scope or $rootScope in order to make it easy to toggle various UI elements (log-in button, log-out button, etc).

  • Shivesh

    I think malix is right for sending JSON Back use res.json()

  • Wanny Miarelli

    the cookie sent back from CouchDB is HTTPONLY, how can you interact with it inside AngularJS ?

    • http://carter.rabasa.com crabasa

      In the scenario above it isn’t necessary to interact with the cookie. Using cookies without HTTPONLY can create security concerns for your application. Check out this post by Jeff Atwood for some more info on this:


      • Wanny Miarelli

        Maybe i miss something but you are reading it with node.js

        var cookie = headers[‘set-cookie’][0];
        var authSession = cookie.split(‘;’)[0].split(‘=’)[1];
        addLoggedInUser(authSession, username);
        callback(null, cookie);

        so the question is how can you read the cookie with Javascript if HTTPOnly flag is true ?

        PS : i know, i will never use a non-Httponly cookie :D

        • http://carter.rabasa.com crabasa

          Ah, I understand now. The code you’re looking at is running on the server-side as part of my Node.js application. So the cookies are being manipulated in the context of server code, not browser code.

          Although I’m using JS on both the browser and the server, the code bases are distinct. My AngularJS app doesn’t have special access to my Node app. They are integrated via a RESTful API.

          • Wanny Miarelli

            Yes,tthis is what I missed :D Server Side JS is able to read the cookie.
            My bad !
            Really, thanks for your time.

  • vjennings

    wow! very high quality stuff! when can we hope for the finale?

  • Phil Imperato

    Thank you! This was really helpful!

    • http://carter.rabasa.com/ Carter Rabasa

      Hey Phil, glad you found it useful. Thanks for the kind words :)

      • Phil Imperato

        One question, is this still the preferred way of dealing with CouchDb auth? Because this is a almost 2 years old.

        • http://carter.rabasa.com/ Carter Rabasa

          I have not heard of any changes in CouchDB security/auth that would impact this tutorial. Please let me know if you don’t find that to be the case of course.