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?
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
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();
}
}
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With