156

I want to get the current user, so I can access fields like their email address. But I can't do that in asp.net core. This is my code:

HttpContext almost is null in constructor of controller. It's not good to get a user in each action. I want to get the user's information once and save it to ViewData;

public DashboardController()
{
    var user = HttpContext.User.GetUserId();
}
Jack
  • 460
  • 3
  • 14
Mehran Hafizi
  • 1,890
  • 2
  • 9
  • 14

14 Answers14

206
User.FindFirst(ClaimTypes.NameIdentifier).Value

EDIT for constructor

Below code works:

public Controller(IHttpContextAccessor httpContextAccessor)
{
    var userId = httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value 
}

Edit for RTM

You should register IHttpContextAccessor:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpContextAccessor();
    }
Wiebe Tijsma
  • 9,907
  • 4
  • 51
  • 65
adem caglin
  • 17,749
  • 8
  • 44
  • 67
68

Simple way that works and I checked.

private readonly UserManager<IdentityUser> _userManager;
public CompetitionsController(UserManager<IdentityUser> userManager)
{
    _userManager = userManager;
}

var user = await _userManager.GetUserAsync(HttpContext.User);

then you can all the properties of this variables like user.Email. I hope this would help someone.

Edit:

It's an apparently simple thing but bit complicated cause of different types of authentication systems in ASP.NET Core. I update cause some people are getting null.

For JWT Authentication (Tested on ASP.NET Core v3.0.0-preview7):

var email = HttpContext.User.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;

var user = await _userManager.FindByEmailAsync(email);
Ahmad
  • 2,266
  • 3
  • 21
  • 27
31

I have to say I was quite surprised that HttpContext is null inside the constructor. I'm sure it's for performance reasons. Have confirmed that using IPrincipal as described below does get it injected into the constructor. Its essentially doing the same as the accepted answer, but in a more interfacey-way.


For anyone finding this question looking for an answer to the generic "How to get current user?" you can just access User directly from Controller.User. But you can only do this inside action methods (I assume because controllers don't only run with HttpContexts and for performance reasons).

However - if you need it in the constructor (as OP did) or need to create other injectable objects that need the current user then the below is a better approach:

Inject IPrincipal to get user

First meet IPrincipal and IIdentity

public interface IPrincipal
{
    IIdentity Identity { get; }
    bool IsInRole(string role);
}

public interface IIdentity
{
    string AuthenticationType { get; }
    bool IsAuthenticated { get; }
    string Name { get; }
}

IPrincipal and IIdentity represents the user and username. Wikipedia will comfort you if 'Principal' sounds odd.

Important to realize that whether you get it from IHttpContextAccessor.HttpContext.User, ControllerBase.User or ControllerBase.HttpContext.User you're getting an object that is guaranteed to be a ClaimsPrincipal object which implements IPrincipal.

There's no other type of User that ASP.NET uses for User right now, (but that's not to say other something else couldn't implement IPrincipal).

So if you have something which has a dependency of 'the current user name' that you want injected you should be injecting IPrincipal and definitely not IHttpContextAccessor.

Important: Don't waste time injecting IPrincipal directly to your controller, or action method - it's pointless since User is available to you there already.

In startup.cs:

   // Inject IPrincipal
   services.AddHttpContextAccessor();
   services.AddTransient<IPrincipal>(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);

Then in your DI object that needs the user you just inject IPrincipal to get the current user.

The most important thing here is if you're doing unit tests you don't need to send in an HttpContext, but only need to mock something that represents IPrincipal which can just be ClaimsPrincipal.

One extra important thing that I'm not 100% sure about. If you need to access the actual claims from ClaimsPrincipal you need to cast IPrincipal to ClaimsPrincipal. This is fine since we know 100% that at runtime it's of that type (since that's what HttpContext.User is). I actually like to just do this in the constructor since I already know for sure any IPrincipal will be a ClaimsPrincipal.

If you're doing mocking, just create a ClaimsPrincipal directly and pass it to whatever takes IPrincipal.

Exactly why there is no interface for IClaimsPrincipal I'm not sure. I assume MS decided that ClaimsPrincipal was just a specialized 'collection' that didn't warrant an interface.

Sean Worle
  • 767
  • 1
  • 6
  • 19
Simon_Weaver
  • 120,240
  • 73
  • 577
  • 618
  • 2
    This allows you to inject the current user anywhere in your application, great answer! – Machado Mar 29 '19 at 21:13
  • 2
    This doesn't work. I always get `null` for the injected `IPrincipal`. I also needed to add the transient service as `…GetService()?.HttpContext.User…` (with the `?`) because it would crash otherwise (GetService returned null). – ygoe May 03 '19 at 10:25
  • You can just do `services.AddTransient(provider => provider.GetService().HttpContext.User);` as HttpContext.User is a ClaimsPrincipal. – Jay Zelos Jun 16 '20 at 22:35
25

Have another way of getting current user in Asp.NET Core - and I think I saw it somewhere here, on SO ^^

// Stores UserManager
private readonly UserManager<ApplicationUser> _manager; 

// Inject UserManager using dependency injection.
// Works only if you choose "Individual user accounts" during project creation.
public DemoController(UserManager<ApplicationUser> manager)  
{  
    _manager = manager;  
}

// You can also just take part after return and use it in async methods.
private async Task<ApplicationUser> GetCurrentUser()  
{  
    return await _manager.GetUserAsync(HttpContext.User);  
}  

// Generic demo method.
public async Task DemoMethod()  
{  
    var user = await GetCurrentUser(); 
    string userEmail = user.Email; // Here you gets user email 
    string userId = user.Id;
}  

That code goes to controller named DemoController. Won't work without both await (won't compile) ;)

