Alisbha Khan
Alisbha Khan

Reputation: 133

AntiforgeryToken not generating __RequestVerificationToken hidden field Blazor Server

I have an app built using .NET8 Blazor with InteractiveServerRenderMode.

App.razor is configured like in Template you get when you create Blazor Server App with Global Interactivity and Identity.

I have one Toolbar component where I am trying to add SignOut Functionality with following form

<AuthorizeView>
<Authorized>
<form action="Account/Logout" method="post">
    <AntiforgeryToken />
    <input type="hidden" name="ReturnUrl" value="@currentUrl" />
   <button type="submit">Sign out</button>
</form>
</Authorized>
</AuthorizeView>

But when I submit this form with Sign out button I get following error

BadHttpRequestException: Invalid anti-forgery token found when reading parameter "string returnUrl" from the request body as form.

This error is right as when I inspect element I don't see __RequestVerificationToken field. It is not beind rendered I even tried to use @attribute [RequireAntiforgeryToken] but it is still not working.

I also tried to register it in Program.cs as

var app = builder.Build();
if (app.Environment.IsDevelopment())
    app.UseMigrationsEndPoint();
else
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
//app.UseAuthentication();
//app.UseAuthorization();
app.UseAntiforgery();
app.MapRazorComponents<App>().AddInteractiveServerRenderMode();
app.MapAdditionalIdentityEndpoints();
app.Run();

Anyone who can point what I am doing wrong here?

Upvotes: 10

Views: 1937

Answers (6)

Cardin
Cardin

Reputation: 29

you can add an input hidden field, and in the code, get the token, try this solution in blazor server side app

  1. In the program.cs, add this

    builder.Services.AddAntiforgery(options =>
        {
            options.HeaderName = "RequestVerificationToken"; // Nom de l'en-tête à utiliser
        });
    
    app.UseAntiforgery(); // this line goes after app.UseAuthentication and app.UseAuthorization

  1. Then in the _Imports.razor add
@using System.Net.Http
@using Microsoft.AspNetCore.Antiforgery
  1. In your form just place next to the logout button an input hidden field that call a function to get the token
@inject NavigationManager Navigation
@inject IHttpContextAccessor _httpContextAccessor;

<Authorized>
    <form action="authentication/logout" method="post">
        
        <input type="hidden" name="__RequestVerificationToken" value="@GetTokenAntiforgery()" />  @* this line *@
        <div class="nav-item px-3 ctn__submit">
            <img src="/img/box-arrow-right.svg" class="bi bi-box-arrow-right icon__submit" />
            <button class="btn__submit nav-link" type="submit">
                Se Déconnecter @* @context.User.Identity?.Name *@
            </button>
        </div>
    </form>
</Authorized>

@code {
    public string GetTokenAntiforgery()
    {
        var httpContext = _httpContextAccessor.HttpContext;

        var tokens = _antiforgery.GetAndStoreTokens(httpContext ?? throw new InvalidOperationException("HttpContext is not available."));
        if (string.IsNullOrEmpty(tokens.RequestToken))
        {
            throw new InvalidOperationException("RequestToken is not available");
        }
        return tokens.RequestToken;
    }
}

Upvotes: 0

Perfeitor
Perfeitor

Reputation: 87

Replace <AntiforgeryToken /> with <input type="hidden" name="__RequestVerificationToken" value="@GetAntiforgeryToken()" /> and write the following method:

public string GetAntiforgeryToken()
{
    var httpContext = _httpContextAccessor.HttpContext;
    if (httpContext == null)
    {
        throw new InvalidOperationException("HttpContext is not available.");
    }
    var tokens = _antiforgery.GetAndStoreTokens(httpContext);
    return tokens.RequestToken;
}

Make sure you inject IAntiforgery and IHttpContextAccessor into your class, usually in the constructor:

private readonly IAntiforgery _antiforgery;
private readonly IHttpContextAccessor _httpContextAccessor;

