(feature): JWT token handle methods
This commit is contained in:
parent
307cce4606
commit
e4da2e68b3
52
README.md
52
README.md
@ -13,6 +13,7 @@ MaksIT.Core is a collection of helper methods and extensions for .NET projects,
|
||||
- [Password Hasher](#password-hasher)
|
||||
- [DataTable Extensions](#datatable-extensions)
|
||||
- [DateTime Extensions](#datetime-extensions)
|
||||
- [JWT Token Generation and Validation](#jwt-token-generation-and-validation)
|
||||
- [Available Methods](#available-methods)
|
||||
- [Enumeration Methods](#enumeration-methods)
|
||||
- [Guid Methods](#guid-methods)
|
||||
@ -21,6 +22,7 @@ MaksIT.Core is a collection of helper methods and extensions for .NET projects,
|
||||
- [Security Methods](#security-methods)
|
||||
- [DataTable Methods](#datatable-methods)
|
||||
- [DateTime Methods](#datetime-methods)
|
||||
- [JWT Methods](#jwt-methods)
|
||||
- [Contributing](#contributing)
|
||||
- [License](#license)
|
||||
- [Contact](#contact)
|
||||
@ -166,6 +168,50 @@ DateTime nextThursday = DateTime.Now.NextWeekday(DayOfWeek.Thursday);
|
||||
Console.WriteLine(nextThursday); // Output: Date of the next Thursday
|
||||
```
|
||||
|
||||
### JWT Token Generation and Validation
|
||||
|
||||
The `JwtGenerator` class provides methods for generating and validating JWT tokens, as well as generating refresh tokens. This is useful for implementing secure authentication and authorization mechanisms in your applications.
|
||||
|
||||
#### Generate a JWT Token
|
||||
|
||||
```csharp
|
||||
using MaksIT.Core.Security;
|
||||
|
||||
string secret = "your_secret_key";
|
||||
string issuer = "your_issuer";
|
||||
string audience = "your_audience";
|
||||
double expiration = 30; // Token expiration in minutes
|
||||
string username = "user123";
|
||||
List<string> roles = new List<string> { "Admin", "User" };
|
||||
|
||||
string token = JwtGenerator.GenerateToken(secret, issuer, audience, expiration, username, roles);
|
||||
Console.WriteLine("Generated JWT Token: " + token);
|
||||
```
|
||||
|
||||
#### Validate a JWT Token
|
||||
|
||||
```csharp
|
||||
string tokenToValidate = "your_jwt_token";
|
||||
|
||||
JWTTokenClaims? claims = JwtGenerator.ValidateToken(secret, issuer, audience, tokenToValidate);
|
||||
if (claims != null)
|
||||
{
|
||||
Console.WriteLine($"Username: {claims.Username}");
|
||||
Console.WriteLine("Roles: " + string.Join(", ", claims.Roles));
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Invalid token.");
|
||||
}
|
||||
```
|
||||
|
||||
#### Generate a Refresh Token
|
||||
|
||||
```csharp
|
||||
string refreshToken = JwtGenerator.GenerateRefreshToken();
|
||||
Console.WriteLine("Generated Refresh Token: " + refreshToken);
|
||||
```
|
||||
|
||||
## Available Methods
|
||||
|
||||
### Enumeration Methods
|
||||
@ -241,6 +287,12 @@ Console.WriteLine(nextThursday); // Output: Date of the next Thursday
|
||||
- **IsSameMonth(this DateTime date, DateTime targetDate)**: Checks if two dates are in the same month and year.
|
||||
- **GetDifferenceInYears(this DateTime startDate, DateTime endDate)**: Returns the difference in years between two dates.
|
||||
|
||||
### JWT Methods
|
||||
|
||||
- **GenerateToken**: Generates jwt-token-generation-and-validationa JWT token.
|
||||
- **ValidateToken**: Validates a JWT token and extracts claims.
|
||||
- **GenerateRefreshToken**: Generates a secure refresh token.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions to this project are welcome! Please fork the repository and submit a pull request with your changes. If you encounter any issues or have feature requests, feel free to open an issue on GitHub.
|
||||
|
||||
58
src/MaksIT.Core.Tests/Security/JwtGeneratorTests.cs
Normal file
58
src/MaksIT.Core.Tests/Security/JwtGeneratorTests.cs
Normal file
@ -0,0 +1,58 @@
|
||||
using MaksIT.Core.Security;
|
||||
|
||||
|
||||
namespace MaksIT.Core.Tests.Security;
|
||||
|
||||
public class JwtGeneratorTests {
|
||||
private const string Secret = "supersecretkey12345678901234567890";
|
||||
private const string Issuer = "testIssuer";
|
||||
private const string Audience = "testAudience";
|
||||
private const double Expiration = 30; // 30 minutes
|
||||
private const string Username = "testUser";
|
||||
private readonly List<string> Roles = new List<string> { "Admin", "User" };
|
||||
|
||||
[Fact]
|
||||
public void GenerateToken_ShouldReturnValidToken() {
|
||||
// Act
|
||||
var token = JwtGenerator.GenerateToken(Secret, Issuer, Audience, Expiration, Username, Roles);
|
||||
|
||||
// Assert
|
||||
Assert.False(string.IsNullOrEmpty(token));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateToken_ShouldReturnClaimsPrincipal_WhenTokenIsValid() {
|
||||
// Arrange
|
||||
var token = JwtGenerator.GenerateToken(Secret, Issuer, Audience, Expiration, Username, Roles);
|
||||
|
||||
// Act
|
||||
var jwtTokenClaims = JwtGenerator.ValidateToken(Secret, Issuer, Audience, token);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(jwtTokenClaims);
|
||||
Assert.Equal(Username, jwtTokenClaims.Username);
|
||||
Assert.Contains(jwtTokenClaims.Roles ?? new List<string>(), c => c == "Admin");
|
||||
Assert.Contains(jwtTokenClaims.Roles ?? new List<string>(), c => c == "User");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateToken_ShouldReturnNull_WhenTokenIsInvalid() {
|
||||
// Arrange
|
||||
var invalidToken = "invalidToken";
|
||||
|
||||
// Act
|
||||
var principal = JwtGenerator.ValidateToken(Secret, Issuer, Audience, invalidToken);
|
||||
|
||||
// Assert
|
||||
Assert.Null(principal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenerateRefreshToken_ShouldReturnNonEmptyString() {
|
||||
// Act
|
||||
var refreshToken = JwtGenerator.GenerateRefreshToken();
|
||||
|
||||
// Assert
|
||||
Assert.False(string.IsNullOrEmpty(refreshToken));
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
<!-- NuGet package metadata -->
|
||||
<PackageId>MaksIT.Core</PackageId>
|
||||
<Version>1.0.2</Version>
|
||||
<Version>1.0.3</Version>
|
||||
<Authors>Maksym Sadovnychyy</Authors>
|
||||
<Company>MAKS-IT</Company>
|
||||
<Product>MaksIT.Core</Product>
|
||||
@ -26,5 +26,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.1.0" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.1.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
80
src/MaksIT.Core/Security/JwtGenerator.cs
Normal file
80
src/MaksIT.Core/Security/JwtGenerator.cs
Normal file
@ -0,0 +1,80 @@
|
||||
using System.Text;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
|
||||
namespace MaksIT.Core.Security;
|
||||
|
||||
public class JWTTokenClaims {
|
||||
public required string? Username { get; set; }
|
||||
public required List<string>? Roles { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public static class JwtGenerator {
|
||||
public static string GenerateToken(string secret, string issuer, string audience, double expiration, string username, List<string> roles) {
|
||||
var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));
|
||||
var credentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
|
||||
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new Claim(ClaimTypes.Name, username),
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
|
||||
};
|
||||
|
||||
claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role)));
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
issuer: issuer,
|
||||
audience: audience,
|
||||
claims: claims,
|
||||
expires: DateTime.Now.AddMinutes(Convert.ToDouble(expiration)),
|
||||
signingCredentials: credentials
|
||||
);
|
||||
|
||||
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||
}
|
||||
|
||||
|
||||
|
||||
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),
|
||||
ValidateIssuer = true,
|
||||
ValidIssuer = issuer,
|
||||
ValidateAudience = true,
|
||||
ValidAudience = audience,
|
||||
ValidateLifetime = true,
|
||||
ClockSkew = TimeSpan.Zero
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
return new JWTTokenClaims {
|
||||
Username = username,
|
||||
Roles = roles
|
||||
};
|
||||
}
|
||||
catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public static string GenerateRefreshToken() {
|
||||
var randomNumber = new byte[32];
|
||||
using (var rng = RandomNumberGenerator.Create()) {
|
||||
rng.GetBytes(randomNumber);
|
||||
return Convert.ToBase64String(randomNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user