20

I am creating a Core 2.0 Web API project that uses JWT for authentication and authorization. My controller methods that I want to secure are all decorated with the Authorize attribute.

This is working. If I pass the JWT in the Bearer header, I get a 200. If I fail to pass the JWT, I get the 401. All working. In my JWT, I have stored the User ID in the 'UserId' field when authorizing..

var claimsdata = new[] {
                    new Claim("UserId", user.Id.ToString()),

I then have an extension method:

public static string GetUserId(this IPrincipal user)
        {
            if (user == null)
                return string.Empty;

            var identity = (ClaimsIdentity)user.Identity;
            IEnumerable<Claim> claims = identity.Claims;
            return claims.FirstOrDefault(s => s.Type == "UserId")?.Value;
        }

On my controller method, with 'Authorize', I often need the ID of the user. So I call my GetUserId method. This works. However, I am unsure if this is the best way to get the Id from the token.

int.TryParse(User.GetUserId(), out _userId);

I need to use that code on all controllers. I can't do it in the constructor, as .. that's wrong I think.

Am I doing the right thing here?

Craig
  • 18,074
  • 38
  • 147
  • 248

4 Answers4

34

ControllerBase contains User property that is type of ClaimsPrincipal

You can access user claims by User.Claims and no need for IPrincipal

Create a base controller which contains GetUserId method as protected

public abstract class BaseController : Controller
{        
    protected int GetUserId()
    {
        return int.Parse(this.User.Claims.First(i => i.Type == "UserId").Value);
    }
}

And all controllers inherit form this, now all controllers can access UserId

Hesam Faridmehr
  • 1,176
  • 8
  • 20
  • Great Thank you this helps, Now the Question is How do I get this user id to any service.? I would not like to pass the User/Claims to any service. is there any way directly access to any normal class.? would you please help.? – Kd Nimavat Oct 21 '22 at 06:09
15

Firstly I create IUserProvider interface with IHttpContextAccessor injection to make mocks for these interfaces in unit tests.

   public interface IUserProvider
   {
        string GetUserId();
   }

Than implementation is

    public class UserProvider : IUserProvider
    {
        private readonly IHttpContextAccessor _context;

        public UserProvider (IHttpContextAccessor context)
        {
            _context = context ?? throw new ArgumentNullException(nameof(context));
        }

        public string GetUserId()
        {
            return _context.HttpContext.User.Claims
                       .First(i => i.Type == ClaimTypes.NameIdentifier).Value;
        }
    }

So you can use interface IUserProvider in your controller without inheritance

    [Authorize]
    [ApiController]
    public class MyController : ControllerBase
    {        
        private readonly IUserProvider _userProvider;

        public MyController(IUserProvider userProvider)
        {            
            _userProvider = userProvider ?? throw new ArgumentNullException(nameof(userProvider ));
        }

        [HttpGet]
        [Route("api/My/Something")]
        public async Task<ActionResult> GetSomething()
        {
            try
            {
                var userId= _userProvider.GetUserId();
            }
        }
     }
Larissa Savchekoo
  • 6,412
  • 1
  • 13
  • 7
6

Also you can use

Extension Method

like this

public static long GetUserID(this ClaimsPrincipal User)
{
   return long.Parse(User.Claims.First(i => i.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier").Value);
}

and implement in your controller like this

[HttpDelete("DeleteAddress")]
public async Task<IActionResult> DeleteAddress([FromQuery] long AddressID)
{
   try
   {
      long userID = this.User.GetUserID();
      await _addressService.Delete(userID, AddressID);
      return Ok();
   }
   catch (Exception err)
   {
      return Conflict(err.Message);
   }     
}

I hope it will help you

Akbar Asghari
  • 613
  • 8
  • 19
  • Thanks. This answer actually makes a lot more sense to me. Not sure why using a controller base class was voted up more. The User Id is stored in the User Claims, so the extension method will allow you to access the User Id from the User object. I think this makes more sense because the User Id is related to the User object. It is not related to a controller. So, you write the extension method, then add a reference on each controller to use the extension method. – Skyrider Feb 14 '22 at 14:27
-4
var authenticatedUser = User.Identities.Select(c => c.Claims).ToArray()[0].ToArray()[0];

var userid = await userManager.FindByNameAsync(authenticatedUser['email']).Id;
Chris Catignani
  • 5,040
  • 16
  • 42
  • 49
  • 1
    Welcome to stack overflow. Maybe you can take a look at [markdown help](https://stackoverflow.com/editing-help) to improve your answers. – blmayer Jul 19 '21 at 22:05
  • 2
    The post contains too few details. Please add some explanation and improve the format with markdown help from the comment above – SENya Jul 19 '21 at 22:12