(feature): test updates, jwt secret generation method
This commit is contained in:
		
							parent
							
								
									4a6e89c350
								
							
						
					
					
						commit
						f692c3d28c
					
				| @ -14,7 +14,7 @@ public class JwtGeneratorTests { | ||||
|   [Fact] | ||||
|   public void GenerateToken_ShouldReturnValidToken() { | ||||
|     // Act | ||||
|     var token = JwtGenerator.GenerateToken(Secret, Issuer, Audience, Expiration, Username, Roles); | ||||
|     var (token, jwtTokenClaims) = JwtGenerator.GenerateToken(Secret, Issuer, Audience, Expiration, Username, Roles); | ||||
| 
 | ||||
|     // Assert | ||||
|     Assert.False(string.IsNullOrEmpty(token)); | ||||
| @ -23,7 +23,7 @@ public class JwtGeneratorTests { | ||||
|   [Fact] | ||||
|   public void ValidateToken_ShouldReturnClaimsPrincipal_WhenTokenIsValid() { | ||||
|     // Arrange | ||||
|     var token = JwtGenerator.GenerateToken(Secret, Issuer, Audience, Expiration, Username, Roles); | ||||
|     var (token, _) = JwtGenerator.GenerateToken(Secret, Issuer, Audience, Expiration, Username, Roles); | ||||
| 
 | ||||
|     // Act | ||||
|     var jwtTokenClaims = JwtGenerator.ValidateToken(Secret, Issuer, Audience, token); | ||||
| @ -55,4 +55,16 @@ public class JwtGeneratorTests { | ||||
|     // Assert | ||||
|     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.Cryptography; | ||||
| using System.IdentityModel.Tokens.Jwt; | ||||
| 
 | ||||
| using Microsoft.IdentityModel.Tokens; | ||||
| 
 | ||||
| 
 | ||||
| namespace MaksIT.Core.Security; | ||||
| 
 | ||||
| public class JWTTokenClaims { | ||||
| @ -15,10 +15,9 @@ public class JWTTokenClaims { | ||||
|   public DateTime? ExpiresAt { get; set; } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| public static class JwtGenerator { | ||||
|   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 issuedAt = DateTime.UtcNow; | ||||
| @ -26,26 +25,24 @@ public static class JwtGenerator { | ||||
| 
 | ||||
|     var claims = new List<Claim> | ||||
|     { | ||||
|           new Claim(ClaimTypes.Name, username), | ||||
|           new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), | ||||
|           new Claim(JwtRegisteredClaimNames.Iat, new DateTimeOffset(issuedAt).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64), | ||||
|           new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(expiresAt).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64) | ||||
|       }; | ||||
|             new Claim(ClaimTypes.Name, username), | ||||
|             new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), | ||||
|             new Claim(JwtRegisteredClaimNames.Iat, new DateTimeOffset(issuedAt).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))); | ||||
| 
 | ||||
|     var token = new JwtSecurityToken( | ||||
|     var tokenDescriptor = new JwtSecurityToken( | ||||
|         issuer: issuer, | ||||
|         audience: audience, | ||||
|         claims: claims, | ||||
|         expires: DateTime.Now.AddMinutes(Convert.ToDouble(expiration)), | ||||
|         expires: expiresAt, | ||||
|         signingCredentials: credentials | ||||
|     ); | ||||
| 
 | ||||
|     var jwtToken = new JwtSecurityTokenHandler().WriteToken(token); | ||||
|     var jwtToken = new JwtSecurityTokenHandler().WriteToken(tokenDescriptor); | ||||
| 
 | ||||
| 
 | ||||
|     // Create the JWTTokenClaims object | ||||
|     var tokenClaims = new JWTTokenClaims { | ||||
|       Username = username, | ||||
|       Roles = roles, | ||||
| @ -56,13 +53,13 @@ public static class JwtGenerator { | ||||
|     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) { | ||||
|     try { | ||||
|       var key = Encoding.UTF8.GetBytes(secret); | ||||
| 
 | ||||
|       var tokenHandler = new JwtSecurityTokenHandler(); | ||||
| 
 | ||||
|       var validationParameters = new TokenValidationParameters { | ||||
|         ValidateIssuerSigningKey = true, | ||||
|         IssuerSigningKey = new SymmetricSecurityKey(key), | ||||
| @ -76,34 +73,45 @@ public static class JwtGenerator { | ||||
| 
 | ||||
|       var principal = tokenHandler.ValidateToken(token, validationParameters, out var validatedToken); | ||||
| 
 | ||||
|       var username = principal?.Identity?.Name; | ||||
|       var roles = principal?.Claims.Where(c => c.Type == ClaimTypes.Role).Select(c => c.Value).ToList(); | ||||
|       // Validate the algorithm used | ||||
|       if (validatedToken is JwtSecurityToken jwtToken && jwtToken.Header.Alg != SecurityAlgorithms.HmacSha256) | ||||
|         throw new SecurityTokenException("Invalid token algorithm"); | ||||
| 
 | ||||
| 
 | ||||
|       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 | ||||
|       }; | ||||
|       return ExtractClaims(principal); | ||||
|     } | ||||
|     catch { | ||||
|       return null; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   public static string GenerateRefreshToken() { | ||||
|     var randomNumber = new byte[32]; | ||||
|     using (var rng = RandomNumberGenerator.Create()) { | ||||
|       rng.GetBytes(randomNumber); | ||||
|       return Convert.ToBase64String(randomNumber); | ||||
|     } | ||||
|   public static string GenerateRefreshToken() => Convert.ToBase64String(GetRandomBytes(32)); | ||||
| 
 | ||||
|   // Private helper method to extract claims | ||||
|   private static JWTTokenClaims? ExtractClaims(ClaimsPrincipal principal) { | ||||
|     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