0

I'm using .net core with JWT for authentication. And I'm trying to access user claims from HttpContextAccessor in my custom attribute. From within the app, I have a UserService.cs (code below) where I can do this. But on OnActionExecuting in my custom attribute, the claims come as an empty error. Even if I call the function from my user service, the claims aren't there.

My ultimate objective is to get the user's id to check if the user has admin access. I don't wanna store the admin access status on the token.

enter image description here

UserService.cs


    public AuthenticatedUserClaims AuthenticatedUser()
            {
                var userClaims = new AuthenticatedUserClaims();
                var claims = _contextAccessor.HttpContext.User.Claims;
                var enumerable = claims as Claim[] ?? claims.ToArray();
                var userId = enumerable.SingleOrDefault(x => x.Type == "UserId")?.Value;
                userClaims.UserName = enumerable.SingleOrDefault(x => x.Type == "UserName")?.Value;
                userClaims.FullName = enumerable.SingleOrDefault(x => x.Type == "FullName")?.Value;
                if (userId != null && !string.IsNullOrEmpty(userId)) userClaims.UserId = int.Parse(userId);
    
                return userClaims;
            }

My Custom Attribute

        [AttributeUsage(AttributeTargets.Class| AttributeTargets.Method)]
        public class PermissionsRequiredAttribute: ActionFilterAttribute
        {
            private readonly IHttpContextAccessor _contextAccessor;
    
            public PermissionsRequiredAttribute(IHttpContextAccessor contextAccessor)
            {
                _contextAccessor = contextAccessor;
            }
            public override void OnActionExecuting(ActionExecutingContext context)
            {
                var claims = _contextAccessor.HttpContext.User.Claims;
                var claimsList = claims as Claim[] ?? claims.ToArray();
                // claimsList = Claims[0]??
                // context.Result = new UnauthorizedResult();
                base.OnActionExecuting(context);
            }
            
        }

Attribute Usage

     [HttpGet("{id}")]
     [ServiceFilter(typeof(PermissionsRequiredAttribute))]
     public async Task<ActionResult<Beneficiary>> GetBeneficiary([FromRoute] int id) { //... }

ConfigureServices on Startup.cs

    services.AddTransient<IHttpContextAccessor, HttpContextAccessor>();
    services.AddScoped<PermissionsRequiredAttribute>();

Thanks in advance :)

wiredmartian
  • 261
  • 3
  • 14

1 Answers1

0

I found a "hackaround" based on this answer, but I still don't know why my user HttpContext is empty.


    [AttributeUsage(AttributeTargets.Class| AttributeTargets.Method)]
    public class PermissionsRequiredAttribute: ActionFilterAttribute
    {
        private readonly IUser _user;

        public PermissionsRequiredAttribute(IUser user)
        {
            _user = user;
        }

        public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            bool headers = context.HttpContext.Request.Headers.TryGetValue("Authorization", out var tokens);
            string token = tokens.FirstOrDefault()?.Split(" ")[1];
            if (string.IsNullOrEmpty(token)) 
                context.Result = new UnauthorizedResult();
            var handler = new JwtSecurityTokenHandler();
            JwtSecurityToken securityToken = (JwtSecurityToken) handler.ReadToken(token);
            IEnumerable<Claim> claims = securityToken.Payload.Claims;
            string userId = claims.SingleOrDefault(c => c.Type == "UserId")?.Value;
            if (!string.IsNullOrEmpty(userId))
                context.Result = new UnauthorizedResult();
            var currentUser = await _user.GetUser(int.Parse(userId ?? throw new UnauthorizedAccessException()));
            if (!currentUser.isAdmin)
                context.Result = new UnauthorizedResult();
            await base.OnActionExecutionAsync(context, next);
        }

    }

EDIT: 16-10-2021

I found a solution I'm happy with, it looks like I just needed to add the line in Startup.cs in ConfigureServices(IServiceCollection services)

 services.AddHttpContextAccessor();

And now the line below has values Claims

var claims = _contextAccessor.HttpContext.User.Claims;
wiredmartian
  • 261
  • 3
  • 14