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.57k stars 10.05k forks source link

Blazor multiple authentication schemes do not play nice with RenderModeServer #50122

Open SPWizard01 opened 1 year ago

SPWizard01 commented 1 year ago

Is there an existing issue for this?

Describe the bug

Consider the following scenario:

An app is running inside an org that is using on prem AD which syncs with Azure, you want to be able to host your blazor app inside IIS server on prem, but you also want to be able to configure specific route(component) inside blazor to be protected by Microsoft.Identity.Web package.

It partially works until you specify render mode to be RenderModeServer.

Expected Behavior

My understanding that it should fulfill Authorize attribute completely and if you say you want the user to be authorized with OpenIdConnectDefaults.AuthenticationScheme via EntraId policy then it should use that and not Windows auth and vice versa,

Steps To Reproduce

Program.cs

//you can supply OpenIdConnectDefaults.AuthenticationScheme as default scheme
//the behaviour wont change, it will only change the user "Name" that is returned inside component.

builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate()
.AddMicrosoftIdentityWebApp(cfg =>
{
    cfg.Instance = globalConfig.AzureAd.Instance;
    cfg.TenantId = globalConfig.AzureAd.TenantId;
    cfg.ClientId = globalConfig.AzureAd.ClientId;
    cfg.CallbackPath = globalConfig.AzureAd.CallbackPath;
    ...
});
...
builder.Services.AddAuthorization(options =>
{
    var entraId = new AuthorizationPolicyBuilder()
    .AddAuthenticationSchemes(OpenIdConnectDefaults.AuthenticationScheme)
    .RequireAuthenticatedUser()
    .Build();

    options.AddPolicy("EntraId", entraId);

    var kerb = new AuthorizationPolicyBuilder()
    .AddAuthenticationSchemes(NegotiateDefaults.AuthenticationScheme)
    .RequireAuthenticatedUser()
    .Build();
    options.AddPolicy("Kerberos", kerb);

    // By default, all incoming requests will be authorized according to the default policy.
    options.FallbackPolicy = options.DefaultPolicy;
});

Component.razor

@using Microsoft.AspNetCore.Authorization;
@using Microsoft.Identity.Web;
@using System.Diagnostics;
@page "/authbugssr"
@attribute [Authorize("EntraId")]
@* Uncomment line bellow and observe OnInitializedAsync firing at least couple of time while providing different user each time first is the user required by configured policy, last is the user configured by default policy*@
@* @attribute [RenderModeServer] *@
@inject AuthenticationStateProvider AuthProvider

<CascadingAuthenticationState>
    <AuthorizeView Policy="EntraId">
        <Authorized>
            Hello, @context.User.Identity.Name!
        </Authorized>
        <NotAuthorized>
            Not authed
        </NotAuthorized>
    </AuthorizeView>
</CascadingAuthenticationState>
@code {

    protected async override Task OnInitializedAsync()
    {
        base.OnInitialized();
        var user = (await AuthProvider.GetAuthenticationStateAsync()).User;
        Debugger.Break();
    }
}

Authorize to your App (so it saves id token to your cookies) then try accessing /authbugssr with RenderModeServer and without it.

Exceptions (if any)

None

.NET Version

8.0.100-preview.7.23376.3

Anything else?

.NET SDK: Version: 8.0.100-preview.7.23376.3 Commit: daebeea8ea

Runtime Environment: OS Name: Windows OS Version: 10.0.19044 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\8.0.100-preview.7.23376.3\

.NET workloads installed: There are no installed workloads to display.

Host: Version: 8.0.0-preview.7.23375.6 Architecture: x64 Commit: 65b696cf5e RID: win-x64

.NET SDKs installed: 6.0.301 [C:\Program Files\dotnet\sdk] 8.0.100-preview.7.23376.3 [C:\Program Files\dotnet\sdk]

.NET runtimes installed: Microsoft.AspNetCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 7.0.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 8.0.0-preview.7.23375.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 7.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 8.0.0-preview.7.23375.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 6.0.6 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 7.0.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 8.0.0-preview.7.23376.1 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other architectures found: x86 [C:\Program Files (x86)\dotnet] registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]

