MVC AntiForgeryToken and WIF gotcha

We are in the process of implementing a Windows Identity Foundation based login. And in this process, we had some problems with an MVC site. The AntiForgeryTokens were suddenly invalid. We got the following error on every postback in our MVC app:


A required anti-forgery token was not supplied or was invalid.

Stack trace:
at System.Web.Mvc.ValidateAntiForgeryTokenAttribute.OnAuthorization(AuthorizationContext filterContext)
at System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor)
at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
at System.Web.Mvc.Controller.ExecuteCore()
at System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext)
at System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext)
at System.Web.Mvc.MvcHandler.c__DisplayClass8.b__4()
at System.Web.Mvc.Async.AsyncResultWrapper.c__DisplayClass1.b__0()
at System.Web.Mvc.Async.AsyncResultWrapper.c__DisplayClass8`1.b__7(IAsyncResult _)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.End()
at System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult)
at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

After an afternoon of head-scratching and googling, I decided to download the source code of MVC myself (a big thank you to Microsoft for providing it) and see what was wrong.

This StackOverFlow post led me in the right direction:

A couple of posts helped me build our MVC webs against the MVC source code in lieu of the official MVC distribution:

(I eventually went for the last approach, although the source server stuff is pretty cool too).

Stepping through the MVC code, I found that the following code was the problem:

  • In MVC2:System.Web.Mvc.ValidateAntiForgeryTokenAttribute.OnAuthorization, SystemWebMvc\Mvc\ValidateAntiForgeryTokenAttribute.cs, lines 68-69
  • In MVC3:System.Web.Helpers.AntiForgeryWorker.Validate, System.Web.WebPages\Helpers\AntiForgeryWorker.cs, lines 89 and 90

The code looks like this.

            string currentUsername = AntiForgeryData.GetUsername(context.User);
            if (!String.Equals(formToken.Username, currentUsername, StringComparison.OrdinalIgnoreCase)) {
                // error: form token is not valid for this user
                // (don't care about cookie token)
                throw CreateValidationException();
            }

I found out that the current user in our MVC app had the Name set to null. However, in the AntiForgeryData class, the Username property is a bit magic:

        public string Username {
            get {
                return _username ?? String.Empty;
            }
            set {
                _username = value;
            }
        }

However, The AntiForgeryData.GetUserName is not:

        internal static string GetUsername(IPrincipal user) {
            if (user != null) {
                IIdentity identity = user.Identity;
                if (identity != null && identity.IsAuthenticated) {
                    return identity.Name;
                }
            }

            return String.Empty;
        }

So, the following statement:

String.Equals(formToken.Username, currentUsername, StringComparison.OrdinalIgnoreCase)

will evaluate as:

String.Equals("", null, StringComparison.OrdinalIgnoreCase)

which evaluates to false, and, hence, an HttpAntiForgeryException will be thrown.

Why did we get this problem when introducint WIF? Because we now have a ClaimsIdentity Principal as the user in the web application, and we hadn’t bothered populating it with anything but the claims we used in the target application, and, as of now, the Name property was not one of them (or so we thought).

The solution to the problem was to add a Microsoft.IdentityModel.Claims.ClaimTypes.Name claim to the output claims identity in our implementation of SecurityTokenService.GetOutputClaimsIdentity. And suddenly the AntiForgeryToken validates again.

I would say that this should be categorized as a bug in the MVC framework, it is an edge case setting the Current Identity’s User Name to null, however, it should still have worked 🙂

This entry was posted in .NET, C#, MVC, WIF. Bookmark the permalink.

4 Responses to MVC AntiForgeryToken and WIF gotcha

  1. Pingback: Cross-Site Forgery Requests (2) « mabbled

  2. Bharat says:

    I am having the same problem in MVC 4.0. Any idea how to resolve this issue in it, as I am not able to find any such above menioned code in ValidateAntiForgeryTokenAttribute.cs

    • erikbra says:

      Most of this post just explains why the issue arises. You can easily resolve it by adding a “Microsoft.IdentityModel.Claims.ClaimTypes.Name” claim to your claimset. Does that solve your problem?

  3. Roshan says:

    Awesome.. Thanks for sharing this info. It was very useful to me. You saved a lot of my time.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s