# cpp-jwt **Repository Path**: yuzu-mirror/cpp-jwt ## Basic Information - **Project Name**: cpp-jwt - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-03-09 - **Last Updated**: 2024-03-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README
payload
function used in the above example to create jwt_object
object can only take strings. For anything else, it will throw a compilation error.
For adding claims having values other than string, jwt_object
class provides add_claim
API. We will also see few other APIs in the next example. Make sure to read the comments :).
```cpp
#include jwt_object
class is basically a composition of the JWT component classes, which are jwt_header
& jwt_payload
. For convenience jwt_object
exposes only few important APIs to the user, the remaining APIs under jwt_header
and jwt_payload
can be accessed by calling jwt_object::header()
and jwt_object::payload()
APIs.
## API Philosophy
I wanted to make the code easy to read and at the same time make most of the standard library and the modern features.
It also uses some metaprogramming tricks to enforce type checks and give better error messages.
The design of `parameters` alleviates the pain of remembering positional arguments. Also makes the APIs more extensible for future enhancements.
The library has 2 sets of APIs for encoding and decoding:
- API which takes an instance of std::error_code
These APIs will report the errors by setting the `error_code`. This does not mean that these API would not throw. Memory allocation errors would still be thrown instead of setting the error_code.
- API which throws exceptions
All the errors would be thrown as exception.
## Support
Algorithms and features supported
- [x] HS256
- [x] HS384
- [x] HS512
- [x] RS256
- [x] RS384
- [x] RS512
- [x] ES256
- [x] ES384
- [x] ES512
- [x] Sign
- [x] Verify
- [x] iss (issuer) check
- [x] sub (subject) check
- [x] aud (audience) check
- [x] exp (expiration time) check
- [x] nbf (not before time) check
- [x] iat (issued at) check
- [x] jti (JWT id) check
- [x] JWS header addition support. For eg "kid" support.
## External Dependencies
- OpenSSL (Version >= 1.0.2j)
Might work with older version as well, but I did not check that.
- Google Test Framework
For running the tests
- nlohmann JSON library
The awesome JSON library :)
## Thanks to...
- ben-collins JWT library
- Howard Hinnant for the stack allocator
- libstd++ code (I took the hashing code for string_view)
## Compiler Support
Tested with clang-5.0 and g++-6.4.
With issue#12, VS2017 is also supported.
## Building the library
### using conan
```shell
mkdir build
cd build
conan install .. --build missing
cmake ..
cmake --build . -j
```
### using debian
```shell
sudo apt install nlohmann-json3-dev
sudo apt install libgtest-dev
sudo apt install libssl-dev
mkdir build
cd build
cmake ..
cmake --build . -j
```
## Consuming the library
This library is uses cmake as a build system.
```cmake
# you can use cmake's `find_package` after installation or `add_subdirectory` when vendoring this repository
find_package(cpp-jwt REQUIRED)
# or
add_subdirectory(third_party/cpp-jwt)
add_executable(main main.cpp)
target_link_libraries(main cpp-jwt::cpp-jwt)
```
You can also use this library as a conan package, its available in the [conan center](https://conan.io/center/cpp-jwt):
just add `cpp-jwt[>=1.2]` to your conanfile.txt.
It can also be installed using [vcpkg](https://github.com/microsoft/vcpkg) by adding `"cpp-jwt"` to the dependencies in your `vcpkg.json` file.
## Parameters
There are two sets of parameters which can be used for creating `jwt_object` and for decoding.
All the parameters are basically a function which returns an instance of a type which are modelled after ParameterConcept
(see jwt::detail::meta::is_parameter_concept
).
- jwt_object
creation parameters
- payload
Used to populate the claims while creating the `jwt_object` instance.
There are two overloads of this function:
- Takes Initializer list of pair
Easy to pass claims with string values which are all known at the time of object creation.
Can be used like:
```cpp
jwt_object obj {
payload({
{"iss", "some-guy"},
{"sub", "something"},
{"X-pld", "data1"}
}),
... // Add other parameters
};
```
Claim values which are not strings/string_views cannot be used.
- Takes any type which models MappingConcept
(see detail::meta::is_mapping_concept
)
This overload can accept std::map
or std::unordered_map
like containers.
Can be used like:
```cpp
mapjwt::string_view
- algorithm
Used to pass the type of algorithm to use for encoding.
There are two overloads of this function:
- Takes jwt::string_view
Can pass the algorithm value in any case. It is case agnostic.
- Takes value of type enum class jwt::algorithm
- headers
Used to populate fields in JWT header. It is very similar to `payload` function parameter.
There are two overloads for this function which are similar to how payload
function is.
This parameter can be used to add headers other that alg and typ.
Same as the case with payload, only string values can be used with this. For adding values of other
data types, use add_header
API of jwt_header
class.
For example adding `kid` header with other additional data fields.
```cpp
jwt_object obj{
algorithm("HS256"),
headers({
{"kid", "12-34-56"},
{"xtra", "header"}
})
... // Add other parameters
};
```
- Decoding parameters
- algorithms
This is a mandatory parameter which takes a sequence of algorithms (as string) which the user would like to permit when validating the JWT. The value in the header for "alg" would be matched against the provided sequence of values. If nothing matches InvalidAlgorithmError
exception or InvalidAlgorithm
error would be set based upon the API being used.
There are two overloads for this function:
- Takes initializer-list of string values
- Takes in any type which satifies the SequenceConcept (see idetail::meta::is_sequence_concept
)
```cpp
jwt::decode(algorithms({"none", "HS256", "RS256"}), ...);
OR
std::vectorKeyNotPresentError
/ KeyNotPresent
exception/error.
- leeway
Optional parameter. Used with validation of "Expiration" and "Not Before" claims.
The value passed should be `seconds` to account for clock skew.
Default value is `0` seconds.
- verify
Optional parameter. Suggests if verification of claims should be done or not.
Takes a boolean value.
By default verification is turned on.
- issuer
Optional parameter.
Takes a string value.
Validates the passed issuer value against the one present in the decoded JWT object. If the values do not match InvalidIssuerError
or InvalidIssuer
exception or error_code is thrown/set.
- aud
Optional parameter.
Takes a string value.
Validates the passed audience value against the one present in the decoded JWT object. If the values do not match InvalidAudienceError
or InvalidAudience
exception or error_code is thrown/set.
- sub
Optional parameter.
Takes a string value.
Validates the passed subject value against the one present in the decoded JWT object. If the values do not match InvalidSubjectError
or InvalidSubject
exception or error_code is thrown/set.
- validate_iat
Optional parameter.
Takes a boolean value.
Validates the IAT claim. Only checks whether the field is present and is of correct type. If not throws/sets InvalidIATError
or InvalidIAT
.
Default value is false.
- validate_jti
Optional parameter.
Takes a boolean value.
Validates the JTI claim. Only checks for the presence of the claim. If not throws or sets InvalidJTIError
or InvalidJTI
.
Default is false.
## Claim Data Types
For the registered claim types the library assumes specific data types for the claim values. Using anything else is not supported and would result in runtime JSON parse error.
Claim | Data Type
-----------------------------------
Expiration(exp) | uint64_t (Epoch time in seconds)
-----------------------------------
Not Before(nbf) | uint64_t (Epoch time in seconds)
-----------------------------------
Issuer(iss) | string
-----------------------------------
Audience(aud) | string
-----------------------------------
Issued At(iat) | uint64_t (Epoch time in seconds)
-----------------------------------
Subject(sub) | string
-----------------------------------
JTI(jti) | NoneAlgorithmUsed
will be set in the error_code, but it usually should not be treated as a hard error when NONE algorithm is used intentionally.
- Decode Errors
Used for reporting errors at the time of decoding. Different categories of decode errors are:
```cpp
enum class DecodeErrc
{
// No algorithms provided in decode API
EmptyAlgoList = 1,
// The JWT signature has incorrect format
SignatureFormatError,
// The JSON library failed to parse
JsonParseError,
// Algorithm field in header is missing
AlgHeaderMiss,
// Type field in header is missing
TypHeaderMiss,
// Unexpected type field value
TypMismatch,
// Found duplicate claims
DuplClaims,
// Key/Secret not passed as decode argument
KeyNotPresent,
// Key/secret passed as argument for NONE algorithm.
// Not a hard error.
KeyNotRequiredForNoneAlg,
};
```
- Verification errors
Used for reporting verification errors when the verification falg is set to true in decode API.
Different categories of decode errors are:
```cpp
enum class VerificationErrc
{
//Algorithms provided does not match with header
InvalidAlgorithm = 1,
//Token is expired at the time of decoding
TokenExpired,
//The issuer specified does not match with payload
InvalidIssuer,
//The subject specified does not match with payload
InvalidSubject,
//The field IAT is not present or is of invalid type
InvalidIAT,
//Checks for the existence of JTI
//if validate_jti is passed in decode
InvalidJTI,
//The audience specified dowes not match with payload
InvalidAudience,
//Decoded before nbf time
ImmatureSignature,
//Signature match error
InvalidSignature,
// Invalid value type used for known claims
TypeConversionError,
};
```
Exceptions:
There are exception types created for almost all the error codes above.
- MemoryAllocationException
Derived from std::bad_alloc
. Thrown for memory allocation errors in OpenSSL C API.
- SigningError
Derived from std::runtime_error
. Thrown for failures in OpenSSL APIs while signing.
- DecodeError
Derived from std::runtime_error
. Base class for all decoding related exceptions.
- SignatureFormatError
Thrown if the format of the signature is not as expected.
- KeyNotPresentError
Thrown if key/secret is not passed in with the decode API if the algorithm used is something other than "none".
- VerificationError
Derived from std::runtime_error
. Base class exception for all kinds of verification errors. Verification errors are thrown only when the verify decode parameter is set to true.
- InvalidAlgorithmError
- TokenExpiredError
- InvalidIssuerError
- InvalidAudienceError
- InvalidSubjectError
- InvalidIATError
- InvalidJTIError
- ImmatureSignatureError
- InvalidSignatureError
- TypeConversionError
NOTE: See the error code section for explanation on above verification errors or checkout exceptions.hpp
header for more details.
## Additional Header Data
Generally the header consists only of `type` and `algorithm` fields. But there could be a need to add additional header fields. For example, to provide some kind of hint about what algorithm was used to sign the JWT. Checkout JOSE header section in RFC-7515.
The library provides APIs to do that as well.
```cpp
#include boost::string_ref
?
Sorry, I love boost! But, do not want it to be part of dependency.
If you use C++17 or greater `std::string_view` gets used instead and `jwt::string_view` implementation does not get included.
- You are not using the stack allocator or the shart string anywhere. Why to include it then ?
I will be using it in few places where I am sure I need not use `std::string` especially in the signing code.
- Why the complete `nlohmann JSON` is part of your library ?
Honestly did not know any better way. I know there are ways to use third party github repositories, but I do not know how to do that. Once I figure that out, I may move it out.
- Am I bound to use `nlohmann JSON` ? Can I use some other JSON library ?
As of now, ys. You cannot use any other JSON library unless you change the code. I would have liked to provide some adaptors for JSON interface. Perhaps in future, if required.
- Error codes and exceptions....heh?
Yeah, I often wonder if that was the right approach. I could have just stuck with error codes and be happy. But that was a good learning time for me.
- Where to find more about the usage ?
Checkout the tests. It has examples for all the algorithms which are supported.
- Support for C++11 seems trivial based on the changes required. Why not support C+11 then ?
Its 2018 now! If I ever start getting requests to have support for C++11, then I will surely consider it.
- The Metaprogramming concept checks for Sequence and Mapping looks sad.
Yeah I know. Just hacked something very basic.
## License
MIT License
Copyright (c) 2017 Arun Muralidharan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.