Reputation: 11
I have a Blazor server project that has auth set up through Entra Id. I can get user info including their roles in the components, but I also need to identify who the current user is in a class.
The main reason I need the user info is that when an error happens in a class (say database becomes unavailable while retrieving some data), I would catch the error, amend all the metadata I can (including who the user was when this happened) and then log it to a file, in order to make troubleshooting easier.
I used to do this easily in ASP.NET forms but I am new to .NET Core and Blazor and there are not much documentation when it comes to .NET 8 and Entra Id.
For now the only work around I have found is that in the components, when I need to create an instance of an object or run a static method, I pass on the current logged in user info to the class which then can be used while logging error. I would prefer to have a class that gets this done for me so that I don't need to run the same code in every component.
Any help would be appreciated.
Here is my code - component:
@code
{
string loggedinUser = "";
private void Log()
{
loggedinUser = GetClaimsPrincipalData().Result;
Building.Test(loggedinUser);
}
private async Task<string?> GetClaimsPrincipalData()
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
string username = "";
if (user.Identity is not null && user.Identity.IsAuthenticated)
{
username = user.FindFirst(c => c.Type == ClaimTypes.Email)?.Value;
}
else
{
Logger.Log("MessageType", "Error Number", "SourceFileName", "Unknown User", "Message");
}
return username;
}
}
And this is the class:
using Microsoft.Extensions.Logging;
using System.Data.SqlClient;
using System.Data;
namespace MyApp.Models
{
public class Building
{
public static void Test(string UserName)
{
Logger.Log("MessageType", "Error Number", "SourceFileName", UserName, "Message");
}
}
}
Update: As per @qiang-fu 's comment, if I have the following class, how can I get the username from other classes?
public class AuthTest
{
private readonly AuthenticationStateProvider _authenticationStateProvider;
public AuthTest(AuthenticationStateProvider authenticationStateProvider)
{
this._authenticationStateProvider = authenticationStateProvider;
}
public async Task<string> Username()
{
var authState = await _authenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
string username = "Unknown";
if (user.Identity is not null && user.Identity.IsAuthenticated)
{
username = user.FindFirst(c => c.Type == ClaimTypes.Email)?.Value;
}
return username;
}
public AuthTest()
{
}
}
Thanks
Upvotes: 1
Views: 1406
Reputation: 8631
You cannot use dependency injection for static class/method. So just make a normal class like below with inject "AuthenticationStateProvider".
public class Building
{
private readonly AuthenticationStateProvider _authenticationStateProvider;
public Building(AuthenticationStateProvider authenticationStateProvider)
{
this._authenticationStateProvider = authenticationStateProvider;
}
private async Task Log()
{
var authState = await _authenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
string username = "";
if (user.Identity is not null && user.Identity.IsAuthenticated)
{
username = user.FindFirst(c => c.Type == ClaimTypes.Email)?.Value;
Logger.Log("MessageType", "Error Number", "SourceFileName", username, "Message");
}
else
{
Logger.Log("MessageType", "Error Number", "SourceFileName", "Unknown User", "Message");
}
}
}
Then register it in the program.cs
builder.Services.AddScoped<Building>();
Then you can use it in component by inject them
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject Building Building
@code{
private void Log()
{
Building.Log(AuthenticationStateProvider);
}
}
Upvotes: 1