dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.43k stars 4.76k forks source link

HttpClient SafeAccessTokenHandle disposed issue in WindowsIdentity.RunImpersonatedAsync #108408

Open ww2406 opened 1 month ago

ww2406 commented 1 month ago

Description

Periodically, I am experiencing an ObjectDisposedError on SafeAccessTokenHandle when accessing through a server-side Blazor app with Windows authentication enabled.

Reproduction Steps

It doesn't happen all the time, so I'm not sure exactly what reproduces it. This is the code I've used (including a clone I tried, which doesn't resolve the issue).

Named client configuration:

services.AddHttpClient(Constants.WinHttpClient)
            .ConfigurePrimaryHttpMessageHandler(handler => new SocketsHttpHandler()
            {
                Credentials = CredentialCache.DefaultCredentials,
                DefaultProxyCredentials = CredentialCache.DefaultNetworkCredentials
            });

Service code:

var client = _httpClientFactory.CreateClient(Constants.WinHttpClient);
        var content = new StringContent(JsonSerializer.Serialize(new
        {
            StartDate = startDate,
            EndDate = endDate
        }), Encoding.UTF8, MediaTypeNames.Application.Json);
        var message = new HttpRequestMessage()
        {
            Method = HttpMethod.Post,
            RequestUri = new Uri(_configuration.GetSection("AppSettings:API_URL").Value),
            Content = content
        };

        var authState = await _authenticationStateProvider.GetAuthenticationStateAsync();

        var user = authState.User;

        // clone the windows user to hopefully avoid object disposed error
        // ^ this doesn't work, will also fail when cloning
        using var windowsUser = (WindowsIdentity?)((ClaimsIdentity?)user.Identity)?.Clone(); ;
        if (windowsUser is null)
        {
            return null;
        }

        try
        {
            using var accessToken = windowsUser.AccessToken;

            var response =
                await WindowsIdentity.RunImpersonatedAsync(accessToken, () => client.SendAsync(message));
           // ^ previously would fail here when getting the token

            if (!response.IsSuccessStatusCode)
            {
                return null;
            }

            var data = await JsonSerializer.DeserializeAsync<ApiV1Response<List<ResultDto>>>(
                await response.Content.ReadAsStreamAsync(), new JsonSerializerOptions(JsonSerializerDefaults.Web));
            return data?.Data ?? new List<ResultDto>();
        }
        catch (Exception ex)
        {   
            _logger.LogError(ex, "An error occurred while getting data.");
        }

        return null;

Expected behavior

The safe access token handle is not disposed while I am executing my code.

Actual behavior

The safe access token handle is being disposed before my code executes.

Regression?

I don't know for sure that it is, but there were previous attempts to address this...

26446

https://github.com/dotnet/aspnetcore/issues/3217

Known Workarounds

No response

Configuration

.NET 8.0.401 Win 10 x64 Blazor: Browser is Edge, but shouldn't make a difference here

Other information

No response

dotnet-policy-service[bot] commented 1 month ago

Tagging subscribers to this area: @dotnet/ncl See info in area-owners.md if you want to be subscribed.

dotnet-policy-service[bot] commented 1 month ago

Tagging subscribers to this area: @dotnet/ncl, @bartonjs, @vcsjones See info in area-owners.md if you want to be subscribed.

rzikm commented 2 weeks ago

Hi, can you provide a self-contained repro project which we can run locally to investigate the issue?

dotnet-policy-service[bot] commented 2 weeks ago

This issue has been marked needs-author-action and may be missing some important information.

dotnet-policy-service[bot] commented 2 days ago

This issue has been automatically marked no-recent-activity because it has not had any activity for 14 days. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will remove no-recent-activity.