JwtServicePackage 10.0.20
dotnet add package JwtServicePackage --version 10.0.20
NuGet\Install-Package JwtServicePackage -Version 10.0.20
<PackageReference Include="JwtServicePackage" Version="10.0.20" />
<PackageVersion Include="JwtServicePackage" Version="10.0.20" />
<PackageReference Include="JwtServicePackage" />
paket add JwtServicePackage --version 10.0.20
#r "nuget: JwtServicePackage, 10.0.20"
#:package JwtServicePackage@10.0.20
#addin nuget:?package=JwtServicePackage&version=10.0.20
#tool nuget:?package=JwtServicePackage&version=10.0.20
๐ JwtServicePackage
A production-ready JWT authentication and token lifecycle engine for .NET, designed for security, scalability, and real-world distributed systems.
Unlike traditional JWT setups that rely on static secrets and stateless validation only, this package introduces a full token security lifecycle system with:
- Key rotation
- Multi-key validation (zero-downtime rotation)
- Refresh token lifecycle management
- Automatic claims preservation on refresh
- Token revocation (blacklisting)
- Replay attack detection
- Session control per user/device
๐ Updates & Release Notes
๐ What's New in Version 10.0.20
- Automatic Claims Preservation on Refresh:
RefreshToken()can now take an optionalexpiredAccessToken. If no new claims are provided, the service extracts and carries forward the claims from the previous token automaticallyโeliminating redundant database lookups during token renewal. - Flexible Claims Overloads: Support for
IEnumerable<Claim>alongsideDictionary<string, object>across token generation and refresh methods. - Enhanced Token & Principal Helpers: Added
GetPrincipalFromExpiredToken(),GetUserIdFromPrincipal(),GetClaimFromToken<T>(), and generic claims access viaTokenValidationResult.GetClaimValue<T>(). - Absolute Refresh Expiration Window: Refresh token rotation retains the original absolute session expiration window configured in
appsettings.json(RefreshTokenExpiryDays). - DI & Extension Method Cleanups: Fixed static DI extension method signatures (
this IServiceCollection) and key resolver performance inAddJwtAuthentication.
IMPORTANT: Version 10.0.20 is the latest recommended release. Users on
10.0.10or earlier are encouraged to upgrade.
๐ Features
๐ Authentication Core
- Generate Access + Refresh token pairs using standard
Claimcollections or dictionary key-value maps. - Claims-based identity support with built-in extraction helpers.
- Fast token decoding and
ClaimsPrincipalextraction utilities.
๐ Token Lifecycle Management
- Smart refresh token rotation with claim preservation.
- Absolute persistence window matching
appsettings.json. - Per-user active session limits (
MaxActiveTokensPerUser). - Revocation options (single access token, refresh token, or all user sessions).
- Device and IP-aware session tracking.
๐ง Security Enhancements
- Replay attack detection (JTI tracking) with configurable window (
EnableTokenReplayDetectionMinutes). - Token blacklisting for instant access revocation.
- Cryptographic hash-based refresh token storage.
- Active per-user session limits.
๐ Key Management System
- Automatic key generation if not provided.
- Rotating signing keys with KeyId (
kid). - Multi-key validation for zero-downtime key rotation.
- Old signing keys retained during the active refresh window.
๐ฆ Installation
dotnet add package JwtServicePackage --version 10.0.20
โ๏ธ Configuration
Add to appsettings.json:
{
"JwtSettings": {
"SecretKey": "your-initial-secret-key-32chars-minimum",
"Issuer": "your-app",
"Audience": "your-app-users",
"AccessTokenExpiryMinutes": 15,
"RefreshTokenExpiryDays": 7,
"EnableKeyRotation": true,
"KeyRotationIntervalDays": 7,
"EnableTokenReplayDetection": false,
"EnableTokenReplayDetectionMinutes": 5,
"EnableTokenBlacklisting": true,
"MaxActiveTokensPerUser": 5
}
}
๐งฉ Setup (Program.cs)
builder.Services.AddJwtAuthentication(builder.Configuration);
builder.Services.AddHttpContextAccessor();
app.UseAuthentication();
app.UseAuthorization();
๐ Usage
- Generating Tokens
Using standard Claim collections (Recommended):
var claims = new List<Claim>
{
new(ClaimTypes.Email, user.Email),
new(ClaimTypes.Name, $"{user.FirstName} {user.LastName}"),
new(ClaimTypes.Role, user.RoleKey)
};
// Add multi-value claims such as permissions easily
foreach (var perm in userPermissions)
{
claims.Add(new Claim("permissions", perm));
}
var tokens = _jwtService.GenerateTokenPair(
userId: user.UserId.ToString(),
claims: claims,
deviceInfo: Request.Headers.UserAgent.ToString(),
ipAddress: HttpContext.Connection.RemoteIpAddress?.ToString()
);
Or using Dictionary<string, object>:
var customClaims = new Dictionary<string, object>
{
[ClaimTypes.Email] = user.Email,
[ClaimTypes.Role] = user.RoleKey,
["permissions"] = new[] { "read:users", "write:users" }
//OR
["permissions"] = string.Join(",", new[] { "read:users", "write:users" })
};
var tokens = _jwtService.GenerateTokenPair(
userId: user.UserId.ToString(),
customClaims: customClaims
);
- Validating Tokens
var result = _jwtService.ValidateAccessToken(accessToken);
if (!result.IsValid)
{
// Handle invalid/expired token
var error = result.ErrorMessage;
var errorType = result.ErrorType; // e.g. TokenValidationErrorType.Expired
}
// Extract strongly-typed claims directly from validation result
var email = result.GetClaimValue<string>(ClaimTypes.Email);
- Smart Token Refresh (Zero Database Overhead)
In v10.0.20, passing the expiredAccessToken automatically preserves all claims from the expired token into the new access tokenโsaving unnecessary database calls:
[HttpPost("refresh")]
public IActionResult Refresh([FromBody] RefreshRequestDto request)
{
// Refreshes the pair, retains existing claims, and maintains original session expiry window
var tokenPair = _jwtService.RefreshToken(
refreshToken: request.RefreshToken,
expiredAccessToken: request.AccessToken,
updatedClaims: null, // Pass null to automatically preserve claims from expired token
newDeviceInfo: Request.Headers.UserAgent.ToString(),
newIpAddress: HttpContext.Connection.RemoteIpAddress?.ToString()
);
return Ok(tokenPair);
}
Note: If user roles or permissions have changed, you can pass updated claims into updatedClaims to override the old payload.
- Extraction & Helper Utilities
// Get ClaimsPrincipal from an expired access token (lifetime check ignored)
ClaimsPrincipal? principal = _jwtService.GetPrincipalFromExpiredToken(expiredAccessToken);
// Extract User ID directly from a principal
string? userId = _jwtService.GetUserIdFromPrincipal(principal);
// Extract a specific claim value directly from a token string
string? userEmail = _jwtService.GetClaimFromToken<string>(token, ClaimTypes.Email);
// Decode raw token claims map
Dictionary<string, object> claimsMap = _jwtService.DecodeToken(token);
๐ซ Revocation
// Revoke a specific access token JTI
_jwtService.RevokeToken(accessToken, reason: "User logged out");
// Revoke a refresh token
_jwtService.RevokeRefreshToken(refreshToken, reason: "Security rotation");
// Revoke all active sessions for a user (e.g., password reset)
_jwtService.RevokeAllUserTokens(userId, reason: "Password changed");
๐ค Access Current User in Controllers
Use standard ASP.NET Core HttpContext claims:
var userId = HttpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var userEmail = HttpContext.User.FindFirst(ClaimTypes.Email)?.Value;
๐ Security Model
Client Request โโโบ JwtBearer Middleware โโโบ Cryptographic Validation
โ
โผ
JwtService โโโบ Business Security Rules (Blacklist / Replay / Revocation)
JWT Middleware: Handles signature, expiration, issuer, and audience validation.
JwtService: Handles lifecycle state, blacklisting, replay protection, and key rotation management.
๐ Background Cleanup
Automatic background cleanup runs hourly via BackgroundService:
Purges expired and revoked refresh tokens.
Cleans old revoked JTIs older than 7 days.
Flushes expired JTI entries from the replay tracking cache.
๐ License
MIT License - free for commercial and personal use.
๐จโ๐ป Author
Created and Maintained by: Ethern-Myth
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
-
net10.0
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 10.0.5)
- Microsoft.Extensions.Caching.Memory (>= 10.0.5)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Options (>= 10.0.5)
- Microsoft.IdentityModel.Tokens (>= 8.17.0)
- System.IdentityModel.Tokens.Jwt (>= 8.17.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Read the notes changes for version 10.0.20