CoreJsonWebToken 2025.11.11

dotnet add package CoreJsonWebToken --version 2025.11.11
                    
NuGet\Install-Package CoreJsonWebToken -Version 2025.11.11
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CoreJsonWebToken" Version="2025.11.11" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CoreJsonWebToken" Version="2025.11.11" />
                    
Directory.Packages.props
<PackageReference Include="CoreJsonWebToken" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CoreJsonWebToken --version 2025.11.11
                    
#r "nuget: CoreJsonWebToken, 2025.11.11"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CoreJsonWebToken@2025.11.11
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CoreJsonWebToken&version=2025.11.11
                    
Install as a Cake Addin
#tool nuget:?package=CoreJsonWebToken&version=2025.11.11
                    
Install as a Cake Tool

JWT Authentication for DeveloperKit

NuGet License .NET Standard

A robust and easy-to-implement JWT (JSON Web Token) authentication library for ASP.NET Core applications. Part of the DeveloperKit ecosystem, this component provides secure token generation, validation, and management with minimal configuration.

✨ Features

  • Secure Token Generation

    • HMAC, RSA, and ECDSA signing algorithms
    • Configurable token expiration and validation parameters
    • Support for custom claims and token metadata
  • Flexible Configuration

    • Simple setup with sensible defaults
    • Support for multiple issuers and audiences
    • Custom token validation parameters
    • Seamless integration with ASP.NET Core Identity
  • Security First

    • Secure key management
    • Automatic token validation
    • Protection against common JWT vulnerabilities
    • Support for token refresh mechanisms
  • Developer Experience

    • Clean, intuitive API
    • Comprehensive logging
    • Detailed error messages
    • Async support throughout

🚀 Getting Started

Prerequisites

  • .NET 6.0+ or .NET Standard 2.0+
  • ASP.NET Core 6.0+
  • Visual Studio 2022 or VS Code with C# Dev Kit (recommended)

Installation

dotnet add package DevKit.JWT

🔧 Configuration

Basic Setup

public void ConfigureServices(IServiceCollection services)
{
    // Add JWT Authentication
    services.AddJwtAuthentication(Configuration.GetSection("Jwt"));
    
    // Add Controllers and other services
    services.AddControllers();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // Other middleware...
    
    app.UseAuthentication();
    app.UseAuthorization();
    
    // Other middleware...
}

appsettings.json

{
  "Jwt": {
    "Issuer": "https://your-issuer.com",
    "Audience": "your-audience",
    "SecretKey": "your-256-bit-secret-key-at-least-32-characters-long",
    "ExpireMinutes": 60,
    "RefreshTokenExpireDays": 7
  }
}

Advanced Configuration

services.AddJwtAuthentication(options =>
{
    // Required
    options.Issuer = "https://your-issuer.com";
    options.Audience = "your-audience";
    options.SecretKey = Configuration["Jwt:SecretKey"];
    
    // Optional with defaults shown
    options.ExpireMinutes = 60;
    options.ClockSkew = TimeSpan.FromMinutes(5);
    options.ValidateIssuer = true;
    options.ValidateAudience = true;
    options.ValidateLifetime = true;
    options.ValidateIssuerSigningKey = true;
    
    // Refresh token options
    options.RefreshTokenExpireDays = 7;
    
    // Custom token validation parameters
    options.TokenValidationParameters = new TokenValidationParameters
    {
        // Override any parameters as needed
        NameClaimType = JwtRegisteredClaimNames.Sub,
        RoleClaimType = ClaimTypes.Role
    };
});
services.AddJsonWebToken(options =>
{
    // Clave de seguridad (mínimo 16 caracteres)
    options.SecurityKey = "your-very-long-secret-key-here";
    
    // Emisor del token
    options.ValidIssuer = "https://api.yourdomain.com";
    
    // Cliente que puede usar el token
    options.ValidAudience = "client-123";
    
    // Duración del token en minutos
    options.ExpireInMinutes = 120;
}, ServiceLifetime.Scoped);
}

## 💻 Usage Examples

### 1. Basic Token Generation

