(feature): test updates, jwt secret generation method
This commit is contained in:
		
							parent
							
								
									4a6e89c350
								
							
						
					
					
						commit
						f692c3d28c
					
				| @ -14,7 +14,7 @@ public class JwtGeneratorTests { | |||||||
|   [Fact] |   [Fact] | ||||||
|   public void GenerateToken_ShouldReturnValidToken() { |   public void GenerateToken_ShouldReturnValidToken() { | ||||||
|     // Act |     // Act | ||||||
|     var token = JwtGenerator.GenerateToken(Secret, Issuer, Audience, Expiration, Username, Roles); |     var (token, jwtTokenClaims) = JwtGenerator.GenerateToken(Secret, Issuer, Audience, Expiration, Username, Roles); | ||||||
| 
 | 
 | ||||||
|     // Assert |     // Assert | ||||||
|     Assert.False(string.IsNullOrEmpty(token)); |     Assert.False(string.IsNullOrEmpty(token)); | ||||||
| @ -23,7 +23,7 @@ public class JwtGeneratorTests { | |||||||
|   [Fact] |   [Fact] | ||||||
|   public void ValidateToken_ShouldReturnClaimsPrincipal_WhenTokenIsValid() { |   public void ValidateToken_ShouldReturnClaimsPrincipal_WhenTokenIsValid() { | ||||||
|     // Arrange |     // Arrange | ||||||
|     var token = JwtGenerator.GenerateToken(Secret, Issuer, Audience, Expiration, Username, Roles); |     var (token, _) = JwtGenerator.GenerateToken(Secret, Issuer, Audience, Expiration, Username, Roles); | ||||||
| 
 | 
 | ||||||
|     // Act |     // Act | ||||||
|     var jwtTokenClaims = JwtGenerator.ValidateToken(Secret, Issuer, Audience, token); |     var jwtTokenClaims = JwtGenerator.ValidateToken(Secret, Issuer, Audience, token); | ||||||
| @ -55,4 +55,16 @@ public class JwtGeneratorTests { | |||||||
|     // Assert |     // Assert | ||||||
|     Assert.False(string.IsNullOrEmpty(refreshToken)); |     Assert.False(string.IsNullOrEmpty(refreshToken)); | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   [Fact] | ||||||
|  |   public void GenerateSecret_ShouldReturnDifferentValuesOnSubsequentCalls() { | ||||||
|  |     // Act | ||||||
|  |     string secret1 = JwtGenerator.GenerateSecret(); | ||||||
|  |     string secret2 = JwtGenerator.GenerateSecret(); | ||||||
|  | 
 | ||||||
|  |     // Assert | ||||||
|  |     Assert.False(string.IsNullOrEmpty(secret1)); | ||||||
|  |     Assert.False(string.IsNullOrEmpty(secret2)); | ||||||
|  |     Assert.NotEqual(secret1, secret2); // Ensure the secrets are unique | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,11 +1,11 @@ | |||||||
| using System.Text; | using System; | ||||||
|  | using System.Text; | ||||||
|  | using System.Linq; | ||||||
| using System.Security.Claims; | using System.Security.Claims; | ||||||
| using System.Security.Cryptography; | using System.Security.Cryptography; | ||||||
| using System.IdentityModel.Tokens.Jwt; | using System.IdentityModel.Tokens.Jwt; | ||||||
| 
 |  | ||||||
| using Microsoft.IdentityModel.Tokens; | using Microsoft.IdentityModel.Tokens; | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| namespace MaksIT.Core.Security; | namespace MaksIT.Core.Security; | ||||||
| 
 | 
 | ||||||
| public class JWTTokenClaims { | public class JWTTokenClaims { | ||||||
| @ -15,10 +15,9 @@ public class JWTTokenClaims { | |||||||
|   public DateTime? ExpiresAt { get; set; } |   public DateTime? ExpiresAt { get; set; } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| public static class JwtGenerator { | public static class JwtGenerator { | ||||||
|   public static (string, JWTTokenClaims) GenerateToken(string secret, string issuer, string audience, double expiration, string username, List<string> roles) { |   public static (string, JWTTokenClaims) GenerateToken(string secret, string issuer, string audience, double expiration, string username, List<string> roles) { | ||||||
|     var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret)); |     var secretKey = GetSymmetricSecurityKey(secret); | ||||||
|     var credentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256); |     var credentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256); | ||||||
| 
 | 
 | ||||||
|     var issuedAt = DateTime.UtcNow; |     var issuedAt = DateTime.UtcNow; | ||||||
| @ -26,26 +25,24 @@ public static class JwtGenerator { | |||||||
| 
 | 
 | ||||||
|     var claims = new List<Claim> |     var claims = new List<Claim> | ||||||
|     { |     { | ||||||
|           new Claim(ClaimTypes.Name, username), |             new Claim(ClaimTypes.Name, username), | ||||||
|           new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), |             new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), | ||||||
|           new Claim(JwtRegisteredClaimNames.Iat, new DateTimeOffset(issuedAt).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64), |             new Claim(JwtRegisteredClaimNames.Iat, new DateTimeOffset(issuedAt).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64), | ||||||
|           new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(expiresAt).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64) |             new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(expiresAt).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64) | ||||||
|       }; |         }; | ||||||
| 
 | 
 | ||||||
|     claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role))); |     claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role))); | ||||||
| 
 | 
 | ||||||
|     var token = new JwtSecurityToken( |     var tokenDescriptor = new JwtSecurityToken( | ||||||
|         issuer: issuer, |         issuer: issuer, | ||||||
|         audience: audience, |         audience: audience, | ||||||
|         claims: claims, |         claims: claims, | ||||||
|         expires: DateTime.Now.AddMinutes(Convert.ToDouble(expiration)), |         expires: expiresAt, | ||||||
|         signingCredentials: credentials |         signingCredentials: credentials | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     var jwtToken = new JwtSecurityTokenHandler().WriteToken(token); |     var jwtToken = new JwtSecurityTokenHandler().WriteToken(tokenDescriptor); | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     // Create the JWTTokenClaims object |  | ||||||
|     var tokenClaims = new JWTTokenClaims { |     var tokenClaims = new JWTTokenClaims { | ||||||
|       Username = username, |       Username = username, | ||||||
|       Roles = roles, |       Roles = roles, | ||||||
| @ -56,13 +53,13 @@ public static class JwtGenerator { | |||||||
|     return (jwtToken, tokenClaims); |     return (jwtToken, tokenClaims); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| 
 |   public static string GenerateSecret(int keySize = 32) => Convert.ToBase64String(GetRandomBytes(keySize)); | ||||||
| 
 | 
 | ||||||
|   public static JWTTokenClaims? ValidateToken(string secret, string issuer, string audience, string token) { |   public static JWTTokenClaims? ValidateToken(string secret, string issuer, string audience, string token) { | ||||||
|     try { |     try { | ||||||
|       var key = Encoding.UTF8.GetBytes(secret); |       var key = Encoding.UTF8.GetBytes(secret); | ||||||
| 
 |  | ||||||
|       var tokenHandler = new JwtSecurityTokenHandler(); |       var tokenHandler = new JwtSecurityTokenHandler(); | ||||||
|  | 
 | ||||||
|       var validationParameters = new TokenValidationParameters { |       var validationParameters = new TokenValidationParameters { | ||||||
|         ValidateIssuerSigningKey = true, |         ValidateIssuerSigningKey = true, | ||||||
|         IssuerSigningKey = new SymmetricSecurityKey(key), |         IssuerSigningKey = new SymmetricSecurityKey(key), | ||||||
| @ -76,34 +73,45 @@ public static class JwtGenerator { | |||||||
| 
 | 
 | ||||||
|       var principal = tokenHandler.ValidateToken(token, validationParameters, out var validatedToken); |       var principal = tokenHandler.ValidateToken(token, validationParameters, out var validatedToken); | ||||||
| 
 | 
 | ||||||
|       var username = principal?.Identity?.Name; |       // Validate the algorithm used | ||||||
|       var roles = principal?.Claims.Where(c => c.Type == ClaimTypes.Role).Select(c => c.Value).ToList(); |       if (validatedToken is JwtSecurityToken jwtToken && jwtToken.Header.Alg != SecurityAlgorithms.HmacSha256) | ||||||
|  |         throw new SecurityTokenException("Invalid token algorithm"); | ||||||
| 
 | 
 | ||||||
| 
 |       return ExtractClaims(principal); | ||||||
|       var issuedAtClaim = principal?.Claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.Iat)?.Value; |  | ||||||
|       var expiresAtClaim = principal?.Claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.Exp)?.Value; |  | ||||||
| 
 |  | ||||||
|       DateTime? issuedAt = issuedAtClaim != null ? DateTimeOffset.FromUnixTimeSeconds(long.Parse(issuedAtClaim)).UtcDateTime : (DateTime?)null; |  | ||||||
|       DateTime? expiresAt = expiresAtClaim != null ? DateTimeOffset.FromUnixTimeSeconds(long.Parse(expiresAtClaim)).UtcDateTime : (DateTime?)null; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|       return new JWTTokenClaims { |  | ||||||
|         Username = username, |  | ||||||
|         Roles = roles, |  | ||||||
|         IssuedAt = issuedAt, |  | ||||||
|         ExpiresAt = expiresAt |  | ||||||
|       }; |  | ||||||
|     } |     } | ||||||
|     catch { |     catch { | ||||||
|       return null; |       return null; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public static string GenerateRefreshToken() { |   public static string GenerateRefreshToken() => Convert.ToBase64String(GetRandomBytes(32)); | ||||||
|     var randomNumber = new byte[32]; | 
 | ||||||
|     using (var rng = RandomNumberGenerator.Create()) { |   // Private helper method to extract claims | ||||||
|       rng.GetBytes(randomNumber); |   private static JWTTokenClaims? ExtractClaims(ClaimsPrincipal principal) { | ||||||
|       return Convert.ToBase64String(randomNumber); |     var username = principal.Identity?.Name; | ||||||
|     } |     var roles = principal.Claims.Where(c => c.Type == ClaimTypes.Role).Select(c => c.Value).ToList(); | ||||||
|  | 
 | ||||||
|  |     var issuedAtClaim = principal.Claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.Iat)?.Value; | ||||||
|  |     var expiresAtClaim = principal.Claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.Exp)?.Value; | ||||||
|  | 
 | ||||||
|  |     DateTime? issuedAt = issuedAtClaim != null ? DateTimeOffset.FromUnixTimeSeconds(long.Parse(issuedAtClaim)).UtcDateTime : (DateTime?)null; | ||||||
|  |     DateTime? expiresAt = expiresAtClaim != null ? DateTimeOffset.FromUnixTimeSeconds(long.Parse(expiresAtClaim)).UtcDateTime : (DateTime?)null; | ||||||
|  | 
 | ||||||
|  |     return new JWTTokenClaims { | ||||||
|  |       Username = username, | ||||||
|  |       Roles = roles, | ||||||
|  |       IssuedAt = issuedAt, | ||||||
|  |       ExpiresAt = expiresAt | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Private helper method to get a symmetric security key | ||||||
|  |   private static SymmetricSecurityKey GetSymmetricSecurityKey(string secret) => new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret)); | ||||||
|  | 
 | ||||||
|  |   // Private helper method for generating random bytes | ||||||
|  |   private static byte[] GetRandomBytes(int size) { | ||||||
|  |     var bytes = new byte[size]; | ||||||
|  |     RandomNumberGenerator.Fill(bytes); | ||||||
|  |     return bytes; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Maksym Sadovnychyy
						Maksym Sadovnychyy