Skip to content

Conversation

Copilot
Copy link
Contributor

@Copilot Copilot AI commented Sep 13, 2025

The ComponentStatePersistenceManager.InferRenderModes method was iterating forward through the _registeredCallbacks collection, which caused callbacks to be skipped when other callbacks modified the collection during iteration.

Problem

In Blazor Web Applications with global WASM interactivity, the ResourceCollectionProvider disposes its subscription during the persistence process, which removes its entry from the _registeredCallbacks collection while the collection is being iterated. This causes subsequent callbacks (particularly the DefaultAntiforgeryStateProvider) to be skipped, leading to missing antiforgery tokens and resulting in AntiforgeryValidationException during logout operations.

Solution

Changed the InferRenderModes method to iterate backwards through the _registeredCallbacks collection, matching the pattern already implemented in the TryPauseAsync method. This ensures that all registered callbacks are executed even when some callbacks remove themselves from the collection during iteration.

Before:

for (var i = 0; i < _registeredCallbacks.Count; i++)

After:

for (var i = _registeredCallbacks.Count - 1; i >= 0; i--)

This change includes the same explanatory comment used in TryPauseAsync to maintain code consistency and document the reasoning behind the backward iteration pattern.

Testing

  • Verified existing unit tests continue to pass
  • Confirmed the build succeeds with the changes
  • Added comprehensive unit test PersistStateAsync_InvokesAllCallbacksWhenFirstCallbackUnregistersItself that validates all callbacks are invoked even when the first callback unregisters itself during execution
  • The fix addresses the root cause identified in the issue analysis

Fixes #58822.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@Copilot Copilot AI changed the title [WIP] [Blazor] Fix AntiforgeryValidationException during Logout in Blazor Web Application with global WASM interactivi Fix ComponentStatePersistenceManager iteration to prevent AntiforgeryValidationException in Blazor WASM Sep 13, 2025
Copilot finished work on behalf of javiercn September 13, 2025 18:26
@Copilot Copilot AI requested a review from javiercn September 13, 2025 18:26
@javiercn javiercn marked this pull request as ready for review September 13, 2025 19:05
@javiercn javiercn requested a review from a team as a code owner September 13, 2025 19:05
@Copilot Copilot AI review requested due to automatic review settings September 13, 2025 19:05
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes a critical iteration bug in the ComponentStatePersistenceManager.InferRenderModes method that was causing AntiforgeryValidationException in Blazor WASM applications. The issue occurred when callbacks modified the collection during forward iteration, causing subsequent callbacks to be skipped.

  • Changed iteration direction from forward to backward in InferRenderModes method
  • Added explanatory comment matching the existing pattern in TryPauseAsync
  • Ensures all registered callbacks are executed even when some remove themselves during iteration

Copy link
Member

@ilonatommy ilonatommy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good but could use a unit test.

@javiercn
Copy link
Member

@copilot can you add a unit test for this? The test should verify that all callbacks get invoked even when a callback unregisters itself during a PauseAsync invocation. To make sure the test is relevant, the first callback registered should be the one that unregisters itself.

Copy link
Contributor Author

Copilot AI commented Sep 16, 2025

@copilot can you add a unit test for this? The test should verify that all callbacks get invoked even when a callback unregisters itself during a PauseAsync invocation. To make sure the test is rele...

Added the requested unit test PersistStateAsync_InvokesAllCallbacksWhenFirstCallbackUnregistersItself. The test specifically ensures the first callback registered unregisters itself during a PauseAsync invocation, and verifies that all callbacks still get executed due to the backward iteration pattern. The test passes successfully. (590aaa5)

Copy link
Contributor Author

Copilot AI commented Sep 16, 2025

Looks good but could use a unit test.

Added a comprehensive unit test as requested. The test verifies that all callbacks are invoked even when the first callback unregisters itself during execution, which validates the backward iteration pattern fix. (590aaa5)

Copilot finished work on behalf of javiercn September 16, 2025 12:11
@javiercn javiercn enabled auto-merge (squash) September 16, 2025 13:01
@javiercn
Copy link
Member

/backport to release/10.0

Copy link
Contributor

Started backporting to release/10.0: https://github.com/dotnet/aspnetcore/actions/runs/17766651506

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Logout doesn't work in Blazor Web Application with global WASM interactivity (AntiforgeryValidationException)

3 participants