Search code examples
asp.net-coreblazorforms-authenticationblazor-webassemblyblazor-client-side

Blazor Authentication - Http calls does work for anonymous


I have been playing with blazor with .net 5.0. I am stuck with a simple but annoying issue: I am trying to load data from API but every call to the Server blazor expects the user to be signed in.

I understood if we took out [Authorize] tag from the API controller and don't have @attribute [Authorize] in the razor page, and that would be it. But I am struggling to understand why my API call still expects AccessToken in protected override async Task OnInitializedAsync() the error messages don't say much around why.

Microsoft.AspNetCore.Components.WebAssembly.Authentication.AccessTokenNotAvailableException: ''
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.SendAsyncCore(HttpRequestMessage request, HttpCompletionOption completionOption, Boolean async, Boolean emitTelemetryStartStop, CancellationToken cancellationToken)

I haven't touched the App. cs file of the web assembly..

Can anyone help please.

<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
                <NotAuthorized>
                    @if (!context.User.Identity.IsAuthenticated)
                    {
                        <RedirectToLogin />
                    }
                    else
                    {
                        <p>You are not authorized to access this resource.</p>
                    }
                </NotAuthorized>
                <Authorizing>
                    <h4>Authentication in progress...</h4>
                </Authorizing>                
            </AuthorizeRouteView>
        </Found>
        <NotFound>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>

Solution

  • I figured the solution ... should have looked at it first.

    it was actually the App.CS fill in the Client Project. Http endpoint is initiated there, and by default the code-gen adds BaseAddressAuthorizationMessageHandler to the httpclient.

    builder.Services.AddHttpClient("private", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
           .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
    

    how I solved it was using named clients one for Private and one for Public like so

    builder.Services.AddHttpClient("private", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
        .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
    
    builder.Services.AddHttpClient("public", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
    

    and in pages inject IHttpClientFactory

    @inject IHttpClientFactory ClientFactory
    

    and create a named client from the factory.

    var client = ClientFactory.CreateClient("public");
    

    This worked.