0

in our application I'd like to use the user management of our fat client. For this I have written a custom AuthenticationStateProvider:

public class MyAuthenticationStateProvider : ServerAuthenticationStateProvider, IAuthentorizationService, IDisposable
{
    public MyAuthenticationStateProvider (IPermissionManager permissionManager)
    {
         //User management service of the fat client
        _permissionManager = permissionManager;
    }

    public override Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        if (_permissionManager.PermissionUser == null)
        {
            var emptyUser = new ClaimsPrincipal(new ClaimsIdentity(new Claim[0]));
            return Task.FromResult(new AuthenticationState(emptyUser));
        }
        var identity = new ClaimsIdentity(new[]
        {
            new Claim(ClaimTypes.Name, _permissionManager.PermissionUser.User.GetName())
        }, "FatClientAuthentication");
        var user = new ClaimsPrincipal(identity);
        return Task.FromResult(new AuthenticationState(user));
    }

    public async Task<bool> LoginUser(string userName, string password)
    {
        //Login via WCF connection
        var response = await _clientProxy.Login(new LoginRequest
        {
            LoginUserName = userName,
            Password = password

        });
        response.LogExceptionIfFaulted(_logger);
        if (response.Ok)
        {
            _permissionManager.Initialize(response.LoggedInUser);
            NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
        }
        return response.Ok;
    }

The login works fine. For testing purposes I always log in with fixed user credentials. After the successful login I fire the NotifyAuthenticationStateChanged event, which results in a correct call of the GetAuthenticationStateAsync method. The now logged in user is correctly wrapped inside the AuthenticationState. When debugging the code I can see that the Identity with the name claim is the correct user and the IsAuthenticated property is true.

However, when using the "AuthorizeView" component, I always get an "empty user" (no name claim, no user name, IsAuthenticated is false)

I now have a small component just for testing:

<AuthorizeView>
<Authorized>
    <h2>User @context.User.Identity.Name</h2> is logged in!
    Claims:
    <ul>
        @foreach (var claim in context.User.Claims)
        {
            <li>[email protected]; [email protected]</li>
        }
    </ul>
    @context.User.Claims
    <p>Current count: @currentCount</p>

    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
</Authorized>
<NotAuthorized>
    <h2>User @context.User.Identity.Name</h2> @*this is an empty string*@
    <h2>Authentication Type: @context.User.Identity.AuthenticationType</h2> @*empty*@
    <h2>Authenticated: @context.User.Identity.IsAuthenticated</h2>@*false*@
    No user is logged in!
</NotAuthorized>

Im using the AuthorizeRouteView and the CascadingAuthenticationState in the App.razor like in the official sample displayed in https://learn.microsoft.com/en-us/aspnet/core/security/blazor/?view=aspnetcore-3.1

Accessing the AuthenticationState via an CascadingParameter also results in the same "empty user".

Appreciate any help,

tilt32

EDIT 1

So I looked into the login behaviour again, making sure that the event is called. I then figured out, that my AuthenticationStateChanged event has no subscribers (is null). My impression was, that something in the framework attaches to this event at startup. Maybe I did forget some configuration method call in the startup ? This is what I do in the configure services:

    services.AddScoped<AuthenticationStateProvider, MyAuthenticationStateProvider>();
            services.AddScoped<ServerAuthenticationStateProvider, MyAuthenticationStateProvider>();
//Interface which I use in my LoginCompontent and at Startup to log in with the default user or some real user credentials
            services.AddScoped<IAuthenticationService, MyAuthenticationStateProvider>();

I also tried the approach suggested by user enet. Sadly with no success, the result was the same. During the login a call to NotifyAuthenticationStateChanged and hence to the event with no subscribers is done.

The WCF service we use in the background requires a logged in user. Hence i made a guest user with limited rights to solve this issue. So the app steps into the GetAuthenticationStateAsync and tries to fire the AuthenticationStateEvent directly after startup (during a loading screen).

EDIT 2

So I now tried some additional setup steps, from which Microsoft wrote in the Blazor documentation, that they should not be necessary for server-side blazor:

The ConfigureServices now looks like this

//Authentication & Authorization setup
services.AddOptions();
services.AddAuthenticationCore();
services.AddAuthorizationCore();
services.AddScoped<IPermissionManager, SingleUserPermissionManager>();
services.AddScoped<AuthenticationStateProvider, MyAuthenticationStateProvider>();
services.AddScoped<ServerAuthenticationStateProvider, MyAuthenticationStateProvider>();
services.AddScoped<IAuthenticationService, MyAuthenticationStateProvider>();

In the Configure(IApplicationBuilder app, IWebHostEnvironment env) Method, I added the following calls:

app.UseAuthentication();
app.UseAuthorization();

This did also have no effect.

1 Answer 1

1

I think the AuthenticationState object is not available because the AuthenticationStateChanged event is not invoked from the AuthenticationStateProvider, and thus your AuthorizeView and your CascadingAuthenticationState components are not aware of the state change. Check your logic once more in this direction. Also make sure that you properly add the subclassed provider to the DI container. I tend to believe that the issue is with this. Please, show all the relevant code from the ConfigureServices method.

Update: Please, try this:

services.AddScoped<MyAuthenticationStateProvider>();
services.AddScoped<AuthenticationStateProvider>(provider => 
  provider.GetRequiredService<MyAuthenticationStateProvider>());

Hope this helps...

Sign up to request clarification or add additional context in comments.

2 Comments

Hello, thanks for your reply. The only thing I do regarding the Authorization & Authentication in the ConfigureServices method, is one call to services.AddScoped<AuthenticationStateProvider, MyAuthenticationStateProvider>(). From what I understood from the Microsoft documentation, this should be enough?
Thanks for the reply. I updated the question. Somehow, my AuthenticationStateChangedevent is null

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.