```csharp
public class AuthService
{
    private readonly IJwtTokenGenerator _tokenGenerator;
    private readonly ILogger<AuthService> _logger;

    public AuthService(IJwtTokenGenerator tokenGenerator, ILogger<AuthService> logger)
    {
        _tokenGenerator = tokenGenerator;
        _logger = logger;
    }

    public async Task<AuthResponse> AuthenticateAsync(LoginRequest request)
    {
        // 1. Validate user credentials (replace with your actual user validation logic)
        var user = await _userService.ValidateCredentialsAsync(request.Username, request.Password);
        if (user == null)
        {
            _logger.LogWarning("Authentication failed for user {Username}", request.Username);
            return AuthResponse.Failed("Invalid username or password");
        }

        // 2. Generate access token
        var claims = new List<Claim>
        {
            new(JwtRegisteredClaimNames.Sub, user.Id),
            new(JwtRegisteredClaimNames.Name, user.Username),
            new(JwtRegisteredClaimNames.Email, user.Email),
            new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            new("custom_claim", "custom_value") // Add custom claims as needed
        };

        // Add roles as claims
        foreach (var role in user.Roles)
        {
            claims.Add(new Claim(ClaimTypes.Role, role));
        }

        // 3. Generate access token
        var accessToken = _tokenGenerator.GenerateAccessToken(claims);
        
        // 4. Generate refresh token
        var refreshToken = _tokenGenerator.GenerateRefreshToken();
        
        // 5. Store refresh token (implementation depends on your storage)
        await _tokenStore.StoreRefreshTokenAsync(user.Id, refreshToken, 
            DateTime.UtcNow.AddDays(7)); // Match with RefreshTokenExpireDays

        _logger.LogInformation("User {UserId} authenticated successfully", user.Id);
        
        return new AuthResponse
        {
            AccessToken = accessToken,
            RefreshToken = refreshToken,
            ExpiresIn = TimeSpan.FromMinutes(60).TotalSeconds,
            TokenType = "Bearer"
        };
    }
}

### 2. Token Refresh Flow

```csharp
public class TokenService
{
    private readonly IJwtTokenGenerator _tokenGenerator;
    private readonly IRefreshTokenService _refreshTokenService;
    private readonly ILogger<TokenService> _logger;

    public TokenService(
        IJwtTokenGenerator tokenGenerator,
        IRefreshTokenService refreshTokenService,
        ILogger<TokenService> logger)
    {
        _tokenGenerator = tokenGenerator;
        _refreshTokenService = refreshTokenService;
        _logger = logger;
    }

    public async Task<AuthResponse> RefreshTokenAsync(string accessToken, string refreshToken)
    {
        // 1. Validate the expired access token (without lifetime validation)
        var principal = _tokenGenerator.GetPrincipalFromExpiredToken(accessToken);
        var userId = principal.FindFirstValue(JwtRegisteredClaimNames.Sub);
        
        if (string.IsNullOrEmpty(userId))
        {
            _logger.LogWarning("Invalid access token format");
            return AuthResponse.Failed("Invalid token");
        }

        // 2. Validate refresh token
        var storedToken = await _refreshTokenService.GetRefreshTokenAsync(userId, refreshToken);
        if (storedToken == null || storedToken.IsUsed || storedToken.IsRevoked || 
            storedToken.ExpiryDate <= DateTime.UtcNow)
        {
            _logger.LogWarning("Invalid refresh token for user {UserId}", userId);
            return AuthResponse.Failed("Invalid refresh token");
        }

        // 3. Mark token as used to prevent reuse
        await _refreshTokenService.MarkAsUsedAsync(storedToken.Id);

        // 4. Generate new tokens
        var newAccessToken = _tokenGenerator.GenerateAccessToken(principal.Claims);
        var newRefreshToken = _tokenGenerator.GenerateRefreshToken();
        
        // 5. Store the new refresh token
        await _refreshTokenService.StoreRefreshTokenAsync(
            userId, 
            newRefreshToken, 
            DateTime.UtcNow.AddDays(7));

        _logger.LogInformation("Refreshed tokens for user {UserId}", userId);
        
        return new AuthResponse
        {
            AccessToken = newAccessToken,
            RefreshToken = newRefreshToken,
            ExpiresIn = TimeSpan.FromMinutes(60).TotalSeconds,
            TokenType = "Bearer"
        };
    }
}

### 3. Protected API Endpoints

