With ASP.NET Core 3.0 out, I am trying to leverage server-side Blazor capabilities in my MVC Core app. I've started by creating a simple navigation component, from which I am trying to let users sign out with a button that points at a controller action. However, I am running into an Anti-Forgery Token Validation error that reads:
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker: Information: Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.ValidateAntiforgeryTokenAuthorizationFilter'.
And then the server returns an HTTP 400 status code.
I'm following the Blazor App template code (Blazor version 0.7.0 with Visual Studio 2019):
<AuthorizeView>
<Authorized>
<a href="/Account/Manage">Hello, @context.User.Identity.Name!</a>
<form method="post" action="/Account/Logout">
<button type="submit">Sign out</button>
</form>
</Authorized>
<NotAuthorized>
<a href="/Account/Register">Register</a>
<a href="/Account/Login">Log in</a>
</NotAuthorized>
</AuthorizeView>
My MVC Account
Controller has a Logout action annotated with [ValidateAntiForgeryToken]
, like this:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout()
{
await _signInManager.SignOutAsync();
_logger.LogInformation("User logged out.");
return RedirectToAction(nameof(HomeController.Index), "Home");
}
In the past, I would add a @Html.AntiForgeryToken()
in a form to conform with my annotated Logout action. In newer versions of ASP.NET Core you no longer needed to add this manually, as it is included within form tag helpers. But neither approach seems to translate or be available in a Blazor component.
How would you call this MVC action from a Blazor component and provide the Anti-forgery token for the controller validation? Or is this validation not needed at all anymore?
Even if my sign out case is not a big threat, this would be the same for calling any other action annotated similarly.
I might be missing a technical concept of CSRF that applies to the scenario of mixing MVC and Blazor.
If you are developer, either fresher or experienced, you definitely have a little knowledge of Anti-Forgery Token in an MVC application. This is a built-in functionality provided by Microsoft. Developers often use it in their application. Everyone knows that this functionality is used for security purposes to stop attacks from hackers.
Remember the following: 1 Anti-forgery token validation is enabled by default in Razor Pages. 2 You can disable validation either globally or on individual pages by using [IgnoreAntiforgeryToken]. 3 You can prevent forms from creating anti-forgery tokens by using asp-antiforgery="false" in the form tag helper. More items...
In traditional HTML-based apps, antiforgery tokens are passed to the server using hidden form fields. In modern JavaScript-based apps and SPAs, many requests are made programmatically. These AJAX requests may use other techniques (such as request headers or cookies) to send the token.
Because JS interop occurs over the Internet and the client uses a remote browser, Blazor Server apps share most web app security concerns. This topic describes common threats to Blazor Server apps and provides threat mitigation guidance focused on Internet-facing apps.
I have been trying to get anti-forgery to work for a Logout POST. I think I just managed to get it working. I'm open to suggestions for improvements!
I used an MS doc on Blazor Server additional security scenarios which explains a method for storing OIDC access and refresh tokens for use in Blazor components. I modified that example in order to use an anti-forgery token in a similar way as follows:
TokenProvider
class to store the anti-forgery token.public class TokenProvider
{
public string AntiforgeryToken { get; set; }
}
TokenProvider
to DI in Startup.ConfigureServices()
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<TokenProvider>();
...
}
_Host.cshtml
and passed it as a parameter to my App
component@page "/"
@namespace My.Pages
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery antiforgery
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
var token = antiforgery.GetAndStoreTokens(HttpContext).RequestToken;
}
...
<body>
<component type="typeof(App)" render-mode="ServerPrerendered" param-AntiforgeryToken="token" />
...
App.razor
then populates the TokenProvider
during OnInitializedAsync()
@inject TokenProvider tokenProvider
<Router ....
</Router>
@code {
[Parameter]
public string AntiforgeryToken { get; set; }
protected override Task OnInitializedAsync()
{
tokenProvider.AntiforgeryToken = AntiforgeryToken;
return base.OnInitializedAsync();
}
}
TokenProvider
can then be consumed via injection in my Logout.razor
Component@inject TokenProvider tokenProvider
...
<form method="post" action="Logout">
<input name="__RequestVerificationToken" type="hidden" value="@tokenProvider.AntiforgeryToken">
<button type="submit" class="nav-link btn btn-link" title="Log out">
<span class="oi oi-account-logout"></span>
</button>
</form>
...
Not sure why this Exception popped up. This worked for me though. Just add this to the end of your _Host.cshtml file.
@Html.AntiForgeryToken()
</body>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With