dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.43k stars 10.02k forks source link

AuthorizeView non-functional with InteractiveServer and Windows (Negotiate) Auth #52921

Closed Stroniax closed 10 months ago

Stroniax commented 10 months ago

Is there an existing issue for this?

Describe the bug

The AuthorizeView component renders authorized content only for a fraction of a second before switching to rendering the unauthorized content, when using InteractiveServer render mode with Windows authorization (Microsoft.AspNetCore.Authentication.Negotiate).

Expected Behavior

The authorize content should render and remain displayed.

Steps To Reproduce

Based on the .net 8 Blazor Web App template: a repro is at https://github.com/Stroniax/ProofOfConcept.WindowsAuthBlazorWebAuthorizeView.

The significant files are:

/Program.cs
/Components/Pages/Home.razor
// in Program.cs
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
builder.Services.AddCascadingAuthenticationState();
// in Home.razor
@rendermode InteractiveServer
<AuthorizeView>
  <Authorized>
    @context.User.Identity?.Name is authorized!
  </Authorized>
  <NotAuthorized>
    User is not authorized.
  </NotAuthorized>
</AuthorizeView>

Exceptions (if any)

No response

.NET Version

8.0.100

Anything else?

.NET SDK:
 Version:           8.0.100
 Commit:            57efcf1350
 Workload version:  8.0.100-manifests.8d38d0cc

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.22621
 OS Platform: Windows
 RID:         win-x64
 Base Path:   C:\Program Files\dotnet\sdk\8.0.100\

Host:
  Version:      8.0.0
  Architecture: x64
  Commit:       5535e31a71

.NET SDKs installed:
  7.0.311 [C:\Program Files\dotnet\sdk]
  8.0.100 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 6.0.25 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 7.0.14 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 8.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 6.0.25 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 7.0.14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 8.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 6.0.25 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 7.0.14 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 8.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Stroniax commented 10 months ago

By accident I found out a little more info here, the first token that I receive is the WindowsPrincipal that I would expect, with all claims and everything in order. A fraction of a second later the component reloads with a ClaimsPrincipal that has no claims; hence no authorization.

I am very new to blazor, but my understanding is that server-side rendering happens through signalr. My current idea is that maybe this connection isn't knowing that it needs to authenticate? Maybe something about the first load does indicate auth is required and then consecutive changes to the page's state don't pass along the credential.


Edit 2023-12-27

From my understanding the initial page load is rendered "statically", eg all done on the server. After the page loads, the signalr connection is established to set up the "interactive" parts of the page, and somehow does some reloading of some components. It is indeed at this time that the credential is lost because this connection does not pass the authentication state. Through some tutorial I found out a little more about AuthenticationStateProvider possibly used client-side to get a subset of the auth state from server-side, so I'll be trying to figure out if I can implement that into my InteractiveServer-only app to see if I can use the AuthorizeView component properly.

Stroniax commented 10 months ago

Looks like the following lines of code (which were not hightlighted in the IIS docs but are highlighted for Kestrel) in setup are required and fix this problem:

services.AddAuthorization(options => options.FallbackPolicy = options.DefaultPolicy);

I didn't add this until I found this reddit post which included the required lines. I don't see a way to do this with the AddAuthorizationBuilder extension method either, at least not directly - I think that's probably why I didn't include it in the first place, and static pages were still loading which is what I tested with when I first added the package.