5

We have existing ASP.NET Core application(.NET 5) which uses angular as UI framework.
We created a Blazor WASM client library which we want to use in this application alongside with already existing angular framework.

Following documentations this is how we configured it in Startup.Configure method to "serve" balzor app from the dedicated directory "blazor-app" in wwwroot.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // ...
    app.UseBlazorFrameworkFiles("/blazor-app");
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
        endpoints.MapFallbackToFile("blazor-app/index.html");
    });
}

How we can configure Blazor app, so it's index.html is returned only for authenticated users?

For example something like this?

[Authorize]
public class ClientController
{
    public IActionResult ClientApp()
    {
        // returns Blazor app index.html
    }
}
      
Basin
  • 887
  • 1
  • 14
  • 28
  • You can use the app.razor - `CascadingAuthenticationState` to allow only authenticated users to get to your html files, see [this](https://stackoverflow.com/a/66698529/14973743) – Anand Sowmithiran Jan 10 '22 at 14:15
  • @AnandSowmithiran, thank you, but this will not prevent to anybody to load original `index.html` which will load blazor framework files to the browser. – Basin Jan 11 '22 at 02:13
  • You might want to reconsider this approach. The way Blazor works (both wasm and server side) is as follows: Client sends a request, request returns an HTML page and that page loads a java script which in turn bootstraps the application. Using auth before loading html is an out of sequence step. – Mayur Ekbote Jan 13 '22 at 11:24
  • @Basin how were you doing this with Angular? It's no different with Blazor WASM. Both are SPAs which means the browser needs to retrieve their code as part of loading `index.html` first. If you configure the *web server* to require Basic authentication, Windows Authentication or any other kind of authentication you can restrict who can load `index.html` and hence the Angular or Blazor SPA – Panagiotis Kanavos Jan 14 '22 at 17:28
  • If you want to use your SPA to authenticate, you're really asking how to restrict the content displayed, not the `index.html` page. – Panagiotis Kanavos Jan 14 '22 at 17:31
  • The question is confusing. But analysing all the details, what I understand is: you are not asking a typical 'how to do authentication in Blazor'. But also, you ask *it's index.html is only returned for authenticated user*, which sounds a bit 'off' to me. Html is served from an endpoint, a.k.a Action. But I doubt you are asking 'how to secure an Action' either. My suspect is, you just don't want to include the `blazor wasm .js` in the returned view for the visitors. The `js` file sure is big, and would be a performance issue for the visitors (for the first time of course). Is this the case? – Ergis Jan 14 '22 at 17:50

3 Answers3

0

There are a couple of approaches to doing authentication before serving the view. I decided to do it by ASP.Net Session management.

Add session service to your application:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddDistributedMemoryCache();
    services.AddSession(options =>
    {
        options.IdleTimeout = TimeSpan.FromSeconds(3600); // an hour
        options.Cookie.HttpOnly = true;
        options.Cookie.IsEssential = true;
    });
   
}

Then use the session in your app:

app.UseRouting();
// Call UseSession after UseRouting and before MapRazorPages and MapDefaultControllerRoute
app.UseSession();
app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
});

Now, before serving the view check if the user is authenticated or not:

public IActionResult Index()
{
    string KEY = "Authentication-Key";
    if (string.IsNullOrEmpty(HttpContext.Session.GetString(KEY))|| 
        !IsValid(HttpContext.Session.GetString(KEY))){
        // Redirect to {Action = Index} of {Controller = Login}
        return RedirectToAction("Index", "Login"); 
    }
    return View(); // consider that this is the blazor view
}
private bool IsValid(string KEY)
{
    // Implement your validation mechanism
    
    return KEY == "THIS IS THE AUTH KEY";
}

Let's assume that the user is redirected to the Login page after it has been detected not authenticated and again got authenticated and receives a key for authentication:

string KEY = "Authentication-Key";
HttpContext.Session.SetString(KEY, "THIS IS THE AUTH KEY");

Finally, when she/he returns to the Index page (ex: blazor view) either manually or automatically, the view is served.

A Farmanbar
  • 4,381
  • 5
  • 24
  • 42
0

Index.html is an entry point to your app. If you want to have different UI for authorized and unauthorized users (ex no menu), you should provide different layouts. Below code is part of App.razor. The key is to have an additional layout component (UnauthorizedLayout), which will be shown if the user is not authorized. This layout does not contain any elements in it, and its sole purpose is to host the login view.

AuthenticationService is just an example how to get a reference to user, you can use any method you prefer.

<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
    @if (AuthenticationService.User == null)
    {
        <AppRouteView RouteData="@routeData" DefaultLayout="@typeof(UnauthorizedLayout)" />
    }
    else
    {
        <AppRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    }
    @*<FocusOnNavigate RouteData="@routeData" Selector="h1" />*@
</Found>
<NotFound>
    ...
</NotFound>

UnathorizedLayout.razor:

@inherits LayoutComponentBase

<div id="container">
    @Body
</div>
Goran
  • 6,328
  • 6
  • 41
  • 86
0
<AuthorizeView>
<Authorized>
    <main class="main" >

        <div class="container-fluid" data-layout="container">

            <nav class="navbar navbar-light navbar-vertical navbar-expand-xl">

                
                <NavMenu></NavMenu>
            </nav>
            <div class="content">
                <!-- navbar -->
                <Toolbar></Toolbar>

                <!-- content -->
                @Body

                <!-- footer -->
                <Footer></Footer>
            </div>
        </div>

    </main>
</Authorized>
<NotAuthorized>
   <RedirectToLogin />

</NotAuthorized>

RedirectToLogin

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

    protected override async Task OnInitializedAsync()
    {
        var authenticationState = await AuthenticationStateTask;

        if (authenticationState?.User?.Identity is null || !authenticationState.User.Identity.IsAuthenticated)
        {
            Navigation.NavigateTo("/Login", true);
        }
    }
}
Mirko MyRent
  • 111
  • 1
  • 4