Get Current User in a Blazor component

42,941

Solution 1

There are three possibilities for getting the user in a component (a page is a component):

  1. Inject IHttpContextAccessor and from it access HttpContext and then User; need to register IHttpContextAccessor in Startup.ConfigureServices, normally using AddHttpContextAccessor. Edit: according to the Microsoft docs you must not do this for security reasons.
  2. Inject an AuthenticationStateProvider property, call GetAuthenticationStateAsync and get a User from it
  3. Wrap your component in a <CascadingAuthenticationState> component, declare a Task<AuthenticationState> property and call it to get the User (similar to #2)

See more here: https://docs.microsoft.com/en-us/aspnet/core/security/blazor.

Solution 2

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();
}

Solution 3

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>());

Solution 4

For blazor wasm in net 5.0 and above. Here is how I did,

  1. Wrap your <App> component inside <CascadingAuthenticationState> as shown below,
<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly">
        <Found Context="routeData">
            ...
        </Found>
        <NotFound>
            ...
        </NotFound>
    </Router>
</CascadingAuthenticationState>
  1. Then add Task<AuthenticationState> CascadingParameter inside any component as shown below,
public class AppRootBase : ComponentBase
{
    [CascadingParameter] private Task<AuthenticationState> authenticationStateTask { get; set; }
}
  1. Now you can access logged in user Identity and Claims 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.

Solution 5

I've had a similar requirement and have been using:

var authstate = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authstate.User;
var name = user.Identity.Name;

I already had an AuthenticationStateProvider in my startup.cs and added it to the constructor of my custom class.

Share:
42,941
Wes H
Author by

Wes H

Updated on February 18, 2022

Comments

  • Wes H
    Wes H about 2 years

    I'm starting a new site with Blazor and Windows Authentication and need to identify the current user viewing the page/component.

    For a Razor Page, the current user name can be accessed with Context.User.Identity.Name, but that doesn't seem to work in a Blazor component. I've tried injecting HttpContext into the component but the Context is null at runtime.

    As a bonus, I will eventually want to incorporate this into Startup.cs so I only need to get the username once and can leverage a corporate user class (with EF Core) for my applications. Answers tailored to that use case would also be appreciated.

  • enet
    enet about 4 years
    HttpContext shouldn't be used in Blazor app. HttpContext is HTTP-based object. Blazor Server App (client part and server part ) communicate over WebSocket connection, which lacks the concept of HTTP. Using Windows Authentication makes things worse. I don't mind that you've chosen to ignore my comment above, but users of this post must be warned, abd the sooner, the better.
  • enet
    enet about 4 years
    "Inject IHttpContextAccessor and from it access HttpContext and then User" . This is not true. What does a WebSocket connection have to do with HTTP requests
  • Ricardo Peres
    Ricardo Peres about 4 years
    You don’t know what you are talking about. This has nothing to do with Windows Authentication. Blazor is served through HTTP, and the model of it that we are talking about is Server, of course. More, SignalR uses WebSockets among other protocols, like long polling, even though WebSockets is probably the typical one
  • MichaelB
    MichaelB almost 4 years
    This approach worked in development on IIS Express, but resulted in nulls when deployed. AuthenticationStateProvider with GetAuthenticationStateAsync worked fine. See Docs
  • The Muffin Man
    The Muffin Man almost 4 years
    This doesn't work when deployed. The docs even say not to do it.
  • bitshift
    bitshift almost 4 years
    This seems right, but I am using a base class for my component. Wont I need the directive injection in the base class as well? I am trying to do just that and calling the method on the userservice in the base constructor, but the useservice is null
  • bitshift
    bitshift almost 4 years
    disregard, cant use it in the base class constructor. its not available at that point.
  • aryeh
    aryeh over 3 years
    According to the docs here UserManager and SigninManager aren't supported in Razor components.
  • Luk164
    Luk164 over 3 years
    @aryeh Well they did work, no problems with compilation, no warnings either
  • aryeh
    aryeh over 3 years
    It also worked for me, I'm trying to find out what it means 'not supported' since it does work.
  • FrankCastle616
    FrankCastle616 over 3 years
    Option 2 worked for me and was easily used to show user name in page. @authStateProvider.GetAuthenticationStateAsync().Result.User‌​.Identity.Name
  • Big Daddy
    Big Daddy about 3 years
    I tried this OOB and I don't get the name. I'm authenticating using ADB2C. Any Startup.cs setup needed?
  • Balagurunathan Marimuthu
    Balagurunathan Marimuthu about 3 years
    @MichaelB I am also having a same problem. Could you suggest a way to get claims using AuthenticationStateProvider as common function for all my components? In that way, we can avoid fetch claims on each component...
  • Balagurunathan Marimuthu
    Balagurunathan Marimuthu about 3 years
    @Wes H It doesn't work when deployed to production server environment. Can you please give alternate solution to achieve the same...?
  • JC Castro
    JC Castro about 3 years
    Option 1 worked for me while on development but once published in IIS did not work. ?? So I ended using option 2
  • István Piroska
    István Piroska almost 3 years
    1, IHttpContextAccessor -> no, no no, never! :) Without going into a "professional" explanation, practically it gives back the server's HttpContext which is shared between users!! Practically it leads some issues I run into right now: a, user infos, roles are mixed up, b, by changing a theme will be applied to every other users!! So what I learnt today: Never user IHttpContextAccessor and never do any Singleton/Static stuff! Check very carefully the injected services!! Some services registered as services.AddXYZ will register as Singleton automatically! Option 2 and 3 are fine, good advice.
  • Beltway
    Beltway over 2 years
    I think the additions to Startup should not be necessary in .NET 5.0.
  • enet
    enet over 2 years
    This answer, provided by the asker, is wrong. I've commented on the question about it, before any answered had been provided. But folks here, including the asker, opt to ignore it. Please, read the comment by Balagurunathan Marimuthu above...
  • spacemonki
    spacemonki over 2 years
    You can also leave out var name = user.Identity.Name; and just have var user = authstate.User.Identity.Name;
  • Philippe
    Philippe over 2 years
    This is not working for me with Blazor WASM + .Net 5.0. When I login, authState.User.Identity.Name is alway null. But when I refresh, .Name is OK.
  • Philippe
    Philippe over 2 years
    To make it work, I had to change OnInitializedAsync() to OnParametersSetAsync() since user.Identity.Name was always null after sucessfully login.
  • Macadameane
    Macadameane over 2 years
    @IstvánPiroska - that doesn't seem right. As per the documentation, the HttpContext "Encapsulates all HTTP-specific information about an individual HTTP request." This includes the user of that request if Identity is in place. If I'm incorrect, I'd like to know. Can you point to a "professional" explanation? I have always used HttpContext in the past to retrieve the current principal claim.
  • Mike
    Mike over 2 years
    In addition, this seems to only work from the "main" page thread. Any asynchronous actions, including await, make httpContextAccessor.HttpContext be null.
  • Guy Lowe
    Guy Lowe over 2 years
    As stated in the answer above do not do this. See docs.microsoft.com/en-us/aspnet/core/fundamentals/… for more info.