Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Something wrong with AuthorizationMessageHandler in .net 6 #38486

Closed
NguyenN95 opened this issue Nov 18, 2021 · 12 comments
Closed

Something wrong with AuthorizationMessageHandler in .net 6 #38486

NguyenN95 opened this issue Nov 18, 2021 · 12 comments
Assignees
Labels
area-blazor Includes: Blazor, Razor Components feature-blazor-wasm This issue is related to and / or impacts Blazor WebAssembly feature-blazor-wasm-auth investigate Pillar: Technical Debt

Comments

@NguyenN95
Copy link

Hello, I currently follow this doc about attaching token to outgoing request for my blazor wasm (.net 6) based on this doc ASP.NET Core Blazor WebAssembly additional security scenarios but I got 2 issues:

  • The first one I config like this
builder.Services.AddScoped<AuthorizationMessageHandler>();
builder.Services.AddScoped(sp => new HttpClient(
    sp.GetRequiredService<AuthorizationMessageHandler>()
    .ConfigureHandler(
        authorizedUrls: new[]
        {
            "http://localhost:5075/api/users",
            "http://localhost:5075/api/products",
        }
    ))
{
    BaseAddress = new Uri("http://localhost:5075/api/")
});

and when I use it. It give me error in console chrome like this

Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: The inner handler has not been assigned.
System.InvalidOperationException: The inner handler has not been assigned.
   at System.Net.Http.DelegatingHandler.SetOperationStarted()
   at System.Net.Http.DelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Components.WebAssembly.Authentication.AuthorizationMessageHandler.<>n__0(HttpRequestMessage request, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Components.WebAssembly.Authentication.AuthorizationMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   at System.Net.Http.Json.HttpClientJsonExtensions.<GetFromJsonAsyncCore>d__13`1[[System.Collections.Generic.List`1[[EcommerceClient.Models.ProductsModel, EcommerceClient, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext()
   at EcommerceClient.Services.ProductService.GetProductsAsync() in F:\Nguyen\Project\Hobby-Projects\Ecommerce\dotnet\EcommerceClient\Services\ProductService.cs:line 30
   at EcommerceClient.Pages.Index.OnInitializedAsync() in F:\Nguyen\Project\Hobby-Projects\Ecommerce\dotnet\EcommerceClient\Pages\Index.razor:line 11
   at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
  • For the second one I use like this:
builder.Services.AddScoped<CustomAuthorizationMessageHandler>();
builder.Services.AddHttpClient("WebAPI", client =>
{
    client.BaseAddress = new Uri("http://localhost:5075/api/");
})
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("WebAPI"));

CustomAuthorizationMessageHandler.cs

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

namespace EcommerceClient.Services
{
    public class CustomAuthorizationMessageHandler : AuthorizationMessageHandler
    {
        public CustomAuthorizationMessageHandler(IAccessTokenProvider provider, NavigationManager navigation) : base(provider, navigation)
        {
            ConfigureHandler(
                authorizedUrls: new[] {
                   "http://localhost:5075/api/users",
                   "http://localhost:5075/api/products"
                });
        }
    }
}

It seems fine when first loading but when I go to another url and start use it. It gives me those error below:

Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: The added or subtracted value results in an un-representable DateTime. (Parameter 'value')
System.ArgumentOutOfRangeException: The added or subtracted value results in an un-representable DateTime. (Parameter 'value')
   at System.DateTime.ThrowDateArithmetic(Int32 param)
   at System.DateTime.AddTicks(Int64 value)
   at System.DateTime.Add(Double value, Int32 scale)
   at System.DateTime.AddMinutes(Double value)
   at System.DateTimeOffset.AddMinutes(Double minutes)
   at Microsoft.AspNetCore.Components.WebAssembly.Authentication.AuthorizationMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   at EcommerceClient.Services.AuthService.LoginAsync(LoginModel loginModel) in F:\Nguyen\Project\Hobby-Projects\Ecommerce\dotnet\EcommerceClient\Services\AuthService.cs:line 48
   at EcommerceClient.Pages.Auth.Login.HandleValidSubmitAsync() in F:\Nguyen\Project\Hobby-Projects\Ecommerce\dotnet\EcommerceClient\Pages\Auth\Login.razor:line 25
   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
   at Microsoft.AspNetCore.Components.Forms.EditForm.HandleSubmitAsync()
   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)

which indicates line 48 in my AuthService:

using System.Net.Http.Json;

namespace EcommerceClient.Services;

public class AuthService : IAuthService
{
    private readonly string Prefix = "auth";
    private readonly JsonSerializerOptions _jsonSerializerOptions = new JsonSerializerOptions
    {
        PropertyNameCaseInsensitive = true
    };
    private readonly ILogger<AuthService> _logger;
    private readonly HttpClient _client;
    private readonly AuthenticationStateProvider _provider;
    private readonly IStorageService _storageService;

    public AuthService(
        ILogger<AuthService> logger,
        HttpClient client,
        AuthenticationStateProvider provider,
        IStorageService storageService)
    {
        _logger = logger;
        _client = client;
        _provider = provider;
        _storageService = storageService;
    }

    public async Task<LoginResponse> LoginAsync(LoginModel loginModel)
    {
        try
        {
            var responseMessage = await _client.PostAsJsonAsync<LoginModel>($"{Prefix}/login", loginModel);
            var loginResult = await JsonSerializer.DeserializeAsync<LoginResponse>(
                await responseMessage.Content.ReadAsStreamAsync(),
                _jsonSerializerOptions
            );

            if (responseMessage.IsSuccessStatusCode)
            {
                await _storageService.SetItemAsync(Constant.JWT_TOKEN_NAME, loginResult.Token);
            }

            return loginResult!;
        }
        catch (Exception ex)
        {
            throw ex;  // This line only for debug purpose and also they indicates here ???
            return new LoginResponse { Error = ex.Message };
        }
    }

    public async Task LogoutAsync()
    {
        await _storageService.RemoveItemAsync(Constant.JWT_TOKEN_NAME);
        _client.DefaultRequestHeaders.Authorization = null;
    }

    public async Task<LoginResponse> RegisterAsync(SignupModel signupModel)
    {
        try
        {
            var responseMessage = await _client.PostAsJsonAsync<SignupModel>($"{Prefix}/signup", signupModel);
            var loginResult = await JsonSerializer.DeserializeAsync<LoginResponse>(
                await responseMessage.Content.ReadAsStreamAsync(),
                _jsonSerializerOptions
            );

            if (responseMessage.IsSuccessStatusCode)
                await _storageService.SetItemAsync(Constant.JWT_TOKEN_NAME, loginResult.Token);

            return loginResult!;
        }
        catch (Exception ex)
        {
            throw ex; // This line only for debug purpose
            return new LoginResponse { Error = ex.Message };
        }
    }
}

P\S: Before I use those everything still normal.
Thank you.

@TanayParikh TanayParikh added the area-blazor Includes: Blazor, Razor Components label Nov 18, 2021
@javiercn javiercn added feature-blazor-wasm This issue is related to and / or impacts Blazor WebAssembly feature-blazor-wasm-auth labels Nov 18, 2021
@mkArtakMSFT mkArtakMSFT added investigate Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. labels Nov 18, 2021
@ghost
Copy link

ghost commented Nov 18, 2021

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

@mkArtakMSFT
Copy link
Member

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

@mkArtakMSFT mkArtakMSFT added this to the .NET 7 Planning milestone Nov 18, 2021
@mkArtakMSFT
Copy link
Member

For context, here are the two issues reported here:

  1. It seems that our docs don't call out the need to assign InnerHandler and that's the culprit here. So we need to update the docs to show how to do that.
  2. We still need to investigate the second issue where the DateTime construction / parsing failed

@NguyenN95
Copy link
Author

@ghost ghost added Needs: Attention 👋 This issue needs the attention of a contributor, typically because the OP has provided an update. and removed Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. labels Nov 19, 2021
@OsirisTerje
Copy link

I can confirm the same issue, using net6.0, and using the code from the Refit library/docs https://github.com/reactiveui/refit#bearer-authentication for non-DI:

var api = RestService.For<ISomeThirdPartyApi>(new HttpClient(new AuthHeaderHandler(tenantProvider, authTokenStore))
    {
        BaseAddress = new Uri("https://api.example.com")
    }
);

and I get the following crash:

Message: 
System.InvalidOperationException : The inner handler has not been assigned.

  Stack Trace: 
DelegatingHandler.SetOperationStarted()
DelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
AuthHeaderHandler.<>n__0(HttpRequestMessage request, CancellationToken cancellationToken)
AuthHeaderHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) line 119
HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
<<BuildCancellableTaskFuncForMethod>b__0>d.MoveNext() line 245
--- End of stack trace from previous location ---
VerifiserAuthenticationTest.ThatAuthenticationWithJwkKeypairsWorks() line 21
GenericAdapter`1.GetResult()
AsyncToSyncAdapter.Await(Func`1 invoke)
TestMethodCommand.Execute(TestExecutionContext context)
<>c__DisplayClass4_0.<PerformWork>b__0()
<>c__DisplayClass1_0`1.<DoIsolated>b__0(Object _)
ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
ContextUtils.DoIsolated(ContextCallback callback, Object state)
ContextUtils.DoIsolated[T](Func`1 func)
SimpleWorkItem.PerformWork()

