Open-source project
jwt-dotnet/jwt avatar
jwt-dotnet/jwt

Jwt.Net: a JWT library where the interfaces are the API and the builder is the shortcut

Jwt.Net, a JWT (JSON Web Token) implementation for .NET

2,190 stars468 forksC#NOASSERTION

At a glance

What is it?
Three NuGet packages, an interface-first encoder and decoder, a fluent builder over the top, and a target framework list that still reaches back to .NET Framework 3.5.
Who is it for?
Jwt.Net is a good fit when you want control over serialization, key handling or the exact set of claims, because every stage is an interface you can replace. The builder exists for people who do not want that, and the ASP.NET Core package is the piece that makes it a one-liner in a web app.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 12 days ago.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Three packages, not one

The distribution story is three NuGet packages rather than one. `JWT` is the library itself, `JWT.Extensions.DependencyInjection` registers it in a Microsoft dependency injection container, and `JWT.Extensions.AspNetCore` wires it into ASP.NET Core authentication. Each has a stable and a preview listing, so the beta line is visible rather than hidden.

The library's one line description is that it supports generating and decoding JSON Web Tokens per RFC 7519. The supported target list is worth reading twice because it says something about the project's priorities: .NET Framework 3.5, .NET Framework 4.0 to 4.8, .NET Standard 1.3 and 2.0, and .NET 6.0. A package that still compiles for .NET Framework 3.5 is a package written for maximum reach, which constrains what the code can use.

The repository is C#, has 2,190 stars and 468 forks, and lists `authorization`, `c-sharp`, `json` and `jwt` as topics. The tree carries a solution, `Directory.Packages.props` for central package version management, a `.pipelines/` directory and a `JwtStrongNameKey.snk`, which is the strong naming key. The `CHANGELOG.md` is generated, and the README ends with a License section.

Encoding: five interfaces and one constructor call

The encoding path assembles explicit collaborators. You provide the algorithm, a JSON serializer, a base64url encoder, and the encoder takes a payload dictionary and a key:

c#
IJwtAlgorithm algorithm = new RS256Algorithm(certificate);
IJsonSerializer serializer = new JsonNetSerializer();
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
const string key = null; // not needed if algorithm is asymmetric

var token = encoder.Encode(payload, key);
Console.WriteLine(token);

The names are the design statement. `IJwtAlgorithm` is swappable, so moving from RS256 to HMAC is a constructor argument rather than a rewrite. `IJsonSerializer` is the interesting one, because it is the reason this library supports custom serialization settings, a documented section of the README along with a variant for configuring the default `JsonNetSerializer`.

Over the top of all of that sits a fluent builder for the common case:

c#
var token = JwtBuilder.Create()
                      .WithAlgorithm(new RS256Algorithm(certificate))
                      .AddClaim("exp", DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds())
                      .AddClaim("claim1", 0)
                      .AddClaim("claim2", "claim2-value")
                      .Encode();

Note that the expiry is added as a plain claim. Nothing adds it for you, which is a deliberate choice consistent with how the rest of the API works.

Decoding gives you typed exceptions, not a boolean

The decode path mirrors the encode path and adds a validator plus a date provider. What matters for callers is the exception surface, because the README shows three distinct types rather than one generic failure:

c#
catch (TokenNotYetValidException)
{
    Console.WriteLine("Token is not valid yet");
}
catch (TokenExpiredException)
{
    Console.WriteLine("Token has expired");
}
catch (SignatureVerificationException)
{
    Console.WriteLine("Token has invalid signature");
}

Three separate exception types for not-yet-valid, expired and bad signature is the kind of detail that only matters once you have had to write the catch-all version and lost the reason. The validator is constructed with a serializer, an `IDateTimeProvider` and the algorithm, and `MustVerifySignature()` is the explicit opt-in on the builder path.

You can also deserialize straight into a .NET type rather than getting JSON back:

c#
var payload = decoder.DecodeToObject<IDictionary<string, object>>(token, secret);

The README's table of contents also covers parsing just the token header without decoding the payload, which is the operation you need when you are routing on issuer or key id and do not want to trust the body yet.

Clock skew is a setting, because the validator reads the clock through an interface

