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.
Access to browser navigation from Blazor is provided via the NavigationManager service. This can be injected into a Blazor component using @inject in a razor file, or the [Inject] attribute in a CS file. The NavigationManager service has two members that are of particular interest; NavigateTo and LocationChanged .
If one or more common services are required by the Server and Client projects of a hosted Blazor WebAssembly solution, you can place the common service registrations in a method in the Client project and call the method to register the services in both projects. var builder = WebApplication. CreateBuilder(args); ...
There is an option available to enable authentication for the Blazor app when you create the application. To enable authentication for Blazor server app, click on “Change” under “Authentication section and select “Individual User Accounts” option and then click on “Ok” button when you create a new Blazor server app.
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
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!
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;
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With