Mirror of cpp-jwt
Find a file
2017-12-29 18:22:09 +05:30
examples Added second example for readme 2017-12-29 17:29:24 +05:30
include/jwt Readme changes for example code and fixed header decoding 2017-12-29 16:26:37 +05:30
tests Readme changes for example code and fixed header decoding 2017-12-29 16:26:37 +05:30
cmake_command Cmake support added for tests. Complete.Huh. 2017-12-28 15:44:47 +05:30
CMakeLists.txt Readme changes for example code and fixed header decoding 2017-12-29 16:26:37 +05:30
LICENSE Create LICENSE 2017-12-27 15:31:51 +05:30
README.md Readme updated with support 2017-12-29 18:22:09 +05:30

CPP-JWT

A C++14 library for JSON Web Tokens(JWT)

A little library built with lots of ❤︎ for working with JWT easier. By Arun Muralidharan.

Table of Contents

What is it ?

For the uninitiated, JSON Web Token(JWT) is a JSON based standard (RFC-7519) for creating assertions or access tokens that consists of some claims (encoded within the assertion). This assertion can be used in some kind of bearer authentication mechanism that the server will provide to clients, and the clients can make use of the provided assertion for accessing resources.

Few good resources on this material which I found useful are:

Example

Lets dive into see a simple example of encoding and decoding in Python. Taking the example of pyjwt module from its docs.

>>import jwt
>>key = 'secret'
>>
>>encoded = jwt.encode({'some': 'payload'}, key, algorithm='HS256')
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg'
>>
>>decoded = jwt.decode(encoded, key, algorithms='HS256')
{'some': 'payload'}

Now, lets look at our C++ code doing the same thing.

#include <iostream>
#include "jwt/jwt.hpp"

int main() {
  using namespace jwt::params;

  auto key = "secret"; //Secret to use for the algorithm
  //Create JWT object
  jwt::jwt_object obj{algorithm("HS256"), payload({{"some", "payload"}}), secret(key)};

  //Get the encoded string/assertion
  auto enc_str = obj.signature();
  std::cout << enc_str << std::endl;

  //Decode
  auto dec_obj = jwt::decode(enc_str, algorithms({"hs256"}), secret(key));
  std::cout << dec_obj.header() << std::endl;
  std::cout << dec_obj.payload() << std::endl;

  return 0;
}

It outputs:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg
{"alg":"HS256","typ":"JWT"}
{"some":"payload"}

Almost the same API, except for some ugliness here and there. But close enough!

Lets take another example in which we will see to add payload claim having type other than string. The 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 :).

  #include <chrono>
  #include <cassert>
  #include <iostream>
  #include "jwt/jwt.hpp"

  int main() {
    using namespace jwt::params;

    jwt::jwt_object obj{algorithm("hs256"), secret("secret"), payload({{"user", "admin"}})};

    //Use add_claim API to add claim values which are
    // _not_ strings.
    // For eg: `iat` and `exp` claims below.
    // Other claims could have been added in the payload
    // function above as they are just stringy things.
    obj.add_claim("iss", "arun.muralidharan")
       .add_claim("sub", "test")
       .add_claim("id", "a-b-c-d-e-f-1-2-3")
       .add_claim("iat", 1513862371)
       .add_claim("exp", std::chrono::system_clock::now() + std::chrono::seconds{10})
       ;

    //Use `has_claim` to check if the claim exists or not
    assert (obj.has_claim("iss"));
    assert (obj.has_claim("exp"));

    //Use `has_claim_with_value` to check if the claim exists
    //with a specific value or not.
    assert (obj.payload().has_claim_with_value("id", "a-b-c-d-e-f-1-2-3"));
    assert (obj.payload().has_claim_with_value("iat", 1513862371));

    //Remove a claim using `remove_claim` API.
    //Most APIs have an overload which takes enum class type as well
    //It can be used interchangeably with strings.
    obj.remove_claim(jwt::registered_claims::expiration);
    assert (not obj.has_claim("exp"));

    //Using `add_claim` with extra features.
    //Check return status and overwrite
    bool ret = obj.payload().add_claim("sub", "new test", false/*overwrite*/);
    assert (not ret);

    // Overwrite an existing claim
    ret = obj.payload().add_claim("sub", "new test", true/*overwrite*/);
    assert ( ret );

    assert (obj.payload().has_claim_with_value("sub", "new test"));

    return 0;
  }

The 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

  • HS256
  • HS384
  • HS512
  • RS256
  • RS384
  • RS512
  • ES256
  • ES384
  • ES512
  • Sign
  • Verify
  • iss (issuer) check
  • sub (subject) check
  • aud (audience) check
  • exp (expiration time) check
  • nbf (not before time) check
  • iat (issued at) check
  • jti (JWT id) check