Jay Jay Jay
  • 1,800
  • 1
  • 23
  • 42
Hekkaryk
  • 472
  • 5
  • 13
20

It would appear that as of now (April of 2017) that the following works:

public string LoggedInUser => User.Identity.Name;

At least while within a Controller

fuglede
  • 14,583
  • 2
  • 38
  • 72
Grandizer
  • 2,319
  • 3
  • 37
  • 68
  • 4
    You cannot implicitly convert type 'System.Security.Principal.IIdentity' to 'string'. – Anthony Huang Mar 15 '18 at 19:21
  • 3
    string LoggedInUser = User.Identity.Name; – Alic W Sep 01 '18 at 03:27
  • 6
    As someone who hadn't seen the `=>` operator used like this before, it is called an "Expression Body Definition" and is described in [this documentation](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/lambda-operator). Just in case future people like me are wondering. – Nathan Clement Oct 22 '18 at 16:46
  • Your code couldn't possibly compile prior to the edit, given that there's no conversion from `IIdentity` to `string`, as also stated in the top comment. The edit simply fixed that. I'm not sure how you reached your conclusion either (in particular since "editor" points are given only to users below 2k reputation). – fuglede Jan 27 '20 at 16:28
13

Perhaps I didn't see the answer, but this is how I do it.

  1. .Net Core --> Properties --> launchSettings.json

You need to have change these values

"windowsAuthentication": true, // needs to be true
"anonymousAuthentication": false,  // needs to be false 

Startup.cs --> ConfigureServices(...)

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

MVC or Web Api Controller

private readonly IHttpContextAccessor _httpContextAccessor;
//constructor then
_httpContextAccessor = httpContextAccessor;

Controller method:

string userName = _httpContextAccessor.HttpContext.User.Identity.Name;

Result is userName e.g. = Domain\username

Tom Stickel
  • 16,699
  • 6
  • 102
  • 108
4

My problem was to access the logged in User as an object in the cshtml file. Considering you wanted the user in ViewData, this approach might be helpful:

In the cshtml file

@using Microsoft.AspNetCore.Identity
@inject UserManager<ApplicationUser> UserManager

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>
    @UserManager.FindByNameAsync(UserManager.GetUserName(User)).Result.Email
    </title>
  </head>
  <body>

  </body>
</html>
MikeMajara
  • 661
  • 6
  • 16
  • Any idea how you might get a navigation property to load (Company name of a company navigation property on my ApplicationUser class). Didn't see a way to include navigation properties. – Hunter Nelson Oct 02 '18 at 05:39
3

In addition to existing answers I'd like to add that you can also have a class instance available app-wide which holds user-related data like UserID etc.

It may be useful for refactoring e.g. you don't want to fetch UserID in every controller action and declare an extra UserID parameter in every method related to Service Layer.

I've done a research and here's my post.

You just extend your class which you derive from DbContext by adding UserId property (or implement a custom Session class which has this property).

At filter level you can fetch your class instance and set UserId value.

After that wherever you inject your instance - it will have the necessary data (lifetime must be per request, so you register it using AddScoped method).

Working example:

public class AppInitializationFilter : IAsyncActionFilter
{
    private DBContextWithUserAuditing _dbContext;

    public AppInitializationFilter(
        DBContextWithUserAuditing dbContext
        )
    {
        _dbContext = dbContext;
    }

    public async Task OnActionExecutionAsync(
        ActionExecutingContext context,
        ActionExecutionDelegate next
        )
    {
        string userId = null;
        int? tenantId = null;

        var claimsIdentity = (ClaimsIdentity)context.HttpContext.User.Identity;

        var userIdClaim = claimsIdentity.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
        if (userIdClaim != null)
        {
            userId = userIdClaim.Value;
        }

        var tenantIdClaim = claimsIdentity.Claims.SingleOrDefault(c => c.Type == CustomClaims.TenantId);
        if (tenantIdClaim != null)
        {
            tenantId = !string.IsNullOrEmpty(tenantIdClaim.Value) ? int.Parse(tenantIdClaim.Value) : (int?)null;
        }

        _dbContext.UserId = userId;
        _dbContext.TenantId = tenantId;

        var resultContext = await next();
    }
}

