Angular2 ASP.NET Core AntiForgeryToken
Asked Answered
M

4

14

I have an Angular2 app. It is running within ASP.NET 5 (Core).
It makes Http calls to the controller which is working fine.

But now I need to establish Cross Site Scripting projection.

How do I generate a new token on each Http request and then subsequently perform the AntiForgeryToken check in Angular2 apps?

Note: My data forms in Angular are not produced from an MVC view but entirely written in Angular2 and call web services only.

All the examples I have seen are out dated and do not work / do not work fully.

How do I integrate AntiForgeryToken checks in Angular2 against ASP.NET 5 where forms are pure Angular?

Thanks.

Mummery answered 29/3, 2016 at 21:12 Comment(0)
D
8

A custom action filter is not necessary. It can all be wired up in Startup.cs.

using Microsoft.AspNetCore.Antiforgery;

(...)

public void ConfigureServices(IServiceCollection services)
{
  services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");

  (...)
}

public void Configure(IApplicationBuilder app, IAntiforgery antiforgery)
{
  app.Use(next => context =>
  {
    if (context.Request.Path == "/")
    {
      //send the request token as a JavaScript-readable cookie, and Angular will use it by default
      var tokens = antiforgery.GetAndStoreTokens(context);
      context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions { HttpOnly = false });
    }
    return next(context);
  });

  (...)
}

Then all you need in your controllers is the [ValidateAntiForgeryToken] decorator wherever you want to enforce that a token is provided.

For reference, I found this solution here - AspNet AntiForgery Github Issue 29.

Delatorre answered 6/4, 2017 at 20:34 Comment(2)
I followed above steps but i am getting 400 bad request error. My "post" request goes to server as a "options: method type. I asked one question regarding this issue- #44842088Kauri
Some additional information I discovered today - if you are hosting in IIS, you should configure Data Protection on the server in order to persist the keys used to generate the AntiForgeryToken. If you do not, tokens will become invalid when the app restarts. See here for info on how to configure - learn.microsoft.com/en-us/aspnet/core/publishing/…Delatorre
G
5

I am using a action filter to send the request tokens. Simply apply it to the actions you want a new antiforgery token, e.g. Angular2 SPA, WebAPI action, etc.

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class AngularAntiForgeryTokenAttribute : ActionFilterAttribute
{
    private const string CookieName = "XSRF-TOKEN";
    private readonly IAntiforgery antiforgery;

    public AngularAntiForgeryTokenAttribute(IAntiforgery antiforgery)
    {
        this.antiforgery = antiforgery;
    }

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        base.OnResultExecuting(context);

        if (!context.Cancel)
        {
            var tokens = antiforgery.GetAndStoreTokens(context.HttpContext);

            context.HttpContext.Response.Cookies.Append(
                CookieName,
                tokens.RequestToken,
                new CookieOptions { HttpOnly = false });
        }
    }
}
/* HomeController */

[ServiceFilter(typeof(AngularAntiForgeryTokenAttribute), IsReusable = true)]
public IActionResult Index()
{
    return View();
}

/* AccountController */

[HttpPost()]
[AllowAnonymous]
[ValidateAntiForgeryToken]
// Send new antiforgery token
[ServiceFilter(typeof(AngularAntiForgeryTokenAttribute), IsReusable = true)]
public async Task<IActionResult> Register([FromBody] RegisterViewModel model)
{
    //...
    return Json(new { }); 
}

Register the attribute in Startup, and configure Antiforgery service to read the request token form "X-XSRF-TOKEN" header.

public class Startup
{
    // ...

    public void ConfigureServices(IServiceCollection services)
    {
        // ...

        services.AddScoped<AngularAntiForgeryTokenAttribute>();
        services.AddAntiforgery(options =>
        {
            options.HeaderName = "X-XSRF-TOKEN";
        });
    }
}
Gauvin answered 24/11, 2016 at 17:4 Comment(2)
Any suggestions on how to use this approach in a SPA? The GET actions practically never get hit (except /Home/Index), how do you deliver a new token for every form that will make a POST later?Hoseahoseia
Try applying the AngularAntiForgeryTokenAttribute to your POST endpoints. You should be able to renew the token in POST actions.Gauvin
K
0

I think you need to make custom AntiForgeryValidationToken attribute that supports sending token via header instead of form values. Then add token to header of every request from your Angular2 app to your api. Example here How do you set global custom headers in Angular2?

Kuibyshev answered 7/4, 2016 at 14:30 Comment(0)
C
0

To validate the token from a header you can use something like this:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
    public sealed class ValidateHeaderAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException(nameof(filterContext));
            }

            var httpContext = filterContext.HttpContext;
            if (httpContext.Request.Headers["__RequestVerificationToken"] == null)
            {
                httpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                httpContext.Response.StatusDescription = "RequestVerificationToken missing.";

                filterContext.Result = new JsonResult
                {
                    Data = new { ErrorMessage = httpContext.Response.StatusDescription },
                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
                };
                return;
            }
            var cookie = httpContext.Request.Cookies[System.Web.Helpers.AntiForgeryConfig.CookieName];
            System.Web.Helpers.AntiForgery.Validate(cookie != null ? cookie.Value : null, httpContext.Request.Headers["__RequestVerificationToken"]);
        }
    }

Then you just add [ValidateHeaderAntiForgeryToken] on the methods in your controller. Note though, this is from a MVC 5, ASP.NET 4.5.2 project, so you may have to alter it slightly to adjust to .NET Core. Also I modified this to return a JSON result if the token is missing, you can remove that part if you don't handle the error response and output it to the user. Credits for the core part of this attribute goes to: https://nozzlegear.com/blog/send-and-validate-an-asp-net-antiforgerytoken-as-a-request-header

The hard part is how to generate the AntiForgeryToken without using @Html.AntiForgeryToken() in pure Angular 2 application (without access to .cshtml files). I'm looking for an answer to that as well.

Confusion answered 19/9, 2016 at 22:15 Comment(1)
I'm wondering if you ever found an answer to how to create the token without using @Html.AntiForgeryToken()?Miche

© 2022 - 2024 — McMap. All rights reserved.