11

I am trying to develop an app using Blazor WebAssembly and I am wondering about how I can protect my whole application if the user is not authenticated. The behavior I would implement is:

  • If an anonymous user asks for any page, then he will be redirected to the login page

Better

  • a user must be authenticated for using this app

At the moment I've implemented this behavior applying the [Authorize] attribute to every page, but I would like to centralize it.

I've achieved this goal on Blazor Server Side applying the [Authorize] attribute inside the _host.razor component.

Is there a solution even for Blazor Client Side?

Leonardo Lurci
  • 1,069
  • 1
  • 11
  • 23

5 Answers5

16

There may be slicker ways of doing this, but this is what worked for me:

Assuming you've configured Authentication correctly according to these instructions

In your MainLayout.razor (which is used by default for all components) add a code block as follows:

@code{ 

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

    protected async override Task OnInitializedAsync()
    {
        base.OnInitialized();
        var user = (await AuthStat).User;
        if(!user.Identity.IsAuthenticated)
        {
            NavigationManager.NavigateTo($"authentication/login?returnUrl={Uri.EscapeDataString(NavigationManager.Uri)}");
        }
    }
}

If the user is not authenticated, we redirect to the built-in The RemoteAuthenticatorView component at the "authentication/" enpoint with the "login" action. This should kick-off authentication

Brett
  • 351
  • 3
  • 8
  • 3
    You need to `@inject NavigationManager NavigationManager`, otherwise `CS0120: An object reference is required for the non-static field, method, or property 'NavigationManager.Uri'`. – Arsinclair Dec 12 '20 at 21:09
  • 1
    Another thing, `NavigationManager.Uri` returns fully qualified URL, but if AspNet Identity is used as Authentication Provider, it will intend to redirect you to a local Url using `LocalRedirect(returnUrl);`. So it wants a local URL that can be obtained with `NavigationManager.ToBaseRelativePath(NavigationManager.Uri)`. – Arsinclair Dec 12 '20 at 22:15
7

Basically to apply authorization to all pages in BlazorApp.Client, you have to add:

@attribute [Microsoft.AspNetCore.Authorization.Authorize]

...to your _Imports.razor file.

Furthermore, you can add:

@attribute [Microsoft.AspNetCore.Authorization.AllowAnonymous]

...on pages that don't require authorization.

Also if you wanted to redirect a user to any page, here is something I came up with:

<NotAuthorized>
    @if (true) { navMan.NavigateTo("login"); }
</NotAuthorized>

...where navMan is an injected instance of NavigationManager. Here i'm redirecting the user to my Login.razor if they try to access an authorized user only page .

mend0k
  • 131
  • 2
  • 10
  • is the `` hack still the best thing blazor wasm has to offer to redirect to the login page? – Benni Nov 21 '20 at 22:54
2

I tried the solution from @Brett and it worked but on redirect back to the page the user came from it ended up saying Authorizing..., Checking login state... and then finally Completing login... and got stuck there. The user then had to click on a link or manually type the previous URL to get back.

enter image description here

enter image description here

enter image description here

Microsoft now has documentation for "Require authorization for the entire app".

https://docs.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/?view=aspnetcore-5.0#require-authorization-for-the-entire-app

According to the documentation you can either:

  • Use the Authorize attribute directive in the _Imports.razor file:
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
  • Add the Authorize attribute to each Razor component in the Pages folder.

I added the code to _Imports.razor but then I only received a white screen for content:

enter image description here

I then noticed that https://localhost:44123/authentication/login also gave me a white screen that Shared\RedirectToLogin.razor normally points to. I then added @attribute [AllowAnonymous] to Pages\Authentication.razor and then everything worked as expected and I did not get stuck.

enter image description here

With this solution I could also see the default You are logged out. message.

enter image description here

Ogglas
  • 38,157
  • 20
  • 203
  • 266
1

In my trip to get familiar with Blazor, I am following a tutorial and the author has a nice and clean way to solve this problem as well. As everything seems to be a component in Blazor, your login page probably is a component as well. It is at least in the tutorial. So all he does is this:

<NotAuthorized>
    <Login />
</NotAuthorized>

And you need to add the correct using to your login component of course.

The downside to this solution is that the url doesn't match the page you see when you aren't logged in.

Cornelis
  • 1,287
  • 5
  • 16
  • 36
0

You could leverage the <NotAuthorizedContent> template of the <Router> component as described here:

<CascadingAuthenticationState>
    <Router AppAssembly="typeof(Startup).Assembly">
        <NotFoundContent>
            <p>Sorry, there's nothing at this address.</p>
        </NotFoundContent>
        <NotAuthorizedContent>
            <h1>Sorry</h1>
            <p>You're not authorized to reach this page. You may need to log in as a different user.</p>
        </NotAuthorizedContent>
        <AuthorizingContent>
            <p>Please wait...</p>
        </AuthorizingContent>
    </Router>
</CascadingAuthenticationState>

Replace the contents of <NotAuthorizedContent> with a custom component called something like RedirectToLogin whose OnInitializedAsync checks if the user is logged in, and if not, does the redirect.

Sipke Schoorstra
  • 1,716
  • 1
  • 14
  • 21