<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Thieves Like Us]]></title><description><![CDATA[Opinions and code. But mostly opinions.]]></description><link>http://brianmajewski.com/</link><generator>Ghost 0.6</generator><lastBuildDate>Mon, 25 May 2026 19:21:05 GMT</lastBuildDate><atom:link href="http://brianmajewski.com/tag/authentication/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[(Re)Learning Backbone Part 10]]></title><description><![CDATA[<p><strong>Logging In</strong></p>

<p>When we last left off, we had enforced authentication on our <code>users</code> endpoints and provided an endpoint to authenticate against. Today, we are going to add a Login screen and route to present our credentials to the backend. Upon success, we'll store the returned token and present it</p>]]></description><link>http://brianmajewski.com/2015/03/03/relearning-backbone-part-10/</link><guid isPermaLink="false">fba991ee-c41d-4fa5-8755-101d041ff747</guid><category><![CDATA[backbone]]></category><category><![CDATA[javascript]]></category><category><![CDATA[node]]></category><category><![CDATA[authentication]]></category><dc:creator><![CDATA[Brian Majewski]]></dc:creator><pubDate>Wed, 04 Mar 2015 01:47:29 GMT</pubDate><media:content url="http://brianmajewski.com/content/images/2015/02/free_login_sign_up_buttons_by_button_finder-d4pyzqo.jpg" medium="image"/><content:encoded><![CDATA[<img src="http://brianmajewski.com/content/images/2015/02/free_login_sign_up_buttons_by_button_finder-d4pyzqo.jpg" alt="(Re)Learning Backbone Part 10"><p><strong>Logging In</strong></p>

<p>When we last left off, we had enforced authentication on our <code>users</code> endpoints and provided an endpoint to authenticate against. Today, we are going to add a Login screen and route to present our credentials to the backend. Upon success, we'll store the returned token and present it on all subsequent requests.</p>

<p>Let's start by introducing a <code>globals</code> object. I like to create an object to hang constant values off of that are needed throughout the app.</p>

<p><em>/app/globals.js</em>  </p>

<pre><code class="language-javascript">define(function (require) {

    'use strict';

    return {
        auth: {
            TOKEN_KEY: 'authToken',
            USER_KEY: 'userId'
        },
        urls: {
            AUTHENTICATE: '/api/authenticate'
        }
    }
});
</code></pre>

<p>Here we've defined two keys we are going to use to store and retrieve authentication data and the URL we need to hit to authenticate. For entities, like <em>User</em>, we store the URL with the model, but for URLs that don't neatly map to a model, I like to keep them here in globals.</p>

<p>So, with our authentication in place, if we try to hit <code>/#users</code>, we'll get a 401 error when we call the <code>/api/users</code> endpoint, and our screen will only show our header and our footer. Let's have our app respond to 401 errors by redirecting the user to a login screen.</p>

<p>In <code>/app/main.js</code>, before we create our Router, add the following code:</p>

<pre><code class="language-javascript">$.ajaxSetup({
    statusCode: {
        401: function () {
            console.log('AJAX Handler - 401 Error Received');
            mediator.trigger('router:navigate', {route: 'login', options: {trigger: true}});
        }
    }
});
</code></pre>

<p>We are using jQuery to make a global change to our AJAX handling. Now, any AJAX call that gets a 401 error will get trapped and we'll trigger the router to take us to our login page. Let's update our router to handle this.</p>

<pre><code class="language-javascript">routes: {  
        '': 'home',
        'home': 'home',
        'users': 'users',
        'login': 'login'
    },

login: function() {  
    console.log('routing - login');
    require(['login/view'], function(View){
        mediator.trigger('page:displayView', new View());
    });
}
</code></pre>

<p>We've updated our route table to include a login path, and created a handler for it. This should seem familiar by now. We'll need a template and a view.</p>

<p><em>/app/login/template.hbs</em>  </p>

<pre><code class="language-markup">&lt;div class="well"&gt;  
    &lt;h4&gt;&lt;b&gt;Login&lt;/b&gt;&lt;/h4&gt;
&lt;/div&gt;  
&lt;div class="alert alert-warning" style="display: none;"&gt;&lt;/div&gt;  
&lt;form class="form-horizontal login-form"&gt;  
    &lt;div class="form-group"&gt;
        &lt;label for="email" class="col-lg-2 control-label"&gt;Email&lt;/label&gt;

        &lt;div class="col-lg-10"&gt;
            &lt;input type="text" class="form-control" id="email" placeholder="Email"&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="form-group"&gt;
        &lt;label for="password" class="col-lg-2 control-label"&gt;Password&lt;/label&gt;

        &lt;div class="col-lg-10"&gt;
            &lt;input type="password" class="form-control" id="password" placeholder="Password"&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;button type="button" class="btn btn-primary js-login pull-right"&gt;Login&lt;/button&gt;
&lt;/form&gt;  
</code></pre>

<p>This is a simple form that will allow us to collect an email address and a password. It also has an <em>alert</em> div we will use to display error messages and a submit button with the class <code>js-login</code> which will trigger the submission action.</p>

