Clint Berry

Full-stack Web Developer

Wannabe Entrepreneur

Backbone.js apps with Authentication Tutorial

Posted on 01 Sep 2012 in Backbone, JavaScript, PHP | 28 comments

backbonelocked
At my current company I am working on my first large-scale production backbone.js app and I couldn’t be happier. After using backbone.js for a few months I have caught the vision and I am becoming more and more proficient. But every once and a while I still run into problems I would consider basic, but I can’t seem to find much help on the interwebs. Authentication with backbone.js apps was one of those problems. So I am posting the solution I came up with in hopes it will benefit someone else, and hopefully will garner some feedback or potentially better ways to solve authentication with Backbone.js.

Starting Code Base

To start this tutorial, I will be using an already created backbone.js application called Backbone Directory, created by Christophe Coenraets who has some great tutorials and information about backbone on his blog. He has some mobile versions of the app in the code base as well, but we will be working in the “web” directory.

Project Overview

Backbone Directory uses the Slim PHP framework on the server to communicate with backbone, but the principles we will be going over are language agnostic. In addition, Slim is based on the Sinatra (Ruby) methodology which in turn translates to Express.js framework for Node.js (JavaScript), and Tornado (Python).

Setting Up (Very) Basic Server Side Authentication

To get this started, we need to setup the server side login functions, and also a way to protect API requests so no data goes to anyone that isn’t authenticated. First, let’s add a login function to the api/index.php in the web directory:

// file: api/index.php
session_start(); // Add this to the top of the file

/**
 * Quick and dirty login function with hard coded credentials (admin/admin)
 * This is just an example. Do not use this in a production environment
 */
function login() {
    if(!empty($_POST['email']) && !empty($_POST['password'])) {
        // normally you would load credentials from a database. 
        // This is just an example and is certainly not secure
        if($_POST['email'] == 'admin' && $_POST['password'] == 'admin') {
            $user = array("email"=>"admin", "firstName"=>"Clint", "lastName"=>"Berry", "role"=>"user");
            $_SESSION['user'] = $user;
            echo json_encode($user);
        }
        else {
            echo '{"error":{"text":"You shall not pass..."}}';
        }
    }
    else {
        echo '{"error":{"text":"Username and Password are required."}}';
    }
}

This is a very basic login function that is obviously not secure, but will do the job for us, since our focus is really on the backbone side of things. The key thing to note here, is that since we are using backbone, even the login function works as a JSON api request. We don’t generate any HTML, we simply send back JSON data with a user identity, or an error if something went wrong. Now we need to associate this function with a route in Slim, so add the following code under the other defined routes in index.php:

// file: api/index.php
// I add the login route as a post, since we will be posting the login form info
$app->post('/login', 'login');

Now we also need to make sure no data gets sent to anyone that isn’t authorized. So now we define an authorize function to check that a user has the right permissions to get the data:

// File: api/index.php
/**
 * Authorise function, used as Slim Route Middlewear
 */
function authorize($role = "user") {
    return function () use ( $role ) {
        // Get the Slim framework object
        $app = Slim::getInstance();
        // First, check to see if the user is logged in at all
        if(!empty($_SESSION['user'])) {
            // Next, validate the role to make sure they can access the route
            // We will assume admin role can access everything
            if($_SESSION['user']['role'] == $role || 
                $_SESSION['user']['role'] == 'admin') {
                //User is logged in and has the correct permissions... Nice!
                return true;
            }
            else {
                // If a user is logged in, but doesn't have permissions, return 403
                $app->halt(403, 'You shall not pass!');
            }
        }
        else {
            // If a user is not logged in at all, return a 401
            $app->halt(401, 'You shall not pass!');
        }
    };
}

The authorize function uses some PHP closure Kung Fu, but the key is to return HTTP error codes to backbone. In our case we are going to return a 401 error (unauthorized) if a user is trying to access something they need to be logged in for, and a 403 (forbidden) if the user is logged in but doesn’t have enough privs to get the data he wants.

More Info: Check out Slim Route Middleware and PHP Closures

The last thing we need to do in our server-side code is add the middleware to the routes we want to protect:

// File: api/index.php
$app->get('/employees', authorize('user'), 'getEmployees');
$app->get('/employees/:id',	authorize('user'),'getEmployee');
$app->get('/employees/:id/reports',	authorize('admin'),'getReports');
$app->get('/employees/search/:query', authorize('user'),'getEmployeesByName');
$app->get('/employees/modifiedsince/:timestamp', authorize('user'), 'findByModifiedDate');

Setting Up Backbone Views

Now let’s get to the good stuff: Setting up our backbone views. For authentication we will of course need a login view:

