Duende.Labs.JsonWebToken 2026.905.22

Prefix Reserved
dotnet add package Duende.Labs.JsonWebToken --version 2026.905.22
                    
NuGet\Install-Package Duende.Labs.JsonWebToken -Version 2026.905.22
                    
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="Duende.Labs.JsonWebToken" Version="2026.905.22" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Duende.Labs.JsonWebToken" Version="2026.905.22" />
                    
Directory.Packages.props
<PackageReference Include="Duende.Labs.JsonWebToken" />
                    
Project file
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 Duende.Labs.JsonWebToken --version 2026.905.22
                    
#r "nuget: Duende.Labs.JsonWebToken, 2026.905.22"
                    
#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 Duende.Labs.JsonWebToken@2026.905.22
                    
#: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=Duende.Labs.JsonWebToken&version=2026.905.22
                    
Install as a Cake Addin
#tool nuget:?package=Duende.Labs.JsonWebToken&version=2026.905.22
                    
Install as a Cake Tool

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 encryption
    • RSA-OAEP / RSA-OAEP-256 / RSA-OAEP-384 / RSA-OAEP-512 - RSA key encryption
    • A128KW / 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 - WithAgeEqualOrOver for 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 kid header 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

  1. Simplicity First: Clean, understandable code over excessive abstraction
  2. Zero Dependencies: Pure .NET BCL, no external packages
  3. Specification-Driven: RFC-compliant implementation
  4. Modern C#: Leverages .NET 10 features (spans, source generation, etc.)
  5. Security by Default: Safe defaults, secure implementations
  6. Performance Focused: Optimized hot paths, minimal allocations
  7. 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

  1. Simplicity First: Clean, understandable code over excessive abstraction
  2. Zero Dependencies: Pure .NET BCL, no external packages
  3. Specification-Driven: RFC-compliant implementation (RFC 7519, 7515, 7516)
  4. Modern C#: Leverages .NET 10 features (spans, source generation, primary constructors)
  5. Security by Default: Safe defaults, secure implementations, CVE-resistant
  6. Performance Focused: Optimized hot paths, minimal allocations, span-based parsing
  7. 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: FakeTimeProvider from Microsoft.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.Json source 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

Security Resources

Additional Documentation

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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Duende.Labs.JsonWebToken:

Package Downloads
Duende.Labs.AccessTokenManagement

Automatic access token management for OAuth client credential flows

Duende.Labs.JsonWebToken.MicrosoftIdentityModel

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2026.905.22 0 9/5/2026
2026.902.21 59 9/2/2026
2026.806.8 109 8/6/2026
2026.804.7 114 8/4/2026
2026.729.6 119 7/29/2026
2026.728.5 424 7/28/2026
2026.728.4 114 7/28/2026
2026.727.3 117 7/27/2026
2026.727.2 112 7/27/2026
2026.723.1 125 7/23/2026
0.0.0-alpha.0.606 36 9/2/2026
0.0.0-alpha.0.595 47 8/31/2026
0.0.0-alpha.0.578 52 8/29/2026
0.0.0-alpha.0.576 63 8/23/2026
0.0.0-alpha.0.566 59 8/13/2026
0.0.0-alpha.0.563 56 8/12/2026
0.0.0-alpha.0.560 59 8/11/2026
0.0.0-alpha.0.559 61 8/11/2026
0.0.0-alpha.0.558 72 8/10/2026
0.0.0-alpha.0.551 57 8/9/2026