JwtServicePackage 10.0.20

dotnet add package JwtServicePackage --version 10.0.20
                    
NuGet\Install-Package JwtServicePackage -Version 10.0.20
                    
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="JwtServicePackage" Version="10.0.20" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="JwtServicePackage" Version="10.0.20" />
                    
Directory.Packages.props
<PackageReference Include="JwtServicePackage" />
                    
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 JwtServicePackage --version 10.0.20
                    
#r "nuget: JwtServicePackage, 10.0.20"
                    
#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 JwtServicePackage@10.0.20
                    
#: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=JwtServicePackage&version=10.0.20
                    
Install as a Cake Addin
#tool nuget:?package=JwtServicePackage&version=10.0.20
                    
Install as a Cake Tool

๐Ÿ” JwtServicePackage

A production-ready JWT authentication and token lifecycle engine for .NET, designed for security, scalability, and real-world distributed systems.

NuGet Version NuGet Downloads

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 optional expiredAccessToken. 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> alongside Dictionary<string, object> across token generation and refresh methods.
  • Enhanced Token & Principal Helpers: Added GetPrincipalFromExpiredToken(), GetUserIdFromPrincipal(), GetClaimFromToken<T>(), and generic claims access via TokenValidationResult.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 in AddJwtAuthentication.

IMPORTANT: Version 10.0.20 is the latest recommended release. Users on 10.0.10 or earlier are encouraged to upgrade.


๐Ÿš€ Features

๐Ÿ” Authentication Core

  • Generate Access + Refresh token pairs using standard Claim collections or dictionary key-value maps.
  • Claims-based identity support with built-in extraction helpers.
  • Fast token decoding and ClaimsPrincipal extraction 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

  1. 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
);
  1. 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);
  1. 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.

  1. 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 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. 
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
10.0.20 44 10/1/2026
10.0.10 109 8/28/2026
10.0.5 159 4/12/2026

Read the notes changes for version 10.0.20