<p><em>/app/login/view.js</em>  </p>

<pre><code class="language-javascript">define(function (require) {

    'use strict';

    var Backbone = require('backbone');
    var globals = require('globals');
    var mediator = require('mediator');
    var template = require('hbs!login/template');

    return Backbone.View.extend({
        el: '#content',

        events: {
            'click .js-login': 'login'
        },

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

        login: function (e) {
            e.preventDefault();
            var formValues = {
                email: this.$('#email').val(),
                password: this.$('#password').val()
            };
            this.$('.alert').hide();
            console.log('login with ', formValues);
            $.ajax({
                url: globals.urls.AUTHENTICATE,
                type: 'POST',
                dataType: 'json',
                data: formValues,
                success: function(response){
                    if (response.success){
                        window.localStorage.setItem(globals.auth.TOKEN_KEY, response.token);
                        window.localStorage.setItem(globals.auth.USER_KEY, response._id);
                        mediator.trigger('router:navigate', {route:'home', options: {trigger: true}});
                    } else {
                        self.$('.alert-warning').text(response.message).show();
                    }
                }
            });
        }
    });
});
</code></pre>

<p>This is a standard view that listens for a click event on our submit button. When it receives one, we go to the <em>login</em> method.</p>

<p>Here, we get the values from the form fields. If the alert box is visible, we hide it. Finally, we make an AJAX call to our authentication endpoint, using the URL we defined in our globals. </p>

<p>If you remember, our authentication routine does not send back an HTTP error code. This allows us to handle any errors here. In our case, the <em>else</em> block, when the response message <em>success</em> property is false, takes the message returned from the server, adds it to the alert box and makes it visible.</p>

<p>When we successfully enter our credentials, we use local storage to store the auth token and the user id that was returned to us. Finally, we navigate to the home screen. Later, we can add code to redirect the user to their original destination but for now, we'll go home.</p>

<p>So what happens if we try to go to <code>/#users</code> again. Another 401 error! Just because we logged in once, the server has no idea that it is still us unless we tell it. To do this, we will include that token we just received in the header of all our AJAX calls. Go back to where we trapped for the 401 error and update the handler</p>

<pre><code class="language-javascript">$.ajaxSetup({
    statusCode: {
        401: function (context) {
            mediator.trigger('router:navigate', {route: 'login', options: {trigger: true}});
        }
    },
       beforeSend: function (xhr) {
        var token = window.localStorage.getItem(globals.auth.TOKEN_KEY);
        xhr.setRequestHeader('x-access-token', token);
    }
});
</code></pre>

<p>We've added the <em>beforeSend</em> method. This will retrieve the token from local storage, and set the <em>x-access-token</em> header to that value. If you recall, that is where our authenticated routes look for the token when determining if the caller is authenticated. Now if we hit our users page, the token we previously stored will be sent with our call, we'll be authenticated, and the page will display properly.</p>

<p>If you need to try again, you can use your browser's dev tools to remove the token from local storage, effectively logging you out. Let's make this a little easier on ourselves. Later, we'll be customizing our header view with information specific to the current user. For now, let's add a <em>logout</em> link.</p>

<p>In <code>/page/headerTemplate.hbs</code> replace the <code>[Navigation Elements]</code> place holder with</p>

<pre><code class="language-markup">&lt;li&gt;&lt;a href="#logout"&gt;&lt;span class="fa fa-lg fa-times-circle-o"&gt;&lt;/span&gt; Logout&lt;/a&gt;&lt;/li&gt;  
</code></pre>

<p>and update your router</p>

<p><em>/app/router.js</em>  </p>

<pre><code class="language-javascript">routes: {  
    '': 'home',
    'home': 'home',
    'users': 'users',
    'login': 'login',
    'logout': 'logout'
},

logout: function() {  
    window.localStorage.removeItem(globals.auth.TOKEN_KEY);
    window.localStorage.removeItem(globals.auth.USER_KEY);    
    this.home();
 }
</code></pre>

<p>This simply deletes our authentication info and sends the user to the home page. If it seems a little messy that we are messing with local storage in different parts of our app, you're right. Later when we coordinate loading the currently authenticated user, we'll create some convenience methods that we can trigger with messages through the mediator.</p>

<p><strong>Next Steps</strong></p>

<p>When we load our app, we will want to make sure we have the user object associated with the currently logged in user. We've already stored the id, so it is just a matter of loading the user. With that User, we'll customize the header. Additionally, we relied on the 401 error to redirect us to the login page. We'll be adding an <em>isAuthenticated</em> check to the router itself so that we can redirect to the login screen without needing to make a server call round trip.</p>

<p><em>(You can check out the project at it's current state at commit: <a href="https://github.com/bmajewski/relearning-backbone/commit/8a0a2b3c06575454a7ffae2b03b579d3bc47acba">8a0a2b3c06575454a7ffae2b03b579d3bc47acba</a>)</em></p>]]></content:encoded></item></channel></rss>