MiniJwt.Core is a lightweight, minimal JWT library for .NET that provides a simple and efficient way to generate and validate JWT tokens using attributes on object properties to define claims. It's designed to be dependency-injection friendly, multi-target framework compatible, and easy to integrate.
Getting Started Guide - Installation and quick start
Configuration Guide - Detailed configuration options
Examples - Code examples and integration patterns
FAQ - Common questions and security best practices
The repository includes three runnable sample applications demonstrating different integration scenarios:
- ConsoleMinimal - Basic console app for token generation and validation
- ASPNetCoreAuth - Full ASP.NET Core web API with JWT authentication
- WorkerService - Background service example with periodic token generation
- .NET 8+
Via the .NET CLI (after the package is published to NuGet):
dotnet add package MiniJwt.Core{
"MiniJwt": {
"SecretKey": "a-very-long-secret-key-at-least-32-bytes-...",
"Issuer": "MyApp",
"Audience": "MyClient",
"ExpirationMinutes": 60
}
}using Microsoft.Extensions.DependencyInjection;
using MiniJwt.Core.Models;
using MiniJwt.Core.Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<MiniJwtOptions>(builder.Configuration.GetSection("MiniJwt"));
// The service depends on IOptions<MiniJwtOptions> and ILogger<MiniJwtService>
builder.Services.AddSingleton<IMiniJwtService, MiniJwtService>();
var app = builder.Build();Note: ILogger<MiniJwtService> is provided automatically by the framework DI. You can choose AddSingleton, AddScoped or AddTransient depending on your needs; the service is stateless after construction and computes the key bytes in the constructor, so Singleton is often suitable.
using MiniJwt.Core.Attributes;
public class UserJwtPayload
{
[MiniJwtClaim("id")]
public int Id { get; set; }
[MiniJwtClaim("email")]
public string? Email { get; set; }
[MiniJwtClaim("name")]
public string? Name { get; set; }
}// Example inside a controller or service where IMiniJwtService is injected
public class AuthController : ControllerBase
{
private readonly IMiniJwtService _jwt;
public AuthController(IMiniJwtService jwt)
{
_jwt = jwt;
}
public IActionResult Login()
{
var payload = new UserJwtPayload { Id = 1, Email = "[email protected]", Name = "Jean" };
var token = _jwt.GenerateToken(payload);
if (token == null) return StatusCode(500, "Failed to generate token");
return Ok(new { token });
}
}var principal = _jwt.ValidateToken(token);
if (principal == null)
{
// Invalid token
}
else
{
// Valid token, access claims via principal.Claims
}var user = _jwt.ValidateAndDeserialize<UserJwtPayload>(token);
if (user == null)
{
// Invalid token or missing claims
}
else
{
// user.Id, user.Email, user.Name are populated if present in the token
}If you create a service instance manually in a test, provide an ILogger<MiniJwtService>. Example using NullLogger:
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
var options = Options.Create(new MiniJwtOptions
{
SecretKey = "IntegrationTestSecretKey_LongEnough_For_HS256_0123456789",
Issuer = "MiniJwt.Tests",
Audience = "MiniJwt.Tests.Client",
ExpirationMinutes = 60
});
var svc = new MiniJwtService(options, NullLogger<MiniJwtService>.Instance, new JwtSecurityTokenHandler());For testable time-dependent behavior, the library supports TimeProvider (built-in for .NET 8+ or via Microsoft.Bcl.TimeProvider for earlier versions). You can inject a FakeTimeProvider for deterministic testing:
using Microsoft.Extensions.Time.Testing;
var fakeTimeProvider = new FakeTimeProvider();
fakeTimeProvider.SetUtcNow(new DateTimeOffset(2024, 1, 15, 10, 0, 0, TimeSpan.Zero));
var svc = new MiniJwtService(
options,
NullLogger<MiniJwtService>.Instance,
new JwtSecurityTokenHandler(),
fakeTimeProvider
);
// Generate token at the fixed time
var token = svc.GenerateToken(user);
// Advance time for further testing
fakeTimeProvider.Advance(TimeSpan.FromMinutes(5));- If
GenerateTokenreturnsnull, check the length of theSecretKey. It must be at least 32 bytes (for HS256). - For validation errors, use
ValidateTokenand enable logs to see exceptions captured by the service. - When publishing via CI (GitHub Actions), use
vMAJOR.MINOR.PATCHtags to trigger package creation and to set the package version.
- Never store the secret key in plain text in a public repository.
- Use a secrets manager (Azure Key Vault, GitHub Secrets, etc.) for your keys in CI/CD.
Q: How do I set the package version when packing?
A: You can use /p:PackageVersion=1.2.3 with dotnet pack or pack from a project that already has the Version set in the csproj.
Q: Why does ValidateAndDeserialize<T> require T to have a parameterless constructor?
A: The service creates an instance of T using the parameterless constructor and then assigns properties from the claims.
Contributions are welcome! Please:
- Follow the existing code style
- Add tests for new features
- Update documentation as needed
- Keep the library minimal and focused
See the examples documentation for development guidelines.
If you encounter issues:
- Check the FAQ
- Review the sample applications
- Search existing issues
- Open a new issue with:
- .NET version used
- Minimal reproduction code
- Logs/stacktraces
This project is licensed under the MIT License - see the LICENSE file for details.
Made with ❤️ by jeanlrnt