JWTSimple 2.0.1
dotnet add package JWTSimple --version 2.0.1
NuGet\Install-Package JWTSimple -Version 2.0.1
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="JWTSimple" Version="2.0.1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="JWTSimple" Version="2.0.1" />
<PackageReference Include="JWTSimple" />
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 JWTSimple --version 2.0.1
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: JWTSimple, 2.0.1"
#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 JWTSimple@2.0.1
#: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=JWTSimple&version=2.0.1
#tool nuget:?package=JWTSimple&version=2.0.1
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
JWTSimple 🔑
JWTSimple is a lightweight authentication and authorization library for .NET 8+ applications.
Features
- JWT authentication with issuer, audience, and token lifetime support
- Refresh token generation and validation
- Secure password hashing
- Simple dependency injection (DI) registration
- Support for custom user identifier types (e.g., Guid, int)
- Flexible identity claim — use email, username, or any custom value
- Fully generic refresh token model with typed Id and UserId
🚀 Installation
dotnet add package JWTSimple --version 2.0.1
Quick Start
1. Configure JWT
using JWTSimple.Extensions;
using JWTSimple.Models;
var builder = WebApplication.CreateBuilder(args);
// 1. Configure JWT settings
var jwtOptions = new JwtOptions
{
SecretKey = Environment.GetEnvironmentVariable("JWT_SECRET")
?? "super-secret-key-that-must-be-very-long-32-characters!",
Issuer = Environment.GetEnvironmentVariable("JWT_ISSUER") ?? "MyJWTSimple",
Audience = Environment.GetEnvironmentVariable("JWT_AUDIENCE") ?? "MyApps",
ExpiryInMinutes = 15 // Short-lived access token
};
// 2. Register JWTSimple dependencies (specify your user ID type)
builder.Services.AddCustomAuth<Guid>(jwtOptions);
builder.Services.AddControllers();
var app = builder.Build();
// 3. Enable authentication and authorization middleware
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
2. Implement the User Model
using System.Security.Claims;
using JWTSimple.Models;
public class AppUser : IAuthUser<Guid>
{
public Guid Id { get; set; }
public string Email { get; set; } = string.Empty;
public string Username { get; set; } = string.Empty;
public string PasswordHash { get; set; } = string.Empty;
public string Role { get; set; } = "User";
// Optional: custom claims embedded into the JWT
public IEnumerable<Claim> CustomClaims => new List<Claim>
{
new Claim(ClaimTypes.Role, Role)
};
// Refresh tokens — specify the same TId as in IAuthUser<TId>
public List<RefreshToken<Guid>> RefreshTokens { get; set; } = new();
}
Note:
IAuthUser<TId>no longer requires anIdentityproperty. You decide what value goes into thenameclaim when callingGenerateToken.
3. Configure EF Core
using Microsoft.EntityFrameworkCore;
public class AppDbContext : DbContext
{
public DbSet<AppUser> Users { get; set; }
public DbSet<RefreshToken<Guid>> RefreshTokens { get; set; }
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AppUser>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasIndex(e => e.Email).IsUnique();
entity.HasIndex(e => e.Username).IsUnique();
entity.HasMany(u => u.RefreshTokens)
.WithOne()
.HasForeignKey(e => e.UserId)
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<RefreshToken<Guid>>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasIndex(e => e.Token).IsUnique();
});
}
}
Note: Use
RefreshToken<int>if your user ID type isint, orRefreshToken<Guid>forGuid. BothIdandUserIdwill use the same type.
4. Authentication Example
using System.Security.Claims;
using Microsoft.AspNetCore.Mvc;
using JWTSimple.Interfaces;
using JWTSimple.Models;
[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
private readonly IPasswordHasher _passwordHasher;
private readonly IJwtTokenService<Guid> _jwtService;
private readonly JwtOptions _jwtOptions;
private static readonly List<AppUser> MockUserDatabase = new();
public AuthController(
IPasswordHasher passwordHasher,
IJwtTokenService<Guid> jwtService,
JwtOptions jwtOptions)
{
_passwordHasher = passwordHasher;
_jwtService = jwtService;
_jwtOptions = jwtOptions;
}
[HttpPost("login")]
public IActionResult Login([FromBody] LoginRequest request)
{
var user = MockUserDatabase.FirstOrDefault(u => u.Email == request.Email);
if (user == null)
return Unauthorized("Invalid email or password");
var isPasswordValid = _passwordHasher.VerifyPassword(request.Password, user.PasswordHash);
if (!isPasswordValid)
return Unauthorized("Invalid email or password");
var accessToken = _jwtService.GenerateToken(user, _jwtOptions, user.Email);
var refreshTokenValue = _jwtService.GenerateRefreshToken();
var refreshToken = new RefreshToken<Guid>
{
Id = Guid.NewGuid(),
Token = refreshTokenValue,
ExpiresAt = DateTime.UtcNow.AddDays(30),
UserId = user.Id
};
user.RefreshTokens.Add(refreshToken);
return Ok(new AuthResponse(accessToken, refreshTokenValue));
}
[HttpPost("login-by-username")]
public IActionResult LoginByUsername([FromBody] UsernameLoginRequest request)
{
var user = MockUserDatabase.FirstOrDefault(u => u.Username == request.Username);
if (user == null)
return Unauthorized("Invalid username or password");
var isPasswordValid = _passwordHasher.VerifyPassword(request.Password, user.PasswordHash);
if (!isPasswordValid)
return Unauthorized("Invalid username or password");
var accessToken = _jwtService.GenerateToken(user, _jwtOptions, user.Username);
var refreshTokenValue = _jwtService.GenerateRefreshToken();
var refreshToken = new RefreshToken<Guid>
{
Id = Guid.NewGuid(),
Token = refreshTokenValue,
ExpiresAt = DateTime.UtcNow.AddDays(30),
UserId = user.Id
};
user.RefreshTokens.Add(refreshToken);
return Ok(new AuthResponse(accessToken, refreshTokenValue));
}
[HttpPost("refresh")]
public IActionResult Refresh([FromBody] RefreshRequest request)
{
try
{
var principal = _jwtService.GetPrincipalFromExpiredToken(request.AccessToken, _jwtOptions.SecretKey);
var userIdString = principal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (string.IsNullOrEmpty(userIdString) || !Guid.TryParse(userIdString, out var userId))
return Unauthorized("Invalid token");
var user = MockUserDatabase.FirstOrDefault(u => u.Id == userId);
if (user == null)
return Unauthorized("User not found");
var savedToken = user.RefreshTokens.FirstOrDefault(t => t.Token == request.RefreshToken);
if (savedToken == null || savedToken.IsExpired)
return Unauthorized("Invalid or expired refresh token");
var newAccessToken = _jwtService.GenerateToken(user, _jwtOptions, user.Email);
var newRefreshTokenValue = _jwtService.GenerateRefreshToken();
user.RefreshTokens.Remove(savedToken);
user.RefreshTokens.Add(new RefreshToken<Guid>
{
Id = Guid.NewGuid(),
Token = newRefreshTokenValue,
ExpiresAt = DateTime.UtcNow.AddDays(30),
UserId = user.Id
});
return Ok(new AuthResponse(newAccessToken, newRefreshTokenValue));
}
catch
{
return Unauthorized("Failed to refresh token");
}
}
}
// DTO models
public record LoginRequest(string Email, string Password);
public record UsernameLoginRequest(string Username, string Password);
public record RefreshRequest(string AccessToken, string RefreshToken);
public record AuthResponse(string AccessToken, string RefreshToken);
Breaking Changes in v2.0
| v1.x | v2.0 |
|---|---|
IAuthUser<TId> required Identity property |
Identity removed from interface |
GenerateToken(user, options) |
GenerateToken(user, options, identity) |
RefreshToken without Id |
RefreshToken<TId> with fully typed Id and UserId |
IEmailAuthenticatable, IUsernameAuthenticatable, IPhoneAuthenticatable |
Removed — no longer needed |
RefreshToken.AssociatedAccessTokenId |
Removed — simplified model |
Migration:
// Before (v1.x)
public class User : IAuthUser<Guid>
{
public Guid Id { get; set; }
public string Identity { get; set; } = null!;
public string PasswordHash { get; set; } = null!;
public List<RefreshToken> RefreshTokens { get; set; } = new();
}
var token = _jwtService.GenerateToken(user, options);
// After (v2.0)
public class User : IAuthUser<Guid>
{
public Guid Id { get; set; }
public string Email { get; set; } = null!;
public string PasswordHash { get; set; } = null!;
public List<RefreshToken<Guid>> RefreshTokens { get; set; } = new();
}
var token = _jwtService.GenerateToken(user, options, user.Email);
var refreshToken = new RefreshToken<Guid>
{
Id = Guid.NewGuid(),
Token = tokenValue,
ExpiresAt = DateTime.UtcNow.AddDays(30),
UserId = user.Id
};
Configuration
The library uses the following JwtOptions class:
public class JwtOptions
{
public string SecretKey { get; set; } = string.Empty;
public string Issuer { get; set; } = string.Empty;
public string Audience { get; set; } = string.Empty;
public int ExpiryInMinutes { get; set; } = 30;
}
Important:
SecretKeymust be at least 32 characters long.
Architecture & Core Components
IPasswordHasher— password hashing and verificationIJwtTokenService<TId>— JWT and refresh token generationJwtOptions— JWT configurationRefreshToken<TId>— fully generic refresh token model with typedIdandUserIdIAuthUser<TId>— minimal user contract (Id,PasswordHash, optionalCustomClaims)
License
This project is licensed under the MIT License.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. 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 was computed. 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.
-
net8.0
- BCrypt.Net-Next (>= 4.2.0)
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 8.0.28)
- System.IdentityModel.Tokens.Jwt (>= 8.19.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.