Asked by:
How to handle JwtSecurityToken in a load balancer environment?

Question
-
User-1104215994 posted
Hello guys,
I have a web API 2 rest service. I implemented a JwtSecurityToken structure. When the client sends a request to my service, I am validating this client and send JwtSecurityToken in response. The other day, I was thinking about what should I do if there is a need for LB. I mean let's say 2 servers behind the load balancer in order to distribute the requests. For now, I deployed my rest API on a test server. But if I deploy my rest API on two servers which are behind the load balancer, is there any problem in terms of validating tokens? Should I implement another logic for creating/validating tokens?
Here is my token creation code:
public static string createToken(string username) { //Set issued at date DateTime issuedAt = DateTime.UtcNow; //set the time when it expires DateTime expires = DateTime.UtcNow.AddMinutes(2); var tokenHandler = new JwtSecurityTokenHandler(); //create a identity and add claims to the user which we want to log in ClaimsIdentity claimsIdentity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, username) }); const string sec = "abc"; var now = DateTime.UtcNow; var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec)); var signingCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey, Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256Signature); //create the jwt var token = (JwtSecurityToken) tokenHandler.CreateJwtSecurityToken(issuer: "http://111.111.111.111:1907/api/v3/token", audience: "http://111.111.111.111:1907/api/v3/token", subject: claimsIdentity, notBefore: issuedAt, expires: expires, signingCredentials: signingCredentials); var tokenString = tokenHandler.WriteToken(token); return tokenString; }
And <g class="gr_ gr_17 gr-alert gr_gramm gr_inline_cards gr_run_anim Grammar only-ins replaceWithoutSep" id="17" data-gr-id="17">validation</g> code:
internal class TokenValidationHandler : DelegatingHandler { private static bool TryRetrieveToken(HttpRequestMessage request, out string token) { token = null; IEnumerable<string> authzHeaders; if (!request.Headers.TryGetValues("Authorization", out authzHeaders) || authzHeaders.Count() > 1) { return false; } var bearerToken = authzHeaders.ElementAt(0); token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken; return true; } protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { HttpStatusCode statusCode; string token; //determine whether a jwt exists or not if (!TryRetrieveToken(request, out token)) { statusCode = HttpStatusCode.Unauthorized; //allow requests with no token - whether a action method needs an authentication can be set with the claimsauthorization attribute return base.SendAsync(request, cancellationToken); } try { const string sec = "abc"; var now = DateTime.UtcNow; var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec)); SecurityToken securityToken; JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler(); TokenValidationParameters validationParameters = new TokenValidationParameters() { ValidAudience = "http://111.111.111.111:1907/api/v3/token", ValidIssuer = "http://111.111.111.111:1907/api/v3/token", ValidateLifetime = true, ValidateIssuerSigningKey = true, LifetimeValidator = this.LifetimeValidator, IssuerSigningKey = securityKey }; //extract and assign the user of the jwt Thread.CurrentPrincipal = handler.ValidateToken(token, validationParameters, out securityToken); HttpContext.Current.User = handler.ValidateToken(token, validationParameters, out securityToken); return base.SendAsync(request, cancellationToken); } catch (SecurityTokenValidationException e) { statusCode = HttpStatusCode.Unauthorized; } catch (Exception ex) { statusCode = HttpStatusCode.InternalServerError; } return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(statusCode) { }); } public bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters) { if (expires != null) { if (DateTime.UtcNow < expires) return true; } return false; } }
Wednesday, April 24, 2019 5:46 AM
All replies
-
User475983607 posted
The main purpose of using a JWT is that it can be passed around. It should not matter if the application is load balanced if you are following standard programming practices.
Wednesday, April 24, 2019 12:23 PM -
User-1104215994 posted
What do you mean by standard programming practices? Please be more clear. In my rest API, I am creating token and validation it. If there will be more than one IIS server, then this means there will be more than one token creation and validation code.
Wednesday, April 24, 2019 7:48 PM -
User475983607 posted
If there will be more than one IIS server, then this means there will be more than one token creation and validation code.I do not understand you concern. It should work as long as you are using the same Token Signing/Validation.
Wednesday, April 24, 2019 8:13 PM