Environment variables: Not set

global.json file: Not found

Learn more: https://aka.ms/dotnet/info

Download .NET: https://aka.ms/dotnet/download

SPWizard01 commented 1 year ago

After further investigation it seems that this is a bug with Blazor hub (i.e. the SignalR Client/Server is not configurable to specify auth scheme) therefore the Web request(1st request coming through) is using correct auth method while the SignalR connection (2nd request once RenderModeServer is specified) reverts to default scheme.

It would most likely be possible to manually workaround this issue once the configureSignalR would be accounted for inside the Blazor.start method.

https://github.com/dotnet/aspnetcore/blob/31a26e080ae5652cd3c9b414078d1b61a0b89010/src/Components/Web.JS/src/Platform/Circuits/CircuitStartOptions.ts#L15C12

However it seems that this version of blazor.web.js is not yet in the preview build.

Of course it would be best if the SignalR client for Blazor hub "knew" what type of auth it should use :)

SteveSandersonMS commented 1 year ago

Thanks for the report. Are you able to supply a minimal repro project, as a public GitHub repo? We will then investigate.

ghost commented 1 year ago

Hi @SPWizard01. We have added the "Needs: Author Feedback" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

ghost commented 1 year ago

Thank you for filing this issue. In order for us to investigate this issue, please provide a minimalistic repro project that illustrates the problem.

SPWizard01 commented 1 year ago

Thanks for the report. Are you able to supply a minimal repro project, as a public GitHub repo? We will then investigate.

Hi Steve,

Here you go: https://github.com/SPWizard01/MultiAuthBug

Make sure to change https://github.com/SPWizard01/MultiAuthBug/blob/7913909ee576c4f9adec633e61b545f1c6583cf5/Program.cs#L12 with appropriate values once you registered your app in Azure :)

ghost commented 1 year ago

Thank you for contacting us. Due to a lack of activity on this discussion issue we're closing it in an effort to keep our backlog clean. If you believe there is a concern related to the ASP.NET Core framework, which hasn't been addressed yet, please file a new issue.

This issue will be locked after 30 more days of inactivity. If you still wish to discuss this subject after then, please create a new issue!

SPWizard01 commented 1 year ago

still an issue with blazor server rendering because the websocket uses the default auth supplied and not the one requested in the Authorize attribute

halter73 commented 4 months ago

I'm reopening this because unlike the AuthorizationMiddleware and the default PolicyEvaluator it uses, AuthorizeViewCore does not take into account AuthorizationPolicy.AuthenticationSchemes at all and instead uses the IAuthorizationService directly.

This is a pretty fundamental limitation, because AuthorizeViewCore/AuthorizeRouteView has no reference to Http.Abstraction and HttpContext which is necessary to reauthenticate, but we could probably do a better job of warning in this scenario. We already throw when an authentication scheme is specified via an attribute (e.g. [Authorize(AuthenticationScheme "Cookies")], but we do not when you specify a policy that that configures and authentication scheme via AuthorizationPolicyBuilder.AddAuthenticationSchemes as demonstrated in this issue.

See PolicyEvaluator:

https://github.com/dotnet/aspnetcore/blob/afc520c49d9857c68d2faae5ae89821ae671bb1b/src/Security/Authorization/Policy/src/PolicyEvaluator.cs#L34-L42

vs AuthorizeViewCore:

https://github.com/dotnet/aspnetcore/blob/afc520c49d9857c68d2faae5ae89821ae671bb1b/src/Components/Authorization/src/AuthorizeViewCore.cs#L108-L130

SignalR has a similar issue when it evaluates auth policies for hub invocations (@BrennanConroy):

https://github.com/dotnet/aspnetcore/blob/afc520c49d9857c68d2faae5ae89821ae671bb1b/src/SignalR/server/Core/src/Internal/DefaultHubDispatcher.cs#L653-L662

At the very least we should throw the same way no matter how you try to specify a per-page (or hub method) authentication scheme. Ideally, we could make it just work no matter how you try to specify a per-page authentication scheme if you only have per-page interactivity, but this would require using the specified authentication scheme to authenticate the per-page circuit.