aspnetcore icon indicating copy to clipboard operation
aspnetcore copied to clipboard

Blazor Delay in AccessToken being ready and authorized view showing

Open jbomhold3 opened this issue 4 years ago • 13 comments

Describe the bug

When making an HTTP call inside a component in the <Authorized> component the access token is not ready for a short time after login even when the authorized content renders. The call throws a exception however oddly enough the request still has a token attached.

blazor.webassembly.js:1 crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: ''
Microsoft.AspNetCore.Components.WebAssembly.Authentication.AccessTokenNotAvailableException: ''
  at Microsoft.AspNetCore.Components.WebAssembly.Authentication.AuthorizationMessageHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x32e45d0 + 0x0030e> in <filename unknown>:0 
  at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x32cf170 + 0x0014c> in <filename unknown>:0 
  at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered (System.Threading.Tasks.Task`1[TResult] sendTask, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationTokenSource cts, System.Boolean disposeCts) <0x32e6f08 + 0x00134> in <filename unknown>:0 
  at System.Net.Http.Json.HttpClientJsonExtensions.GetFromJsonAsyncCore[T] (System.Threading.Tasks.Task`1[TResult] taskResponse, System.Text.Json.JsonSerializerOptions options, System.Threading.CancellationToken cancellationToken) <0x32f0800 + 0x000cc> in <filename unknown>:0 

To Reproduce

Make a HTTP with automatically attached access token as described in https://devblogs.microsoft.com/aspnet/blazor-webassembly-3-2-0-preview-5-release-now-available/ In the components oninit or after render method. results in Microsoft.AspNetCore.Components.WebAssembly.Authentication.AccessTokenNotAvailableException Refresh the page no more error.

Further technical details

  • ASP.NET Core version Blazor WASM rc1

jbomhold3 avatar May 04 '20 00:05 jbomhold3

Having the same issue in the GA version. Have only seen it happen on an iOS device, however.

reedptaylor avatar May 27 '20 14:05 reedptaylor

Same issue here, when you try to send authenticated request in the GA version in component after-render. Token is not available despite the successful authentication

zajda82 avatar Jul 14 '20 07:07 zajda82

Hi @javiercn , is there any update on this Issue? I am still seeing this on Blazor WASM 3.2.1

cswierczynski-diam avatar Aug 31 '20 21:08 cswierczynski-diam

This is something @captainsafia was looking for 5.0

javiercn avatar Sep 01 '20 11:09 javiercn

I am seeing this as well, is there any work around available at this time?

knight1219 avatar Sep 01 '20 17:09 knight1219

Ok so anyone having this issue: use a IHttpClientFactory to create the instance of the HttpClient in DI instead of trying to have the DI create the HttpClient. I have no idea why it works, but it does. Maybe something isn't getting picked up after the auth happens and an existing HttpClient cant find it?

Sounds crazy, but I just had it work. Hopefully this will help others.

knight1219 avatar Sep 01 '20 18:09 knight1219

Ok so anyone having this issue: use a IHttpClientFactory to create the instance of the HttpClient in DI instead of trying to have the DI create the HttpClient. I have no idea why it works, but it does. Maybe something isn't getting picked up after the auth happens and an existing HttpClient cant find it?

Sounds crazy, but I just had it work. Hopefully this will help others.

Can you provide a small snippet of what you mean please? I'm also running in to this.

Thanks!

ericdunn avatar Sep 02 '20 14:09 ericdunn

Ok so anyone having this issue: use a IHttpClientFactory to create the instance of the HttpClient in DI instead of trying to have the DI create the HttpClient. I have no idea why it works, but it does. Maybe something isn't getting picked up after the auth happens and an existing HttpClient cant find it? Sounds crazy, but I just had it work. Hopefully this will help others.

Can you provide a small snippet of what you mean please? I'm also running in to this.

Thanks!

Sure!

So at least for my purpose, I have service classes that call the HttpClient:

