Get Current User in a Blazor component
There are three possibilities for getting the user in a component (a page is a component):
- Inject
IHttpContextAccessor
and from it accessHttpContext
and thenUser
; need to registerIHttpContextAccessor
inStartup.ConfigureServices
, normally usingAddHttpContextAccessor
. Edit: according to the Microsoft docs you must not do this for security reasons. - Inject an
AuthenticationStateProvider
property, callGetAuthenticationStateAsync
and get aUser
from it - Wrap your component in a
<CascadingAuthenticationState>
component, declare aTask<AuthenticationState>
property and call it to get theUser
(similar to #2)
See more here: https://docs.microsoft.com/en-us/aspnet/core/security/blazor.
For blazor wasm
in net 5.0
and above. Here is how I did,
- Wrap your
<App>
component inside<CascadingAuthenticationState>
as shown below,
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
...
</Found>
<NotFound>
...
</NotFound>
</Router>
</CascadingAuthenticationState>
- Then add
Task<AuthenticationState>
CascadingParameter
inside any component as shown below,
public class AppRootBase : ComponentBase
{
[CascadingParameter] private Task<AuthenticationState> authenticationStateTask { get; set; }
}
- Now you can access logged in user
Identity
andClaims
inside component as shown below,
protected override async Task OnInitializedAsync()
{
var authState = await authenticationStateTask;
var user = authState.User;
if (user.Identity.IsAuthenticated)
{
Console.WriteLine($"{user.Identity.Name} is authenticated.");
}
}
Here is the reference from Microsoft docs.
I've now been able to get it to work with a general class, as well as a component.
To get access to the HttpContext
User; in ConfigureServices
, in Startup.cs
add
services.AddHttpContextAccessor();
I have a CorporateUserService
class for my CorporateUser
class. The service class gets a DbContext
through constructor injection.
I then created a new CurrentCorporateUserService
that inherits from the CorporateUserService
. It accepts a DbContext
and an IHttpContextAccessor
through constructor injection
public class CurrentCorporateUserService : CorporateUserService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public CurrentCorporateUserService(IHttpContextAccessor httpContextAccessor,
MyDbContext context) : base(context)
{
_httpContextAccessor = httpContextAccessor;
}
...
The base service class has a method GetUserByUsername(string username)
. The Current service class adds an additional method
public CorporateUser GetCurrentUser()
{
return base.GetUserByUsername(_httpContextAccessor.HttpContext.User.Identity.Name.Substring(8));
}
The Current service class is registered in Startup.cs
services.AddScoped<CurrentCorporateUserService>();
Once that is done, I can use the CurrentCorporateUserService
in a component with directive injection.
[Inject]
private CurrentCorporateUserService CurrentCorporateUserService { get; set; } =
default!;
or in any class, with constructor injection.
public MyDbContext(DbContextOptions<MyDbContext> options,
CurrentCorporateUserService CurrentCorporateUserService)
: base(options)
{
_currentUser = CurrentCorporateUserService.GetCurrentUser();
}
Making it a project wide service means all my developers do not have to concern themselves with how to get the Current User, they just need to inject the service into their class.
For example, using it in MyDbContext
makes the current user available to every save event. In the code below, any class that inherits the BaseReport
class will automatically have the report metadata updated when the record is saved.
public override Int32 SaveChanges()
{
var entries = ChangeTracker.Entries().Where(e => e.Entity is BaseReport
&& (e.State == EntityState.Added || e.State == EntityState.Modified));
foreach (var entityEntry in entries)
{
((BaseReport)entityEntry.Entity).ModifiedDate = DateTime.Now;
((BaseReport)entityEntry.Entity).ModifiedByUser = _currentUser.Username;
}
return base.SaveChanges();
}
For me the solution mentioned in the first answer 2. option worked perfect: I am using Blazor server side on .Net Core 5.0 . I injected
@inject AuthenticationStateProvider GetAuthenticationStateAsync
in my Blazor page and added the following in the code section:
protected async override Task OnInitializedAsync()
{
var authstate = await GetAuthenticationStateAsync.GetAuthenticationStateAsync();
var user = authstate.User;
var name = user.Identity.Name;
}
In my startup.cs, I have the following lines:
services.AddScoped<ApiAuthenticationStateProvider>();
services.AddScoped<AuthenticationStateProvider>(p =>
p.GetRequiredService<ApiAuthenticationStateProvider>());