DuendeSoftware / BFF

Framework for ASP.NET Core to secure SPAs using the Backend-for-Frontend (BFF) pattern

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

400 status code when using AutoValidateAntiforgeryTokenAttribute

dotnetspark opened this issue · comments

I want to exclude some endpoints from being checked for antiforgery (GET only). Hence, I'm using the following,

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

According to the AutoValidateAntiforgeryTokenAuthorizationFilter implementation, GET will be excluded from checking antiforgery. However, after I'm logged on, the following request is a POST and I get a 400. Somehow, when I remove the above filter then app works as usual. The reason I needed to add that filter was because AsBffApiEndpoint() is requiring antiforgery header in all endpoints, and I need some of them to be bypassed.

Also, in Blazor6 sample code I see MapRemoteBffApiEndpoint(...).RequireAccessToken(TokenType.UserOrClient) is not being used. Is this the new pattern?

Our BFF doesn't use the ASP.NET Core anti-forgery mechanism. We describe here how it works in our library.

Indeed. I just got 401 for bff/user which is a GET. Then I got the 400 Bad Request - Request Too Long. Meaning if I want to bypass Antiforgery check on all GET endpoints, how should I do it?

You can flag any endpoints that you want to exclude from antiforgery with our attribute

[BffApi(requireAntiForgeryCheck = false)]

Great. Now, I'm trying to find a way to not add it manually. Somehow to use AssBffApiEndpoint(false) on the list of endpoints I need

I've tried to use the following but didn't work. I get a 401 if I don't pass the header.

  var requireAntiForgeryCheck = true;
  app.Use(async (context, next) =>
  {
      var endpoint = context.GetEndpoint();
      var getAttribute = endpoint.Metadata.GetMetadata<HttpGetAttribute>();
      if (getAttribute != null)
      {
          var bffApiAttribute = endpoint.Metadata.GetMetadata<BffApiAttribute>();
          if (bffApiAttribute != null)
          {
              var routeAttribute = endpoint.Metadata.GetMetadata<RouteAttribute>();
              if (routeAttribute != null && routeAttribute.Template.StartsWith("api/internal")l)
              {
                  requireAntiForgeryCheck = false;
              }
          }
      }

      await next();
  });

  app.UseAuthentication();
  app.UseBff();
  app.UseAuthorization();
  
  app.UseEndpoints(endpoints =>
  {
      endpoints.MapBffManagementEndpoints();
      endpoints.MapControllers().RequireAuthorization().AsBffApiEndpoint(requireAntiForgeryCheck);
  });

I figured it out. See below.

public static TBuilder AsBffApiEndpointBypassAntiforgeryOnGET<TBuilder>(this TBuilder builder, string routePrefix) where TBuilder : IEndpointConventionBuilder
{
    builder.Add(endpointBuilder =>
    {
        var getAttribute = endpointBuilder.Metadata.FirstOrDefault(m => m.GetType() == typeof(HttpGetAttribute)) as HttpGetAttribute;
        var routeAttribute = endpointBuilder.Metadata.FirstOrDefault(m => m.GetType() == typeof(RouteAttribute)) as RouteAttribute;
        if (getAttribute != null && routeAttribute != null && routeAttribute.Template.StartsWith(routePrefix))
        {
            endpointBuilder.Metadata.Add(new BffApiAttribute(false));
        }
        else
        {
            endpointBuilder.Metadata.Add(new BffApiAttribute(true));
        }
    });

    return builder;
}

Then in Startup.cs

app.UseEndpoints(endpoints =>
{
    endpoints.MapBffManagementEndpoints();
    endpoints.MapControllers().RequireAuthorization().AsBffApiEndpointBypassAntiforgeryOnGET("api/Foo");
});

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue.