User.Identity.Name is empty in Asp.NET Core 2.0 API Controller
Asked Answered
A

4

16

I am new to ASP.NET core itself. However, I am creating WebAPIs in ASP.NET Core 2.0. I have configured JWT Bearer Token based authentication. Below is my Controller which return token.

    [AllowAnonymous]
[Route("api/[controller]")]
public class TokenController : Controller
{
    private readonly UserManager<UserEntity> userManager;
    private readonly SignInManager<UserEntity> signInManager;

    public TokenController(UserManager<UserEntity> userManager, SignInManager<UserEntity> signInManager)
    {
        this.userManager = userManager;
        this.signInManager = signInManager;
    }

    // GET: api/values
    [HttpGet]
    public async Task<IActionResult> Get(string username, string password, string grant_type)
    {
        {
            var user = await userManager.FindByEmailAsync(username);

            if (user != null)
            {
                var result =await signInManager.CheckPasswordSignInAsync(user, password, false);
                if (result.Succeeded)
                {

                    var claims = new[]
                    {
                        new Claim( JwtRegisteredClaimNames.Sub, username),
                        new Claim( JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                        new Claim( JwtRegisteredClaimNames.GivenName, "SomeUserID")
                    };

                    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("secretesecretesecretesecretesecretesecrete"));
                    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

                    var token = new JwtSecurityToken( issuer: "test",
                                                     audience: "test",
                                                     claims: claims,
                                                      expires: DateTime.Now.AddDays(15),
                                                      signingCredentials: creds);

                    return Ok(new { access_token = new JwtSecurityTokenHandler().WriteToken(token), expires_on=DateTime.Now.AddDays(15) });

                }
            }
        }

        return BadRequest("Could not create token");
    }


}

But when calling ValuesController API which is decorated with [Authorize] attributes. I am getting User.Identity.Name is empty. I am not getting any information about user. I am not sure, My token controller is correctly written. As long as it is protecting my ValuesController, I assume, it is correct. However, I might be missing something. Please help.

Note: I am developing using Visual Studio 2017 with Mac Community addition

Almetaalmighty answered 18/10, 2017 at 23:33 Comment(0)
V
33

Yes, you need to specify the claim for the unique name which is translated into the user.identity.name:

new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName)
Vapid answered 19/10, 2017 at 0:28 Comment(6)
I am having another issue after this. userManager.GetUserAsyc(User) is returning null. However User.Identity.Name is returning me right value. What am I missing here?Almetaalmighty
Try GetUserByName or Email insteadVapid
I used FindByNameAsync method in the last. Thanks, I tried above method as User was populated with value. But I guess, it won't work for JWT token.Almetaalmighty
I thought we wouldn't have to especially use user.UserName to reach Identity.Name. But it worked.Ballyrag
I tried, but its not working still getting null and User.Claims also getting empty array.Dish
Where are you adding/doing this?Wishywashy
C
25

I've also been having this problem with ASP.Net Core 2, and I'm really surprised no one's discovered the other cause of this problem.

When my webapp is deployed to IIS, "User.Identity.Name" always returns null. The IIS site has anonymous access disabled, and windows authentication is enabled.

BUT.

I didn't realise that my ASP.Net Core 2 has a "launchSettings.json" file, quietly hidden under the Properties folder, and in there, there's also some iisSettings, and in here "windowsAuthentication" was, strangely, set as false by default.

enter image description here

Changing "windowsAuthentication" to true, and "anonymousAuthentication" to false solved the problem for me.

After doing this, "User.Identity.Name" did finally contain the correct username.

But what the heck is this setting ? Why would this get priority over the actual settings we've setup in IIS Manager ?!

Campeche answered 4/9, 2018 at 7:31 Comment(5)
Why was this voted down ? This is exactly relevant to the question asked, and this answer hasn't been presented anywhere else. Isn't that the point of StackOverflow ?Campeche
Maybe it didn't solve the problem for everybody, like mine is still broken after trying your suggested fix.Lianeliang
Because it didn't solve the problem for everybody doesn't mean it isn't a valid cause. I don't think this deserves the downvotes. I'd disagree with Mike's argument, though—settings in launchSettings.jsonshould always override settings on the IIS server. How else do you get to set general conditions and allow some applications to override them? So, it's a good answer to the original question, but the last paragraph is unnecessary.Norma
@MikeGledhill In my case as well, it worked perfectly. Stackoverflow must ask for a comment before downvotting..Catchfly
That finger is disturbing somehowMcguinness
R
8

Had this problem too (Core 3.1) using the "DefaultIdentity" (Individual User Accounts). User.Identity.Name is null, User.Identity.IsAuthenticated = true. By using httpContextAccessor you can get the userId an with that id you can find the user and the UserName. In your controller add

using System.Security.Claims;
...
private readonly IHttpContextAccessor _httpContextAccessor;
public MyController(MyContext context, IHttpContextAccessor httpContextAccessor)
{
  _context = context;
  _httpContextAccessor = httpContextAccessor;
}

// Any method username needed
[HttpGet("{id}")]
public async Task<ActionResult<MyInfo>> GetMyInfo(int id)
{
  var userId = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
  var user = _context.AspNetUsers.Find(userId);
  var userName = user.UserName;
  ...
}

In the Startup.cs add the following line:

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Renelle answered 13/4, 2020 at 9:51 Comment(2)
Thanks, saved me a alot of time figuring out!Importation
You dont need to use HttpContextAccessor in Controller. You already have HttpContext.User in controller.Discalced
G
0

For Azure OAuth v2, use preferred_username instead of unique_name (see this and this).

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;

serviceCollection.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
    options.TokenValidationParameters.RoleClaimType = "roles";
    options.TokenValidationParameters.NameClaimType = "preferred_username";
    //options.TokenValidationParameters.NameClaimType = "email"; // or if you want to use user's email for User.Identity.Name

    //below lines of code can be removed. just there if you want some code to be executed right after user is validated. 
    options.Events.OnTokenValidated = async context =>
    {
        var personFirstName = context.Principal.FindFirstValue("given_name") ?? string.Empty;
        var personLastName = context.Principal.FindFirstValue("family_name") ?? string.Empty;
        var personEmail = context.Principal.FindFirstValue("email")?.ToLower();
        var personName = context.Principal.Identity.Name;
    };
});

Then in your controllers, you will get username from User.Identity.Name

Gean answered 14/10, 2021 at 14:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.