Authentication and authorization

Both authentication and authorization play a crucial part in every Blazor Server website. Authentication is the process of acquiring user information. Authorization is the process of using acquired information to check if the user has the right to access certain resources or not. This guide will walk you through on how to do authentication and authorization in Blazor Server.

You can download the example code used in this topic on GitHub.

Prerequisites

To prepare your website for authentication and authorization, you should have a basic understanding of the following:


Authenticating users

Authentication is the process of acquiring user information. Therefore, authentication must happen before you can authorize users. Once the user is successfully authenticated, you need to store the authenticated results into local storage and later on, retrieve the results and verify that.

Login flow

Logout flow

Provide authentication state flow

Setup the AuthenticationStateProvider

Blazor Server uses AuthenticationStateProvider to authenticating users. You need to replace the built-in AuthenticationStateProvider to do the authentication yourself.

  1. Create an extension class of AuthenticationStateProvider. We call it WebsiteAuthenticator in our example.
public class WebsiteAuthenticator : AuthenticationStateProvider
{
    private readonly ProtectedLocalStorage _protectedLocalStorage;
    private readonly SimulatedDataProviderService _dataProviderService;

    public WebsiteAuthenticator(ProtectedLocalStorage protectedLocalStorage, SimulatedDataProviderService dataProviderService)
    {
        _protectedLocalStorage = protectedLocalStorage;
        _dataProviderService = dataProviderService;
    }

    public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        var principal = new ClaimsPrincipal();

        try
        {
            var storedPrincipal = await _protectedLocalStorage.GetAsync<string>("identity");

            if (storedPrincipal.Success)
            {
                var user = JsonConvert.DeserializeObject<User>(storedPrincipal.Value);
                var (_, isLookUpSuccess) = LookUpUser(user.Username, user.Password);
                    
                if (isLookUpSuccess)
                {
                    var identity = CreateIdentityFromUser(user);
                    principal = new(identity);
                }
            }
        }
        catch
        {

        }

        return new AuthenticationState(principal);
    }

    public async Task LoginAsync(LoginFormModel loginFormModel)
    {
        var (userInDatabase, isSuccess) = LookUpUser(loginFormModel.Username, loginFormModel.Password);
        var principal = new ClaimsPrincipal();

        if (isSuccess)
        {
            var identity = CreateIdentityFromUser(userInDatabase);
            principal = new ClaimsPrincipal(identity);
            await _protectedLocalStorage.SetAsync("identity", JsonConvert.SerializeObject(userInDatabase));
        }

        NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(principal)));
    }

    public async Task LogoutAsync()
    {
        await _protectedLocalStorage.DeleteAsync("identity");
        var principal = new ClaimsPrincipal();
        NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(principal)));
    }

    private static ClaimsIdentity CreateIdentityFromUser(User user)
    {
        return new ClaimsIdentity(new Claim[]
        {
            new (ClaimTypes.Name, user.Username),
            new (ClaimTypes.Hash, user.Password),
            new("age", user.Age.ToString())
        }, "BlazorSchool");
    }

    private (User, bool) LookUpUser(string username, string password)
    {
        var result = _dataProviderService.Users.FirstOrDefault(u => username == u.Username && password == u.Password);

        return (result, result is not null);
    }
}
You have to implement SimulatedDataProviderService to store and retrieve the data from the database yourself.

You need an empty try-catch block when retrieving local storage because local storage is only available in the client. If the Blazor Server renders any component that requires authenticated users, the method GetAuthenticationStateAsync will be called and local storage will not be available there. The method GetAuthenticationStateAsync will be called again later by the client. This also helps make sensitive data secure to any search engine.

  1. Register the WebsiteAuthenticator in Startup.cs.
public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddScoped<WebsiteAuthenticator>();
    services.AddScoped<AuthenticationStateProvider>(sp => sp.GetRequiredService<WebsiteAuthenticator>());
}

The first line initilizes your class as a scoped service. The second line is to replace your scoped service into built-in AuthenticationStateProvider.

  1. Wrap CascadingAuthenticationState around App.razor.
<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
        ...
    </Router>
</CascadingAuthenticationState>

Creating a login page

The login page allows users to input their information for you to verify. It can be an independent page or you can also use the Content Projection technique to reduce the amount of work when you want to display a login form if the user is not authenticated.

  1. Inject your implementation class of AuthenticationStateProvider.
@inject WebsiteAuthenticator WebsiteAuthenticator
  1. Create a login form. See Blazor Server Forms.
<EditForm Model="LoginFormModel" Context="Login">
    <div>
        <label> Username: 
            <InputText @bind-Value="LoginFormModel.Username"></InputText>
        </label>
    </div>
    <div>
        <label> Password:
            <InputText type="password" @bind-Value="LoginFormModel.Password"></InputText>
        </label>
    </div>
    <div>
        <button @onclick="TryLogin">Submit</button>
    </div>
</EditForm>

@code {
    private LoginFormModel LoginFormModel { get; set; } = new();

    private async Task TryLogin()
    {
        await WebsiteAuthenticator.LoginAsync(LoginFormModel);
    }
}

Authorizing users

Authorization is the process of using acquired information to check if the user has the right to access certain resources or not. For example, you only want unauthenticated users to see the login form and vice versa. The first thing you need to do is to authorize your login form. You can use AuthorizeView along with its render fragments: NotAuthorized, Authorizing, Authorized to do that. AuthorizeView is a pre-built component using Content Projection technique to determine what to display to the users according to their authenticated status.
  1. Wrap the login form into AuthorizeView component and also, inside the NotAuthorized render fragment. By doing this, you will hide the login form when a user is authenticated.
<AuthorizeView Context="Account">
    <NotAuthorized>
        <EditForm Model="LoginFormModel" Context="Login">
            ...
        </EditForm>
    </NotAuthorized>
</AuthorizeView>
  1. Right now, when the user is authenticated, they will see a blank page and you need to notify them that they are authenticated. Add Authorized render fragment to notify them.
<AuthorizeView Context="Account">
    <NotAuthorized>
        ...
    </NotAuthorized>
    <Authorized>
        <button type="button" class="btn btn-primary" @onclick="TryLogout">Logout</button>
    </Authorized>
</AuthorizeView>

@code {
    ...
    private async Task TryLogout()
    {
        await WebsiteAuthenticator.LogoutAsync();
    }
}
  1. In some cases, the authentication process can take a long time to provide the authenticated result. In that case, you also want the user to see something instead of a blank page. Add Authorizing render fragment to show the process of authenticating the user. The time taken to authenticate a user depends on the GetAuthenticationStateAsync method of your class.
<AuthorizeView Context="Account">
    <NotAuthorized>
        ...
    </NotAuthorized>
    <Authorized>
        ...
    </Authorized>
    <Authorizing>
        Authorizing in process...
    </Authorizing>
</AuthorizeView>
BLAZOR SCHOOL
Designed and built with care by our dedicated team, with contributions from a supportive community. We strive to provide the best learning experience for our users.
Docs licensed CC-BY-SA-4.0
Copyright © 2021-2025 Blazor School
An unhandled error has occurred. Reload 🗙