Search code examples
c#ajaxasp.net-mvcantiforgerytoken

Processing AntiForgeryToken send with Ajax


Hello I'm following this tutorial:

And I'm trying to send Ajax request which include AntiforgeryToken. Here my ajax request:

$(document).ready(function () {
    @functions{
        public string TokenHeaderValue()
        {
            string cookieToken, formToken;
            AntiForgery.GetTokens(null, out cookieToken, out formToken);
            return cookieToken + ":" + formToken;                
        }
    }
    $('.z').on('click', function (event) {
        event.preventDefault();
        $.ajax({
            url: "/DeviceUsage/Return",
            type: "POST",
            contentType: "application/json; charset=utf-8",
            dataType: 'html',
            headers: {
                'RequestVerificationToken': '@TokenHeaderValue()'
            },
            data: JSON.stringify({ dev: { DeviceInstanceId: $('#DeviceInstanceId').val(), UserId: "1", StorageId: $('#StorageId').val() } }),
            error: function (data) {
                alert("wystąpił nieokreślony błąd " + data);
            },
            success: function (data) {
                $('.modal-body').html(data);
            }
        })
    })
});

here my controller:

[HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Return(DeviceUsage dev)
    {
        if(dev.StorageId==3)
        {
            ModelState.AddModelError("", "Nie można oddać na własne biurko");
            ViewBag.StorageId = new SelectList(unitOfWork.storageRepository.Get(), "Id", "Name", dev.StorageId);
            return PartialView(dev);
        }
        dev.UserId = 1;
        unitOfWork.deviceUsageRepository.Update(dev);
        unitOfWork.Save();
        return RedirectToAction("MyDevices");
    }

But in this tutorial they show function like:

void ValidateRequestHeader(HttpRequestMessage request)
{
string cookieToken = "";
string formToken = "";

IEnumerable<string> tokenHeaders;
if (request.Headers.TryGetValues("RequestVerificationToken", out tokenHeaders))
{
    string[] tokens = tokenHeaders.First().Split(':');
    if (tokens.Length == 2)
    {
        cookieToken = tokens[0].Trim();
        formToken = tokens[1].Trim();
    }
}
AntiForgery.Validate(cookieToken, formToken);
}

But I have no idea where to put this code in my controller and how to call this function. Can anyone explain me how to use above code?


Solution

  • What they are showing in Anti-CSRF and AJAX section of the tutorial is a non-standard token validation method. In this example you would not use [ValidateAntiForgeryToken], but rather run the validation manually. Firstly you inject additional header in ajax call:

            headers: {
                'RequestVerificationToken': '@TokenHeaderValue()'
            },
    

    and then read and validate token from the header in your action:

    [HttpPost]
    public ActionResult Return(DeviceUsage dev)
    {
        ValidateRequestHeader(Request);
        //process action
    }
    void ValidateRequestHeader(HttpRequestBase request)
    {
        string cookieToken = "";
        string formToken = "";
    
        if (request.Headers["RequestVerificationToken"] != null)
        {
            string[] tokens = request.Headers["RequestVerificationToken"].Split(':');
            if (tokens.Length == 2)
            {
                cookieToken = tokens[0].Trim();
                formToken = tokens[1].Trim();
            }
        }
        AntiForgery.Validate(cookieToken, formToken);
    }
    

    Notice that ValidateRequestHeader() reads the header set earlier by jQuery call. Also, I've amended the method slightly to accept HttpRequestBase.

    Tip: To avoid adding ValidateRequestHeader() to every controller that responds to ajax calls, add it to your base controller if you have any, and derive all controllers from the base. Or even better create your own [ValidateAntiForgeryAjaxToken] attribute.