The expiration section quotes RFC 7519 section 4.1.4 directly: the `exp` claim identifies the time on or after which the token must not be accepted for processing. The value is seconds since the Unix epoch, and if it is present and in the past, verification fails.

What is interesting is the mechanism. Because the validator depends on `IDateTimeProvider`, you can substitute the notion of now. The README's example moves the clock backwards five minutes with `GetNow().AddMinutes(-5)` to produce a token that expired five minutes ago, which is how you test the expired path without waiting. Any real clock skew allowance is the same technique, supplying an offset provider instead of the default `UtcDateTimeProvider`.

The README also documents a section on turning off parts of token validation, with a fluent builder equivalent. That is the escape hatch for tokens you issue and verify yourself where one of the standard checks is in the way, and it is documented rather than hidden behind an internal method.

Release history is mostly dependency updates, which is a maintenance signal

The release named 20260706.1 is an accumulated changelog. It records JWT 11.1.0, JWT.Extensions.AspNetCore 11.1.0 and JWT.Extensions.DependencyInjection 3.1.0 with Newtonsoft.Json moved to 13.0.4, System.Text.Json to 10.0.8 and Microsoft.Extensions.DependencyInjection.Abstractions to 10.0.8. Below that sit the 11.0.0 betas, including a move from .NET 7 to .NET 8, and a change that removed System.Text.Json when targeting .NET 6 and higher because the framework already provides it.

The ASP.NET Core extension has its own line worth noting: 11.0.0-beta3 converted it to an event model to allow dependency injection of custom event classes, and beta4 made that event model support async and await. That is the feature that turns the ASP.NET package from a fixed pipeline into something you can extend.

A changelog made of dependency bumps is not exciting to read, but for a cryptography-adjacent library it is the behaviour you want. The last push recorded on the repository is 2026-09-25, with 10 open issues.

ASP.NET Core registration and the licensing question

The README's ASP.NET Core section covers registering the authentication handler that validates JWT, and separately covers custom factories to produce an Identity or an AuthenticationTicket. The second is the one that matters in practice, because the default mapping from claims to a .NET identity is a policy decision that applications frequently need to override.

One thing the README does not settle is licensing. GitHub reports no license identifier for the repository, and a `LICENSE.md` file is present in the tree. Those two facts do not agree, and the file itself is not reproduced here, so the practical advice for anyone shipping this in a product is to read `LICENSE.md` directly before relying on it. The same applies to the `JwtStrongNameKey.snk` file: strong naming is present, which is a distribution concern rather than a licence grant.

The README also carries a single sponsor block for Auth0 near the top. That is worth naming only because it is the one piece of the page that is not technical documentation.

Editorial conclusion

Jwt.Net is a good fit when you want control over serialization, key handling or the exact set of claims, because every stage is an interface you can replace. The builder exists for people who do not want that, and the ASP.NET Core package is the piece that makes it a one-liner in a web app. What the README does not settle is the licensing picture: GitHub reports no license identifier while a `LICENSE.md` sits in the tree, so check the file before shipping it commercially. Version 11.1.0 is the current line as of the 2026-07-06 release, the supported targets run from .NET Framework 3.5 to .NET 6, and the last push recorded is 2026-09-25.

Frequently asked questions

How do I create a JWT?

With this library, construct an `IJwtAlgorithm`, an `IJsonSerializer` and an `IBase64UrlEncoder`, wrap them in a `JwtEncoder`, and call `Encode` with your payload dictionary and key. The fluent `JwtBuilder.Create()` route does the same thing with chained calls.

What is JWT and its purpose?

A JSON Web Token is a compact, URL-safe token whose claims are signed, so a recipient can verify who issued it and that it was not altered, without a database lookup. This library implements the generation and decoding side of RFC 7519 for .NET.

What are the 3 components of a JWT token?

A header naming the algorithm, a payload carrying the claims, and a signature over the first two, with each part base64url encoded and separated by dots. The README's encoding and header parsing examples reflect that three-part structure, and the header can be read on its own without decoding the payload.

Official sources

  1. Issues
  2. jwt-dotnet/jwt on GitHub
  3. README
  4. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/jwt-dotnet-jwt.svg)](https://hysenlabs.com/projects/jwt-dotnet-jwt)