public YourClass(IAntiforgery antiforgery, IHttpContextAccessor httpContextAccessor)
{
    _antiforgery = antiforgery;
    _httpContextAccessor = httpContextAccessor;
}

Upvotes: 2

marzman95
marzman95

Reputation: 11

Do you have the Server Side Blazor Service enabled in your app? I ran into the same exception when logging out a user, after builder.Services.AddServerSideBlazor(); was added to Program.cs. This caused both <EditForm ...> and <AntiforgeryToken /> not creating a hidden input field with such antiforgery token on the forms, causing this exception when processing a form.

Solution thus was to remove builder.Services.AddServerSideBlazor(); from Program.cs.

I think it has to do with having Interactive/Client Side mode and Server Side mode simultaneously included in the Blazor app. Server Side does not need such tokens if I understand the documentation correctly, as the app runs on the server completely and validation is happening per connection. While the interactive mode is validating sessions instead, thus requiring the tokens.

Upvotes: 0

Artem Koloskov
Artem Koloskov

Reputation: 21

Not sure what exactly is happening here, but here's how i encountered this problem and how i "fixed" it.

I've been adapting the the Blazor WebApp template's code to my layout, that layout is built using MudBlazor (version 6.20).

I've put Logout div inside the MudMenu component with an ActivationEvent, which would activate this menu dynamically on MouseOver.

Here's sample code:

<MudMenu AnchorOrigin="Origin.BottomRight" ActivationEvent="@MouseEvent.MouseOver">
    <ActivatorContent>
        ...
    </ActivatorContent>

    <ChildContent>
        <AuthorizeView>
            <Authorized>
                ...

                <div class="nav-item px-3">
                    <form action="Account/Logout" method="post">
                        <AntiforgeryToken />
                        <input type="hidden" name="ReturnUrl" value="@currentUrl" />
                        <button type="submit" class="nav-link">
                            <span class="bi bi-arrow-bar-left-nav-menu" aria-hidden="true"></span> Logout
                        </button>
                    </form>
                </div>

                ...
            </Authorized>
        </AuthorizeView>
    </ChildContent>
</MudMenu>

And that caused the problem for me, the __RequestVerificationToken field wasn't being generated in the form's HTML.

So I've ditched the MudMenu in favor of simple buttons:

<MudStack Row="true">
    <AuthorizeView>
        <Authorized>
            <MudNavLink Href="Account/Manage">
                ...
            </MudNavLink>

            <div class="nav-item px-3">
                <form action="Account/Logout" method="post">
                    <AntiforgeryToken />
                    <input type="hidden" name="ReturnUrl" value="@currentUrl" />
                    <button type="submit" class="nav-link">
                        <span class="bi bi-arrow-bar-left-nav-menu" aria-hidden="true"></span> Logout
                    </button>
                </form>
            </div>
        </Authorized>
    </AuthorizeView>
</MudStack>

And now it works. So i guess there's was something interfering with the <AntiforgeryToken /> component. Hoppe this will help someone to identify what is causing this for them.

Upvotes: 2

Mike Cameron
Mike Cameron

Reputation: 25

In my case, I had to add one line to the IdentityComponentsEndpointRouteBuilderExtensions.cs file...

        var accountGroup = endpoints.MapGroup("/Account");
        accountGroup.DisableAntiforgery();

Upvotes: 0

Brando Zhang
Brando Zhang

Reputation: 28267

BadHttpRequestException: Invalid anti-forgery token found when reading parameter "string returnUrl" from the request body as form.

According to the error message, you should make sure you have put the app.UseAntiforgery(); middleware with the right order inside the program.cs.

Please note, according to this github issue, you need also put app.UseAntiforgery() after app.UseAuthentication() middleware to avoid this kind of issue.

More details, you could refer to below codes:

...
var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();
app.UseAntiforgery();

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

// Add additional endpoints required by the Identity /Account Razor components.
app.MapAdditionalIdentityEndpoints();

app.Run();

Result:

enter image description here

Upvotes: 4

Related Questions