Search code examples
blazorblazor-server-sideasp.net-blazor

Blazor Custom Authentication State Provider


I have created a custom authentication state provider which checks the username and password on our internal LDAP service. My below code works fine during initial login. But after login if I press F5 or refresh the page than it automatically goes to Login Page. Can someone please help?

public class CustomAuthenticationStateProvider : AuthenticationStateProvider
{
    private readonly ILdapAuthenticationService _ldapAuthenticationService;
    private ClaimsPrincipal _cachedClaimsPrincipal;

    public CustomAuthenticationStateProvider(ILdapAuthenticationService 
ldapAuthenticationService)
    {
        _ldapAuthenticationService = ldapAuthenticationService;
    }

    public override async 
Task<Microsoft.AspNetCore.Components.Authorization.AuthenticationState>
        GetAuthenticationStateAsync()
    {
        if (_cachedClaimsPrincipal != null)
            return await Task.FromResult(
                new 
Microsoft.AspNetCore.Components.Authorization.AuthenticationState(_cachedClaimsPrincipal));

        return await Task.FromResult(new 
Microsoft.AspNetCore.Components.Authorization.AuthenticationState(new ClaimsPrincipal(new 
ClaimsIdentity())));
    }


    public void ValidateLogin(string username, string password)
    {
        if (string.IsNullOrEmpty(username)) throw new Exception("Enter username");
        if (string.IsNullOrEmpty(password)) throw new Exception("Enter password");

        if (_ldapAuthenticationService.AuthenticateUser(username, password))
        {
            _cachedClaimsPrincipal = _ldapAuthenticationService.CurrentUser.ClaimsPrincipal;
        }
        
        NotifyAuthenticationStateChanged(
            Task.FromResult(new 
 AuthenticationState(_ldapAuthenticationService.CurrentUser.ClaimsPrincipal)));
    }
}

public class RedirectToLogin : ComponentBase
{
    [Inject]
    protected NavigationManager NavigationManager { get; set; }

    [CascadingParameter]
    private Task<Microsoft.AspNetCore.Components.Authorization.AuthenticationState> 
authenticationStateTask { get; set; }

    protected override void OnInitialized()
    {
        NavigationManager.NavigateTo("/");
    }
}

RedirectToLogin is created so that user has to authenticate before browsing any page


Solution

  • Your CustomAuthenticationStateProvider will have a scope that dies after the request, so caching the ClaimsPrincipal in a local member of this class won't work:

    private ClaimsPrincipal _cachedClaimsPrincipal;
    

    Add a debug log on the constructor and you'll see its created for each request (hence any stored value in _cachedClaimsPrincipal is lost).

    public CustomAuthenticationStateProvider(ILdapAuthenticationService 
        ldapAuthenticationService)
    {
        System.Diagnostics.Debug.WriteLine("Constructor");
        _ldapAuthenticationService = ldapAuthenticationService;
    }
    

    You need to cache it in a persistent location (e.g. Session object for Server-Side or Local Storage for client side).