Abblix.JWT 2.4.0

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

Abblix.JWT

A JWT and JOSE toolkit for .NET, built entirely on the platform's cryptographic primitives and System.Text.Json.Nodes - no dependency on Microsoft.IdentityModel.Tokens. It implements JWS (RFC 7515), JWE (RFC 7516), JWK (RFC 7517) and JWA (RFC 7518), and is the token layer behind Abblix OIDC Server, usable on its own.

What's New in Version 2.4

🚀 Features

  • Complete JWE key management: every key-management algorithm of RFC 7518 section 4, including AES key wrapping (RFC 3394) and opt-in password-based encryption with a bounded work factor
  • External signing keys: a custodian seam through which the private half of a key is held outside the process, served by Abblix.JWT.Vault for HashiCorp Vault and OpenBao Transit and Abblix.JWT.Azure for Azure Key Vault
  • Replay prevention for single-use tokens, with one store serving every token profile

✏️ Improvements

  • Clock tolerance travels as one value with separate past and future halves, so a window such as the one FAPI 2.0 prescribes is expressed exactly
  • A NumericDate is read as RFC 7519 section 2 defines it: a fractional or exponent-written number is a date, and a claim that cannot be read is refused by name rather than thrown
  • The signed discovery document uses the standard algorithm for a key that declares none (RFC 7517 section 4.4)

Install

dotnet add package Abblix.JWT

Issue and validate a token

services.AddJsonWebTokens();

The registration provides IJsonWebTokenCreator and IJsonWebTokenValidator. Issuing signs the token with the key you pass, and encrypts it when you also pass an encryption key:

var key = JsonWebKeyFactory.CreateRsa(PublicKeyUsages.Signature, SigningAlgorithms.RS256);

var jwt = await creator.IssueAsync(
    new JsonWebToken
    {
        Header = { Algorithm = SigningAlgorithms.RS256 },
        Payload =
        {
            Issuer = "https://issuer.example.com",
            Audiences = ["https://api.example.com"],
            ExpiresAt = DateTimeOffset.UtcNow.AddMinutes(5),
        },
    },
    key);

Validation returns a verdict rather than throwing on a bad token: the result is a Result carrying either the parsed token or the error naming what failed - malformed input, unknown algorithm, bad signature, unmet issuer or audience policy - and the parameters carry the checks as delegates, so issuer trust and key resolution stay the caller's policy.

Two classes of failure still surface as exceptions, both after the signature has been verified: an exp/nbf value that is not a number or falls outside DateTimeOffset's range, and a resolved key carrying no public material - wrap the call if your endpoint must answer every input with a protocol error:

var result = await validator.ValidateAsync(jwt, new ValidationParameters
{
    ValidateIssuer = issuer => Task.FromResult(issuer == "https://issuer.example.com"),
    ValidateAudience = audiences => Task.FromResult(audiences.Contains("https://api.example.com")),
    ResolveIssuerSigningKeys = issuer => KnownKeysOf(issuer),
});

The payload is a JsonObject underneath, so claims keep their JSON types - numbers, arrays and nested objects need no string round-trips, and custom claims are first-class.

Algorithms

  • Signing: RS256/RS384/RS512, PS256/PS384/PS512, ES256/ES384/ES512, HS256/HS384/HS512.
  • Key management: RSA-OAEP, RSA-OAEP-256, AES-GCM key wrapping (A128GCMKW/A192GCMKW/A256GCMKW), direct encryption (dir).
  • Content encryption: A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM.

Two further key-management families ship and stay off until a host asks for them, each because of a cost the default should not impose. AddRsaPkcs1KeyManagement() enables RSA1_5. AddPbes2KeyManagement() enables PBES2-HS256+A128KW, PBES2-HS384+A192KW and PBES2-HS512+A256KW, where the inbound token's p2c header dictates PBKDF2 work before the token has been authenticated; the iteration count is bounded to [1000, 10000] even once enabled. Interop with a partner that requires either is one call, not a missing feature.

Hardening built in

The validation pipeline enforces what the specifications say a careless implementation forgets: a key that declares an alg is never used for another algorithm when producing or verifying a JWS (RFC 8725 Section 3.1; JWE key unwrapping selects by kid and the header's alg, so a decryption key's declared alg is not a filter there). An HMAC key shorter than its hash output is rejected (RFC 7518 Section 3.2).

A crit header names only parameters a registered handler understands, and an unhandled critical parameter rejects the token, on the JWE envelope as on the JWS (RFC 7515 Section 4.1.11). A host that does understand such a parameter registers a handler for it by name, AddCriticalHeaderHandler<MyHandler>("my-ext"): the name is the registration key, so it cannot be claimed without a handler behind it.

Replay protection

Every JWT profile that forbids replay asks the same question - has this identifier been presented before? - so the primitive lives here rather than in each of them: IReplayCache reserves an identifier and answers whether the sighting is the first, in one call, so no caller can read, decide and write in three steps another caller slips between.

services.AddSingleton<IReplayCache>(provider =>
    provider.CreateService<DistributedReplayCache>(Dependency.Override("MyApp:ReplayPrevention:")));