```csharp
[ApiController]
[Route("api/[controller]")]
[Authorize] // Requires authentication by default
public class UsersController : ControllerBase
{
    private readonly IUserService _userService;
    private readonly ILogger<UsersController> _logger;

    public UsersController(IUserService userService, ILogger<UsersController> logger)
    {
        _userService = userService;
        _logger = logger;
    }

    [HttpGet("me")]
    public async Task<IActionResult> GetCurrentUser()
    {
        var userId = User.FindFirstValue(JwtRegisteredClaimNames.Sub);
        var user = await _userService.GetUserByIdAsync(userId);
        
        if (user == null)
        {
            _logger.LogWarning("User {UserId} not found", userId);
            return NotFound();
        }

        _logger.LogInformation("Retrieved profile for user {UserId}", userId);
        return Ok(user);
    }

    [HttpPut("profile")]
    public async Task<IActionResult> UpdateProfile(UpdateProfileRequest request)
    {
        var userId = User.FindFirstValue(JwtRegisteredClaimNames.Sub);
        var result = await _userService.UpdateProfileAsync(userId, request);
        
        if (!result.Succeeded)
        {
            _logger.LogWarning("Failed to update profile for user {UserId}", userId);
            return BadRequest(result.Errors);
        }

        _logger.LogInformation("Updated profile for user {UserId}", userId);
        return NoContent();
    }

    [HttpGet("admin-only")]
    [Authorize(Roles = "Admin")] // Requires Admin role
    public IActionResult AdminOnly()
    {
        return Ok("This is an admin-only endpoint");
    }

    [HttpGet("custom-policy")]
    [Authorize(Policy = "MinimumAge")] // Requires custom policy
    public IActionResult MinimumAge()
    {
        return Ok("You meet the minimum age requirement");
    }
}

## 🔐 Security Best Practices

### 1. Secure Token Storage

```csharp
// In your token storage implementation
public class RefreshTokenStore : IRefreshTokenService
{
    private readonly IDataProtector _protector;
    private readonly ApplicationDbContext _context;
    private readonly ILogger<RefreshTokenStore> _logger;

    public RefreshTokenStore(
        IDataProtectionProvider dataProtectionProvider,
        ApplicationDbContext context,
        ILogger<RefreshTokenStore> logger)
    {
        _protector = dataProtectionProvider.CreateProtector("RefreshTokenPurpose");
        _context = context;
        _logger = logger;
    }

    public async Task<string> StoreRefreshTokenAsync(string userId, string token, DateTime expiryDate)
    {
        // Encrypt the token before storing
        var protectedToken = _protector.Protect(token);
        
        var refreshToken = new RefreshToken
        {
            UserId = userId,
            Token = protectedToken,
            ExpiryDate = expiryDate,
            CreatedAt = DateTime.UtcNow,
            IsRevoked = false,
            IsUsed = false
        };

        _context.RefreshTokens.Add(refreshToken);
        await _context.SaveChangesAsync();
        
        _logger.LogInformation("Stored refresh token for user {UserId}", userId);
        
        // Return the original (unencrypted) token to the client
        return token;
    }

    public async Task<RefreshToken> GetRefreshTokenAsync(string userId, string token)
    {
        // Find all non-expired, non-revoked, non-used tokens for the user
        var tokens = await _context.RefreshTokens
            .Where(t => t.UserId == userId && 
                       !t.IsRevoked && 
                       !t.IsUsed && 
                       t.ExpiryDate > DateTime.UtcNow)
            .ToListAsync();

        // Try to find a matching token
        foreach (var storedToken in tokens)
        {
            try
            {
                var unprotectedToken = _protector.Unprotect(storedToken.Token);
                if (unprotectedToken == token)
                {
                    return storedToken;
                }
            }
            catch (CryptographicException ex)
            {
                _logger.LogError(ex, "Failed to unprotect refresh token {TokenId}", storedToken.Id);
                continue;
            }
        }

        return null;
    }
}

### 2. Token Validation

