I am moving from MVC to a SPA + API-approach on a new application im making and am facing some questions I cant seem to find the answer for.
I have a Web API in .Net Core 2 that is using Identity for its user-store. I am protecting the API by issuing JWT-tokens which my SPA is sending in as a bearer-token on every request to my controllers. The issuing code is:
private async Task<object> GenerateJwtTokenAsync(ApplicationUser user)
{
var roles = await _userManager.GetRolesAsync(user);
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Sub, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(ClaimTypes.NameIdentifier, user.Id)
};
claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role)));
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtKey"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var expires = DateTime.Now.AddDays(Convert.ToDouble(_configuration["JwtExpireDays"]));
var token = new JwtSecurityToken(
_configuration["JwtIssuer"],
_configuration["JwtIssuer"],
claims,
expires: expires,
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
My question is: What is the best practice for getting user information needed to serve the request in the controller?
As Im getting the JWT-token I have the user-information, but the extended information on the user, such as which company they work for is in my SQL-database.
// GET: api/Agreements
[HttpGet]
public async Task<IEnumerable<Agreement>> GetAgreementsAsync()
{
var user = await _userManager.GetUserAsync(HttpContext.User);
return _context.Agreements.Where(x => x.CompanyId == user.CompanyId);
}
Do I need to make another turn to the DB to get this information on each request? Should this information be put in the JWT-token, and in that case, in which field to you put "custom"-information?
When validating a JWT, generally, the current hash value and the original hash value are parsed, or decoded, then compared to verify the token signature is authentic. All of our backend API quickstarts use SDKs that perform JWT validation and parsing for you.
JSON Web Tokens (commonly known as JWT) is an open standard to pass data between client and server, and enables you to transmit data back and forth between the server and the consumers in a secure manner. This article talks about how you can take advantage of JWTs to protect APIs.
Preferably when authorizing you would like to stay stateless, which means that when client passes authentication and gets JWT token, then server can authorize requests with the JWT token on the fly. Which means that server won't look for JWT token anywhere, not in database or memory. So you don't have overhead of looking in database or elsewhere.
To answer your question you should also know the reason behind Claims: "The set of claims associated with a given entity can be thought of as a key. The particular claims define the shape of that key; much like a physical key is used to open a lock in a door. In this way, claims are used to gain access to resources." from MSDN
You don't need to make another request to DB, but keep in mind that Claims are best used for authorization purposes, so mainly add extra claims in JWT so you can later authorize for API resources without going to the database. You can also add your custom claims to then token which then get encrypted into JWT Token and sent to the client.
After authentication you can store the companyId and/or userId in the claims and you can name any string you would like, because that's how claim's constructor is implemented. And when the request arrives you can get the companyId from the claims. For example name it simply "companyId".
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Sub, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(ClaimTypes.NameIdentifier, user.Id),
new Claim("companyId", user.companyId.ToString()) // Like this
};
And then write getter for the company id claim
HttpContext.User.FindFirst("companyId").Value
Also keep in mind that, for complex user data you shouldn't use claims like this because this data pass in network and you don't want huge JWT Tokens. Also it is not good practice, for that you can use HttpContext.Session where you can store data and get it when the request comes. This is not place to write in detail about Session storage.
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