Hedgehog
Hedgehog

Reputation: 89

Authentication state provider not working inside custom message handler

Custom message handler

It is expected to be logged out if the token cannot be renewed.

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
    CancellationToken cancellationToken)
{
    await SetAuthorizationHeader(request);
    var response = await base.SendAsync(request, cancellationToken);

    if (IsRefreshRequired(response))
    {
        var tokenRefreshed = await _authService.TryRefreshTokenAsync();

        if (tokenRefreshed)
        {
            await SetAuthorizationHeader(request);
            return await base.SendAsync(request, cancellationToken);
        }

        await _authService.LogoutAsync();
        _navigationManager.NavigateTo(Routes.App.Login);
    }

    return response;
}

private async Task SetAuthorizationHeader(HttpRequestMessage request)
{
    var accessToken = await _localStorage.GetItemAsync<string>(Auth.AccessToken);

    if (!string.IsNullOrWhiteSpace(accessToken))
    {
        request.Headers.Authorization = new AuthenticationHeaderValue(Auth.Scheme, accessToken);
    }
}

Authentication service

public async Task LogoutAsync()
{
    await RemoveTokensAsync();
    AuthStateProvider.NotifyAuthenticationStateChanged();
}

Custom authentication state provider

public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
    var accessToken = await _localStorage.GetItemAsync<string>(Auth.AccessToken);

    if (string.IsNullOrWhiteSpace(accessToken))
    {
        return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
    }

    var claims = TokenParser.ParseClaims(accessToken);
    var user = new ClaimsPrincipal(new ClaimsIdentity(claims, Auth.AuthType));

    return new AuthenticationState(user);
}

public void NotifyAuthenticationStateChanged()
{
    NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
}

After calling LogoutAsync method, it successfully redirects me to the Login page, but it does not update the header:

enter image description here

If I will update the page or I will call LogoutAsync not from handler but from razor component all will work:

enter image description here

Also it does not depend on http client I am using.

Upvotes: 0

Views: 1144

Answers (1)

Hedgehog
Hedgehog

Reputation: 89

Based on the comments. Thanks @enet and @HenkHolterman.

Root cause: The StateHasChanged() logic does not propagate 'out and up'. The logout is caused by a call on an inner component, the LoginDisplay component is not involved in the default updating.

Solution: UI rerender. There is no single way how to do it.

In my case I changed
_navigationManager.NavigateTo(Routes.App.Login); to _navigationManager.NavigateTo(Routes.App.Logout);

As result logout page triggers changing authentication state logic on component level and then redirects user to the login page.

Other way to do it -- define an event handler in message handler which should be triggered from within the LogoutAsync, after the call to NotifyAuthenticationStateChanged. The subscriber to this event should be the MainLayout component, which re-renders.

Upvotes: 1

Related Questions