Blazor's nifty trick for sharing auth between server and client in .NET 8
With .NET 8 the default render mode is static SSR.
Your components are rendered on the server, and plain HTML returned to the browser.
You can think of it as a Razor components equivalent to MVC, or Razor Pages.
Your page (or, in this case, component) is short lived, only existing for as long as it takes to render it, and return the resulting HTML to the browser.
If you need to, you can also render any given component using one of the interactive mode (Blazor Server, or Blazor WASM),
But this begs a question, how to handle Authentication and Authorization when you have all these components flying around, using different render modes.
Turns out MS have come up with a nifty way to share auth state between server and client.
To see it in action, you can create a new project:
This creates a project with auth and interactivity (WASM and Server) enabled.
Transmit Auth state across render modes#
If you look at the resulting project, you’ll find a Components > Account folder.
It contains the components for auth UI in your app, to handle user login, logout, registration etc.
The class we’re interested in is called PersistingRevalidatingAuthenticationStateProvider
This class (as well as having a crazily long name) acts as the glue which holds auth together in your app, even between render modes.
There’s quite a bit going on in this class.
I won’t include the entire thing here, but there are some key moving parts which help explain how auth state is communicated between render modes.
First, it inherits RevalidatingServerAuthenticationStateProvider
a base class for revalidating auth state at regular intervals.
This line indicates that the security stamp for the current user should be revalidated every 30 minutes:
The constructor does two important things:
First, it wires up a handler to AuthenticationStateChanged
.
Any time the authentication state changes (user logs in, out etc.) this method will capture the new state and assign it to the authenticationStateTask
field.
Secondly, it uses PersistentComponentState to register a subscription for OnPersisting
which will fire just before the application switches from server to client (WASM).
OnPersistingAsync
is where the magic happens.
This checks if the user is authenticated, and if so, constructs a new instance of the UserInfo
class containing the user’s details.
It then persists this via PersistentComponentState
.
Now the user’s current auth state is sent ‘across the wire’ to any components running using Blazor WASM.
But how to use that state?
For that we need to head over to the client project.
There we find the other half of the equation, namely PersistentAuthenticationStateProvider
This class has a constructor which takes in PersistentComponentState
.
It then attempts to retrieve the persisted UserInfo
data (sent across from the server).
If it finds anything at this point it uses the persisted data to set the user’s authentication state.
With that, your components running in Interactive WASM mode will know about the currently logged in user.
This also means components such as AuthorizationView
will work, making it possible for you to show different content depending on the user’s auth status.
What it doesn’t do#
This mechanism works well as a way to access user auth details in your WASM components.
But it’s worth knowing it isn’t intended as a mechanism to handle other auth concerns, such as tokens for making API requests.
For that the Blazor template uses good old cookies.
When your user logs in, a cookie is created and stored in their browser.
Subsequent network requests automatically include this cookie.
You can then use ASP.NET’s built-in mechanisms for reading auth status from cookies.
It’s also important to know that the user’s information is sent over to the WASM component via PersistentComponentState
once, when the WASM component fires up for the first time.
That state will then stick around for as long as that component lives.
This means you’ll need to perform a full page load/refresh if the user logs in/out, to ensure that new auth state gets over to the WASM component.