// File: web/js/views/login.js
window.LoginView = Backbone.View.extend({

    initialize:function () {
        console.log('Initializing Login View');
    },

    events: {
        "click #loginButton": "login"
    },

    render:function () {
        $(this.el).html(this.template());
        return this;
    },

    login:function (event) {
        event.preventDefault(); // Don't let this button submit the form
        $('.alert-error').hide(); // Hide any errors on a new submit
        var url = '../api/login';
        console.log('Loggin in... ');
        var formValues = {
            email: $('#inputEmail').val(),
            password: $('#inputPassword').val()
        };

        $.ajax({
            url:url,
            type:'POST',
            dataType:"json",
            data: formValues,
            success:function (data) {
                console.log(["Login request details: ", data]);
               
                if(data.error) {  // If there is an error, show the error messages
                    $('.alert-error').text(data.error.text).show();
                }
                else { // If not, send them back to the home page
                    window.location.replace('#');
                }
            }
        });
    }
});

This view is pretty straight forward. It renders the login template, and put a click event handler on the login button. The event handler fires the login function when the button is clicked and sends an ajax request to our php login function. If an error comes back, we put it in the error div and show that div.

Here is the login template code:

<!-- File: web/tpl/Login.html -->
<h1>Login</h1>
<div class="alert alert-error" style="display:none;">
</div>
<form class="form-horizontal">
  <div class="control-group">
    <label class="control-label" for="inputEmail">Email</label>
    <div class="controls">
      <input type="text" id="inputEmail" placeholder="Email">
    </div>
  </div>
  <div class="control-group">
    <label class="control-label" for="inputPassword">Password</label>
    <div class="controls">
      <input type="password" id="inputPassword" placeholder="Password">
    </div>
  </div>
  <div class="control-group">
    <div class="controls">
      <button type="submit" class="btn" id="loginButton">Sign in</button>
    </div>
  </div>
</form>

Telling Backbone How to Handle 401 & 403 Errors (ajaxSetup)

Now here comes the kicker. We need backbone/jquery to catch any requests that return a 401 or 403 error and handle those requests appropriately. The method I have used to do this is to call the jquery function ajaxSetup which allows us to watch for certain status codes and to handle them appropriately.

// File: web/js/main.js
// Tell jQuery to watch for any 401 or 403 errors and handle them appropriately
$.ajaxSetup({
    statusCode: {
        401: function(){
            // Redirec the to the login page.
            window.location.replace('/#login');
         
        },
        403: function() {
            // 403 -- Access denied
            window.location.replace('/#denied');
        }
    }
});

Now all 401s and 403s will be redirected to appropriate place. (I haven’t implemented the “denied” view yet, but you get the idea)

Lastly we update the backbone routing to include the login url and login view:

// File: web/js/main.js
window.Router = Backbone.Router.extend({

    routes: {
        "": "home",
        "contact": "contact",
        "employees/:id": "employeeDetails",
        "login" : "login"
    },

// ...

    login: function() {
        $('#content').html(new LoginView().render().el);
    }
}

The Final Word (and source code)

That is it! You should now have a password protected REST API for BackboneJS. I have posted the project to github (here), so feel free to check out the code and see it in action. Currently, you will need PHP/Apache with MySQL setup and the database imported. I am working on a Vagrant file for the project so you will be able to see it in action without setting up your own server.

As always, let me know if you have any questions or suggestions.

Source Code: https://github.com/clintberry/backbone-directory-auth

The following two tabs change content below.
I am a full-stack web developer that is passionate about start-ups.

28 comments

  1. Daniel / September 6th, 2012 0:38

    I have a question. I know that REST is stateless, which means that in every request you have to pass the user and the password, so, How this fit in your example? Because you only send the credencials once and then you use the session stored in the server. I’m asking because Im looking for something different with backbone, but I dont know if is posible.
    Good post

  2. Clint / September 6th, 2012 21:13

    A true REST api is typically stateless, like you said. But in my example, I am not setting up a true REST api. I kind of fudge it. :-) Since I know that people connecting to my API are connecting from my backbone app, I am in fact allowed to use cookies. Does that make sense?

  3. Daniel / September 7th, 2012 3:10

    Yes it makes sense and I understand your point. I was just asking because I want to know how Backbone can work with a typically REST server.
    Thanks for the answer.

  4. Alexander / September 22nd, 2012 16:27

    Thanks a lot for this!

    Instead of cookies you might want to switch to a token based “session”. See this article for more information, which promotes tokens over XHR above cookies: http://sitr.us/2011/08/26/cookies-are-bad-for-you.html

  5. Clint / September 22nd, 2012 16:58

    @Alexander: Oauth is great, especially when you want to allow anyone from twitter, google, or anyone else with an authentication server to login to your application. But if you want to restrict access and use Oauth, wouldn’t I have to create my own Oauth authentication server?

  6. Elad Moshe / November 29th, 2012 8:03

    I think that there is a much better solution built in Backbone.
    You can define directly on your Backbone models an “error” method.
    Backbone will call this error automatically whenever an unhandled error occurs (i.e. you do not provide “error” callback to your fetch/save options object).
    If you want to provide a fallback error handling across all you Backbone models you can add “error” method to Backbone.model.prototype.
    This way you can handle errors correctly for all your models, but not for other AJAX call (e.g. you wouldn’t want to handle 401 from Google Maps API the same way you handle Backbone’s 401)

  7. Clint / November 29th, 2012 16:18

    @Elad,

    That is a great idea. That global error handler always made me uneasy. Thanks for the tip!

  8. Elad Moshe / December 3rd, 2012 7:35

    Thanks :)
    Correction to my previous comment – on the model you need to bind to the “error” event and provide event handler.

  9. Miguel Andrade / December 4th, 2012 17:06

    You’re free to do this kind of authentication, but I wouldn’t recommend this as it isn’t scalable and goes against REST principles. Session variables should have never existed to begin with, (I’m beeing a extreme here, I know). : )

    Keep the state on the client.
    Use HTTP. Authenticate all your requests. Use BASIC Auth, DIGEST Auth, OAuth, anything, but please make each request agnostic to the previous one.

  10. Clint / December 4th, 2012 17:37

    Miguel, I think in most instances you are right. As far as scalability, however, I disagree. There are MANY methods to scale session based auth, and it is done all the time by large sites. But if you are just looking to create a public API, then session based auth is not for you. If you are looking for a quick way to add authentication to a basic single page website, session auth can work just fine :-)

  11. David / December 18th, 2012 11:46

    So you’re making a tutorial on backbone authentication but you’re not showing what should be the secure/proper way to do authentication?

  12. Clint / December 18th, 2012 17:03

    @David – I guess that is one way to word it ;-) Although session based auth can be secure. But you are probably right about proper. I am not making a proper API authentication method, just a simple way to add authentication to a backbone app.

  13. Javier / January 25th, 2013 0:46

    Thank you very much Clint!! it was quite easy!!!

  14. Dan / February 20th, 2013 2:37

    How do you protect your service against CSRF style attacks with this method?

    Here, once the user has logged in and have their session set up, they can go to any other site. Couldn’t a malicious site then start using RESTful requests against your server, simply by grabbing a copy of your Javascript which shows all the URLs to use?

  15. Clint / February 20th, 2013 3:16

    Yes, this raw and overly simple session-based authentication would be vulnerable to CSRF. All the same session based security issues would go along with this. But there are lots of ways to overcome this. Adding tokens, limiting the time user credentials are valid, making users type in their passwords again when accessing particularly critical functions, etc. Based on the comments, I should probably be more clear that this is a very simple solution and not to be used in a production environment without some work. I’ll come up with a way to revise the post, or I will just write another one on how to do authentication the right way for an api :-)

  16. Andrés Torres Marroquín / March 14th, 2013 18:20

    Hey just a lil correction, on “The authorize function uses some PHP closure Kung Fu, but the key is to return HTML error codes”, I think it should say HTTP instead HTML.

    Thanks Clint!

  17. Ursula / March 21st, 2013 12:48

    Hi Clint! Thanks for the tutorial it has helped me understand how many things are done. However I have noticed that it doesnt work on IE. Is there a way to make it work in IE?

  18. Clint / April 23rd, 2013 3:50

    Sorry Ursula, I don’t usually check my sample code on IE out of sheer laziness. I probably won’t be able to take a look at it for a while either, sorry.

  19. Anggun Firdaus / June 12th, 2013 22:29

    Great Article..
    can you post how about combine this with code igniter ? i really got stress how do that.thank you..

  20. Silvio Clécio / June 15th, 2013 1:40

    Wow, nice post guy, very helpfull, thank you very much! ;)

  21. Clint Berry / June 19th, 2013 5:15

    @Anggun – I am not a codeigniter guy, but this should get you started: https://github.com/philsturgeon/codeigniter-restserver

  22. Jacob / October 2nd, 2013 15:26

    yo clint, sweet tutorial man, good work! Any chance you could do a version with straight nodejs? I want to suss a mondodb authentication backbonejs project for my first project. cheers!

  23. As Sabiluna / October 26th, 2013 17:52

    nice addition. thanks for sharing

  24. Jeremy / December 5th, 2013 23:15

    Perfect. Exactly what I was looking for. I’m even using Slim as a backend already. Thank you.

  25. As Sabiluna / December 30th, 2013 6:09

    how do i redirect user if they not login, well i’m talking on the client side

  26. Jason / January 2nd, 2014 9:07

    Hi Clint,

    Thanks for an interesting tutorial, How would you suggest one goes further into this perhaps using Oauth 2.0?
    I have yet to come across any informative articles such as yours to take it one step further and perhaps to best practice?
    Thanks Jason

  27. As Sabiluna / January 20th, 2014 3:36

    I extend CSRF to your apps. Read more here

    http://web-scents.blogspot.com/2014/01/more-security-added-for-your-backbonejs.html

Trackbacks/Pingbacks

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>