1

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.

Rani Radcliff
  • 4,856
  • 5
  • 33
  • 60
  • What claims are in the raw token if you check it on jwt.io ? – klekmek Jan 18 '22 at 15:39
  • UserName, CompanyId, and EmployeeCompanyId – Rani Radcliff Jan 18 '22 at 15:41
  • 2
    Did you configure your JWT options in the startup? [Take a look](https://wildermuth.com/2018/04/10/Using-JwtBearer-Authentication-in-an-API-only-ASP-NET-Core-Project) at this blogpost. – klekmek Jan 18 '22 at 15:44
  • @klekmek - No. This is what is in my startup: services.AddTransient(); services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("ICANDatabase"))); services.AddControllers(); – Rani Radcliff Jan 18 '22 at 15:47
  • 1
    I'd advise you the use the built-in JWT Bearer authentication methods for authentication. These will integrate perfect with your controllers and populate the `ClaimsPrincipal`. – klekmek Jan 18 '22 at 15:49
  • This is all new to me and I created this from a tutorial. I am using an "Authorization Filter" which doesn't require [Authorize] on each method, but [TokenAuthenticationFilter] at the top of the controller to require authentication for everything in the controller. The example doesn't show how to use it in the rest of the app. So I am going to see if I can find a full example of this that type of implementation. Thanks a lot! – Rani Radcliff Jan 18 '22 at 15:52
  • Check that you pass the http context to the controller. I had the problem and solved it by IHttpContextAccessor and add service by builder.Services.AddHttpContextAccessor(); – Ali Khancherli Jan 18 '22 at 16:09
  • @AliKhancherli - can you share how I should do that? I would like to try it rather than having to rewrite all of my authentication in my app. – Rani Radcliff Jan 18 '22 at 16:12

2 Answers2

2
//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;
        // ...
    }
-1

@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;
Rani Radcliff
  • 4,856
  • 5
  • 33
  • 60