asp.net-coreasp.net-core-mvcasp.net-core-3.1

Programmatically generating request verification token


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:

enter image description here

If we press the button, we get a 400 "Bad Request" response:

enter image description here

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">

Solution

  • 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.