Duende.Labs.JsonWebToken.MicrosoftIdentityModel
2026.902.21
Prefix Reserved
dotnet add package Duende.Labs.JsonWebToken.MicrosoftIdentityModel --version 2026.902.21
NuGet\Install-Package Duende.Labs.JsonWebToken.MicrosoftIdentityModel -Version 2026.902.21
<PackageReference Include="Duende.Labs.JsonWebToken.MicrosoftIdentityModel" Version="2026.902.21" />
<PackageVersion Include="Duende.Labs.JsonWebToken.MicrosoftIdentityModel" Version="2026.902.21" />
<PackageReference Include="Duende.Labs.JsonWebToken.MicrosoftIdentityModel" />
paket add Duende.Labs.JsonWebToken.MicrosoftIdentityModel --version 2026.902.21
#r "nuget: Duende.Labs.JsonWebToken.MicrosoftIdentityModel, 2026.902.21"
#:package Duende.Labs.JsonWebToken.MicrosoftIdentityModel@2026.902.21
#addin nuget:?package=Duende.Labs.JsonWebToken.MicrosoftIdentityModel&version=2026.902.21
#tool nuget:?package=Duende.Labs.JsonWebToken.MicrosoftIdentityModel&version=2026.902.21
Duende.JsonWebToken
A high-performance, zero-dependency JSON Web Token (JWT) library for .NET 10, featuring comprehensive support for JWS, JWE, and SD-JWT (Selective Disclosure).
Features
Core JWT & JWS (RFC 7519, RFC 7515)
- ✅ Full JWT parsing, creation, and validation
- ✅ Strongly-typed API with all standard claims
- ✅ Signature algorithms: HS256/384/512, RS256/384/512, ES256/384/512, PS256/384/512
- ✅ Complete JOSE header support (alg, typ, kid, cty, jku, jwk, x5u, x5c, x5t, x5t#S256, crit)
- ✅ Claims validation with configurable clock skew
- ✅ TimeProvider abstraction for testable time operations
- ✅ 68% faster parsing than baseline (307ns vs 905ns)
- ✅ Sub-microsecond parsing performance
JWE - JSON Web Encryption (RFC 7516)
- ✅ Content encryption: AES-GCM (128/192/256-bit)
- ✅ Key management algorithms:
dir- Direct symmetric encryptionRSA-OAEP/RSA-OAEP-256/RSA-OAEP-384/RSA-OAEP-512- RSA key encryptionA128KW/A192KW/A256KW- AES Key Wrap (RFC 3394)
- ✅ Public key encryption, private key decryption
- ✅ Authenticated encryption with additional data (AEAD)
- ✅ Opt-in token compression (DEFLATE / RFC 1951) with DoS protection
SD-JWT - Selective Disclosure (draft-ietf-oauth-selective-disclosure-jwt-22)
- ✅ Privacy-preserving selective claim disclosure
- ✅ Salted hash digests (SHA-256/384/512)
- ✅ Holder-controlled claim presentation
- ✅ Nested disclosures (objects and arrays)
- ✅ Key binding JWT for holder proof-of-possession
- ✅ Decoy digests for enhanced privacy
- ✅ Path-based claim representation
- ✅ Unique feature - not available in Microsoft.IdentityModel
SD-JWT VC - Verifiable Credentials (draft-ietf-oauth-sd-jwt-vc-13)
- ✅
vct(Verifiable Credential Type) claim support - ✅ Standard claims helpers (given_name, family_name, birthdate, email, etc.)
- ✅ Structured data support (AddressInfo for addresses)
- ✅ Age attestation (age_equal_or_over without revealing birthdate)
- ✅ Document metadata (issuance_date, document_number, issuing_country)
- ✅ Credential status structure (revocation/suspension support)
- ✅ Credential type allowlist validation
- ✅ Standards-based verifiable credentials - not available in Microsoft.IdentityModel
Security & Performance
- ✅ Zero external dependencies - pure .NET BCL
- ✅ Constant-time signature comparison
- ✅ Token size validation (250 KB default limit, DoS prevention)
- ✅ CVE-resistant design (analyzed CVE-2024-21319, CVE-2017-11429)
- ✅ Source generation for optimal JSON serialization
- ✅ Span-based parsing with minimal allocations
- ✅ 384+ passing tests with comprehensive coverage
Quick Start
Installation
dotnet add package Duende.JsonWebToken
Create and Sign a JWT
using Duende.JsonWebToken;
// Create a token
var jwt = new JsonWebToken
{
Subject = "user123",
Issuer = "https://issuer.example.com",
Audiences = ["https://api.example.com"],
ExpiresAt = TimeProvider.System.GetUtcNow().AddHours(1).UtcDateTime,
AdditionalClaims = new Dictionary<string, JsonElement>
{
["role"] = JsonSerializer.SerializeToElement("admin")
}
};
// Sign with HMAC-SHA256
var key = new SymmetricSecurityKey("your-256-bit-secret"u8.ToArray());
var token = jwt.Encode(key, SecurityAlgorithms.HmacSha256);
Console.WriteLine(token);
Parse and Validate a JWT
// Parse the token
var jwt = JsonWebToken.Parse(token);
// Validate claims
var parameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "https://issuer.example.com",
ValidateAudience = true,
ValidAudience = "https://api.example.com",
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(5),
TimeProvider = TimeProvider.System
};
jwt.Validate(parameters);
Console.WriteLine($"Subject: {jwt.Subject}");
Verify Signature
var isValid = jwt.VerifySignature(key, SecurityAlgorithms.HmacSha256);
if (isValid)
{
Console.WriteLine("Signature verified!");
}
Advanced Usage
Encrypt JWT with JWE
// Direct symmetric encryption
var symmetricKey = new SymmetricSecurityKey(secret256bits);
var jweToken = jwt.Encrypt(
key: symmetricKey,
keyManagementAlgorithm: EncryptionAlgorithms.Direct,
contentEncryptionAlgorithm: EncryptionAlgorithms.Aes256Gcm);
// RSA public key encryption
var rsaKey = new RsaSecurityKey(rsa);
var jweToken = jwt.Encrypt(
key: rsaKey,
keyManagementAlgorithm: EncryptionAlgorithms.RsaOAEP256,
contentEncryptionAlgorithm: EncryptionAlgorithms.Aes256Gcm);
// Decrypt
var decryptedJwt = JsonWebToken.Decrypt(jweToken, rsaKey);
Token Compression (Opt-In)
// Compress large payloads before encryption (RFC 7516)
var jwt = new JsonWebToken
{
Subject = "user123",
AdditionalClaims = new Dictionary<string, JsonElement>
{
["large_data"] = JsonSerializer.SerializeToElement(largeObject)
}
};
// Enable compression with DEFLATE algorithm
var jweToken = jwt.Encrypt(
key: symmetricKey,
keyManagementAlgorithm: EncryptionAlgorithms.Direct,
contentEncryptionAlgorithm: EncryptionAlgorithms.Aes256Gcm,
compressionAlgorithm: CompressionAlgorithms.Deflate); // Opt-in compression
// Decompression happens automatically during decryption
var decryptedJwt = JsonWebToken.Decrypt(jweToken, symmetricKey);
⚠️ Security Warning: Token compression can enable DoS attacks (decompression bombs). Only use compression:
- When you control the token creation
- For large payloads that benefit from compression
- With size limits enforced (10 MB input, 100 MB output by default)
- Never blindly accept compressed tokens from untrusted sources without validation
Selective Disclosure with SD-JWT
using Duende.JsonWebToken.SdJwt;
// Issuer: Create SD-JWT with selective claims
var builder = new SdJwtBuilder()
.WithIssuer("https://issuer.example.com")
.WithSubject("user-123")
.WithClaim("given_name", "John") // Public claim
.WithSelectiveClaim("family_name", "Doe") // Selectively disclosable
.WithSelectiveClaim("birthdate", "1940-01-01")
.WithSelectiveClaim("email", "john.doe@example.com")
.WithHashAlgorithm("sha-256");
var sdJwt = builder.Build(key, "HS256");
// Holder: Create presentation revealing only family_name
var presentation = SdJwtPresentation.Create(sdJwt)
.WithDisclosure("family_name")
.Build();
// Verifier: Verify and access disclosed claims
var verifier = new SdJwtVerifier();
var result = verifier.Verify(presentation, key);
if (result.IsValid)
{
Console.WriteLine($"Family Name: {result.DisclosedClaims["family_name"]}");
// email and birthdate remain hidden
}
SD-JWT VC (Verifiable Credentials)
SD-JWT VC extends SD-JWT with standardized verifiable credential semantics according to draft-ietf-oauth-sd-jwt-vc-13.
using Duende.JsonWebToken.SdJwt;
// Issuer: Create identity credential with vct claim
var identityVc = new SdJwtBuilder()
// Required: Verifiable Credential Type
.WithVerifiableCredentialType("https://credentials.example.gov/identity_credential")
// Standard JWT claims
.WithIssuer("https://gov.example.com")
.WithSubject("did:example:user-123")
.WithIssuedAt(TimeProvider.System.GetUtcNow().DateTime)
.WithExpiresAt(TimeProvider.System.GetUtcNow().AddYears(5).DateTime)
// Standard VC claims (selectively disclosable)
.WithGivenName("Alice", selective: true)
.WithFamilyName("Smith", selective: true)
.WithBirthdate("1990-05-15", selective: true)
.WithEmail("alice@example.com", selective: true)
.WithPhoneNumber("+1-555-0123", selective: true)
// Structured address
.WithAddress(new AddressInfo(
StreetAddress: "123 Main St",
Locality: "Springfield",
Region: "IL",
PostalCode: "62701",
Country: "US"), selective: true)
// Age attestation without revealing birthdate
.WithAgeEqualOrOver(new Dictionary<int, bool>
{
[18] = true,
[21] = true,
[65] = false
})
// Document metadata
.WithDocumentNumber("ID-2024-123", selective: true)
.WithIssuingCountry("US", selective: false)
.WithIssuanceDate(DateTime.UtcNow, selective: false)
// Privacy enhancement
.WithDecoyDigests(3)
.Build(key, "HS256");
// Holder: Present for age verification only
var agePresentation = SdJwtPresentation.Create(identityVc)
.WithDisclosure("age_equal_or_over") // Prove age without revealing birthdate
.Build();
// Verifier: Verify with credential type validation
var result = verifier.Verify(agePresentation, key, new SdJwtVerificationOptions
{
RequireVerifiableCredentialType = true,
ValidVerifiableCredentialTypes = ["https://credentials.example.gov/identity_credential"]
});
if (result.IsValid)
{
Console.WriteLine($"Credential Type: {result.VerifiableCredentialType}");
var ageInfo = result.DisclosedClaims["age_equal_or_over"];
var isOver21 = ageInfo.GetProperty("21").GetBoolean();
Console.WriteLine($"Over 21: {isOver21}");
// Birthdate and name remain hidden
}
SD-JWT VC Features:
- ✅
vct(Verifiable Credential Type) - Required claim identifying credential type - ✅ Standard claims helpers -
WithGivenName,WithBirthdate,WithAddress, etc. - ✅ Age attestation -
WithAgeEqualOrOverfor privacy-preserving age verification - ✅ Document metadata - Issuance date, document number, issuing country
- ✅ Status claim - Optional credential status structure (revocation support)
- ✅ Allowlist validation - Verify against accepted credential types
Use Cases:
- Digital identity wallets (eIDAS2, EU Digital Identity)
- Educational credentials (diplomas, transcripts)
- Employment verification (titles, salaries)
- Healthcare records (vaccination, insurance)
- Age-restricted services (prove 21+ without revealing birthdate)
Key Binding JWT (Holder Proof-of-Possession)
// Issuer: Include holder's public key in cnf claim
var holderKey = ECDsa.Create(ECCurve.NamedCurves.nistP256);
var jwk = JsonWebKey.FromECDsa(holderKey, includePrivateKey: false);
var builder = new SdJwtBuilder()
.WithSubject("user-123")
.WithSelectiveClaim("email", "user@example.com")
.WithHolderKey(jwk);
var sdJwt = builder.Build(issuerKey, SecurityAlgorithms.HmacSha256);
// Holder: Create presentation with key binding
var presentation = SdJwtPresentation.Create(sdJwt)
.WithAllDisclosures()
.WithKeyBinding(
holderKey: holderKey,
audience: "https://verifier.example.com",
nonce: "random-nonce-from-verifier",
timeProvider: TimeProvider.System)
.Build();
// Verifier: Validate with key binding
var result = verifier.VerifyWithKeyBinding(
presentation.Token,
issuerKey,
options: new SdJwtVerificationOptions { ... },
expectedAudience: "https://verifier.example.com",
expectedNonce: "random-nonce-from-verifier");
Working with JOSE Headers
The library supports all standard JOSE (JSON Object Signing and Encryption) header parameters for maximum interoperability:
// X.509 Certificate Headers (for enterprise PKI scenarios)
var jwt = new JsonWebToken
{
Subject = "user-123",
KeyId = "cert-2024",
X509CertificateThumbprint = "base64url-sha1-thumbprint",
X509CertificateSha256Thumbprint = "base64url-sha256-thumbprint",
X509CertificateChain = ["cert1-base64", "cert2-base64", "cert3-base64"],
X509Url = "https://example.com/certs/cert-2024.pem"
};
// Content Type (for nested JWTs)
var nestedJwt = new JsonWebToken
{
Subject = "nested-content",
ContentType = "JWT" // Indicates payload is another JWT
};
// JWK Set URL (for key discovery)
var jwtWithJwks = new JsonWebToken
{
Subject = "user",
JwkSetUrl = "https://example.com/.well-known/jwks.json"
};
// Embedded JWK (public key in header)
var publicKeyJwk = JsonSerializer.Deserialize<JsonElement>("""
{
"kty": "RSA",
"n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78...",
"e": "AQAB"
}
""");
var jwtWithKey = new JsonWebToken
{
Subject = "user",
JsonWebKey = publicKeyJwk
};
// Critical Headers (extensions that must be understood)
var jwtWithCritical = new JsonWebToken
{
Subject = "user",
Critical = ["exp", "nbf"] // Recipient MUST understand these
};
// All headers are preserved during parsing
var token = jwt.EncodeUnsigned();
var parsed = JsonWebToken.Parse(token);
// All header values are accessible on the parsed JWT
Supported Algorithms
Signing (JWS - RFC 7515)
| Algorithm | Description | Key Type |
|---|---|---|
| HS256 | HMAC-SHA256 | Symmetric |
| HS384 | HMAC-SHA384 | Symmetric |
| HS512 | HMAC-SHA512 | Symmetric |
| RS256 | RSASSA-PKCS1-v1_5 SHA-256 | RSA |
| RS384 | RSASSA-PKCS1-v1_5 SHA-384 | RSA |
| RS512 | RSASSA-PKCS1-v1_5 SHA-512 | RSA |
| PS256 | RSASSA-PSS SHA-256 | RSA |
| PS384 | RSASSA-PSS SHA-384 | RSA |
| PS512 | RSASSA-PSS SHA-512 | RSA |
| ES256 | ECDSA P-256 SHA-256 | ECDSA |
| ES384 | ECDSA P-384 SHA-384 | ECDSA |
| ES512 | ECDSA P-521 SHA-512 | ECDSA |
Encryption (JWE - RFC 7516)
Key Management Algorithms
| Algorithm | Description |
|---|---|
| dir | Direct use of shared symmetric key |
| RSA-OAEP | RSA-OAEP with SHA-1 |
| RSA-OAEP-256 | RSA-OAEP with SHA-256 (recommended) |
| RSA-OAEP-384 | RSA-OAEP with SHA-384 |
| RSA-OAEP-512 | RSA-OAEP with SHA-512 |
| A128KW | AES Key Wrap 128-bit |
| A192KW | AES Key Wrap 192-bit |
| A256KW | AES Key Wrap 256-bit |
Content Encryption Algorithms
| Algorithm | Description |
|---|---|
| A128GCM | AES-GCM 128-bit |
| A192GCM | AES-GCM 192-bit |
| A256GCM | AES-GCM 256-bit (recommended) |
Compression Algorithms (Opt-In)
| Algorithm | Description |
|---|---|
| DEF | DEFLATE compression (RFC 1951) - Must be explicitly enabled |
Security Note: Compression is disabled by default. Enable only for large payloads you control. Default limits: 10 MB input, 100 MB output (prevents decompression bombs).
Performance Benchmarks
JWT Operations (vs Microsoft.IdentityModel)
Parsing
| Operation | Duende | Microsoft | Ratio |
|---|---|---|---|
| Parse HS256 | 307 ns | 443 ns | 0.69x ✅ |
| Parse RS256 | 338 ns | 445 ns | 0.76x ✅ |
Creation
| Operation | Duende | Microsoft | Ratio |
|---|---|---|---|
| Create HS256 | 1.73 µs | 1.15 µs | 1.50x |
| Create RS256 | 322 µs | 313 µs | 1.03x |
Memory Allocations
- Parsing: 856 B - 1.08 KB per operation
- Creation: 3.8 KB - 5.42 KB per operation
- SD-JWT: 2-8 KB depending on claim count
SD-JWT Operations
| Operation | Time | Memory |
|---|---|---|
| Create (1 claim) | 3.2 µs | 2.4 KB |
| Create (5 claims) | 7.1 µs | 5.1 KB |
| Create (10 claims) | 12.8 µs | 8.9 KB |
| Parse & Verify | 1.8-4.2 µs | 1.8-3.2 KB |
| Key Binding | ~2 ms | ~4 KB |
Conclusion: Production-ready performance across all operations. Parsing is faster than Microsoft, creation is competitive.
Architecture
Project Structure
src/JsonWebToken/
├── JsonWebToken.cs # High-level JWT API
├── JwtParser.cs # JWT parsing (3-part format)
├── JwtBuilder.cs # JWT building
├── Keys/
│ ├── SecurityKey.cs
│ ├── SymmetricSecurityKey.cs
│ ├── RsaSecurityKey.cs
│ ├── ECDsaSecurityKey.cs
│ └── JsonWebKey.cs # JWK support
├── Signing/
│ ├── SignatureProvider.cs
│ ├── HmacSignatureProvider.cs
│ ├── RsaSignatureProvider.cs
│ ├── RsaPssSignatureProvider.cs
│ └── EcdsaSignatureProvider.cs
├── Validation/
│ ├── TokenValidationParameters.cs
│ └── ClaimsValidator.cs
├── Jwe/ # JWE encryption
│ ├── JweEncryptor.cs
│ ├── JweDecryptor.cs
│ ├── JweParser.cs # 5-part format
│ ├── JweBuilder.cs
│ ├── KeyManagement/
│ │ ├── DirectKeyProvider.cs
│ │ ├── RsaOaepKeyProvider.cs
│ │ └── AesKeyWrapProvider.cs
│ └── ContentEncryption/
│ └── AesGcmEncryptionProvider.cs
├── SdJwt/ # SD-JWT selective disclosure
│ ├── SdJwtBuilder.cs
│ ├── SdJwtParser.cs
│ ├── SdJwtPresentation.cs
│ ├── SdJwtVerifier.cs
│ ├── Disclosure.cs
│ └── DigestCalculator.cs
└── Exceptions/
├── JwtException.cs
├── JweException.cs
└── SdJwtException.cs
Security Features
DoS Prevention
- Token Size Validation: 250 KB default limit (configurable)
- Protects against memory exhaustion attacks
- Applied to both JWS and JWE tokens
Constant-Time Operations
- Signature comparison uses
CryptographicOperations.FixedTimeEquals - Prevents timing attacks
CVE Analysis
Analyzed and protected against:
- CVE-2024-21319: Token replay via
kidheader manipulation- Our implementation validates algorithms strictly
- CVE-2017-11429: XML signature wrapping
- N/A - we don't support XML
Algorithm Security
- ❌ No "none" algorithm support
- ❌ No RSA1_5 (deprecated, vulnerable)
- ✅ Strict algorithm whitelisting
- ✅ Minimum key sizes enforced (RSA: 2048-bit)
Best Practices
- Always validate issuer and audience
- Use strong algorithms (RS256+, ES256+, PS256+)
- Enable lifetime validation with appropriate clock skew
- Use TimeProvider for testable time operations
Testing
Test Coverage
- 342+ passing tests across all features (including 27 SD-JWT VC tests)
- RFC test vectors for JWS, JWE, SD-JWT
- Interoperability tests with Microsoft.IdentityModel
- Security-focused edge case testing (CVE scenarios)
- Performance benchmarks
Run Tests
dotnet test
Run Benchmarks
cd benchmarks/JsonWebToken.Benchmarks
dotnet run -c Release
Testing with Time
All time-dependent operations use TimeProvider for deterministic testing:
// In production code
var parameters = new TokenValidationParameters
{
TimeProvider = TimeProvider.System, // Real time
ValidateLifetime = true
};
// In test code
var fakeTime = new FakeTimeProvider(new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero));
var parameters = new TokenValidationParameters
{
TimeProvider = fakeTime, // Controllable time
ValidateLifetime = true
};
// Advance time in tests
fakeTime.Advance(TimeSpan.FromHours(2));
This eliminates flaky tests and enables precise validation of time-based logic.
Comparison with Microsoft.IdentityModel
Feature Parity
| Feature | Duende | Microsoft | Notes |
|---|---|---|---|
| Core JWT/JWS (RFC 7519, 7515) | ✅ Full | ✅ Full | Complete parity |
| JWE Encryption (RFC 7516) | ✅ Full | ✅ Full | Complete parity |
| SD-JWT (Selective Disclosure) | ✅ Full | ❌ None | Unique to Duende 🎯 |
| SD-JWT VC (Verifiable Credentials) | ✅ Full | ❌ None | Unique to Duende 🎯 |
| Signing: HMAC (HS256/384/512) | ✅ | ✅ | Parity |
| Signing: RSA (RS256/384/512) | ✅ | ✅ | Parity |
| Signing: RSA-PSS (PS256/384/512) | ✅ | ✅ | Parity |
| Signing: ECDSA (ES256/384/512) | ✅ | ✅ | Parity |
| JWE: Direct Encryption (dir) | ✅ | ✅ | Parity |
| JWE: RSA-OAEP variants | ✅ | ✅ | Parity |
| JWE: AES Key Wrap (A128/192/256KW) | ✅ | ✅ | Parity |
| JWE: AES-GCM (A128/192/256GCM) | ✅ | ✅ | Parity |
| Claims Validation | ✅ Full | ✅ Full | Parity |
| Token Size Validation | ✅ | ✅ | Parity |
| TimeProvider | ✅ BCL TimeProvider |
Custom interface | Duende uses modern BCL |
| Dependencies | ✅ Zero | ❌ Multiple | Duende advantage |
| Target Frameworks | .NET 10 only | Multi-target (.NET 4.6.2+) | Microsoft more compatible |
Performance Comparison
Parsing (Lower is Better)
| Operation | Duende | Microsoft | Improvement |
|---|---|---|---|
| Parse HS256 | 307 ns | 443 ns | 31% faster ✅ |
| Parse RS256 | 338 ns | 445 ns | 24% faster ✅ |
Creation (Lower is Better)
| Operation | Duende | Microsoft | Difference |
|---|---|---|---|
| Create HS256 | 1.73 µs | 1.15 µs | 50% slower |
| Create RS256 | 322 µs | 313 µs | 3% slower |
Conclusion: Production-ready performance. Parsing is significantly faster, creation is competitive.
Security Comparison
| Security Feature | Duende | Microsoft | Notes |
|---|---|---|---|
| CVE-2024-21319 (JWE DoS) | ✅ Immune | ⚠️ Fixed in 7.1.2+ | No compression = no vulnerability |
| CVE-2017-11480 (Alg Confusion) | ✅ Immune | ⚠️ Fixed in 5.1.1+ | Explicit algorithm validation |
| Constant-Time Signature Compare | ✅ | ✅ | Both use secure comparison |
| Token Size Limits | ✅ | ✅ | Both support DoS prevention |
| "none" Algorithm | ❌ Rejected | ❌ Rejected | Both secure |
| RSA1_5 (Deprecated) | ❌ Not supported | ❌ Not supported | Both secure |
When to Choose Duende
Choose Duende.JsonWebToken if:
- ✅ Need SD-JWT (Selective Disclosure) for privacy-preserving credentials
- ✅ Need SD-JWT VC (Verifiable Credentials) for standards-based digital identity
- ✅ Building verifiable credentials ecosystem (eIDAS2, OpenID4VC, digital wallets)
- ✅ Want zero dependencies for easier auditing and smaller footprint
- ✅ Target .NET 10+ exclusively
- ✅ Value simpler API for straightforward JWT use cases
- ✅ Building modern identity solutions (age verification, educational credentials, etc.)
Choose Microsoft.IdentityModel if:
- Target .NET Framework or multiple .NET versions
- Need deep Microsoft Entra ID / Azure AD integration
- Require extensive X.509 certificate handling (x5t, x5c headers)
- Need OpenID Connect protocol support (beyond JWT)
- Want mature, battle-tested library (billions of tokens/day)
Design Principles
- Simplicity First: Clean, understandable code over excessive abstraction
- Zero Dependencies: Pure .NET BCL, no external packages
- Specification-Driven: RFC-compliant implementation
- Modern C#: Leverages .NET 10 features (spans, source generation, etc.)
- Security by Default: Safe defaults, secure implementations
- Performance Focused: Optimized hot paths, minimal allocations
- Testability: TimeProvider abstraction, comprehensive test coverage
Requirements
- .NET 10.0 or later
- No external dependencies
License
[Your License Here]
Contributing
Contributions are welcome! Please ensure:
- All tests pass
- New features include tests
- Code follows existing style
- Performance benchmarks show no regressions
Design Principles & Architecture
Core Principles
- Simplicity First: Clean, understandable code over excessive abstraction
- Zero Dependencies: Pure .NET BCL, no external packages
- Specification-Driven: RFC-compliant implementation (RFC 7519, 7515, 7516)
- Modern C#: Leverages .NET 10 features (spans, source generation, primary constructors)
- Security by Default: Safe defaults, secure implementations, CVE-resistant
- Performance Focused: Optimized hot paths, minimal allocations, span-based parsing
- Testability: TimeProvider abstraction for deterministic time operations
Key Design Decisions
Target Framework: .NET 10 only
- Simplifies maintenance and enables latest runtime optimizations
- Access to built-in
System.Buffers.Text.Base64Url(no custom implementation needed) - Latest language features without multi-targeting complexity
TimeProvider Abstraction: All time operations use TimeProvider
- Core library:
TimeProvider.System(default) - Tests:
FakeTimeProviderfromMicrosoft.Extensions.TimeProvider.Testing - Enables deterministic, fast tests without sleep/delays
- Modern .NET best practice vs custom abstractions
Algorithm Security:
- ❌ No "none" algorithm support
- ❌ No RSA1_5 (deprecated, vulnerable to attacks)
- ✅ Explicit algorithm validation required (prevents algorithm confusion attacks)
- ✅ Constant-time signature comparison using
CryptographicOperations.FixedTimeEquals - ✅ Minimum key sizes enforced (RSA: 2048-bit, HMAC: algorithm-specific)
SD-JWT Philosophy:
- Separate namespace (
SdJwt/) to avoid polluting core JWT code - Three-role API: Issuer (
SdJwtBuilder), Holder (SdJwtPresentation), Verifier (SdJwtVerifier) - Zero new dependencies - leverages existing JWT infrastructure
- Follows IETF draft-ietf-oauth-selective-disclosure-jwt-22 (RFC Editor Queue)
JWE Implementation:
- ❌ No compression support - eliminates CVE-2024-21319 vulnerability class
- Authenticated encryption with additional data (AEAD) using AES-GCM
- Multiple key management algorithms (direct, RSA-OAEP variants, AES Key Wrap)
- 5-part compact serialization format
Security by Design
CVE Resistance: Not vulnerable to known Microsoft.IdentityModel CVEs
- CVE-2024-21319 (JWE Compression DoS): No decompression support
- CVE-2017-11480 (Algorithm Confusion): Explicit algorithm validation enforced
Token Size Validation: Default 250 KB limit (configurable)
- Prevents memory exhaustion DoS attacks
- Applied to both JWS and JWE tokens
Constant-Time Operations: Timing attack prevention
- All signature comparisons use
CryptographicOperations.FixedTimeEquals - Critical for HMAC and symmetric operations
Performance Strategy
Span-Based Parsing: Minimal allocations
ReadOnlySpan<byte>for token parsing- Stack allocation where possible
- 68% faster parsing than baseline (307ns vs 905ns for HS256)
Source Generation: Optimized JSON serialization
System.Text.Jsonsource generators for zero-reflection serialization- Reduces allocation and improves throughput
Benchmarked: All changes validated with BenchmarkDotNet
- Regular benchmarking against Microsoft.IdentityModel baseline
- Memory allocation tracking
- Sub-microsecond operations for common paths
References
- RFC 7519 - JSON Web Token (JWT)
- RFC 7515 - JSON Web Signature (JWS)
- RFC 7516 - JSON Web Encryption (JWE)
- RFC 7517 - JSON Web Key (JWK)
- RFC 8725 - JWT Best Current Practices
- draft-ietf-oauth-selective-disclosure-jwt-22 - SD-JWT Specification (RFC Editor Queue)
- draft-ietf-oauth-sd-jwt-vc-13 - SD-JWT VC Specification (Active Internet-Draft)
- OWASP JWT Security Cheat Sheet
Security Resources
- CVE-2024-21319 Analysis - JWE Compression DoS (we're immune)
- CVE-2017-11480 Analysis - Algorithm Confusion (we're protected)
Additional Documentation
- JSONWEBTOKEN-API.md - Detailed API reference with code examples
- SECURITY-CVE-ANALYSIS.md - Comprehensive CVE vulnerability analysis
- COMPARISON-WITH-MICROSOFT.md - In-depth feature comparison matrix
Status
✅ Production Ready
All core features are complete and thoroughly tested. The library is ready for production use.
Completed Features
- ✅ JWT/JWS (Phase 1)
- ✅ SD-JWT Core (Phase 2.1)
- ✅ SD-JWT Advanced (Phase 2.2: Nested, Key Binding, Decoy Digests)
- ✅ SD-JWT VC (Phase 2.3.1: vct, Standard Claims, Age Attestation, Status)
- ✅ JWE Direct Encryption (Phase 3.1)
- ✅ JWE RSA-OAEP (Phase 3.2)
- ✅ JWE AES Key Wrap (Phase 3.3)
- ✅ Token Size Validation (Phase 5)
- ✅ Token Compression (opt-in, DEFLATE)
- ✅ All JOSE Headers (cty, x5t, x5t#S256, x5c, x5u, jku, jwk, crit)
- ✅ Performance Optimization (span-based parsing)
- ✅ Source Generation (System.Text.Json)
- ✅ Comprehensive Testing (384+ tests)
Optional Future Enhancements
- ECDH-ES key agreement
- EdDSA (Ed25519) signing (requires third-party library)
- SD-JWT VC samples and documentation
- Token caching
- ASP.NET Core integration middleware
| Product | Versions 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. |
-
net10.0
- Duende.Labs.JsonWebToken (>= 2026.902.21)
- Microsoft.IdentityModel.JsonWebTokens (>= 8.14.0)
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 |
|---|---|---|
| 2026.902.21 | 41 | 9/2/2026 |
| 2026.806.8 | 98 | 8/6/2026 |
| 2026.804.7 | 104 | 8/4/2026 |
| 2026.729.6 | 111 | 7/29/2026 |
| 2026.728.5 | 109 | 7/28/2026 |
| 2026.728.4 | 96 | 7/28/2026 |
| 2026.727.3 | 111 | 7/27/2026 |
| 2026.727.2 | 104 | 7/27/2026 |
| 2026.723.1 | 106 | 7/23/2026 |
| 0.0.0-alpha.0.606 | 33 | 9/2/2026 |
| 0.0.0-alpha.0.595 | 41 | 8/31/2026 |
| 0.0.0-alpha.0.578 | 44 | 8/29/2026 |
| 0.0.0-alpha.0.576 | 56 | 8/23/2026 |
| 0.0.0-alpha.0.566 | 56 | 8/13/2026 |
| 0.0.0-alpha.0.563 | 55 | 8/12/2026 |
| 0.0.0-alpha.0.560 | 52 | 8/11/2026 |
| 0.0.0-alpha.0.559 | 54 | 8/11/2026 |
| 0.0.0-alpha.0.558 | 67 | 8/10/2026 |
| 0.0.0-alpha.0.551 | 54 | 8/9/2026 |