Search code examples
c#asp.net-coreantiforgerytoken

Why my ASP.net core application does not accept manually generated tokens


Why my ASP.net core application does not accept manually generated tokens

For Ajax requests, I send both header token and cookie token by the request

This is the code I use to create the header Token

public class TokenController : Controller
{
    private readonly IAntiforgery _antiforgery;

    public TokenController(IAntiforgery antiforgery)
    {
        _antiforgery = antiforgery;
    }

    [HttpGet]
    public ActionResult GetHeaderToken()
    {
        var tokens = _antiforgery.GetTokens(HttpContext);

        return Json(tokens.RequestToken);
    }
}

Startup.cs:

public class Startup
{
    //...
    public void ConfigureServices(IServiceCollection services)
    {
        //...
        services.AddAntiforgery(options =>
        {
            options.HeaderName = "HeaderToken";
            options.FormFieldName = "FieldToken";
            options.Cookie = new CookieBuilder
            {
                Name = "CookieToken",
                IsEssential = true,
                HttpOnly = true,
                SecurePolicy = CookieSecurePolicy.SameAsRequest,
                SameSite = SameSiteMode.Lax
            };
        });

        services.AddControllers(options =>
        {
            options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
        });

        services.AddRazorPages(options =>
        {
            options.Conventions
                .ConfigureFilter(new AutoValidateAntiforgeryTokenAttribute());
        })
        .AddRazorRuntimeCompilation();
    }
}

js:

$.ajax = function(options)
{
    return new Promise(async (resolve, reject) =>
    {
        var xhr = new XMLHttpRequest();

        xhr.open(options.method, options.url, true);

        xhr.onerror = function()
        {
            //...
        };

        xhr.onload = function()
        {
            //...
        };

        if (options.method.toLowerCase() === 'get')
        {
            xhr.send($.param(options.data));
        }
        else
        {
            var data = options.data;

            var $tokenField = $(`[name="FieldToken"]`);

            if ($tokenField.length === 0)
            {
                await $.ajax({
                    url: 'Token/GetHeaderToken',
                    method: 'get',
                    complete: function(x)
                    {
                         data["FieldToken"] = x.responseText;
                    }
                });
            }
            else
            {
                data["FieldToken"] = $tokenField.first().val();
            }

            xhr.send(getFormData(options.data));
        }
    });
};

Solution

  • You can get token from form element:

    TokenController.cs

    public class TokenController : Controller
    {
        [HttpGet]
        [Route("[controller]")]
        public ActionResult Index()
        {
            return PartialView("Token");
        }
    }
    

    Token.cshtml:

    @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
    <form method="post"></form>
    

    js:

    $.ajax = function(options)
    {
        return new Promise(async (resolve, reject) =>
        {
            var xhr = new XMLHttpRequest();
    
            xhr.onerror = function()
            {
                //...
            };
    
            xhr.onload = function()
            {
                //...
            };
    
            if (options.method.toLowerCase() === 'get')
            {
                xhr.open(options.method, `${options.url}?${$.param(options.data)}`, true);
    
                xhr.send();
            }
            else
            {
                var $tokenField = $(`[name="FieldToken"]`);
    
                if ($tokenField.length === 0)
                {
                    await $.ajax({
                        url: 'Token/GetHeaderToken',
                        method: 'get',
                        complete: function(x)
                        {
                            $('body').append(x.responseText);
                        }
                    });
    
                    $tokenField = $(`[name="FieldToken"]`);
                }
    
                xhr.open(options.method, options.url, true);
    
                xhr.setRequestHeader("HeaderToken", $tokenField.first().val());
    
                xhr.send(getFormData(options.data));
            }
        });
    };