The missing piece in Blazor client-side authentication tutorial.

Ferry To
5 min readApr 2, 2022

We just want a dummy login screen for demo purpose.

Photo by Pierre Bamin on Unsplash

Back in time when I start learning authentication in Blazor, I feel frustrated as almost every tutorial are talking about creating a Blazor Server project, using Identity framework, and some CURD operations on SERVER side. Hey I know how to do authentication on server side as people learning Blazor are most likely C# developers, we learn Blazor for full-stack C# opportunities so we have additional choice for creating frontend code other than React/Vue/Angular.

This is the missing piece of Blazor client-side authentication tutorial and answers the following questions:

  • How to create a login screen that every user will go to this page by default?
  • How to hide other screens from user access when they are not logged in?

Note: Client side authentication is fragile, don’t depends on it. The aim of this article is not about security, it’s all about UI. There are lots of tips and tricks you can find on the Internet on security hardening for both client and server side.

Steps

  1. Required dependencies:
  • Microsoft.AspNetCore.Authorization
  • Microsoft.AspNetCore.Components.Authorization
  • Microsoft.AspNetCore.Components.WebAssembly.Authentication

Let’s create a Blazor wasm project. We are using .NET 6 in this project.

dotnet new blazorwasm
dotnet add package Microsoft.AspNetCore.Authorization
dotnet add package Microsoft.AspNetCore.Components.Authorization
dotnet add package AspNetCore.Components.WebAssembly.Authentication

2. Create MyCustomAuthenticationStateProvider

  • It must extends from AuthenticationStateProvider
  • Override GetAuthenticationStateAsync() and set default user to an anonymous user with no claims.
  • Prepare faked sign-in and fake users.

This class is where all the magic happens. We will return to this later in this article.

3. Config Program.cs

We need to add these lines to Program.cs :

Caution on how we instantiate an MyAuthenticationStateProvider object and provide it to service container as AuthenticationStateProvider.

Otherwise a default AuthenticationStateProvider object will be used by Blazor instead of our implementation.

4. Setup _Imports.razor

Note we placed @attribute[Authorized] here to make all pages requires authorization by default.

5. Create login screen

Note that we need to place [AllowAnonymous] attribute here to allow user access without sign-in.

Then we have two buttons to simulate sign-in for normal user and admin user:

6. Create RedirectToLogin component

This is a dummy component to simply redirect user back to /login page.

7. Wrap MainLayout with <AuthorizeView>

We wrap the original content in `MainLayout.razor` with `Authorized` tag:

8. Include admin only content in NavMenu

In Shared/NavMenu.razor, we will wrap the original link to fetchData screen with <AuthorizeView> tag and set the role required to see this link to “admin”. We also add a button for user to logout.

9. Wrap routing with <AuthorizeRouteView>

In App.razor, replace <RouteView> tag with <AuthorizeRouteView> tag. Then place our RedirectToLogin component inside <NotAuthorized> tag. So whenever a user access a route that exists but not authorized yet, Blazor will redirect them back to login screen.

10. Config action for unmatched routes.

In App.razor, make sure to fill the NotFound tag, this is what Blazor will do when a user try to access a route that doesn’t exist. If user try to access /something-not-exist, which is not in [‘/','/counter’,'/fetchData','/login'] will just shows what you have put inside the <NotFound> tag. In our case, it displays “Page not found”.

11. Run and test yourself

  1. Run and test our project by dotnet run.
  2. Open browser and go to the root of our Blazor app.
  3. Notice that although you are requesting /, but you are now redirected to /login page.

This is because we have these code in App.razor :

The / route was defined in Index.razor, so it fall-into <Found> tag. And then since we are not logged in, therefore it further fall into <NotAuthorized> tag and finally we landed to <RedirectToLogin />component. When the component initialize, it calls _navigationManager.NavigateTo("/login"). That’s why we see the login screen.

Now go to the address bar of our browser, type in a route that’s not defined in any page. (e.g. “https://localhost:5000/something-not-exist").

Since no route matched, App.razor fall into <NotFound> tag and we will only see a message stating page is not found.

About MyAuthenticationStateProvider :

This class extends AuthenticationStateProvider and override a method:

This method will be called when user trying to access authorized page. Blazor will look for an AuthenticationState object to determine what claims the current user has and whether allow the user access specific pages.

The AuthenticationState object expect an ClaimsPrincipal object with claims like username, roles, etc.

If an ClaimsPrincipal object has no claims, it is considered an anonymous user. This is the trick we used in MyAuthenticationStateProvider:

Finally, return to our login screen and try the different login button.

When you click on the normal sign-in button, you will not see the fetch data link on the navigation menu.

Click the logout button and we will return to login page.

Now click on the admin login button. This time we will see the fetch data link on the navigation menu.

This is because our faked ClaimPrinciple object has the ClaimTypes.Role set to “admin” and we have set the roles required to render the fetch data link to “admin”: <AuthorizViews Roles="admin">

Summary

Today we learned how to create a login page and redirect all unauthorized access back to that page in Blazor. It is completely on client-side for quick UI prototyping purpose, when building your web app you should combine server-side authentication and token management logic in your MyAuthenticationStateProvider. That’s it for now and stay tuned for my next #EverythingInCSharp article. As always you can take a look in the source code [here] if you encounter difficulties. Have a nice day! 😀

--

--

Ferry To

A tech Geek in Hong Kong. ex-Founder my own company. Staff Engineer of a startup. Trying to publish at least one article per month.