For more information see my answer.

Alex Herman
  • 2,115
  • 1
  • 19
  • 42
1

This is old question but my case shows that my case wasn't discussed here.

I like the most the answer of Simon_Weaver (https://stackoverflow.com/a/54411397/2903893). He explains in details how to get user name using IPrincipal and IIdentity. This answer is absolutely correct and I recommend to use this approach. However, during debugging I encountered with the problem when ASP.NET can NOT populate service principle properly. (or in other words, IPrincipal.Identity.Name is null)

It's obvious that to get user name MVC framework should take it from somewhere. In the .NET world, ASP.NET or ASP.NET Core is using Open ID Connect middleware. In the simple scenario web apps authenticate a user in a web browser. In this scenario, the web application directs the user’s browser to sign them in to Azure AD. Azure AD returns a sign-in response through the user’s browser, which contains claims about the user in a security token. To make it work in the code for your application, you'll need to provide the authority to which you web app delegates sign-in. When you deploy your web app to Azure Service the common scenario to meet this requirements is to configure web app: "App Services" -> YourApp -> "Authentication / Authorization" blade -> "App Service Authenticatio" = "On" and so on (https://github.com/Huachao/azure-content/blob/master/articles/app-service-api/app-service-api-authentication.md). I beliebe (this is my educated guess) that under the hood of this process the wizard adjusts "parent" web config of this web app by adding the same settings that I show in following paragraphs. Basically, the issue why this approach does NOT work in ASP.NET Core is because "parent" machine config is ignored by webconfig. (this is not 100% sure, I just give the best explanation that I have). So, to meke it work you need to setup this manually in your app.

Here is an article that explains how to manyally setup your app to use Azure AD. https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/aspnetcore2-2

Step 1: Register the sample with your Azure AD tenant. (it's obvious, don't want to spend my time of explanations).

Step 2: In the appsettings.json file: replace the ClientID value with the Application ID from the application you registered in Application Registration portal on Step 1. replace the TenantId value with common

Step 3: Open the Startup.cs file and in the ConfigureServices method, after the line containing .AddAzureAD insert the following code, which enables your application to sign in users with the Azure AD v2.0 endpoint, that is both Work and School and Microsoft Personal accounts.

services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
    options.Authority = options.Authority + "/v2.0/";
    options.TokenValidationParameters.ValidateIssuer = false;
});

Summary: I've showed one more possible issue that could leed to an error that topic starter is explained. The reason of this issue is missing configurations for Azure AD (Open ID middleware). In order to solve this issue I propose manually setup "Authentication / Authorization". The short overview of how to setup this is added.

1

I know there area lot of correct answers here, with respect to all of them I introduce this hack :

In StartUp.cs

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

and then everywhere you need HttpContext you can use :

httpContext = new HttpContextAccessor().HttpContext;

Hope it helps ;)

Ali Mahmoodi
  • 423
  • 5
  • 7
0

Taking IdentityUser would also work. This is a current user object and all values of user can be retrieved.

private readonly UserManager<IdentityUser> _userManager;
public yourController(UserManager<IdentityUser> userManager)
{
    _userManager = userManager;
}

var user = await _userManager.GetUserAsync(HttpContext.User);
zzdhxu
  • 197
  • 2
  • 20
0

If you are using the scafolded Identity and using Asp.net Core 2.2+ you can access the current user from a view like this:

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

 @if (SignInManager.IsSignedIn(User))
    {
        <p>Hello @User.Identity.Name!</p>
    }
    else
    {
        <p>You're not signed in!</p>
    }

https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-2.2&tabs=visual-studio

Andrew
  • 13,934
  • 8
  • 78
  • 93
0

Most of the answers show how to best handle HttpContext from the documentation, which is also what I went with.

I did want to mention that you'll want to check you project settings when debugging, the default is Enable Anonymous Authentication = true.

m02ph3u5
  • 2,735
  • 6
  • 34
  • 45
-1

I got my solution

var claim = HttpContext.User.CurrentUserID();

public static class XYZ
{
    public static int CurrentUserID(this ClaimsPrincipal claim)
    {
        var userID = claimsPrincipal.Claims.ToList().Find(r => r.Type == 
         "UserID").Value;
        return Convert.ToInt32(userID);
    }
    public static string CurrentUserRole(this ClaimsPrincipal claim)
    {
        var role = claimsPrincipal.Claims.ToList().Find(r => r.Type == 
        "Role").Value;
        return role;
    }
}
  • 1
    While this code may answer the question, providing additional context regarding **how** and **why** it solves the problem would improve the answer's long-term value. – Alexander May 10 '18 at 17:04