@OsirisTerje
Copy link

@mkArtakMSFT You point to the need to assign Innerhandler, but other docs say that should not be needed, and doesn't seem needed in earlier dotnet versions. Anyway, what is the exact assignment one need to do?

If I just do:

InnerHandler = new HttpClientHandler();

Then the call goes through. Is this the right way - or is anything being missed then?

@fmashozhera
Copy link

I am also getting the same error as your second issue
'The added or subtracted value results in an un-representable DateTime.'
Have you find a solution yet?

@NguyenN95
Copy link
Author

Still waiting to fix this

@dombott
Copy link

dombott commented Aug 29, 2022

I had the same problem with The added or subtracted value results in an un-representable DateTime..
My IAccessTokenProvider returned an AccessToken with the Expiry set to the date 01.01.0001, which led to the exception above.
Setting the Expiry manually to the DateTime stored in the token made it work.
Hope this helps anyone.

@ghost
Copy link

ghost commented Sep 14, 2022

Thanks for contacting us.
We're moving this issue to the .NET 8 Planning milestone for future evaluation / consideration. Because it's not immediately obvious that this is a bug in our framework, we would like to keep this around to collect more feedback, which can later help us determine the impact of it. We will re-evaluate this issue, during our next planning meeting(s).
If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues.
To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

@mkArtakMSFT mkArtakMSFT removed the Needs: Attention 👋 This issue needs the attention of a contributor, typically because the OP has provided an update. label Sep 14, 2022
@ghost
Copy link

ghost commented Oct 6, 2023

To learn more about what this message means, what to expect next, and how this issue will be handled you can read our Triage Process document.
We're moving this issue to the .NET 9 Planning milestone for future evaluation / consideration. Because it's not immediately obvious what is causing this behavior, we would like to keep this around to collect more feedback, which can later help us determine how to handle this. We will re-evaluate this issue, during our next planning meeting(s).
If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact work.

@mkArtakMSFT
Copy link
Member

Thanks for contacting us.
We couldn't prioritize this investigation given the number of customers are affected. Hence closing this issue.
You can learn more about our triage process and how we handle issues by reading our Triage Process writeup.

@mkArtakMSFT mkArtakMSFT closed this as not planned Won't fix, can't repro, duplicate, stale Dec 14, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Feb 7, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components feature-blazor-wasm This issue is related to and / or impacts Blazor WebAssembly feature-blazor-wasm-auth investigate Pillar: Technical Debt
Projects
None yet
Development

No branches or pull requests

8 participants