`

public class UserRetrievalService
{
    private readonly HttpClient httpClient;

    public UserService(HttpClient httpClient)
    {
        this.httpClient = httpClient;
    }

    public async Task<User> GetUser()
    {
        var result = await this.httpClient.GetFromJsonAsync<User>(@$"api/me");
        return await Task.FromResult(result );
    }
}

`

I use this in my layout once I have authenticated so I can get user data from my database (dont ask why it's not in the token). So my fix for this was this:

`

public class UserRetrievalService
{
    private readonly IHttpClientFactory httpClientFactory;

    public UserService(IHttpClientFactory httpClientFactory)
    {
        this.httpClientFactory= httpClientFactory;
    }

    public async Task<User> GetUser()
    {
        var httpClient = this.httpClientFactory.CreateClient("ServerAPI");
        var result = await httpClient.GetFromJsonAsync<User>(@$"api/me");
        return await Task.FromResult(result );
    }
}

`

So, but doing this, the HttpClient I need is created from the factory after the token is retrieved and stored in the 'BaseAddressAuthorizationMessageHandler'. Not a fan of doing this, but it works so my boss is a fan :)

knight1219 avatar Sep 04 '20 15:09 knight1219

Cant remember if this is what eventually solved it for me (might have been a different issue I was having). But anytime I’m using my injected HttpClient I surround the request with a try and add a catch for AccessTokenNotAvailableException. It has a method to redirect to the Identity provider to retrieve a new access token (this can happen in the background if you are already authenticated I believe). Snippet: try { await _api.GetAsync(Endpoint); ... } catch (AccessTokenNotAvailableException e) { e.Redirect(); }

NOTE: The redirect may not work properly if your ID provider has a different domain than your app on Safari due to Safari automatically blocking third party cookies.

reedptaylor avatar Sep 04 '20 15:09 reedptaylor

Ok so anyone having this issue: use a IHttpClientFactory to create the instance of the HttpClient in DI instead of trying to have the DI create the HttpClient. I have no idea why it works, but it does. Maybe something isn't getting picked up after the auth happens and an existing HttpClient cant find it? Sounds crazy, but I just had it work. Hopefully this will help others.

Can you provide a small snippet of what you mean please? I'm also running in to this. Thanks!

Sure!

So at least for my purpose, I have service classes that call the HttpClient:

`

public class UserRetrievalService
{
    private readonly HttpClient httpClient;

    public UserService(HttpClient httpClient)
    {
        this.httpClient = httpClient;
    }

    public async Task<User> GetUser()
    {
        var result = await this.httpClient.GetFromJsonAsync<User>(@$"api/me");
        return await Task.FromResult(result );
    }
}

`

I use this in my layout once I have authenticated so I can get user data from my database (dont ask why it's not in the token). So my fix for this was this:

`

public class UserRetrievalService
{
    private readonly IHttpClientFactory httpClientFactory;

    public UserService(IHttpClientFactory httpClientFactory)
    {
        this.httpClientFactory= httpClientFactory;
    }

    public async Task<User> GetUser()
    {
        var httpClient = this.httpClientFactory.CreateClient("ServerAPI");
        var result = await httpClient.GetFromJsonAsync<User>(@$"api/me");
        return await Task.FromResult(result );
    }
}

`

So, but doing this, the HttpClient I need is created from the factory after the token is retrieved and stored in the 'BaseAddressAuthorizationMessageHandler'. Not a fan of doing this, but it works so my boss is a fan :)

Ahh I see what you mean now! Thanks !!

ericdunn avatar Sep 04 '20 19:09 ericdunn

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.

ghost avatar Oct 09 '20 17:10 ghost

Hi folks. Checking in to see if folks are still seeing this issue with .NET 7?

danroth27 avatar Oct 13 '22 17:10 danroth27

Hi @jbomhold3. 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 avatar Oct 13 '22 17:10 ghost

This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment. If it is closed, feel free to comment when you are able to provide the additional information and we will re-investigate.

See our Issue Management Policies for more information.

ghost avatar Oct 17 '22 18:10 ghost