DamianEdwards / RazorSlices

Lightweight Razor-based templates for ASP.NET Core without MVC, Razor Pages, or Blazor.
MIT License
297 stars 12 forks source link

A convenient way to use antiforgery tokens? #38

Closed lnoss closed 8 months ago

lnoss commented 8 months ago

This package is really fun, but I need to discuss about the CRSF protection implementation. Context: since ASP.NET Core in .NET 8 Preview 7, a new middleware for validating antiforgery tokens is added, which is used to mitigate cross-site request forgery attacks.

The antiforgery middleware itself does not short-circuit the execution of the rest of the request pipeline. Instead the middleware sets the IAntiforgeryValidationFeature in the HttpContext.Features of the current request. The middleware expects that each framework implementation (minimal APIs, MVC, Blazor, etc.) will react to this feature and short-circuit execution as expected.

The question is, what is the best way to use this new feature? We can generate a nice-little token from an endpoint itself:

app.MapGet("/antiforgery", (HttpContext context, IAntiforgery antiforgery) =>
{
    var token = antiforgery.GetAndStoreTokens(context);
    // ...
    return Results.Content("", "text/html");
});

But how would you pass it to a Razor Slice? Using a customized sub-model to pass a model and a token seems hacky (maybe not more hacky than a fat ViewData in reality...).

I tried a dependency injection, but no success (@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery af) and I think it just violates the SoC. It currently throws this exception: https://github.com/DamianEdwards/RazorSlices/blob/003766e498474b588de6296a4a32e73d6ae291ac/src/RazorSlices/RazorSlice.ResolveAndCreate.cs#L494 I don't really know why, since the service provider already exists implicitly... If we check the ServiceDescriptors:

Lifetime = Singleton, ServiceType = "Microsoft.AspNetCore.Antiforgery.IAntiforgery", ImplementationType = "Microsoft.AspNetCore.Antiforgery.DefaultAntiforgery"

Basically, I am trying to use Razor Slices with Htmx.Net to compare usages with a Django/Go stack, but since using Razor Slices mean there is no tags helpers, I am a bit lost. Thanks.

Note : still a junior on the .NET environment.

DamianEdwards commented 8 months ago

To use the IServiceProvider with Razor Slices you need to make sure you're passing it in to the call to render the slice. If using Results.Extensions.RazorSlice you need to capture the IServiceProvider as an argument in you route handler delegate, and pass it in as a parameter, e.g.:

app.MapGet("/myslice", (MyModel someModel, IServiceProvider serviceProvider) =>
    Results.Extensions.RazorSlice("/Slices/MySlice.cshtml", someModel, serviceProvider));

The other option would be to just create an extension method on something like HttpContext that grabs the IAntiforgery from RequestServices, generates the token, and returns the token string. Then you could easily call it from your Razor Slice, e.g.:

<form method="post">
    <input type="hidden" name="token" value="@HttpContext?.GetAntiforgeryToken()" />
</form>
lnoss commented 8 months ago

I guess I was looking for an over-engineered solution. Thanks for the tips, I appreciate it!

lnoss commented 8 months ago

Maybe it's a bit off topic, but do you think that the recent RazorComponentResult could be an official alternative for high-performance rendering? I am trying to weigh the pros and cons!

DamianEdwards commented 8 months ago

Maybe it's a bit off topic, but do you think that the recent RazorComponentResult could be an official alternative? I am trying to weigh the pro and cons!

Yes, the new RazorComponentResult is a great option if you want the features of Blazor and are happy to make the associated trade-offs (complexity, performance, .NET 8+ only, etc.). Razor Slices is about straightforward templating with as little overhead as possible, whereas Blazor (and thus RazorComponentResult) is richly featured.

lnoss commented 8 months ago

Closing, since I've received a suitable reply and it's not directly related to the project. Curious developers will stumble upon the issue anyway!