The shipped implementation stores in the host's IDistributedCache, so a single-instance deployment gets process-local behaviour and a scaled-out one gets shared memory by swapping the store. That store offers Get and Set and no compare-and-set, which makes the answer probabilistic within one cache round trip - enough for the profiles that accept it (RFC 9449 Section 11.1 for DPoP proofs, RFC 8935 Section 2 for redelivered Security Event Tokens), and replaceable behind the same interface by a backend-native primitive where it is not.

External keys

Signing and decryption do not require the private key to live in the process: the custodian seam delegates the cryptographic operation to an external holder - AddVaultCustodian for HashiCorp Vault / OpenBao (Abblix.JWT.Vault), AddAzureCustodian for Azure Key Vault (Abblix.JWT.Azure), AddCustodian<T> for one of your own.

Each opens a placement choice you then name, and that is the security posture: UseKeysInCustodian keeps every private half outside the process, UseKeysInProcess mints keys here and has the custodian only seal them.

The placement calls live in this package, which is what lets a host consume JWTs without being an OpenID Provider and still wire a custodian - no OIDC server anywhere in its graph. A backend package adds the transport for its own vault; with your own custodian this reference is the only one you need:

services
    .AddJsonWebTokens()
    .AddCustodian<MyCustodian>()
    .UseKeysInCustodian(new CustodianHeldKeys { SigningKeyName = "sign" });

EXTERNAL_KEYS.md is the shared model, including what a host without our key provider does to publish the custodian's keys itself.

Key rings

Keys the host does not supply itself are minted and rotated by a key ring. AddInMemoryKeyRing(policy) keeps them in the process: right for a single instance, and wrong for several, since each replica mints its own and nothing fails at startup - sign-ins simply break for whoever lands on the wrong replica. So a host that has registered an IKeyRingStore, which is how keys are shared, is refused here. AddKeyRing(policy) is the shared form: it seals each minted key to the custodian's key-encryption key and publishes it through that store, which the builder it returns supplies.

What the ring's keys are then used for is the caller's business - an OpenID Provider publishes them at its JWKS endpoint, another host protects stored sessions with them - which is why the ring lives beside the key material rather than beside either consumer.

Implemented standards

Part of the Abblix product family

Abblix.JWT is the token layer under Abblix.OIDC.Server and Abblix.SecurityEvents; the full family lives in the repository.

License

Abblix.JWT is licensed under the Apache License 2.0.

Contacts

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  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 (6)

Showing the top 5 NuGet packages that depend on Abblix.JWT:

Package Downloads
Abblix.OIDC.Server

OpenID Connect and OAuth 2.0 server for ASP.NET Core, certified by the OpenID Foundation. Add a complete identity provider and authorization server to your own .NET application: every OIDC flow, PKCE, PAR, DPoP, JARM, CIBA, device flow, token exchange and FAPI 2.0. Runs on .NET 8, 9 and 10.

Abblix.OIDC.Server.MVC

ASP.NET Core MVC integration for Abblix OIDC Server, the certified OpenID Connect and OAuth 2.0 provider. Controllers, model binding and routing for every protocol endpoint: add it to a controller-based application and get a complete identity provider.

Abblix.SecurityEvents

Security Event Tokens (RFC 8417) for .NET with Subject Identifiers (RFC 9493), push (RFC 8935) and poll (RFC 8936) delivery, and OpenID Back-Channel Logout. Build, sign, deliver and validate security events between identity providers and relying parties.

Abblix.JWT.Azure

Azure Key Vault integration for Abblix JWT and Abblix OIDC Server: sign tokens and unwrap keys with private keys that never leave the vault. Keep your OpenID Connect signing keys in Azure.

Abblix.OIDC.Server.MinimalAPI

ASP.NET Core Minimal API integration for Abblix OIDC Server, the certified OpenID Connect and OAuth 2.0 provider. Every protocol endpoint as a route handler with no MVC dependency: the lightest way to host an identity provider in .NET.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.4.0 317 9/5/2026
2.3.0 239 6/9/2026
2.2.0 401 2/18/2026
2.1.0 603 12/8/2025
2.0.1 698 12/1/2025
2.0.0 272 11/26/2025
1.6.0 365 8/14/2025
1.5.0 328 6/25/2025
1.4.0 360 4/9/2025
1.3.1 326 12/3/2024
1.3.0.1 298 11/28/2024
1.3.0 296 11/13/2024
1.2.0.1 284 10/16/2024
1.2.0 279 10/11/2024
1.1.0 398 7/9/2024
1.0.100 327 5/3/2024
Loading failed

A JWT and JOSE toolkit that uses the .NET cryptographic primitives and System.Text.Json directly, so a token round-trips with no second serializer and no key-model translation. Sign and verify with every RSA, ECDSA and HMAC algorithm of RFC 7518; encrypt with every key-management algorithm of RFC 7516, including AES key wrapping and opt-in password-based encryption with a bounded work factor; publish and consume JSON Web Keys and key sets with thumbprints and typed header accessors; process critical headers; prevent replay of single-use tokens. Keys can live outside the process: a custodian seam lets HashiCorp Vault, OpenBao Transit or Azure Key Vault hold the private half, served by the companion packages. Clock tolerance travels as one value with separate past and future halves, so a FAPI 2.0 window is expressed exactly. The token layer behind Abblix OIDC Server, usable on its own. Full details: https://github.com/Abblix/Oidc.Server/releases/tag/v2.4