Peter
Peter

Reputation: 113

How to inject service (AuthenticationStateProvider) in Blazor class

I'm struggling to inject a service (AuthenticationStateProvider) in a class in Blazor server. If I do it in a razor component, it is pretty simple:

@inject AuthenticationStateProvider AuthenticationStateProvider

and then

private async Task LogUsername()
{
    var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
    var user = authState.User;

    if (user.Identity.IsAuthenticated)
    {
       ClientMachineName = $"{user.Identity.Name}";
    }
    else
    {
       ClientMachineName = "Unknown";
    }
} 

However I need to do this, i.e. retrieve the authenticated user machine name, in a class instead of a razor component.

I tried for instance:

[Inject]
AuthenticationStateProvider AuthenticationStateProvider { get; set; }

public async Task LogUsername()
{        
    var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
    var user = authState.User;

    if (user.Identity.IsAuthenticated)
    {
        ClientMachineName = $"{user.Identity.Name}";
    }
    else
    {
        ClientMachineName = "Unknown";
    }
}

But this does not seem to work.

Any help would be much appreciated.

Upvotes: 9

Views: 30516

Answers (4)

Stephen Byrne
Stephen Byrne

Reputation: 7485

with Blazor server (.Net Core 3), this worked for me:

public class AuthTest
{
    private readonly AuthenticationStateProvider _authenticationStateProvider;

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

    public async Task<IIdentity> GetIdentity()
    {
        var authState = await _authenticationStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;
        return user.Identity;
    }
}

You need to register this with the ASP.Net Core DI in Startup.ConfigureServices:

services.AddScoped<AuthTest>();

And then inject it on your .razor page:

@page "/AuthTest"
@inject AuthTest authTest;
<button @onclick="@LogUsername">Write user info to console</button>

@code{
    private async Task LogUsername()
    {
        var identity= await authTest.IsAuthenticated();
        Console.WriteLine(identity.Name);
    }

You should see the logged-in username written to the ASP.Net output console.

Update If you want to get the currently logged in user from within a separate class and you're not injecting that onto a blazor page, then follow the guidance here

Upvotes: 11

Peter
Peter

Reputation: 113

Thanks again both @StephenByrne and @Dan - I'm almost there now with my requirements. This is my user service class and it works as expected:

public class AuthUser
{

    private readonly AuthenticationStateProvider _authenticationStateProvider;

    public AuthUser(AuthenticationStateProvider authenticationStateProvider)
    {
        _authenticationStateProvider = authenticationStateProvider;
        var username = _authenticationStateProvider.GetAuthenticationStateAsync().Result;
        FetchMyUser(username.User.Identity.Name);
    }

    public User MyUser { get; set; }

    public void FetchMyUser(string machineName = "Unknown")
    {
        using (IDbConnection connection = new System.Data.SqlClient.SqlConnection(SettingsService.DBConnectionString2016))
        {
            MyUser = connection.QueryFirstOrDefault<User>($"SELECT FirstName FROM MyTable WHERE MachineName = '{machineName}' ;");
        }
    }
}

And then in Startup.cs I add this service as Scoped (this is important, as Dan pointed out below);

services.AddScoped<AuthUser>();

I can then use this service from a .razor component as follows:

@inject AuthUser authUser

Hello @authUser.MyUser.FirstName

The only remaining issue I have is that I don't know how to consume this service in another .cs class. I believe I should not simply create an object of that class (to which I would need to pass the authenticationStateProvider parameter) - that doesn't make much sense. Any idea how I could achive the same as I mentioned in the .razor file but in a .cs class instead ?

Thanks!

Upvotes: 2

Dan
Dan

Reputation: 796

Check out the solution I had to this problem here:

Accessinging an authenticated user outside of a view in Blazor

This should solve your problem.

Edit: If you would like to get the information about the authentication state, what you should do is create a claim on the authentication state with the username or whatever detail you require in it, instead of creating a class and assigning the name to that. That way, in classes that need this information you can just inject a service class that gets all of the claims on the current authentication state. This really should all be done in a custom authentication state provider.

Example:

public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
    MyUser = //DB call to get user information
    var claimsIdentity = new ClaimsIdentity(new[] { new 
    Claim(ClaimTypes.Name, MyUser.Name) }, "Authenticated");
    var user = new ClaimsPrincipal(identity);
    return new AuthenticationState(user);
}

Then in another service you would get the claims with the user information in it and inject that into any other service/class the information is needed.

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

public async Task<string> GetLogin()
{
    var authState = await _authenticationStateProvider.GetAuthenticationStateAsync();
    return authState.User.Claims.Where(c => c.Type == ClaimTypes.Name).FirstOrDefault().Value;
}

Upvotes: 2

Raymond A.
Raymond A.

Reputation: 637

if you in your startup.cs add some services

services.AddScoped<TokenProvider>();
services.AddTransient<TokenRefreshService>();
services.Add<GraphServiceService>();

you can in a razor page inject them by their type

@inject TokenProvider _token
@inject TokenRefreshService _tokenrefresh
@inject GraphServiceService _graphservice

These service classes, you inject them in throught the constructor

public GraphServiceClass(AuthenticationStateProvider _AuthenticationStateProvider, TokenProvider _token)
{
    AuthenticationStateProvider = _AuthenticationStateProvider;
    token = _token;
}

I recommend this: ASP.NET Core Blazor dependency injection

Upvotes: 1

Related Questions