Search code examples
asp.net-coreblazorblazor-server-sidewindows-authentication.net-8.0

How to do Windows Authentication. I can't get NotAuthorized to show


I am using Blazor 8 (server and with per component/ per page) and trying to tie windows authentication into it, but I can't get "NotAuthorized" to work.

Routes.razor

@using Microsoft.AspNetCore.Components.Authorization
<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)">
            <NotAuthorized>
                Not Authroized
            </NotAuthorized>
        </AuthorizeRouteView>
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
</Router>

Weather.razor

@page "/weather"
@using Microsoft.AspNetCore.Authorization
@attribute [StreamRendering]
@attribute [Authorize(Roles = "test")]
<PageTitle>Weather</PageTitle>

<h1>Weather</h1>

<p>This component demonstrates showing data.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>Date</th>
                <th>Temp. (C)</th>
                <th>Temp. (F)</th>
                <th>Summary</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var forecast in forecasts)
            {
                <tr>
                    <td>@forecast.Date.ToShortDateString()</td>
                    <td>@forecast.TemperatureC</td>
                    <td>@forecast.TemperatureF</td>
                    <td>@forecast.Summary</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    private WeatherForecast[]? forecasts;

    protected override async Task OnInitializedAsync()
    {
        // Simulate asynchronous loading to demonstrate streaming rendering
        await Task.Delay(500);

        var startDate = DateOnly.FromDateTime(DateTime.Now);
        var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
        forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = startDate.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = summaries[Random.Shared.Next(summaries.Length)]
        }).ToArray();
    }

    private class WeatherForecast
    {
        public DateOnly Date { get; set; }
        public int TemperatureC { get; set; }
        public string? Summary { get; set; }
        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    }
}

Program.cs

using BlazorApp11.Components;
using Microsoft.AspNetCore.Authentication.Negotiate;

namespace BlazorApp11
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.
            builder.Services.AddRazorComponents()
                .AddInteractiveServerComponents();

            builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme);

            builder.Services.AddAuthorization(options =>
            {
                options.FallbackPolicy = options.DefaultPolicy;
            });

            builder.Services.AddCascadingAuthenticationState();

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (!app.Environment.IsDevelopment())
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();

            app.UseStaticFiles();
            app.UseAntiforgery();

            app.MapRazorComponents<App>()
                .AddInteractiveServerRenderMode();

            app.Run();
        }
    }
}

Am I missing something? When I go to the Weather Page, I get 403 Forbidden with none of my layout. I was expecting to see "Not Authroized"


Solution

  • I reproduced your issue and got a response :

    enter image description here

    I checked the NetWork tab in browser:

    the .net 7 project:

    enter image description here

    .net 8 project:

    enter image description here

    It would send a request while it failed authorize,and the request would be blocked by the middleware

    WorkAround:

    Then I tried to customize the behavior of AuthorizationMiddleware,follow this document

        public class MyAuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler
    {
        private readonly AuthorizationMiddlewareResultHandler defaultHandler = new();
        public async Task HandleAsync(RequestDelegate next,HttpContext context,AuthorizationPolicy policy,PolicyAuthorizationResult authorizeResult)
        {
    
            // Fall back to the default implementation.
            await defaultHandler.HandleAsync(next, context, policy, authorizeResult);
            if (authorizeResult.Forbidden == true)
            {                
                await next.Invoke(context);
            }
            
    
        }
    }
    

    .....

    builder.Services.AddSingleton<IAuthorizationMiddlewareResultHandler, MyAuthorizationMiddlewareResultHandler>();
    

    The content would be displayed now:

    enter image description here

    Update:

    After some researching, I think the issue is mainly related with render mode,if you don't modify the default rendermode(Static Server) in app.razor,the parent component would work as a static page and always send a request ,and the request would be handled by middewares , when you haven't login/ not authorized, you would be redirected to target page like MVC/Razor Page

    So another workaround is apply IteractiveServer render mode to the entire app:

    <Routes  @rendermode="new InteractiveServerRenderMode()"/>
    

    Now when I try with Identity Template,I could see the custom content either:

    enter image description here