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.46k stars 10.03k forks source link

Blazor Server with OpenIdConnect authentication fails when trying to sign out #51702

Closed gthvidsten closed 10 months ago

gthvidsten commented 1 year ago

Is there an existing issue for this?

Describe the bug

Starting a Blazor Server app, logging in using await HttpContext.ChallengeAsync("oidc");, stopping the app, starting the app again, then calling await HttpContext.SignOutAsync("oidc") causes an ObjectDisposedException with the message IFeatureCollection has been disposed.

Expected Behavior

Expected await HttpContext.SignOutAsync("oidc") to work without any exceptions.

Steps To Reproduce

I've set up a .NET7 Blazor Server, the set up OIDC using the following code:

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddServerSideBlazor();

    services.AddAuthentication(options =>
    {
        // I've tried this
        options.DefaultScheme = "MyScheme",
        options.DefaultChallengeScheme = "oidc"

        // and this, but never both at the same time
        options.DefaultAuthenticateScheme = "MyScheme";
        options.DefaultSignInScheme = "MyScheme";
        options.DefaultChallengeScheme = "MyScheme";
    })
    .AddCookie("MyScheme")
    .AddOpenIdConnect("oidc", options =>
    {
        // My OIDC options 
    });
}

public void Configure(IApplicationBuilder app)
{
    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapBlazorHub();
        endpoints.MapFallbackToPage("/_Host");
    });
}

I have a login.cshtml which only calls await HttpContext.ChallengeAsync("oidc");, and I have a logout.cshtml which calls both await HttpContext.SignOutAsync("oidc"); and await HttpContext.SignOutAsync("MyScheme") (in this order).

Exceptions (if any)

ObjectDisposedException when calling SignOutAsync("oidc") saying:

IFeatureCollection has been disposed

.NET Version

7.0.402

Anything else?

ASP.NET Core: 7.0.12 Microsoft.AspNetCore.Authentication.OpenIdConnect: 7.0.12 Visual Studio 2022 Professional: 17.7.5 dotnet --info:

.NET SDK:
 Version:   7.0.402
 Commit:    791db8e2d8

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

Host:
  Version:      7.0.12
  Architecture: x64
  Commit:       4a824ef37c

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

.NET runtimes installed:
  Microsoft.AspNetCore.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.23 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 7.0.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.23 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 7.0.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.23 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 7.0.12 [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
surayya-MS commented 1 year ago

Thank you for contacting us. It looks like you are using the HttpContext after it is disposed. Can you get a full stack trace for the exception?

gthvidsten commented 1 year ago

Here's the full stack trace:

   at Microsoft.AspNetCore.Http.Features.FeatureReferences`1.ThrowContextDisposed()
   at Microsoft.AspNetCore.Http.Features.FeatureReferences`1.Fetch[TFeature,TState](TFeature& cached, TState state, Func`2 factory)
   at Microsoft.AspNetCore.Http.Features.FeatureReferences`1.Fetch[TFeature](TFeature& cached, Func`2 factory)
   at Microsoft.AspNetCore.Http.DefaultHttpRequest.get_Scheme()
   at Microsoft.AspNetCore.Authentication.AuthenticationHandler`1.BuildRedirectUri(String targetPath)
   at Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.BuildRedirectUriIfRelative(String uri)
   at Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.<SignOutAsync>d__15.MoveNext()
   at Microsoft.AspNetCore.Authentication.AuthenticationService.<SignOutAsync>d__18.MoveNext()
   at My.Pages.LogoutModel.<OnGet>d__0.MoveNext() in C:\MyProject\Pages\Logout.cshtml.cs:line 13

For completion, here's my Logout.cshtml:

@page
@model MyProject.Pages.LogoutModel

And the model:

public class LogoutModel : PageModel
{
    public async void OnGet()
    {
        await HttpContext.SignOutAsync("oidc", new AuthenticationProperties { RedirectUri = Url.Content("~/") });
        await HttpContext.SignOutAsync("MyScheme");
    }
}

And to reiterate: If I log in and call Logout.cshtml everything works. If I log in, restart the application, then call Logout.cshtml, both SignOutAsync() calls fails with the exception above.

ghost commented 1 year ago

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

mkArtakMSFT commented 10 months ago

Can you try to fix the signature of your OnGet method to return Task, as follows:

 public async Task OnGet()
ghost commented 10 months ago

Hi @gthvidsten. 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.

gthvidsten commented 10 months ago

Can you try to fix the signature of your OnGet method to return Task, as follows:

 public async Task OnGet()

That did the trick. The exception disappeared. I see you already mentioned this in the Analyzers issue and I agree that this could be caught in an analyzer. I was under the impression that async void could be used for events, and OnGet() sounds like an event where you would expect async void to work, so having an analyzer tell you otherwise would be nice.

mkArtakMSFT commented 10 months ago

Thanks for confirming, @gthvidsten. That's great. Closing this as the analyzer will be the only actionable thing for us here and that's already tracked in a separate issue.