(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)
|
- [Password Hasher](#password-hasher)
|
||||||
- [DataTable Extensions](#datatable-extensions)
|
- [DataTable Extensions](#datatable-extensions)
|
||||||
- [DateTime Extensions](#datetime-extensions)
|
- [DateTime Extensions](#datetime-extensions)
|
||||||
|
- [JWT Token Generation and Validation](#jwt-token-generation-and-validation)
|
||||||
- [Available Methods](#available-methods)
|
- [Available Methods](#available-methods)
|
||||||
- [Enumeration Methods](#enumeration-methods)
|
- [Enumeration Methods](#enumeration-methods)
|
||||||
- [Guid Methods](#guid-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)
|
- [Security Methods](#security-methods)
|
||||||
- [DataTable Methods](#datatable-methods)
|
- [DataTable Methods](#datatable-methods)
|
||||||
- [DateTime Methods](#datetime-methods)
|
- [DateTime Methods](#datetime-methods)
|
||||||
|
- [JWT Methods](#jwt-methods)
|
||||||
- [Contributing](#contributing)
|
- [Contributing](#contributing)
|
||||||
- [License](#license)
|
- [License](#license)
|
||||||
- [Contact](#contact)
|
- [Contact](#contact)
|
||||||
@ -166,6 +168,50 @@ DateTime nextThursday = DateTime.Now.NextWeekday(DayOfWeek.Thursday);
|
|||||||
Console.WriteLine(nextThursday); // Output: Date of the next 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
|
## Available Methods
|
||||||
|
|
||||||
### Enumeration 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.
|
- **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.
|
- **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
|
## 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.
|
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 -->
|
<!-- NuGet package metadata -->
|
||||||
<PackageId>MaksIT.Core</PackageId>
|
<PackageId>MaksIT.Core</PackageId>
|
||||||
<Version>1.0.2</Version>
|
<Version>1.0.3</Version>
|
||||||
<Authors>Maksym Sadovnychyy</Authors>
|
<Authors>Maksym Sadovnychyy</Authors>
|
||||||
<Company>MAKS-IT</Company>
|
<Company>MAKS-IT</Company>
|
||||||
<Product>MaksIT.Core</Product>
|
<Product>MaksIT.Core</Product>
|
||||||
@ -26,5 +26,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="8.0.8" />
|
<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>
|
</ItemGroup>
|
||||||
</Project>
|
</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