jwt-cpp: a header-only JSON Web Token library for C++ with pluggable JSON and crypto
A header only library for creating and validating json web tokens in c++
At a glance
- What is it?
- jwt-cpp is a header-only C++11 library for creating and validating JSON Web Tokens, supporting every algorithm in the standard across HMAC, RSA, PSS, ECDSA, and EdDSA, with the JSON library and the TLS backend both left as choices rather than dependencies.
- Who is it for?
- jwt-cpp fits a C++ service that has to speak JWT and does not want a full platform framework, especially one that already has a JSON library and a TLS stack it would rather reuse. It is the wrong choice if you need JWE encryption rather than JWS signing, and it is a poor fit if your team would be more productive with a JWT library that ships as a managed dependency.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 17 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 October 21, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Header only, and what that buys you
jwt-cpp is a header only library for creating and validating JSON Web Tokens in C++11. The objective is stated as delivering a versatile and universally applicable collection of algorithms, classes, and data structures, encouraging adaptability and integration with other libraries that you may already be employing.
Header-only is the defining packaging choice and it cuts both ways. There is no library to link, no version of a shared object to keep in step across a fleet of binaries, and no ABI concerns when you rebuild. The include tree is just headers, and the whole thing drops into an existing build.
And there is a cost that is easy to forget. Because the implementation is all in headers, the code is compiled into every translation unit that includes it, which means compile times go up and the binary grows. In a large C++ codebase with a wide include graph, that is a real cost, and it is the reason the repository carries a `nuget/` directory, which suggests a packaged distribution for the .NET side of the world, and a `CMakePresets.json` alongside the `CMakeLists.txt`.
The library works on Linux, macOS, and Windows, and the CI badge for cross-platform builds is per platform, which means the three are tested rather than assumed.
The project is MIT licensed, the primary language is C++, and the last push to the repository was on 2026-10-06, with v0.7.2 released in February 2026. The homepage is the documentation site, and the repository also carries a Codacy badge, a coverage badge, and a ConanCenter and Vcpkg badge, which tells you the library is packaged for the two dominant C++ package managers.
Every algorithm in the standard, and validation that is explicit
jwt-cpp supports all algorithms specified in the standard, and the table lists them in five columns.
The HMAC column has HS256, HS384, and HS512. The RSA column has RS256, RS384, and RS512. The ECDSA column has ES256, ES384, ES512, and the secp256k1 variant ES256K. The PSS column has PS256, PS384, and PS512. And the EdDSA column has Ed25519 and Ed448.
The design point about additional algorithms is worth making explicit, because it is the security-relevant part of the list. The modular design facilitates the inclusion of additional algorithms without complications, and contributions of new algorithms are invited by pull request or issue. That extensibility is a feature and it is also the place where a JWT library becomes dangerous, because the algorithm is often how the classic confusion attacks happen.
Which is why the verifier API is built the way it is. You build a verifier and use it to verify a decoded token:
auto verifier = jwt::verify()
.with_issuer("auth0")
.with_claim("sample", jwt::claim(std::string("test")))
.allow_algorithm(jwt::algorithm::hs256{"secret"});
verifier.verify(decoded_token);The `allow_algorithm` call is the important one, and it is explicit rather than inferred. A verifier that accepted whatever algorithm the token's own header declared would be trivially defeated by an attacker who changes the header to `none` or swaps RS256 for HS256. Here you state which algorithm is acceptable, and the verification fails if the token says otherwise. The project also notes that the verifier is stateless so you can reuse it for different tokens, which is the right shape for a verifier you configure once at startup.
The signing side is the mirror image:
auto token = jwt::create()
.set_type("JWS")
.set_issuer("auth0")
.set_payload_claim("sample", jwt::claim(std::string("test")))
.sign(jwt::algorithm::hs256{"secret"});The `set_type` call is the JWS marker, and it is set explicitly rather than left to default.
And the point that is easy to miss for newcomers is stated plainly in the summary: JWTs are not encrypted but rather cryptographically signed to provide non-repudiation. The payload of a JWT is base64, not encrypted, so anyone holding the token can read the claims. A library that makes that clear is doing you a favour.
The JSON library is a template parameter, not a dependency
There is no strict reliance on a specific JSON library, and the mechanism for that is a generic `jwt::basic_claim` templated on a type trait.
The trait provides the semantic JSON types for values, objects, arrays, strings, numbers, integers, and booleans, along with methods to translate between them. And the design offers flexibility in choosing the JSON library that best suits your needs, with a documentation page on selecting a JSON library.
This is the decision that makes the library fit an existing codebase rather than replace part of it. A C++ service that already uses nlohmann, or RapidJSON, or picojson, does not want a second JSON library dragged in by a JWT dependency. Here you write a trait, which is a small adapter, and the library uses it. The example of providing your own traits is:
jwt::basic_claim<my_favorite_json_library_traits> claim(json::object({{"json", true},{"example", 0}}));And the documentation covers how to write a trait's implementation, so this is a supported path rather than a hack.
The TLS story is the same shape. jwt-cpp supports OpenSSL, LibreSSL, and wolfSSL, with a page listing tested versions. The reason a library supports three is that they are the three you will find in a C++ deployment, and a library that hardcoded one would be unusable in the environments where the other two are the default.
Base64 is treated the same way. The library includes `base.h` covering all necessary variants and ships a competent implementation of its own, but base64 implementations are widely available with diverse performance levels, and if you would rather use your own you can define `JWT_DISABLE_BASE64` to exclude the jwt-cpp implementation.
So the three things a JWT library normally drags into your build, JSON, TLS, and base64, are all optional or replaceable here. That is the header-only, integrate-with-what-you-already-use thesis, taken seriously.
The example directory shows the parts people actually need
The example programs in the repository are a better guide to the library's coverage than the feature list is, because they are the situations that come up.
There is `print-claims.cpp`, which is the getting started example, and `rsa-create.cpp` and `rsa-verify.cpp` for the asymmetric case. There is `es256k.cpp` for the secp256k1 curve, which is the one Bitcoin and Ethereum-adjacent systems use and which is a common source of interop problems. There is `jwks-verify.cpp`, for verifying against a JSON Web Key Set, which is how a verifier normally gets keys from an issuer in a modern deployment. There is `private-claims.cpp` for encrypted private claims, which is the JWE-shaped part of the API. There is `partial-claim-verifier.cpp` for verifying only some claims, which is what a resource server wants when it cares about one field and not the whole token. And there is a `traits/` directory for the JSON adapter examples.
The print-claims example is the one to run first, and the summary gives its build commands:
cmake .
cmake --build . --target print-claims
./print-claims
# iss = "auth0"
# sample = "test"And the decoding side of it:
#include <jwt-cpp/jwt.h>
#include <iostream>
int main() {
std::string const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCIsInNhbXBsZSI6InRlc3QifQ.lQm3N2bVlqt2-1L-FsOjtR6uE-L4E9zJutMWKIe1v1M";
auto decoded = jwt::decode(token);
for(auto& e : decoded.get_payload_json())
std::cout << e.first << " = " << e.second << '\n';
}Two details in that code are worth noticing. `jwt::decode` does not verify anything, it just parses, which is the correct separation, since verification needs a key and a policy. And `get_payload_json()` returns the payload as a JSON value rather than as a set of typed claims, so you can iterate it as you would any JSON object, which is convenient when you do not know the claim set in advance.
The CMake structure supports this. There is an `example/CMakeLists.txt` and an `example/CMakePresets.json`, so each example is its own buildable target, and the `--target print-claims` form in the build command is what that produces.
Where jwt-cpp is the wrong choice
The limits are mostly about what the library does not cover and about the trade the header-only model makes.
First, this is JWS only. The signature algorithms, the signing API, and the verifier all concern signed tokens. There is no JWE encryption here, so if your system needs an encrypted token, or a nested JWT where the payload is itself a signed token, this is not the library. The `private-claims.cpp` example shows claims that are not readable by the verifier, which is a signing concern, not an encryption one.
Second, and this is the header-only trade again, compile time and binary size. In a large translation unit the code is compiled repeatedly, and a service with hundreds of translation units pays for it. A JWT library as a compiled dependency does not have that problem. For a large team on a large codebase, that is usually the deciding factor, and it is why the ConanCenter and Vcpkg packages exist alongside the headers.
Third, the library does not solve key management. A JWT library verifies signatures and issues tokens, and the hard operational problem with JWT is where the keys live, how they rotate, and how a service learns a new issuer key without a restart. The `jwks-verify.cpp` example shows the JWKS path, which is a starting point, but the caching, the refresh interval, and the failure behaviour are your code.
Fourth, the security surface you inherit is the TLS library's and the JSON library's, plus this one's. The library supports OpenSSL, LibreSSL, and wolfSSL, and it supports writing your own base64. Every one of those is a choice you are making about which implementations you trust, and jwt-cpp's own contribution is the JWT logic and the algorithm glue.
Fifth, and this is a small but real one, the type system here is looser than it looks. The claim type is templated on a trait, which is flexible, and the trait is something you write, and the boundary between what the library checks and what your trait does is where a mistake will hide. A trait that returns the wrong type for a numeric claim will not be caught by the compiler if the trait is loose, and the token will decode to something you did not expect.
Sixth, the C++11 baseline is old in a good way and an awkward one. It means the library compiles on old toolchains, which is what you want in a long-lived enterprise codebase, and it also means it does not use the newer C++ features that would make some of this more concise.
Editorial conclusion
jwt-cpp fits a C++ service that has to speak JWT and does not want a full platform framework, especially one that already has a JSON library and a TLS stack it would rather reuse. It is the wrong choice if you need JWE encryption rather than JWS signing, and it is a poor fit if your team would be more productive with a JWT library that ships as a managed dependency. Check three things before you adopt it: that the algorithm set covers what your issuer and verifier will actually negotiate, since the library validates the algorithm rather than inferring it, that you pick the JSON traits for the library you already use rather than adding a second JSON dependency, and that you read the base64 and TLS sections, because both are replaceable and both have a default you may want to change.
Frequently asked questions
What is jwt-cpp and which algorithms does it support?
It is a header only C++11 library for creating and validating JSON Web Tokens. It supports all algorithms in the standard: HS256, HS384, and HS512 for HMAC, RS256, RS384, and RS512 for RSA, ES256, ES384, ES512, and ES256K for ECDSA, PS256, PS384, and PS512 for PSS, and Ed25519 and Ed448 for EdDSA.
How does jwt-cpp verify a token, and why does allow_algorithm matter?
You build a verifier with `jwt::verify()`, state the issuer and any claims to check, and call `allow_algorithm` with the algorithm you expect, then call `verify` on the decoded token. Being explicit about the allowed algorithm matters because a JWT states its own algorithm in its header, and a verifier that trusted the header would accept a token whose algorithm had been changed. The verifier is stateless and can be reused for different tokens.
Can I use my own JSON library with jwt-cpp?
Yes. There is no strict reliance on a specific JSON library, and the mechanism is a generic `jwt::basic_claim` templated on a type trait, so you can instantiate it as `jwt::basic_claim<my_favorite_json_library_traits>`. The trait supplies the semantic JSON types and the translations between them, and the documentation includes instructions for writing one.
Does jwt-cpp encrypt the token payload?
No. The summary states plainly that JWTs are not encrypted but rather cryptographically signed to provide non-repudiation, so the claims are readable by anyone who holds the token. What the library provides is signing with a chosen algorithm and verification against a stated algorithm, not encryption of the payload.
Official sources
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.
[](https://hysenlabs.com/projects/thalhammer-jwt-cpp)Was this useful?