Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get userId from JWT on all Controller methods?

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?

like image 292
Craig Avatar asked May 29 '18 08:05

Craig


3 Answers

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

like image 103
Hesam Faridmehr Avatar answered Nov 20 '22 18:11

Hesam Faridmehr


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();
            }
        }
     }
like image 31
Larissa Savchekoo Avatar answered Nov 20 '22 16:11

Larissa Savchekoo


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

like image 6
Akbar Asghari Avatar answered Nov 20 '22 17:11

Akbar Asghari