[Blazor] Propagate SignalR authentication refresh to server circuits - #68221
Open
kotlarmilos wants to merge 2 commits into
Open
[Blazor] Propagate SignalR authentication refresh to server circuits#68221kotlarmilos wants to merge 2 commits into
kotlarmilos wants to merge 2 commits into
Conversation
SignalR can refresh the authentication of an active connection through the /refresh endpoint, and it notifies hubs by calling Hub.OnAuthenticationRefreshedAsync. ComponentHub did not override that method, so a Blazor Server circuit kept serving the principal captured when the connection was established. AuthorizeView and every other AuthenticationStateProvider consumer continued to observe the stale user until the circuit reconnected. ComponentHub now overrides OnAuthenticationRefreshedAsync and forwards Context.User to CircuitHost.SetCircuitUser, which is the same call ConnectCircuit already makes when a circuit is reattached to a new connection. SetCircuitUser pushes the principal through IHostEnvironmentAuthenticationStateProvider, so AuthenticationStateChanged fires and components re-render against the refreshed user. No public API is added, and the existing opt-in surfaces remain unchanged. Fixes dotnet#68170 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a850277c-a3e3-4c6b-970a-834ae5a89b2d
Contributor
There was a problem hiding this comment.
Pull request overview
This PR enables Blazor Server circuits to observe SignalR authentication refresh updates without requiring a reconnect, by flowing the refreshed ClaimsPrincipal into the circuit so AuthenticationStateProvider consumers (e.g., AuthorizeView) re-render with the new identity.
Changes:
- Override
ComponentHub.OnAuthenticationRefreshedAsyncto update the circuit user viaCircuitHost.SetCircuitUser(Context.User). - Add unit coverage for the hub callback behavior (updates circuit user; no-op when no circuit is associated).
- Add an E2E scenario that refreshes auth via
/refreshand asserts UI updates without renegotiating/reconnecting.
Show a summary per file
| File | Description |
|---|---|
| src/Components/Server/src/ComponentHub.cs | Adds OnAuthenticationRefreshedAsync override to propagate refreshed principal into the circuit. |
| src/Components/Server/test/Circuits/ComponentHubTest.cs | Adds unit tests validating circuit user updates on auth refresh. |
| src/Components/test/testassets/Components.TestServer/AuthenticationStartup.cs | Enables authentication refresh for the Blazor hub in the test server. |
| src/Components/test/testassets/Components.TestServer/Pages/_ServerHost.cshtml | Adds test-only fetch interception + helper to invoke the /refresh endpoint from JS. |
| src/Components/test/E2ETest/Tests/AuthTest.cs | Allows app navigation to include an optional query string for test hooks. |
| src/Components/test/E2ETest/ServerExecutionTests/ServerAuthTest.cs | Adds E2E test ensuring refreshed auth updates AuthorizeView output without renegotiation. |
Copilot's findings
- Files reviewed: 6/6 changed files
- Comments generated: 2
Report a rejected refresh promise through the async script callback, so a failed refresh fails the assertion with the error text instead of blocking until the Selenium script timeout. Default the mocked HubCallerContext.User to an empty ClaimsPrincipal, matching how HubConnectionContext.User normalizes a null principal. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a850277c-a3e3-4c6b-970a-834ae5a89b2d
| <script src="_framework/blazor.server.js" autostart="false"></script> | ||
| <script> | ||
| if (new URLSearchParams(location.search).has('captureAuthenticationRefresh')) { | ||
| const originalFetch = window.fetch; |
Member
There was a problem hiding this comment.
This is really hacky, I don't know if you want this in the code base. Ideally, you'd either get the HubConnection and call the refresh method, or have a short lived token that causes the automatic refresh to occur (yes I know that would cause the test to not be instant anymore).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
ComponentHubnow overridesHub.OnAuthenticationRefreshedAsyncso a Blazor server circuit picks up a refreshed principal without reconnecting. SignalR can refresh the authentication of an active connection through the/refreshendpoint added in #67111 and #67964, and it notifies a hub by calling that method.ComponentHubdid not override it, so the circuit kept the principal it captured when the connection was established.AuthorizeViewand otherAuthenticationStateProviderconsumers showed a stale user until the circuit reconnected.The override passes
Context.UsertoCircuitHost.SetCircuitUser, which is the same callConnectCircuitalready makes when a circuit reattaches to a new connection.SetCircuitUsersets the state onIHostEnvironmentAuthenticationStateProvider, which raisesAuthenticationStateChangedand re-renders the affected components.No public API is added. Applications opt in through the existing
EnableAuthenticationRefreshoption on the server andconfigureSignalRinBlazor.starton the client.Fixes #68170