```csharp
// In your startup configuration
services.AddJwtAuthentication(options =>
{
    // ... other options ...
    
    // Enable token validation events for additional security checks
    options.Events = new JwtBearerEvents
    {
        OnTokenValidated = context =>
        {
            // Additional validation can be performed here
            var userId = context.Principal.FindFirstValue(JwtRegisteredClaimNames.Sub);
            
            // Example: Check if user exists and is active
            var userService = context.HttpContext.RequestServices
                .GetRequiredService<IUserService>();
                
            var user = userService.GetUserByIdAsync(userId).GetAwaiter().GetResult();
            
            if (user == null || !user.IsActive)
            {
                context.Fail("User is not active or does not exist");
            }
            
            return Task.CompletedTask;
        },
        OnAuthenticationFailed = context =>
        {
            if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
            {
                context.Response.Headers.Add("Token-Expired", "true");
            }
            
            return Task.CompletedTask;
        }
    };
});

## 📚 API Reference

### Interfaces

#### `IJwtTokenGenerator`
Main interface for JWT token generation and validation.

**Methods:**
- `string GenerateAccessToken(IEnumerable<Claim> claims)` - Generates a JWT access token
- `string GenerateRefreshToken()` - Generates a secure refresh token
- `ClaimsPrincipal GetPrincipalFromExpiredToken(string token)` - Gets principal from expired token (for refresh flow)
- `bool ValidateToken(string token, out ClaimsPrincipal principal)` - Validates a token

#### `IRefreshTokenService`
Interface for managing refresh tokens.

**Methods:**
- `Task<string> StoreRefreshTokenAsync(string userId, string token, DateTime expiryDate)`
- `Task<RefreshToken> GetRefreshTokenAsync(string userId, string token)`
- `Task MarkAsUsedAsync(string tokenId)`
- `Task RevokeTokenAsync(string tokenId)`
- `Task RevokeAllTokensAsync(string userId)`

### Configuration

#### `JwtOptions`
Configuration options for JWT authentication.

**Properties:**
- `string Issuer` - Token issuer (required)
- `string Audience` - Token audience (required)
- `string SecretKey` - Secret key for signing tokens (required)
- `int ExpireMinutes` - Token expiration in minutes (default: 60)
- `int RefreshTokenExpireDays` - Refresh token expiration in days (default: 7)
- `TimeSpan ClockSkew` - Clock skew for token validation (default: 5 minutes)
- `bool ValidateIssuer` - Whether to validate the issuer (default: true)
- `bool ValidateAudience` - Whether to validate the audience (default: true)
- `bool ValidateLifetime` - Whether to validate the lifetime (default: true)
- `bool ValidateIssuerSigningKey` - Whether to validate the signing key (default: true)
- `TokenValidationParameters TokenValidationParameters` - Advanced token validation parameters

## 🔒 Security Considerations

1. **Key Management**
   - Never hardcode secrets in source code
   - Use Azure Key Vault, AWS Secrets Manager, or environment variables in production
   - Rotate signing keys periodically

2. **Token Security**
   - Use HTTPS in production
   - Set appropriate token expiration times
   - Implement token revocation
   - Use secure, random refresh tokens

3. **Best Practices**
   - Implement rate limiting on authentication endpoints
   - Log security events (failed logins, token refreshes, etc.)
   - Use the latest security patches for all dependencies

## 📝 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## 🤝 Contributing

Contributions are welcome! Please read our [contributing guidelines](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.

## 📫 Support

For support, please open an issue in our [issue tracker](https://github.com/davidvazquezpalestino/DeveloperKit/issues).

---

<div align="center">
  Made with ❤️ by the DeveloperKit Team
</div>
Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2025.11.11 353 11/11/2025
2025.10.26 215 10/26/2025
2025.10.11 145 10/11/2025
2025.10.10 162 10/10/2025
2025.8.19 236 8/19/2025
2025.7.13 301 7/14/2025
2025.5.23 188 5/23/2025
2025.5.1 232 5/1/2025
2025.4.30 228 4/30/2025
2025.4.16 279 4/17/2025
2025.4.14 300 4/15/2025
2025.4.9 249 4/9/2025
2025.4.8 235 4/8/2025
2025.4.7 226 4/8/2025
2025.3.22 246 3/22/2025
2025.3.8 208 3/9/2025
2025.2.1 217 2/1/2025
2024.11.12 200 11/12/2024
2024.10.6 204 10/4/2024
2024.9.29 220 9/28/2024
Loading failed