MiniJwt.Core is a lightweight JWT library for .NET that provides a simple way to generate and validate JWT tokens using attributes on your model properties. It's designed to be minimal, dependency-injection friendly, and multi-target framework compatible.
MiniJwt.Core supports:
- .NET 6.0
- .NET 7.0
- .NET 8.0
- .NET 9.0
- .NET 10.0
Yes, the MiniJwtService implementation is thread-safe and can be registered as a singleton in the dependency injection container.
Yes, MiniJwt.Core is production-ready. However, always follow security best practices (see below) for key management, token lifetime, and secure communication.
The most common reasons are:
- Secret key too short: The
SecretKeymust be at least 32 bytes (256 bits) for HS256 algorithm - Invalid configuration: Check that your
MiniJwtOptionsare properly configured - Exceptions during generation: Enable logging to see detailed error messages
// Ensure your secret key is long enough
options.SecretKey = "your-secret-key-at-least-32-bytes-long-for-hs256";builder.Services.AddMiniJwt(options =>
{
options.SecretKey = Environment.GetEnvironmentVariable("JWT_SECRET_KEY")
?? throw new InvalidOperationException("JWT_SECRET_KEY not set");
options.Issuer = builder.Configuration["MiniJwt:Issuer"];
options.Audience = builder.Configuration["MiniJwt:Audience"];
});Yes, use environment-specific appsettings.json files:
appsettings.Development.json- for developmentappsettings.Production.json- for production
Or use environment variables for sensitive configuration like secret keys.
Yes, MiniJwt.Core uses IOptionsMonitor<MiniJwtOptions> which supports runtime configuration updates. Changes to the configuration will be picked up automatically by the service.
MiniJwt.Core includes:
- All properties decorated with
[MiniJwtClaim]attribute - Standard JWT claims:
iss(issuer),aud(audience),nbf(not before),exp(expiration),jti(JWT ID)
Currently, MiniJwt.Core uses attributes to map properties to claims. If you need more flexibility, you can:
- Create a wrapper payload object with attributed properties
- Extend the library with a custom implementation
- Use the underlying
System.IdentityModel.Tokens.Jwtdirectly for advanced scenarios
Use the ExpirationMinutes option:
options.ExpirationMinutes = 60; // 1 hour
options.ExpirationMinutes = 1440; // 24 hours
options.ExpirationMinutes = 0.5; // 30 secondsIf Issuer or Audience are empty strings:
- The claims won't be included in the token
- Validation for these claims will be disabled
For production use, it's strongly recommended to set both for proper token validation.
Common reasons:
- Token expired: Check the
ExpirationMinutessetting - Wrong secret key: Validation must use the same secret key as generation
- Issuer/Audience mismatch: Ensure configuration matches between generation and validation
- Malformed token: The token string may be corrupted or invalid
- Clock skew: Token validation uses zero clock skew by default
Enable logging to see detailed validation error messages.
Tokens are automatically validated for expiration. When a token expires, ValidateToken returns null.
Best practice: Implement a refresh token mechanism:
public class TokenResponse
{
public string AccessToken { get; set; }
public string RefreshToken { get; set; }
public DateTime ExpiresAt { get; set; }
}
// Store refresh tokens securely (database, Redis, etc.)
// Implement endpoint to exchange refresh token for new access tokenMiniJwt.Core is designed for generating and validating your own tokens. To validate tokens from external issuers (e.g., Azure AD, Auth0), use the standard ASP.NET Core JWT authentication middleware with appropriate configuration.
DO:
- ✅ Generate cryptographically random keys (at least 32 bytes)
- ✅ Store keys in secure secret management systems (Azure Key Vault, AWS Secrets Manager, HashiCorp Vault)
- ✅ Use environment variables for keys, never hardcode
- ✅ Rotate keys periodically
- ✅ Use different keys for different environments
DON'T:
- ❌ Commit keys to source control
- ❌ Store keys in plain text in configuration files
- ❌ Use weak or predictable keys
- ❌ Share keys across unrelated applications
- ❌ Expose keys in logs or error messages
# Using openssl
openssl rand -base64 32
# Using PowerShell
[Convert]::ToBase64String((1..32 | ForEach-Object { Get-Random -Maximum 256 }))
# Using C#
var key = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32));Access Tokens:
- Short-lived: 15-60 minutes
- Reduces the impact of token theft
- Use refresh tokens for longer sessions
Refresh Tokens:
- Long-lived: days to weeks
- Store securely on the backend
- Allow users to obtain new access tokens without re-authentication
DO:
- ✅ Use HTTPS/TLS for all communication
- ✅ Send tokens in the
Authorizationheader:Bearer {token} - ✅ Use secure, httpOnly cookies for web applications
- ✅ Implement token revocation mechanisms
DON'T:
- ❌ Send tokens in URLs (they may be logged)
- ❌ Store tokens in localStorage (vulnerable to XSS)
- ❌ Transmit tokens over unencrypted connections
Key rotation ensures compromised keys have limited impact:
public class KeyRotationService
{
private readonly List<MiniJwtOptions> _keyHistory = new();
public void RotateKey(string newKey)
{
// Keep old key for validation of existing tokens
var oldOptions = GetCurrentOptions();
_keyHistory.Add(oldOptions);
// Update to new key for generation
UpdateOptions(new MiniJwtOptions
{
SecretKey = newKey,
// ... other options
});
// Remove keys older than max token lifetime
CleanupOldKeys();
}
public bool ValidateWithAnyKey(string token)
{
// Try current key first
if (ValidateWithCurrentKey(token)) return true;
// Fall back to historical keys
return _keyHistory.Any(opts => ValidateWithKey(token, opts));
}
}For stateless APIs: Yes, validate the token on every request. This is the standard approach for JWT-based authentication.
For stateful applications: You may cache validation results in the user's session, but ensure proper session security and expiration handling.
- Use HTTPS: Always transmit tokens over secure connections
- Short token lifetimes: Limit the damage if tokens are stolen
- Token binding: Bind tokens to specific clients or IP addresses (advanced)
- Refresh tokens: Use short-lived access tokens with secure refresh mechanisms
- Monitoring: Log and monitor for suspicious token usage patterns
- Revocation: Implement a token revocation/blacklist mechanism if needed
XSS (Cross-Site Scripting):
- Don't store tokens in localStorage (use httpOnly cookies or memory)
- Sanitize all user input
- Use Content Security Policy (CSP) headers
CSRF (Cross-Site Request Forgery):
- When using cookies, implement CSRF protection (anti-forgery tokens)
- Using the
Authorizationheader naturally protects against CSRF - Validate the
OriginorRefererheaders
Generally no for access tokens - JWT tokens are stateless by design.
Consider storing:
- Refresh tokens (with secure hashing)
- Revocation lists for specific tokens
- Audit logs of token generation
See the ASPNetCoreAuth sample for a complete example. In summary:
- Register MiniJwt with
AddMiniJwt() - Configure JWT authentication with matching parameters
- Use
[Authorize]attributes on controllers/endpoints - Generate tokens in your login endpoint
Yes, you can use MiniJwt.Core in Blazor Server or in the API backend for Blazor WebAssembly. For Blazor WebAssembly, you'll typically:
- Generate tokens in your API (using MiniJwt.Core)
- Send tokens to the Blazor client
- Include tokens in API requests from the client
- Validate tokens in the API (using MiniJwt.Core)
Yes, generate tokens with MiniJwt.Core and include them in gRPC metadata:
var metadata = new Metadata
{
{ "Authorization", $"Bearer {token}" }
};
var call = client.MyRpcMethod(request, metadata);Validate tokens in your gRPC service using ASP.NET Core authentication middleware.
See the examples documentation for unit testing patterns. Key points:
- Create test instances with
NullLogger<MiniJwtService>.Instance - Use
Options.Create()for test configurations - Mock
IMiniJwtServicein your tests for isolation - Use
FakeTimeProviderfromMicrosoft.Extensions.TimeProvider.Testingfor deterministic time-dependent testing
For time-dependent testing, inject a FakeTimeProvider into the service constructor to control token generation timestamps. See Testing with TimeProvider for examples.
Ensure logging is properly configured:
builder.Logging.AddConsole();
builder.Logging.SetMinimumLevel(LogLevel.Debug);Or in appsettings.json:
{
"Logging": {
"LogLevel": {
"MiniJwt.Core": "Debug"
}
}
}Common issues:
- Secret key differs between environments
- Issuer/Audience configuration mismatch
- System clock differences (ensure NTP synchronization)
- Configuration not loading correctly
Visit jwt.io and paste your token to decode and inspect its claims. Never paste production tokens with real user data on public websites.
For debugging in code:
var handler = new JwtSecurityTokenHandler();
var jsonToken = handler.ReadToken(token) as JwtSecurityToken;
var claims = jsonToken?.Claims.ToList();
// Inspect claims for debuggingMiniJwt.Core is very lightweight:
- Token generation: ~1-5ms depending on payload size
- Token validation: ~1-3ms
- Memory footprint: Minimal (stateless service)
The service can be safely registered as a singleton for optimal performance.
Yes, the service is thread-safe and designed for high-throughput scenarios. For very high loads:
- Register as singleton (recommended)
- Consider caching validation results if validating the same token multiple times
- Use connection pooling and async APIs in your application
For generation: No need - generation is fast.
For validation: For the same token being validated multiple times in a short period, you can cache the validation result, but ensure:
- Cache expires before the token
- Cache is properly secured
- You handle cache invalidation correctly
Open an issue on GitHub with:
- .NET version
- MiniJwt.Core version
- Minimal reproduction code
- Expected vs actual behavior
- Any relevant logs or error messages
Open a feature request issue on GitHub. For better chances of implementation:
- Explain the use case and motivation
- Provide examples of how the feature would be used
- Consider if it fits the "minimal" philosophy of the library
Yes! Contributions are welcome. Please:
- Follow the existing code style
- Add tests for new features
- Update documentation as needed
- Keep the library minimal and focused
- Check this FAQ
- Review the examples
- Check existing GitHub issues
- Open a new issue with your question