5

In my server side Blazor app, the authentication is being handled in a very unconventional way. Essentially, when a user visits the page they are authenticated using their windows credentials. At that point, a custom policy is created to look up that username in an external database (Informix) in which the authorization for that user in the application is found. Specifically for each level of authorization in the app (can update, go to this page, etc.), I am creating a new claim to add to the current user based on permissions stored in the database.

Normally in the view, I just use the AuthenticationStateProvider to get this information and it works with no issues. However, when I need to access the user information in my service classes that are handling the updates/business logic I can't seem to access the claims/User at all. For example, one use case is getting the username stored in the database for the current Windows account based on a claim added during the initial authentication to log their activities/track. Another would be grabbing the current logged in users full name from the database.

I have tried DI in the service classes w/ the authentication state providers and HTTPContext but neither of them work. I know the overall structure isn't ideal but it's what I have to work with. Any insights in how to go about this would be much appreciated!

pyRabbit
  • 803
  • 1
  • 9
  • 33
Dan
  • 746
  • 1
  • 9
  • 14
  • You can define a service class with a constructor that has a parameter of type AuthenticationStateProvider, and add this service to the DI container. I don't understand what is the issue here. – enet Jan 15 '20 at 07:16
  • @enet thanks for pointing me in the right direction...I was able to do so but I had to change my service instances to be scoped instead of singletons... details in my below answer. – Dan Jan 15 '20 at 14:49

1 Answers1

6

The authentication state provider I was injecting was not working because my service class was a singleton whereas the authentication state provider is scoped. In short, you cannot use a scoped class in a singleton (details found here: Cannot consume scoped service IMongoDbContext from singleton IActiveUsersService after upgrade to ASP.NET Core 2.0)

I changed my service classes that were depending on the authentication state provider to be scoped in the startup and it worked without issues.

TLDR; inject the AuthenticationStateProvider into your service class and make that class and any that depend on it Scoped in startup.

services.AddScoped<ServiceClass>();

Then your service class would look like the following:

private readonly AuthenticationStateProvider _authenticationStateProvider;

public ServiceClass(AuthenticationStateProvider authenticationStateProvider)
{
     _authenticationStateProvider = authenticationStateProvider;
}

//Use authenticationStateProvider same as you do in view in class

Any classes that depend on this service class also need to be scoped instead of singletons.

titol
  • 999
  • 13
  • 25
Dan
  • 746
  • 1
  • 9
  • 14
  • 1
    This solution is helpful to others. I already use this technique. However there seems to be a limitation when using this in OnSetParameters Blazor method. There always is the exception "GetAuthenticationStateAsync was called before SetAuthenticationState" – Sven Apr 27 '20 at 12:25
  • 3
    I tried this, but I can't get `AuthenticationStateProvider` to be recognized in my new service class. Because I'm using SignalR-Azure I can't use `HttpContextAccessor` but I still need to user name so I can set my `LastEditedBy` property in my `Context.SaveChanges()` override. – Paul Meems Jun 17 '20 at 15:03