Anti-Forgery Token in Angular Apps

By | September 4, 2015

ASP.NET MVC has a nice feature built in that helps in the prevention of cross-site request forgeries.  For those that may not know what this is, please take a look at the general idea here.  The basic idea looks something like this.  Let’s say that your site has a page with personal information on it; credit card or debit numbers, SSN’s, etc.  A user logs in to your site, and selects the “Remember Me” checkbox at login, which saves a persistent cookie to their computer.  At this point, the cookie exists, even when they shut down their browser.  Now the user gets an email with a link to a page with the URL of the aforementioned page with the users personal information on it.  Because the authentication cookie for your application still exists, the request succeeds, and the attacker now has access to your users information.

In ASP.NET MVC, there is a very simple solution to help protected against this.  In your _Layout page, you can ask that ASP.NET MVC generate what’s called an Anti-Forgery Token, by calling @Html.AntiForgeryToken().  This creates an HTML hidden input field with a randomly generated hash value that is unique to each user session.  You then need to add the ValidateAntiForgeryToken attribute to any ASP.NET MVC controllers that you want to protect.  What this does validate any incoming HTTP requests against the token, as well as a cookie created along with the call to @Html.AntiForgeryToken.  If this validation fails, it is assumed that the request is coming from some other source than the application itself, and an HttpAntiForgeryException is thrown.

So, this is great for ASP.NET MVC controllers, but if you’re using Angular, or some other framework, to create a Single Page Application (SPA), then the only full HTTP page request you’re likely making is the initial one, and from there, you’re using AJAX for any subsequent communication with the web server.  At that point, how do you use the Anti-Forgery Token with your Web API calls?  This post explains that, and is specific to ASP.NET Web API and Angular JS.

First, you need to tell Web API how to look for the token.  The ValidateAntiForgeryToken attribute exists in the System.Web.MVC namespace and assembly, so it won’t help us with Web API.  In this case, we need to create our own attribute to handle this.  This attribute will need to be added to any System.Web.Http.ApiController instance that you want protected.

What we’re doing is creating a custom AuthorizeAttribute, which Web API will use to determine authorization by calling the overridden IsAuthorized method.  We check for the existence of an HTTP header called __RequestVerificationToken (we’ll create that header in the client side Angular code next), as well as a cookie that was created by calling @Html.AntiForgeryToken.  You then pass the values obtained from these to AntiForgery.Validate, which will throw an HttpAntiForgeryException should it not like what it gets.

Next, we need to tell Angular how to pass along the __RequestVerificationToken value whenever an AJAX call is made.  We can do this using Angular Interceptors, which provide a mechanism for intercepting any AJAX call requests, and/or responding to their responses.

Inside the interceptor, when an AJAX request starts, we try to locate the hidden form field created by the call to @Html.AntiForgeryToken, which will have the name of __RequestVerificationToken.  If found, we append a new HTTP Header to the request called __RequestVerificationToken that has this value.  Recall that the ValidateSPAAntiForgeryToken above is looking for this header.  If you don’t call @Html.AntiForgeryToken in your original page, the __RequestVerificationToken HTTP Header will not be added, and the attribute will throw an HttpAntiForgeryException.

This is how your API call is protected.  If another site or application makes the call, it won’t have access to the value in the __RequestVerificationToken hidden field, and will not be able to complete the call.  So, to recap, you need to complete 3 steps to protect your Web API calls:

  1. Attribute any ApiController’s with the ValidateSPAAntiForgeryToken attribute
  2. Register the antiForgeryInterceptor by including the javascript file in your main page
  3. Make sure to call @Html.AntiForgeryToken inside your main page

4 thoughts on “Anti-Forgery Token in Angular Apps

  1. Jeremy

    This is a cool article, and rather straight forward, but the problem that I’m running into is that I don’t want the anti-forgery token’s life-cycle to be the same as the SPA app — that keeps the same token active for a pretty significant amount of time. I want the token to refresh on each navigation. It just closes down the window of time that the application is open for attack. It seems difficult to do when relying on asynchronous calls to the API.

    Reply
    1. Jamie Nordmeyer Post author

      As I just mentioned to Samir, my apologies for the late reply. It’s been a busy couple of weeks for me. I’ll have to do some research on your question. My understanding of the way that ASP.NET implements the Anti-Forgery Token, at least, is that it’s meant to be session specific, not request specific. But I don’t know that for sure, so I’ll be happy to do a little research this weekend, if nothing else, and update this with what I find!

      Reply
  2. Samir

    Good article but then you did not explain how would one call @Html.AntiForgeryToken on a plain Html page? My SPA is divided into js, css, templates. Where js has the angular controllers, services, directives and the normal js files (jquery, blah blah). and the templates folder has the html files (.html files). I understood the WebAPI part of the article and the Angular Interceptor part but not the @Html.AntiForgeryToken part. (My SPA has a common Index.html file which is the main page which has in it)

    Reply
    1. Jamie Nordmeyer Post author

      Hey there, Samir. My apologies for the late reply; it’s been a VERY busy couple of weeks for me. That said, you’re absolutely right, I didn’t mention this for plain HTML files, and that’s something that I’m hoping to remedy shortly, as I have the very same need. When I wrote this, I wasn’t trying to do a full SPA yet in the sense that you’re talking about; I was generating my home page with Razor. But now I’m working on a project with an Index.html file, Webpack, the works, and will need the same thing. I’ll update this article as soon as I have that information.

      Reply

Leave a Reply

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

Time limit is exhausted. Please reload CAPTCHA.