Rani Radcliff
Rani Radcliff

Reputation: 5084

Get Claims in a WebApi Control from a JWT Token

This is my first time attempting to use Asp.Net Core Web Api. I have everything working including authentication and jwt token creation and verification. What I am trying to do is extract the user information that is in the token and use some of it when posting data to the database. I create the token like this:

    public string NewToken(string ApiKey, ICAN_Context context)
    {
        var user = context.TblUserLogins.Where(x => x.ApiKey == ApiKey).FirstOrDefault();
        int? CompanyId = context.TblEmployeeCompanies.Where(x => x.EmployeeId == user.EmployeeId).Select(x => x.CompanyId).FirstOrDefault();
        var identity = new ClaimsIdentity();
        identity.AddClaim(new Claim(ClaimTypes.Name, user.UserName));
        identity.AddClaim(new Claim("CompanyId", CompanyId.ToString(), ClaimValueTypes.Integer32));
        identity.AddClaim(new Claim("EmployeeCompanyId", user.EmployeeCompanyId.ToString(), ClaimValueTypes.Integer32 ));
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(identity),
            Expires = DateTime.UtcNow.AddMinutes(60),
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(secretKey), SecurityAlgorithms.HmacSha256Signature)
        };
        var token = tokenHandler.CreateToken(tokenDescriptor);
        var jwtString = tokenHandler.WriteToken(token);
        return jwtString;

    }

I verify the token using a "filter":

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var tokenManager = (ITokenManager)context.HttpContext.RequestServices.GetService(typeof(ITokenManager));
        var result = true;
        if(!context.HttpContext.Request.Headers.ContainsKey("Authorization"))
        {
            result = false;
        }
        string token = string.Empty;
        if(result)
        {
            token = context.HttpContext.Request.Headers.First(x=>x.Key == "Authorization").Value;
            try
            {
                var claimPrinciple = tokenManager.VerifyToken(token);

            }
            catch(Exception ex)
            {
                result = false;
                context.ModelState.AddModelError("Unauthorized", ex.ToString());
            }
        }
        if(!result)
        {
            context.Result = new UnauthorizedObjectResult(context.ModelState);
        }
    }

I have no problem retrieving the claims info from the token, my question is how do I get the claims from the token in my controller method?

I want to be able to retrieve its values in my methods something like this:

    [HttpPost]
    [Route("~/api/entity/department")]
    public IActionResult CreateDepartment([FromBody] TblCompanyDepartmentsXlu department)
    {
        var identity = (ClaimsIdentity)User.Identity;
      
        _context.Departments.Add(department);
        _context.SaveChanges();
        return Ok("Department created successfully!");
    }

I also tried this from StackOverflow:

    public static ClaimsPrincipal VerifyToken(string jwtToken)
    {
        TokenManager tokenManager = new TokenManager();
        SecurityToken validatedToken;
        TokenValidationParameters validationParameters = new TokenValidationParameters();
        validationParameters.ValidateLifetime = true;
        validationParameters.IssuerSigningKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(tokenManager.secretKey);
        validationParameters.ValidateAudience = false;
        validationParameters.ValidateIssuer = false;
        ClaimsPrincipal principal = new JwtSecurityTokenHandler().ValidateToken(jwtToken, validationParameters, out validatedToken);
    
        return principal;

    }

    [HttpGet]
    [Route("~/api/entity/GetEmployees")]
    public List<TblEmployees> GetEmployees()
    {
        var identity = HttpContext.User.Identity as ClaimsIdentity;
        if (identity != null)
        {
            IEnumerable<Claim> claims = identity.Claims;


        }
        var employees = _context.Employees.ToList();
        return employees;

    }

but identity.Claims is ALWAYS 0. enter image description here

I am able to retrieve the claims right after verifying the token:

                    var claimPrinciple = TokenManager.VerifyToken(token);

I am able to retrieve the claims info here

var claims = claimPrinciple.Identities.First().Claims.ToList();
int? CompanyId = Convert.ToInt32(claims.Where(x => x.Type == "CompanyId").FirstOrDefault().Value);
int EmployeeCompanyId = Convert.ToInt32(claims.Where(x => x.Type == "EmployeeCompanyId").FirstOrDefault().Value);

But I am unable to retrieve them in the controller.

Upvotes: 1

Views: 2605

Answers (2)

Rani Radcliff
Rani Radcliff

Reputation: 5084

@Klekmek was correct. All I needed to do was use the built-in JWT token functionality (which I didn't know existed). I removed the AuthenticationFilter and added this to my startup: To the ConfigureServices section:

services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(jwt => {
            var key = Encoding.ASCII.GetBytes(Configuration["JwtConfig:Secret"]);
            jwt.SaveToken = true;
            jwt.TokenValidationParameters = new TokenValidationParameters()
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateLifetime = true,
                ValidateIssuer = false,
                ValidateAudience = false
            };
        });

And the Configure section:

app.UseAuthentication();

After adding that, this worked:

 var identity = HttpContext.User.Identity as ClaimsIdentity;

Upvotes: -1

Ali Khancherli
Ali Khancherli

Reputation: 129

//startup.cs dotnet 6.0 :
builder.Services.AddHttpContextAccessor();
.
.
.
//where you want to use the IHttpContextAccessor :
//For example in user repository : 
private readonly IHttpContextAccessor _httpContextAccessor;

    public UserRepository(IHttpContextAccessor httpContextAccessor) =>
        _httpContextAccessor = httpContextAccessor;

    public void LogCurrentUser()
    {
        var username = _httpContextAccessor.HttpContext.User.Identity.Name;
        // ...
    }

Upvotes: 2

Related Questions