Skip to content

Blazor Server: Detect and prevent silent user identity changes on SignalR circuit reconnect #65272

@KurtP20

Description

@KurtP20

Is there an existing issue for this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe the problem.

Describe the problem
In a Blazor Server app with cookie-based authentication (e.g., ASP.NET Core Identity), the HTTP auth cookie is shared across browser tabs. When the user logs out and logs in as a different user in one tab, the cookie is updated accordingly. However, existing SignalR circuits in other tabs can reconnect silently using the updated cookie, leading to the circuit executing under a new identity without any user interaction, notification, or explicit check.

This can occur during normal SignalR reconnection scenarios (e.g., network hiccups, transient disconnects), where the client attempts to reconnect and the server rebinds the circuit using the current cookie principal. The framework does not enforce an invariant that the identity at reconnect matches the identity at circuit creation. As a result, a previously authenticated tab can suddenly start executing under a different authenticated principal, which can lead to cross-user contamination, security boundary violations, and unsafe user state.

Why this is an issue

  • Blazor Server circuits are stateful and assume continuity of user context.
  • Shared authentication cookies (standard browser behavior) mean multiple tabs can influence the same session.
  • Silent reconnect (SignalR automatic reconnect) does not provide a hook or safety check to detect identity changes.
  • There is currently no built-in mechanism to detect or reject a reconnect when the authenticated user principal has changed.

Existing issues have discussed cookie changes and reconnection behavior (e.g., auth state not reacting to cookie changes) but do not explicitly address detection or prevention of identity changes on reconnect (see related issues like #50858 which focuses on auth state hydration, not SignalR identity invariants).

Expected behavior
Framework support (opt-in or default) for one or more of the following:

  1. Hard fail on circuit reconnect if the authenticated principal has changed since circuit creation.
  2. A reconnect identity change event/callback so apps can observe and handle it.
  3. A configurable policy (e.g., CircuitIdentityPolicy.RequireStablePrincipalOnReconnect) that enforces user identity stability.
  4. Clear documentation/stabilization of behavior when authentication cookies change during an active Blazor Server session.

Impact
Without such checks, apps - especially enterprise apps - must implement ad-hoc workarounds (e.g., capturing a stable user key and validating it on every component/service invocation), which is error-prone and not centrally enforced.

Steps to reproduce (conceptual)

  1. Blazor Server app with cookie authentication (ASP.NET Core Identity).
  2. Open Tab A, authenticate as UserA.
  3. Open Tab B, authenticate as UserB.
  4. Cause a transient disconnect (e.g., blocking network momentarily).
  5. Allow automatic reconnect to occur in Tab A’s circuit.
  6. Observe that Tab A’s circuit now runs under UserB silently, with no UI update.

Describe the solution you'd like

Guarantee identity continuity for a Blazor Server circuit. If a circuit was created under UserA, it must never continue executing under UserB after a reconnect unless the app explicitly allows it.

When a Blazor Server circuit is established:

  1. Snapshot a stable user identifier from the initial HttpContext.User, e.g.: ClaimTypes.NameIdentifier
  2. On any reconnect / rebind, compare the current HttpContext.User stable id to the circuit’s snapshot.
  3. If it differs:
    • reject the reconnect (do not attach to the existing circuit), and
    • terminate the circuit (or force a hard reload / re-auth).

This prevents cross-user contamination by construction.

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions