Thursday, July 18, 2013

AngularJS and XSRF/CSRF (Cross-Site Request Forgery)

When using AngularJS with a REST API, eventually, you will need to tinker with the XSRF/CSRF support, unless the backend already plays well with AngularJS. A Django + Django-Rest-Framework backend will send you a 403 (Forbidden) error during POST/PUT/DELETE requests, unless the CSRF settings are modified on one or both sides.

The flow works like this:
  1. The server generates a CSRF Token, which it sends to the client (typically through a setting in the cookie or hidden on a form page).
  2. The client records this token and sends it back to the server via an HTTP header.
  3. The server reads the HTTP header, compares it to the known CSRF Token for that session, then allows the request to go through if it matches.

A typical problem is that the backend's name for the cookie setting differs from what AngularJS is looking for, or the header that AngularJS sends back to the backend is not what it expects (or both).

By default, AngularJS looks for a cookie named "XSRF-TOKEN" and sets the header as "X-XSRF-TOKEN" as described here. Fortunately, it's easy to change this on the client side. I typically use a Django backend, and that sends a cookie named "csrftoken" to the browser. The 403 Forbidden error is due to the mismatch.

If using ng-boilerplate (highly recommended):
  1. Open up app.js.
  2. Inject $http and $cookies into the run method.
  3. Read the incoming CSRF token from the cookie or form ('csrftoken' or equivalent).
  4. Send it to the server during each request using the header that the backend is expecting (see the docs for your backend).

angular.module( '[your module name]',
    ... [some dependencies] ...
    'ngCookies',
    ... [other dependencies] ...
)


...

.run( function run( titleService, $http, $cookies ){

    titleService.setSuffix( '[title]' );

    // For CSRF token compatibility with Django

    $http.defaults.headers.post['X-CSRFToken'] = $cookies['csrftoken'];
})

...
;

Don't forget to add "'vendor/angular/angular-cookies.js'," under vendor_files in build.config.js for the build to pick up. Note that you will have to download the AngularJS files to get angular-cookies.js, since it currently does not come with the standard ng-boilerplate install. If you're not using ng-boilerplate, just be sure to add a reference to that JS file in your HTML.

This code will also work when using $resource, since that uses $http underneath. Preferably, we should set this during runtime in $resource itself, but this is not yet supported in the current stable AngularJS release (1.0.7), and I don't want to switch to an unstable version just to get this working.

I prefer this method than doing the modification on the server end, since when using multiple JS libraries, the header would need to be changed/added on the client end anyway. I also prefer transferring the token via the cookie instead of a form, since the latter could affect caching.

Thanks to this StackOverflow post, which provided the main clues on how to solve this in Angular.

Tuesday, July 16, 2013

Starting a New Project with ng-boilerplate

If you haven't checked out Josh Miller's ng-boilerplate, you're missing out. When it comes to best practices, this is the best place to start.

If you're not familiar with Bower, Grunt, Node, Karma, etc., don't worry, because neither am I. But that doesn't matter to me, since after setting up ng-boilerplate, all I need to do is:


  • Run "grunt watch" from the command line
  • Open up my favorite editor (currently, this is PyCharm)
  • Make changes in my html, javascript, css files
  • Save the changes and watch as the browser automatically refreshes itself with all of the updated content


It really doesn't get any easier than that! Additionally, I can simply run "grunt" to compile, run unit tests, combine html templates, and combine js and css files (as well as minify them). This has taken a ton of work off my back so that I can focus a lot more of my time coding.

The setup is fairly straightforward. I develop mostly on my Mac Mini with my test webserver and dev environment running Ubuntu x64 under VirtualBox. Ubuntu's package manager makes everything installation-related a breeze. For those running Ubuntu, if you don't already have NPM (Node.js's package manager), just run the following before going through the ng-boilerplate install:

# add the custom repo
sudo add-apt-repository ppa:chris-lea/node.js

# update repos
sudo apt-get update

# install node.js, which will also install npm
sudo apt-get install nodejs

For existing AngularJS projects, it's going to be tough to try to refactor everything into this structure. If the project is small, it's highly recommended. Otherwise, perhaps you're better off just moving the frequently-changing portions of the site to this format, if possible.

First Post

There is a lot of good information on the web for most JS frameworks, but I've had a difficult time trying finding good examples that detail how-to-do-this-or-that while following the best practices.

Over the years, as I've grown more interested in web technologies and stacks, I have been quietly hoping that a solid successor to Javascript would emerge and take the web by storm. Coming from mainly a C/C++/Java background with scripting experience (mainly Python/Perl), I still see Javascript as a language I have to deal with, rather than one that I love to write in.

When AngularJS came along, the web started to make a lot more sense. Although I'm still a fan of Dojo, AngularJS-style frameworks are the future, and it makes a better fit for what is taught in traditional Computer Engineering/Software programs. Everything else just feels like a hack.



The Goal of This Blog


I'm definitely not an expert in the web and the various technologies it is composed of. But I'm also no stranger to software engineering, and I've always felt the need to seek out the best practices of doing things in order to build stronger, robust, well-architected, and maintainable software/services.

I'll mainly post things as I stumble along, but if there are suggestions on how code snippets can be improved, please comment and I will try to keep posts up-to-date as new best practices emerge.

If you write a comment with a suggestion and found the information on another site, please post the source so that we can give credit where credit is due.