Search code examples
asp.net-mvcasp.net-mvc-3antiforgerytoken

Troubleshooting anti-forgery token problems


I have a form post that consistently gives me an anti-forgery token error.

Here is my form:

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    @Html.EditorFor(m => m.Email)
    @Html.EditorFor(m => m.Birthday)
    <p>
        <input type="submit" id="Go" value="Go" />
    </p>
}

Here is my action method:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Join(JoinViewModel model)
{
    //a bunch of stuff here but it doesn't matter because it's not making it here
}

Here is the machineKey in web.config:

<system.web>
  <machineKey validationKey="mykey" decryptionKey="myotherkey" validation="SHA1" decryption="AES" />
</system.web>

And here is the error I get:

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

I've read that changing users on the HttpContext will invalidate the token, but this isn't happening here. The HttpGet on my Join action just returns the view:

[HttpGet]
public ActionResult Join()
{
    return this.View();
}

So I'm not sure what's going on. I've searched around, and everything seems to suggest that it's either the machineKey changing (app cycles) or the user/session changing.

What else could be going on? How can I troubleshoot this?


Solution

  • After help from Adam, I get the MVC source added to my project, and was able to see there are many cases that result in the same error.

    Here is the method used to validate the anti forgery token:

        public void Validate(HttpContextBase context, string salt) {
            Debug.Assert(context != null);
    
            string fieldName = AntiForgeryData.GetAntiForgeryTokenName(null);
            string cookieName = AntiForgeryData.GetAntiForgeryTokenName(context.Request.ApplicationPath);
    
            HttpCookie cookie = context.Request.Cookies[cookieName];
            if (cookie == null || String.IsNullOrEmpty(cookie.Value)) {
                // error: cookie token is missing
                throw CreateValidationException();
            }
            AntiForgeryData cookieToken = Serializer.Deserialize(cookie.Value);
    
            string formValue = context.Request.Form[fieldName];
            if (String.IsNullOrEmpty(formValue)) {
                // error: form token is missing
                throw CreateValidationException();
            }
            AntiForgeryData formToken = Serializer.Deserialize(formValue);
    
            if (!String.Equals(cookieToken.Value, formToken.Value, StringComparison.Ordinal)) {
                // error: form token does not match cookie token
                throw CreateValidationException();
            }
    
            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();
            }
    
            if (!String.Equals(salt ?? String.Empty, formToken.Salt, StringComparison.Ordinal)) {
                // error: custom validation failed
                throw CreateValidationException();
            }
        }
    

    My problem was that condition where it compares the Identity user name with the form token's user name. In my case, I didn't have the user name set (one was null, the other was an empty string).

    While I doubt many will run into this same scenario, hopefully others will find it useful seeing the underlying conditions that are being checked.