Starting with an empty MVC project, here's my Startup.cs
:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
namespace AntiForgeryExample
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseStatusCodePages();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
}
}
And here's my HomeController.cs
:
using Microsoft.AspNetCore.Mvc;
using System.Net;
namespace AntiForgeryExample
{
public class XyzController : Controller
{
[HttpPost]
[ValidateAntiForgeryToken]
public string Fgh() => "fgh 1";
[HttpGet]
public ContentResult Efg()
{
return new ContentResult()
{
ContentType = "text/html",
StatusCode = (int)HttpStatusCode.OK,
Content = @"<!DOCTYPE html>
<html>
<body>
<form method=""post"" action=""/Xyz/Fgh"">
<button type=""submit"">123</Button>
</form>
</body>
</html>"
};
}
}
}
The following line in Startup.cs
adds the anti-forgery middleware:
services.AddMvc();
So, if we go to http://localhost:52838/Xyz/Efg
, we get the simple page with a single button:
If we press the button, we get a 400 "Bad Request" response:
I'm assuming this is because we haven't passed a valid request verification token as part of the post. The Fgh
method has the ValidateAntiForgeryToken
attribute applied:
[HttpPost]
[ValidateAntiForgeryToken]
public string Fgh() => "fgh 1";
Thus a token is required when calling this method.
As described on this page, normally the code for this token will automatically be included if you use the form
tag helper with ASP.NET Core MVC or a Razor Page. It will look something like this and be included as part of the form
tag:
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkS ... s2-m9Yw">
In the example program I show above, we're programmatically generating the HTML with the form.
My question is, is there a way to programmatically generate the required token from C#, without having to go through using an MVC view or Razor Page. The idea would be that we'd get the token value and then include the input
tag:
<input name="__RequestVerificationToken" type="hidden" value="TOKEN VALUE HERE">
I shared this question on the /r/dotnet
subreddit.
/u/kenos1
provided a very helpful answer there:
You can inject Microsoft.AspNetCore.Antiforgery.IAntiforgery and call GetTokens() on it.
Here’s the documentation: link
As he mentions there, we inject IAntiforgery
at the XyzController
constructor:
private IAntiforgery antiforgery;
public XyzController(IAntiforgery antiforgery_)
{
antiforgery = antiforgery_;
}
We call GetAndStoreTokens
on the IAntiforgery
instance that we injected:
var token_set = antiforgery.GetAndStoreTokens(HttpContext);
And finally, we use the resulting token in the generated HTML:
return new ContentResult()
{
ContentType = "text/html",
StatusCode = (int)HttpStatusCode.OK,
Content = string.Format(@"<!DOCTYPE html>
<html>
<body>
<form method=""post"" action=""/Xyz/Fgh"">
<button type=""submit"">123</Button>
<input name=""__RequestVerificationToken"" type=""hidden"" value=""{0}"">
</form>
</body>
</html>",
token_set.RequestToken)
};
Here is the controller file in its entirety:
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using System.Net;
namespace AntiForgeryExample
{
public class XyzController : Controller
{
private IAntiforgery antiforgery;
public XyzController(IAntiforgery antiforgery_)
{
antiforgery = antiforgery_;
}
[HttpPost]
[ValidateAntiForgeryToken]
public string Fgh() => "fgh 1";
[HttpGet]
public ContentResult Efg()
{
var token_set = antiforgery.GetAndStoreTokens(HttpContext);
return new ContentResult()
{
ContentType = "text/html",
StatusCode = (int)HttpStatusCode.OK,
Content = string.Format(@"<!DOCTYPE html>
<html>
<body>
<form method=""post"" action=""/Xyz/Fgh"">
<button type=""submit"">123</Button>
<input name=""__RequestVerificationToken"" type=""hidden"" value=""{0}"">
</form>
</body>
</html>",
token_set.RequestToken)
};
}
}
}
The official ASP.NET Core 3.1 documentation mentions the GetAndStoreTokens
method here.