Blazor authentication and authorisation - redirect to login

Blazor authentication and authorisation - redirect to login

Blazor uses ASP.NET Core authentication to allow you to control access to areas within your site. A common scenario is to redirect unauthenticated users to the login page.

Authentication and authorisation

Blazor uses the existing ASP.NET Core authentication mechanisms. The scope of this article isn’t to go through all this in detail as it is well documented in the docs and various blog posts. The case we are going to look at is redirecting an unauthenticated user to the login page.

Handling unauthorised

When you create a new Blazor project with authentication your App.razor will look something like this.

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <CascadingAuthenticationState>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </CascadingAuthenticationState>
    </NotFound>
</Router>

A couple of things here, firstly, CascadingAuthenticationState. This provides a cascading parameter to all descendant components. The parameter supplied is Task<AuthenticationState>. From this you can get access to the user (ClaimsPrincipal) and do things like checking if the user is authenticated.

[CascadingParameter]
protected Task<AuthenticationState> AuthState { get; set; }

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    ClaimsPrincipal user = (await AuthState).User;

    if (user.Identity.IsAuthenticated)
    {
        Console.WriteLine($"{user.Identity.Name} is authenticated.");
    }
}

Second is the AuthorizeRouteView component. This behaves the same as the RouteView, displaying pages that match the specified route. Additionally, it will only display pages that the user is authorised to see. The AuthorizeRouteView also contains the same CascadingParameter as the CascadingAuthenticationState component.

The AuthorizeRouteView has two tags in which you can add content to, NotAuthorized and Authorizing. They are used like this.

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
            <NotAuthorized>
                <p>Hey! You're not allowed in here.</p>
            </NotAuthorized>
            <Authorizing>
                <p>Just checking with the boss you can come in.</p>
            </Authorizing>
        </AuthorizeRouteView>
    </Found>
    <NotFound>
        <CascadingAuthenticationState>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </CascadingAuthenticationState>
    </NotFound>
</Router>

On page load, whilst checking the user is authorised to view the page, the Authorizing content will be shown. If the user is not authorised then the NotAuthorized content will be shown. If you do not add any content to either of these tags then default content will be shown. "Authorizing..." and "Not authorized".

The final piece is to use the [Authorize] attribute on pages which you need to restrict access. This attribute can only be used on components that have the @page directive. This is because they are the only components that the router knows about.

e.g.

@page "/user-profile"
@attribute [Authorize]

Roles or Policy can also be used with the [Authorize] attribute.

e.g.

@page "/accounts"
@attribute [Authorize(Roles = "finance")]

or

@page "/membership/requests"
@attribute [Authorize(Policy = "moderator")]

How to redirect

In the example above, we put html directly into the tags, however components are also allowed. This means we can create a component to redirect to the login page, or any page we wish. First, create a component to do the redirect.

RedirectToLogin.cs

public class RedirectToLogin : ComponentBase
{
    [Inject]
    protected NavigationManager NavigationManager { get; set; }

    protected override void OnInitialized()
    {
        NavigationManager.NavigateTo("login");
    }
}

Then add the component to the NotAuthorized tag.

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
            <NotAuthorized>
                <RedirectToLogin />
            </NotAuthorized>
            <Authorizing>
                <p>Just checking with the boss you can come in.</p>
            </Authorizing>
        </AuthorizeRouteView>
    </Found>
    <NotFound>
        <CascadingAuthenticationState>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </CascadingAuthenticationState>
    </NotFound>
</Router>

Finally, add the [Authorize] attribute to pages which require authentication.

Now, if you browse to a page and you have not logged in you are automatically redirected to the login page!

Summary

In this post we have shown how to use the authentication and authorisation mechanisms in Blazor to restrict pages and how to redirect unauthenticated users to the login page.