X

Using Authorizing template of AuthorizeView in client-side Blazor applications

After blogging about authentication in server-side Blazor applications and discovering AuthorizationView component I was eager to find out how to use third authentication state Authorizing that is not available for server-side Blazor applications. This blog shows how AuthorizeView and Authorizing state work in client-side Blazor applications.

NB! The code here is written for ASP.NET Core 3.0 Preview 6. Things shown here may not work with future releases. Credits go to Github user rabberbock for his repository BlazorAuthTest.

AuthorizeView is Blazor component introduced in ASP.NET Core 3.0 Preview 6. It’s simple component that shows child content based on authorization status of user. The following fragment of code is taken from LoginDisplay.razor view of default server-side Blazor application.

<AuthorizeView>
    <Authorized>
        <a href="Identity/Account/Manage">Hello, @context.User.Identity.Name!</a>
        <a href="Identity/Account/LogOut">Log out</a>
    </Authorized>
    <NotAuthorized>
        <a href="Identity/Account/Register">Register</a>
        <a href="Identity/Account/Login">Log in</a>
    </NotAuthorized>
</AuthorizeView>

This is how things work with server-side Blazor applications.

Client-side Blazor applications run in browser and authentication doesn’t happen necessarily on separate page of web application. We may have logon form implemented as Blazor view or component and authentication is done through backing Web API. In this case there is also third authorization status involved – Authorizing.

Authorizing status is fully supported by AuthorizeView component. It is not needed for server-side Blazor application as authentication status is always known in this case.

Using Authorizing state

Getting authentication work with AuthorizeView component is a little bit tricky and needs some additional work. For server-side Blazor applications this additional work is done for us by product group. For client-side Blazor applications we have to create our own implementation of AuthenticationStateProvider.

This provider is telling to AuthorizeView component and other components that depend on existence of authenticated user if we have one or not. Here’s the simple provider I wrote.

public class MyAuthenticationStateProvider : AuthenticationStateProvider
{
    public static bool IsAuthenticated { get; set; }
    public static bool IsAuthenticating { get; set; }

    public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        ClaimsIdentity identity;

        if(IsAuthenticating)
        {
            return null;
        }
        else if(IsAuthenticated)
        {
            identity = new ClaimsIdentity(new List<Claim>
                        {
                            new Claim(ClaimTypes.Name, "TestUser")

                        }, "WebApiAuth");
        }
        else
        {
            identity = new ClaimsIdentity();
        }

        return await Task.FromResult(new AuthenticationState(new ClaimsPrincipal(identity)));
    }

    public void NotifyAuthenticationStateChanged()
    {
        NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
    }
}

I don’t implemented my own authentication logic but went the same way as rabberbock – the provider supports authentication state changes so it’s easy to see how it works.

It seems like right now AuhtorizeView uses the following mappings for authentication states:

  • AuthenticationState.User.Identity.IsAuthenticated == true: user is authenticated and Authorized template is used.
  • AuthenticationState.User.Identity.IsAuthenticated == false: user is not authenticated and NotAuthorized template is used.
  • AuthenticationState == null: no information about authentication state, it is expected that user is authenticating right now.

If you want to find out more then check the source code of AuthorizeViewCore that is base class for AuthorizeView.

Before trying things out we have few more steps to do. First we need to configure dependency injection in Startup class.

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthorizationCore();
        services.AddScoped<MyAuthenticationStateProvider>();
        services.AddScoped<AuthenticationStateProvider>(provider => provider.GetRequiredService<MyAuthenticationStateProvider>());
    }

    public void Configure(IComponentsApplicationBuilder app)
    {
        app.AddComponent<App>("app");
    }
}

To get authentication information spread down the stream we need to define route for cascading authentication state in App.razor file.

<CascadingAuthenticationState>
    <Router AppAssembly="typeof(Program).Assembly">
        <NotFoundContent>
            <p>Sorry, there's nothing at this address.</p>
        </NotFoundContent>
    </Router>
</CascadingAuthenticationState>

Now we have done with all important pieces and it’s time to build a test page.

Testing Authorizing template

I added AuthorizeView to layout page (MainLayout.razor in shared views folder).

@inherits LayoutComponentBase

<div class="sidebar">
    <NavMenu />
</div>

