Protip: Using Anti-Forgery Token with ASP.NET Web API on MVC 4 on AppHarbor

Published on Wednesday, April 24, 2013

I just ran into and solved this problem so I thought I'd share (I'll also be talking about this at my upcoming talk).

Anti-Forgery in MVC

In vanilla MVC, you'd do anti-forgery like this in your Razor view:

@Html.AntiForgeryToken()

Then in a controller (POST):

[ValidateAntiForgeryToken]
public ActionResult DoSomething() { }

Cool. But what about Web API? It uses a totally different pipeline and likely you're interacting with it via JQuery or other AJAX framework.

ValidateHttpAntiForgeryToken

Here are some references I used when trying to implement Anti-Forgery with Web API:

Here's the rub: the two SO posts above implement this quite differently than the MVC 4 SPA template and the last article referenced. Both approaches actually worked locally for me, but both failed once I deployed to AppHarbor.

The long and short of it is that I was using the HTTP header __RequestVerificationToken. This is a no-no and I'm sort of the dumb one here in that I should know not to use custom headers like that.

My friends, the proper way is to use the X-* convention, so the HTTP header in your AJAX requests become X-XSRF-Token instead.

Apparently, AppHarbor was stripping off the other header on AJAX POST. I was receiving an error about the given header could not be found.

The working solution is below:

The machineKey thing is also key to remember on a cloud host/web farm environment.

I hope that helps somebody out there.

Aside

In the last article referenced, the AntiForgery.GetTokens() didn't work for me on AH. I kept getting the error:

System.Web.Mvc.HttpAntiForgeryException (0x80004005): The required anti-forgery cookie "__RequestVerificationToken" is not present.

I didn't get far enough to see what actual cookies were present because I switched to the other method outlined in the two SO posts. I just thought you should know that it just didn't work. I believe it's because GetTokens does not create a new cookie if the cookieToken has a value where as using @Html.AntiForgeryToken() does. The source code makes that clear in the comments. Why it worked locally I have no idea and at this point, don't care!

About Kamran
I'm a technologist, speaker, and Pluralsight author and I specialize in building full-stack solutions with a focus on modern web technology and cloud native architecture.
comments powered by Disqus
comments powered by Disqus