NCC-2909-M
NCC-2909-M

Reputation: 779

ASP.NET Core WebAPI: Validation of the provided antiforgery token failed. The cookie token and the request token were swapped

In my ASP.NET Core 2 WebAPI application I want to use the AntiforgeryToken for my POST, PUT and DELETE controller methods. Reagrding to this documentation I set up the ConfigureServices and Configure methods of my Startup class. On the client side I use Angular 5 and their default configuration for Antiforgery. I can't figure out where the problem is.

Here is a excerpt of my Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    // ...
    services.AddAntiforgery(options =>
    {
        options.Cookie.Name = "XSRF-TOKEN";
        options.HeaderName = "X-XSRF-TOKEN";
        options.FormFieldName = "F-XSFR-TOKEN";
    });
    // ...
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider provider, ILogger<Startup> logger, IAntiforgery antiforgery)
{
    // ...
    app.Use(async (context, next) =>
    {
        var tokens = antiforgery.GetAndStoreTokens(context);
        context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions() { HttpOnly = false });
        await next();
    });
    // ...
}

My Controllers are all like that one:

[Authorize]
[Route("api")]
public class CarController : Controller
{
    #region Variables
    private readonly DataContext _db;
    private ILogger<CarController> _logger;
    #endregion

    #region Constructor
    public CarController(DataContext db, ILogger<CarController> logger)
    {
        _db = db;
        _logger = logger;
    }
    #endregion

    #region Methods
    [AllowAnonymous]
    [HttpGet("[controller]")]
    public IActionResult Get()
    {
        try
        {
            return Ok(_db.Cars);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex.GetHashCode(), ex, ex.Message);
            return BadRequest(ex);
        }
    }

    [HttpPost("[controller]")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Post([FromBody] CreateCar model)
    {
        try
        {
            // Creates a new car.
        }
        catch (Exception ex)
        {
            _logger.LogError(ex.HResult, ex, ex.Message);
            return StatusCode(500, ex);
        }
    }

    [HttpPut("[controller]/{id}")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Put(int id, [FromBody] UpdateCar model)
    {
        try
        {
            // Updates a car
        }
        catch (Exception ex)
        {
            _logger.LogError(ex.HResult, ex, ex.Message);
            return StatusCode(500, ex);
        }
    }
    #endregion
}

Upvotes: 2

Views: 1220

Answers (1)

Corneliu
Corneliu

Reputation: 858

I hit the same problem and I think I found the issue.

TLDR: options.Cookie.Name = "ASP-XSRF-TOKEN";

The options.Cookie.Name in the AddAntiforgery has to be different than the cookie you set manually using context.Response.Cookies.Append.

Try to change the name of one of them and it will work. Right now you override the generated cookie that is using the options.Cookie.Name name with the tokens.RequestToken value.

You can notice the difference in the Developer Tools.

  • The default token generated using options.Cookie.Name is marked as http only (HttpOnly = true)
  • The manually attached token using context.Response.Cookies.Append is marked as HttpOnly = false

The second one is read from JS/Angular (you can read it in JS because the HttpOnly=false and sent as a header in your ajax requests and validated against the default one that can't be read from JS)

Upvotes: 3

Related Questions