Skip to content

Commit 47bd1c4

Browse files
authored
[Blazor] Emit antiforgery only when streaming, a form has been placed on the page or there are interactive render modes configured (#62653)
* Emit antiforgery only when streaming, a form has been placed on the page or there are interactive render modes configured
1 parent 402bfbb commit 47bd1c4

File tree

2 files changed

+27
-16
lines changed

2 files changed

+27
-16
lines changed

src/Components/Endpoints/src/Forms/EndpointAntiforgeryStateProvider.cs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ namespace Microsoft.AspNetCore.Components.Endpoints.Forms;
1010
internal class EndpointAntiforgeryStateProvider(IAntiforgery antiforgery) : DefaultAntiforgeryStateProvider()
1111
{
1212
private HttpContext? _context;
13+
private bool _canGenerateToken;
1314

1415
internal void SetRequestContext(HttpContext context)
1516
{
1617
_context = context;
18+
_canGenerateToken = true;
1719
}
1820

1921
public override AntiforgeryRequestToken? GetAntiforgeryToken()
@@ -24,17 +26,23 @@ internal void SetRequestContext(HttpContext context)
2426
return _currentToken;
2527
}
2628

27-
// We already have a callback setup to generate the token when the response starts if needed.
28-
// If we need the tokens before we start streaming the response, we'll generate and store them;
29-
// otherwise we'll just retrieve them.
30-
// In case there are no tokens available, we are going to return null and no-op.
31-
var tokens = !_context.Response.HasStarted ? antiforgery.GetAndStoreTokens(_context) : antiforgery.GetTokens(_context);
32-
if (tokens.RequestToken is null)
29+
if (_currentToken == null && _canGenerateToken)
3330
{
34-
return null;
31+
// We already have a callback setup to generate the token when the response starts if needed.
32+
// If we need the tokens before we start streaming the response, we'll generate and store them;
33+
// otherwise we'll just retrieve them.
34+
// In case there are no tokens available, we are going to return null and no-op.
35+
var tokens = !_context.Response.HasStarted ? antiforgery.GetAndStoreTokens(_context) : antiforgery.GetTokens(_context);
36+
if (tokens.RequestToken is null)
37+
{
38+
return null;
39+
}
40+
41+
_currentToken = new AntiforgeryRequestToken(tokens.RequestToken, tokens.FormFieldName);
3542
}
3643

37-
_currentToken = new AntiforgeryRequestToken(tokens.RequestToken, tokens.FormFieldName);
3844
return _currentToken;
3945
}
46+
47+
internal void DisableTokenGeneration() => _canGenerateToken = false;
4048
}

src/Components/Endpoints/src/RazorComponentEndpointInvoker.cs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
using System.Text;
77
using System.Text.Encodings.Web;
88
using Microsoft.AspNetCore.Antiforgery;
9+
using Microsoft.AspNetCore.Components.Endpoints.Forms;
910
using Microsoft.AspNetCore.Components.Endpoints.Rendering;
11+
using Microsoft.AspNetCore.Components.Forms;
1012
using Microsoft.AspNetCore.Components.Infrastructure;
1113
using Microsoft.AspNetCore.Diagnostics;
1214
using Microsoft.AspNetCore.Http;
@@ -76,14 +78,6 @@ private async Task RenderComponentCore(HttpContext context)
7678
return;
7779
}
7880

79-
context.Response.OnStarting(() =>
80-
{
81-
// Generate the antiforgery tokens before we start streaming the response, as it needs
82-
// to set the cookie header.
83-
antiforgery!.GetAndStoreTokens(context);
84-
return Task.CompletedTask;
85-
});
86-
8781
if (httpActivityContext != default)
8882
{
8983
_activityLinkStore.SetActivityContext(ComponentsActivityLinkStore.Http, httpActivityContext, null);
@@ -143,8 +137,17 @@ await _renderer.InitializeStandardComponentServicesAsync(
143137
var bufferingFeature = context.Features.GetRequiredFeature<IHttpResponseBodyFeature>();
144138
bufferingFeature.DisableBuffering();
145139

140+
// Store the tokens if not emitted already in case we stream a form in the response.
141+
antiforgery!.GetAndStoreTokens(context);
142+
146143
context.Response.Headers.ContentEncoding = "identity";
147144
}
145+
else if (endpoint.Metadata.GetMetadata<ConfiguredRenderModesMetadata>()?.ConfiguredRenderModes.Length == 0)
146+
{
147+
// Disable token generation on EndpointAntiforgeryStateProvider if we are not streaming.
148+
var provider = (EndpointAntiforgeryStateProvider)context.RequestServices.GetRequiredService<AntiforgeryStateProvider>();
149+
provider.DisableTokenGeneration();
150+
}
148151

149152
// Importantly, we must not yield this thread (which holds exclusive access to the renderer sync context)
150153
// in between the first call to htmlContent.WriteTo and the point where we start listening for subsequent

0 commit comments

Comments
 (0)