<div class="main">
    <div class="top-row px-4">
        <a href="http://blazor.net" target="_blank" class="ml-md-auto">About</a>
        <AuthorizeView>
            <NotAuthorized>
                Not authorized
            </NotAuthorized>
            <Authorizing>
                Authorizing
            </Authorizing>
            <Authorized>
                Authorized
            </Authorized>
        </AuthorizeView>
    </div>

    <div class="content px-4">
        @Body
    </div>
</div>

AuthorizeView component has different message for every authentication status. I followed rabberbock solution and built authentication status buttons to Index page.

@page "/"
@inject MyAuthenticationStateProvider MyAuthStateProvider

<h1>Hello, world!</h1>

Welcome to your new app.

<SurveyPrompt Title="How is Blazor working for you?" />

<button @onclick="@(() => UpdateAuthentication(true))">
    Set authenticated
</button>
<button @onclick="@(() => UpdateAuthentication(false))">
    Set anonymous
</button>
<button @onclick="@(() => UpdateAuthentication(null))">
    Set authenticating
</button>

@code
{
    [CascadingParameter]
    private Task<AuthenticationState> AuthenticationStateTask { get; set; }

    private void UpdateAuthentication(bool? isAuthenticated)
    {
        if (!isAuthenticated.HasValue)
        {
            MyAuthenticationStateProvider.IsAuthenticating = true;
        }
        else
        {
            MyAuthenticationStateProvider.IsAuthenticating = false;
            MyAuthenticationStateProvider.IsAuthenticated = isAuthenticated.Value;
        }

        MyAuthStateProvider.NotifyAuthenticationStateChanged();
    }
}

When starting the application this is how our Index page looks like.

We can click on buttons and see how authentication status is changing on top right corner of page.

Wrapping up

AuhtorizeView component has third authentication state called Authorizing. It is used only by client-side Blazor applications as server-side ones know authentication status anyway. With Authorizing state it is possible to show some spinner or wait icon to user until authentication is done using back-end service. As client-side Blazor doesn’t have any out-of-box authentication method available right now, we have to write our own AuthenticationStateProvider that knows our authentication logic.

Liked this post? Empower your friends by sharing it!
Categories: Blazor

View Comments (4)

  • Gunnar,

    Thanks for this example. It's exactly what I've been looking for.

    When I run this code, the app "appears" to work, but an apparent bug in AuthorizeViewCore.OnParametersSetAsync is causing a NullRefEx in the browser (seen in chrome dev tools console) because that method does not handle a null value for AuthenticationState (which is the situation during the 'Authenticating' scenario.)

    (this appears to be encountered on line 94 of this file https://github.com/aspnet/AspNetCore/blob/master/src/Components/Components/src/Auth/AuthorizeViewCore.cs)

    I'm not sure what the implications are for the state of the app after the exception (probably not good).

    --rick

  • I also saw this null reference exception too. Interesting thing is that this is the only way to get AuthorizeView component to Authorizing state. I played around and didn't noticed any bad consequences. As Blazor is still on preview with client-side one getting to stable later after this autumn I'm not very surprised if the current behavior is temporary and things will change in future versions.

  • I tweaked your code a bit to get the results I was looking for.

    In my new implementation of 'GetAuthenticationStateAsync()', I replace your 'return null' within the check for IsAuthenticating with a tight 'while' loop (while IsAuthenticating) that simply waits until something else resets that value (IsAuthenticating = false).

    That "something else" in my case is the code that submits a login request, which is submitted with a timeout. So either my login request comes back with information about the authentication result (authenticated = true/false) or the request times out (authenticated = false). In both of those outcomes (request is processed or timeout occurs), the IsAuthenticating flag is set to false (and Notify...Changed is invoked).

    At that point, the remainder of the GetAuthenticationStateAsync logic is executed, and the Identity is defined the way you have it.

    Not exactly production-ready code -- especially the reliance on my timeout resetting the IsAuthenticating flag -- but my login/logout process is all working for all scenarios (good credentials, bad credentials, timeout, logout...)

  • I'm writing in May 2021 and this bug is still present in Blazor WASM running on .net 5. The only way I seem to be able to get the "Authorizing" state to work, is setting authstate to null, but that causes the browser exception that you mention

Related Post