Creating JWT Tokens with Microsoft.IdentityModel.JsonWebTokens
Problem Statement
When migrating from System.IdentityModel.Tokens.Jwt to the newer Microsoft.IdentityModel.JsonWebTokens library, developers often struggle to convert existing token generation code. The original approach (JwtSecurityTokenHandler) uses a different pattern than the newer JsonWebTokenHandler, leading to confusion about:
- Proper token descriptor configuration
- Claims representation
- Timestamp handling
- Signing credential setup
Solution: Using JsonWebTokenHandler
Step-by-Step Implementation
Here's the modern approach using Microsoft.IdentityModel.JsonWebTokens:
using System.Text;
using Microsoft.IdentityModel.Tokens;
// 1. Prepare security key
byte[] keyData = Encoding.UTF8.GetBytes("SomeStringFromConfig1234");
var securityKey = new SymmetricSecurityKey(keyData);
// 2. Configure claims (as dictionary)
var claims = new Dictionary<string, object>
{
[ClaimTypes.Name] = "Testuser",
[ClaimTypes.GroupSid] = "Tenant1",
[ClaimTypes.Sid] = "3c545f1c-cc1b-4cd5-985b-8666886f985b"
};
// 3. Set up token descriptor
var descriptor = new SecurityTokenDescriptor
{
Issuer = "MyIssuer",
Audience = "MyAudience",
Claims = claims,
IssuedAt = null, // Explicitly disable auto-generation
NotBefore = DateTime.UtcNow,
Expires = DateTime.UtcNow.AddMinutes(120),
SigningCredentials = new SigningCredentials(
securityKey,
SecurityAlgorithms.HmacSha256Signature
)
};
// 4. Create handler and generate token
var handler = new JsonWebTokenHandler();
handler.SetDefaultTimesOnTokenCreation = false; // Critical for manual timestamp control
string tokenString = handler.CreateToken(descriptor);Key Differences Explained
Claims Representation
Claims are now aDictionary<string, object>instead ofList<Claim>. This directly maps to JWT claims without conversion overhead.Timestamp Control
By settingSetDefaultTimesOnTokenCreation = false, we:- Disable automatic
iat(IssuedAt) generation - Gain explicit control over
nbf(NotBefore) andexp(Expiry) - Avoid duplicate timestamp claims
- Disable automatic
Token Creation Workflow
Uses a handler/descriptor pattern instead of constructing token objects:JsonWebTokenHandler + SecurityTokenDescriptor → CreateToken()
Output Validation
Both approaches generate identical JWT payloads:
{
"aud": "MyAudience",
"iss": "MyIssuer",
"exp": 1709078400,
"nbf": 1708992000,
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "Testuser",
"http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid": "Tenant1",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/sid": "3c545f1c-cc1b-4cd5-985b-8666886f985b"
}Important Considerations
- Key Length: HMAC-SHA256 requires keys ≥256 bits (32+ characters)
- Clock Skew: Production systems should handle minor time mismatches
- Token Validation: Pair with
TokenValidationParametersfor validation
Performance Benefit
Microsoft benchmarks show ~30% faster token processing with JsonWebTokenHandler versus the legacy library
Common Errors
- Automatic timestamps: Forgetting
handler.SetDefaultTimesOnTokenCreation = falseaddsiat - Byte padding: Incorrect key encoding causes security exceptions
- Claim collisions: Duplicate keys in dictionary result in overwrites
Best Practices
- Store sensitive keys in secure configuration (Azure Key Vault, environment variables)
- Validate tokens using the same library version
- Prefer
RsaSecurityKeyover symmetric keys where possible
var validationParams = new TokenValidationParameters
{
ValidIssuer = "MyIssuer",
ValidAudience = "MyAudience",
IssuerSigningKey = securityKey
};
TokenValidationResult result = handler.ValidateToken(tokenString, validationParams);