diff --git a/src/Components/Shared/src/DefaultAntiforgeryStateProvider.cs b/src/Components/Shared/src/DefaultAntiforgeryStateProvider.cs index 5901618e473e..1712f7383a21 100644 --- a/src/Components/Shared/src/DefaultAntiforgeryStateProvider.cs +++ b/src/Components/Shared/src/DefaultAntiforgeryStateProvider.cs @@ -24,9 +24,9 @@ public DefaultAntiforgeryStateProvider(PersistentComponentState state) // don't have access to the request. _subscription = state.RegisterOnPersisting(() => { - state.PersistAsJson(PersistenceKey, _currentToken); + state.PersistAsJson(PersistenceKey, GetAntiforgeryToken()); return Task.CompletedTask; - }, new InteractiveAutoRenderMode()); + }, RenderMode.InteractiveWebAssembly); state.TryTakeFromJson(PersistenceKey, out _currentToken); } diff --git a/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs b/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs index 82110a7b7d64..5e47598990fe 100644 --- a/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs +++ b/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs @@ -896,6 +896,19 @@ public void FormNoAntiforgeryReturnBadRequest(bool suppressEnhancedNavigation) DispatchToFormCore(dispatchToForm); } + [Fact] + public void CanUseAntiforgeryTokenInWasm() + { + var dispatchToForm = new DispatchToForm(this) + { + Url = "forms/antiforgery-wasm", + FormCssSelector = "form", + InputFieldId = "Value", + SuppressEnhancedNavigation = true, + }; + DispatchToFormCore(dispatchToForm); + } + [Theory] [InlineData(true)] [InlineData(false)] diff --git a/src/Components/test/testassets/Components.TestServer/Components.TestServer.csproj b/src/Components/test/testassets/Components.TestServer/Components.TestServer.csproj index 7e74507859d4..6279d994bc7b 100644 --- a/src/Components/test/testassets/Components.TestServer/Components.TestServer.csproj +++ b/src/Components/test/testassets/Components.TestServer/Components.TestServer.csproj @@ -14,6 +14,7 @@ + diff --git a/src/Components/test/testassets/Components.TestServer/RazorComponentEndpointsStartup.cs b/src/Components/test/testassets/Components.TestServer/RazorComponentEndpointsStartup.cs index f2f69e00df34..efbd25d4272e 100644 --- a/src/Components/test/testassets/Components.TestServer/RazorComponentEndpointsStartup.cs +++ b/src/Components/test/testassets/Components.TestServer/RazorComponentEndpointsStartup.cs @@ -9,6 +9,7 @@ using Components.TestServer.RazorComponents.Pages.Forms; using Components.TestServer.Services; using Microsoft.AspNetCore.Components.WebAssembly.Server; +using Microsoft.AspNetCore.Mvc; namespace TestServer; @@ -149,6 +150,11 @@ private static void MapEnhancedNavigationEndpoints(IEndpointRouteBuilder endpoin await response.WriteAsync("

This is a non-Blazor endpoint

That's all

"); }); + endpoints.MapPost("api/antiforgery-form", ([FromForm] string value) => + { + return Results.Ok(value); + }); + static Task PerformRedirection(HttpRequest request, HttpResponse response) { response.Redirect(request.Query["external"] == "true" diff --git a/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/Forms/FormRunningOnWasmCanUseAntiforgeryToken.razor b/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/Forms/FormRunningOnWasmCanUseAntiforgeryToken.razor new file mode 100644 index 000000000000..063d5b075c32 --- /dev/null +++ b/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/Forms/FormRunningOnWasmCanUseAntiforgeryToken.razor @@ -0,0 +1,4 @@ +@page "/forms/antiforgery-wasm" +

FormRunningOnWasmCanUseAntiforgeryToken

+ + diff --git a/src/Components/test/testassets/TestContentPackage/WasmFormComponent.razor b/src/Components/test/testassets/TestContentPackage/WasmFormComponent.razor new file mode 100644 index 000000000000..a89d9afa283a --- /dev/null +++ b/src/Components/test/testassets/TestContentPackage/WasmFormComponent.razor @@ -0,0 +1,74 @@ +@attribute [RenderModeInteractiveWebAssembly] +@using Microsoft.AspNetCore.Components.Forms +@using System.Net.Http +

WasmFormComponent

+ +
+ + @if (OperatingSystem.IsBrowser()) + { + + } + + + +@if (_posted) +{ + @if (_succeeded) + { +

Posting the value succeded.

+ } + else + { +

Posting the value failed.

+ } +} +else +{ +

Antiforgery: @_token

+} + +@code { + string _value; + string _token; + bool _succeeded; + bool _posted; + + [Inject] public AntiforgeryStateProvider AntiforgeryState { get; set; } + [Inject] public NavigationManager Navigation { get; set; } + + protected override void OnInitialized() + { + if (OperatingSystem.IsBrowser()) + { + var antiforgery = AntiforgeryState.GetAntiforgeryToken(); + _token = antiforgery.Value; + } + } + + private async Task SubmitForm() + { + if (OperatingSystem.IsBrowser()) + { + var client = new HttpClient() { BaseAddress = new Uri(Navigation.BaseUri) }; + var antiforgery = AntiforgeryState.GetAntiforgeryToken(); + if (antiforgery != null) + { + _posted = true; + var request = new HttpRequestMessage(HttpMethod.Post, "api/antiforgery-form"); + var content = new FormUrlEncodedContent(new KeyValuePair[] { new ("Value", _value) }); + request.Content = content; + request.Headers.Add("RequestVerificationToken", antiforgery.Value); + var response = await client.SendAsync(request); + if (response.IsSuccessStatusCode) + { + _succeeded = true; + } + else + { + _succeeded = false; + } + } + } + } +}