• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

examples/H03-May-2022-

include/jwt/H20-Nov-2018-

tests/H03-May-2022-

LICENSEH A D20-Nov-20181 KiB

README.mdH A D20-Nov-201825.1 KiB

cmake_commandH A D20-Nov-2018181

README.md

1<h1 align="center">CPP-JWT</h1>
2
3<div align="center">
4  <strong>A C++14 library for JSON Web Tokens(JWT)</strong>
5</div>
6
7<br/>
8
9<div align="center">
10<img src="http://jwt.io/img/logo-asset.svg" />
11</div>
12
13<br/>
14
15<div align="center">
16  <sub>
17    A little library built with lots of ❤︎  for working with JWT easier.
18    By Arun Muralidharan.
19  </sub>
20</div>
21
22## Table of Contents
23- [What is it](#what-is-it)
24- [Example](#example)
25- [API Philosophy](#api-philosophy)
26- [Support](#support)
27- [External Dependencies](#external-dependencies)
28- [Thanks to...](#thanks-to...)
29- [Compiler Support](#compiler-support)
30- [Installation](#installation)
31- [Parameters](#parameters)
32- [Claim Data Types](#claim-data-types)
33- [Advanced Examples](#advanced-examples)
34- [Error Codes & Exceptions](#error-codes-&-exceptions)
35- [Additional Header Data](#additional-header-data)
36- [Things for improvement](#things-for-improvement)
37- [LICENSE](#license)
38
39
40## What is it ?
41For the uninitiated, JSON Web Token(JWT) is a JSON based standard (<a href="https://tools.ietf.org/html/rfc7519">RFC-7519</a>) for creating assertions or access tokens that consists of some claims (encoded within the assertion).
42This 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.
43
44Few good resources on this material which I found useful are:
45  <a href="https://scotch.io/tutorials/the-anatomy-of-a-json-web-token">Anatomy of JWT</a>
46  <a href="https://auth0.com/learn/json-web-tokens/">Learn JWT</a>
47  <a href="https://tools.ietf.org/html/rfc7519">RFC 7519</a>
48
49
50## Example
51  Lets dive into see a simple example of encoding and decoding in Python. Taking the example of <strong>pyjwt</strong> module from its docs.
52
53  ```python
54  >>import jwt
55  >>key = 'secret'
56  >>
57  >>encoded = jwt.encode({'some': 'payload'}, key, algorithm='HS256')
58  'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg'
59  >>
60  >>decoded = jwt.decode(encoded, key, algorithms='HS256')
61  {'some': 'payload'}
62  ```
63
64  Now, lets look at our C++ code doing the same thing.
65  ```cpp
66  #include <iostream>
67  #include "jwt/jwt.hpp"
68
69  int main() {
70    using namespace jwt::params;
71
72    auto key = "secret"; //Secret to use for the algorithm
73    //Create JWT object
74    jwt::jwt_object obj{algorithm("HS256"), payload({{"some", "payload"}}), secret(key)};
75
76    //Get the encoded string/assertion
77    auto enc_str = obj.signature();
78    std::cout << enc_str << std::endl;
79
80    //Decode
81    auto dec_obj = jwt::decode(enc_str, algorithms({"hs256"}), secret(key));
82    std::cout << dec_obj.header() << std::endl;
83    std::cout << dec_obj.payload() << std::endl;
84
85    return 0;
86  }
87  ```
88
89  It outputs:
90  ```
91  eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg
92  {"alg":"HS256","typ":"JWT"}
93  {"some":"payload"}
94  ```
95
96  Almost the same API, except for some ugliness here and there. But close enough!
97
98  Lets take another example in which we will see to add payload claim having type other than string.
99  The <code>payload</code> function used in the above example to create <code>jwt_object</code> object can only take strings. For anything else, it will throw a compilation error.
100
101  For adding claims having values other than string, <code>jwt_object</code> class provides <code>add_claim</code> API. We will also see few other APIs in the next example. Make sure to read the comments :).
102
103  ```cpp
104    #include <chrono>
105    #include <cassert>
106    #include <iostream>
107    #include "jwt/jwt.hpp"
108
109    int main() {
110      using namespace jwt::params;
111
112      jwt::jwt_object obj{algorithm("hs256"), secret("secret"), payload({{"user", "admin"}})};
113
114      //Use add_claim API to add claim values which are
115      // _not_ strings.
116      // For eg: `iat` and `exp` claims below.
117      // Other claims could have been added in the payload
118      // function above as they are just stringy things.
119      obj.add_claim("iss", "arun.muralidharan")
120         .add_claim("sub", "test")
121         .add_claim("id", "a-b-c-d-e-f-1-2-3")
122         .add_claim("iat", 1513862371)
123         .add_claim("exp", std::chrono::system_clock::now() + std::chrono::seconds{10})
124         ;
125
126      //Use `has_claim` to check if the claim exists or not
127      assert (obj.has_claim("iss"));
128      assert (obj.has_claim("exp"));
129
130      //Use `has_claim_with_value` to check if the claim exists
131      //with a specific value or not.
132      assert (obj.payload().has_claim_with_value("id", "a-b-c-d-e-f-1-2-3"));
133      assert (obj.payload().has_claim_with_value("iat", 1513862371));
134
135      //Remove a claim using `remove_claim` API.
136      //Most APIs have an overload which takes enum class type as well
137      //It can be used interchangeably with strings.
138      obj.remove_claim(jwt::registered_claims::expiration);
139      assert (not obj.has_claim("exp"));
140
141      //Using `add_claim` with extra features.
142      //Check return status and overwrite
143      bool ret = obj.payload().add_claim("sub", "new test", false/*overwrite*/);
144      assert (not ret);
145
146      // Overwrite an existing claim
147      ret = obj.payload().add_claim("sub", "new test", true/*overwrite*/);
148      assert ( ret );
149
150      assert (obj.payload().has_claim_with_value("sub", "new test"));
151
152      return 0;
153    }
154  ```
155
156The <code>jwt_object</code> class is basically a composition of the JWT component classes, which are <code>jwt_header</code> & <code>jwt_payload</code>. For convenience <code>jwt_object</code> exposes only few important APIs to the user, the remaining APIs under <code>jwt_header</code> and <code>jwt_payload</code> can be accessed by calling <code>jwt_object::header()</code> and <code>jwt_object::payload()</code> APIs.
157
158
159## API Philosophy
160I wanted to make the code easy to read and at the same time make most of the standard library and the modern features.
161It also uses some metaprogramming tricks to enforce type checks and give better error messages.
162
163The design of `parameters` alleviates the pain of remembering positional arguments. Also makes the APIs more extensible for future enhancements.
164
165The library has 2 sets of APIs for encoding and decoding:
166  - API which takes an instance of <code>std::error_code</code>
167    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.
168  - API which throws exceptions
169    All the errors would be thrown as exception.
170
171## Support
172<strong>Algorithms and features supported</strong>
173- [x] HS256
174- [x] HS384
175- [x] HS512
176- [x] RS256
177- [x] RS384
178- [x] RS512
179- [x] ES256
180- [x] ES384
181- [x] ES512
182- [x] Sign
183- [x] Verify
184- [x] iss (issuer) check
185- [x] sub (subject) check
186- [x] aud (audience) check
187- [x] exp (expiration time) check
188- [x] nbf (not before time) check
189- [x] iat (issued at) check
190- [x] jti (JWT id) check
191- [x] JWS header addition support. For eg "kid" support.
192
193## External Dependencies
194  - <strong>OpenSSL </strong>(Version >= 1.0.2j)
195    Might work with older version as well, but I did not check that.
196  - <strong>Google Test Framework</strong>
197    For running the tests
198  - <strong>nlohmann JSON library</strong>
199    The awesome JSON library :)
200
201## Thanks to...
202    - <a href="https://github.com/benmcollins/libjwt">ben-collins JWT library</a>
203    - Howard Hinnant for the stack allocator
204    - libstd++ code (I took the hashing code for string_view)
205
206## Compiler Support
207
208Tested with <strong>clang-5.0</strong> and <strong>g++-6.4</strong>.
209With issue#12, <strong>VS2017</strong> is also supported.
210
211## Installation
212Use the C++ package manager..... just kidding :)
213This is a header only library, so you can just add it to your include path and start using it. The only somewhat tricky part is to link it with openssl library. Check out the cmake file for building it properly.
214
215For example one can run cmake like:
216```
217cmake -DOPENSSL_ROOT_DIR=/usr/local/Cellar/openssl/1.0.2j/ -DOPENSSL_LIBRARIES=/usr/local/Cellar/openssl/1.0.2j/lib/ -DOPENSSL_INCLUDE_DIR=/usr/local/Cellar/openssl/1.0.2j/include/
218```
219
220## Parameters
221There are two sets of parameters which can be used for creating `jwt_object` and for decoding.
222All the parameters are basically a function which returns an instance of a type which are modelled after <code>ParameterConcept</code> (see <code>jwt::detail::meta::is_parameter_concept</code>).
223
224
225- <strong><code>jwt_object</code> creation parameters</strong>
226  - <strong>payload</strong>
227
228    Used to populate the claims while creating the `jwt_object` instance.
229
230    There are two overloads of this function:
231    - Takes Initializer list of <code>pair<string_view, string_view></code>
232
233      Easy to pass claims with string values which are all known at the time of object creation.
234      Can be used like:
235      ```cpp
236      jwt_object obj {
237        payload({
238            {"iss", "some-guy"},
239            {"sub", "something"},
240            {"X-pld", "data1"}
241          }),
242          ... // Add other parameters
243      };
244      ```
245      Claim values which are not strings/string_views cannot be used.
246
247    - Takes any type which models <code>MappingConcept</code> (see <code>detail::meta::is_mapping_concept</code>)
248
249      This overload can accept <code>std::map</code> or <code>std::unordered_map</code> like containers.
250      Can be used like:
251      ```cpp
252      map<string, string> m;
253      m["iss"] = "some-guy";
254      m["sub"] = "something";
255      m["X-pld"] = "data1";
256
257      jwt_object obj{
258        payload(std::move(m)),
259        ... // Add other parameters
260      };
261      //OR
262      jwt_object obj{
263        payload(m),
264        ... // Add other parameters
265      };
266      ```
267
268  - <strong>secret</strong>
269
270    Used to pass the key which could be some random string or public certificate data as string.
271    The passed string type must be convertible to <code>jwt::string_view</code>
272
273  - <strong>algorithm</strong>
274
275    Used to pass the type of algorithm to use for encoding.
276    There are two overloads of this function:
277    - Takes <code>jwt::string_view</code>
278
279      Can pass the algorithm value in any case. It is case agnostic.
280
281    - Takes value of type <code>enum class jwt::algorithm</code>
282
283  - <strong>headers</strong>
284
285    Used to populate fields in JWT header. It is very similar to `payload` function parameter.
286    There are two overloads for this function which are similar to how <code>payload</code> function is.
287    This parameter can be used to add headers other that <strong>alg</strong> and <strong>typ</strong>.
288
289    Same as the case with payload, only string values can be used with this. For adding values of other
290    data types, use <code>add_header</code> API of <code>jwt_header</code> class.
291
292    For example adding `kid` header with other additional data fields.
293    ```cpp
294    jwt_object obj{
295      algorithm("HS256"),
296      headers({
297        {"kid", "12-34-56"},
298        {"xtra", "header"}
299      })
300      ... // Add other parameters
301    };
302    ```
303
304
305- <strong>Decoding parameters</strong>
306
307  - <strong>algorithms</strong>
308
309    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 <code>InvalidAlgorithmError</code> exception or <code>InvalidAlgorithm</code> error would be set based upon the API being used.
310
311    There are two overloads for this function:
312    - Takes initializer-list of string values
313    - Takes in any type which satifies the <strong>SequenceConcept</strong> (see <code>idetail::meta::is_sequence_concept</code>)
314
315  ```cpp
316  jwt::decode(algorithms({"none", "hs256", "rs256"}), ...);
317
318  OR
319
320  std::vector<std::string> algs{"none", "hs256", "rs256"};
321  jwt::decode(algorithms(algs), ...);
322  ```
323
324  - <strong>secret</strong>
325
326    Optional parameter. To be supplied only when the algorithm used is not "NONE". Else would throw/set <code>KeyNotPresentError</code> / <code>KeyNotPresent</code> exception/error.
327
328  - <strong>leeway</strong>
329
330    Optional parameter. Used with validation of "Expiration" and "Not Before" claims.
331    The value passed should be `seconds` to account for clock skew.
332    Default value is `0` seconds.
333
334  - <strong>verify</strong>
335
336    Optional parameter. Suggests if verification of claims should be done or not.
337    Takes a boolean value.
338    By default verification is turned on.
339
340  - <strong>issuer</strong>
341
342    Optional parameter.
343    Takes a string value.
344    Validates the passed issuer value against the one present in the decoded JWT object. If the values do not match <code>InvalidIssuerError</code> or <code>InvalidIssuer</code> exception or error_code is thrown/set.
345
346  - <strong>aud</strong>
347
348    Optional parameter.
349    Takes a string value.
350    Validates the passed audience value against the one present in the decoded JWT object. If the values do not match <code>InvalidAudienceError</code> or <code>InvalidAudience</code> exception or error_code is thrown/set.
351
352  - <strong>sub</strong>
353
354    Optional parameter.
355    Takes a string value.
356    Validates the passed subject value against the one present in the decoded JWT object. If the values do not match <code>InvalidSubjectError</code> or <code>InvalidSubject</code> exception or error_code is thrown/set.
357
358  - <strong>validate_iat</strong>
359
360    Optional parameter.
361    Takes a boolean value.
362    Validates the IAT claim. Only checks whether the field is present and is of correct type. If not throws/sets <code>InvalidIATError</code> or <code>InvalidIAT</code>.
363
364    Default value is false.
365
366  - <strong>validate_jti</strong>
367
368    Optional parameter.
369    Takes a boolean value.
370    Validates the JTI claim. Only checks for the presence of the claim. If  not throws or sets <code>InvalidJTIError</code> or <code>InvalidJTI</code>.
371
372    Default is false.
373
374
375## Claim Data Types
376For 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.
377
378    Claim                 |  Data Type
379    -----------------------------------
380    Expiration(exp)       |  uint64_t (Epoch time in seconds)
381    -----------------------------------
382    Not Before(nbf)       |  uint64_t (Epoch time in seconds)
383    -----------------------------------
384    Issuer(iss)           |  string
385    -----------------------------------
386    Audience(aud)         |  string
387    -----------------------------------
388    Issued At(iat)        |  uint64_t (Epoch time in seconds)
389    -----------------------------------
390    Subject(sub)          |  string
391    -----------------------------------
392    JTI(jti)              | <Value type not checked by library. Upto application.>
393    -----------------------------------
394
395
396## Advanced Examples
397We will see few complete examples which makes use of error code checks and exception handling.
398The examples are taken from the "tests" section. Users are requested to checkout the tests to find out more ways to use this library.
399
400Expiration verification example (uses error_code):
401```cpp
402#include <cassert>
403#include <iostream>
404#include "jwt/jwt.hpp"
405
406int main() {
407  using namespace jwt::params;
408
409  jwt::jwt_object obj{algorithm("hs256"), secret("secret")};
410  obj.add_claim("iss", "arun.muralidharan")
411     .add_claim("exp", std::chrono::system_clock::now() - std::chrono::seconds{1})
412     ;
413
414  std::error_code ec;
415  auto enc_str = obj.signature(ec);
416  assert (!ec);
417
418  auto dec_obj = jwt::decode(enc_str, algorithms({"hs256"}), ec, secret("secret"), verify(true));
419  assert (ec);
420  assert (ec.value() == static_cast<int>(jwt::VerificationErrc::TokenExpired));
421
422  return 0;
423}
424```
425
426Expiration verification example (uses exception):
427```cpp
428#include <cassert>
429#include <iostream>
430#include "jwt/jwt.hpp"
431
432int main() {
433  using namespace jwt::params;
434
435  jwt::jwt_object obj{algorithm("hs256"), secret("secret")};
436
437  obj.add_claim("iss", "arun.muralidharan")
438     .add_claim("exp", std::chrono::system_clock::now() - std::chrono::seconds{1})
439     ;
440
441  auto enc_str = obj.signature();
442
443  try {
444    auto dec_obj = jwt::decode(enc_str, algorithms({"hs256"}), secret("secret"), verify(true));
445  } catch (const jwt::TokenExpiredError& e) {
446    //Handle Token expired exception here
447    //...
448  } catch (const jwt::SignatureFormatError& e) {
449    //Handle invalid signature format error
450    //...
451  } catch (const jwt::DecodeError& e) {
452    //Handle all kinds of other decode errors
453    //...
454  } catch (const jwt::VerificationError& e) {
455    // Handle the base verification error.
456    //NOTE: There are other derived types of verification errors
457    // which will be discussed in next topic.
458  } catch (...) {
459    std::cerr << "Caught unknown exception\n";
460  }
461
462  return 0;
463}
464```
465
466Invalid issuer test(uses error_code):
467```cpp
468#include <cassert>
469#include <iostream>
470#include "jwt/jwt.hpp"
471
472int main() {
473  using namespace jwt::params;
474
475  jwt::jwt_object obj{algorithm("hs256"), secret("secret"), payload({{"sub", "test"}})};
476
477  std::error_code ec;
478  auto enc_str = obj.signature(ec);
479  assert (!ec);
480
481  auto dec_obj = jwt::decode(enc_str, algorithms({"hs256"}), ec, secret("secret"), issuer("arun.muralidharan"));
482  assert (ec);
483
484  assert (ec.value() == static_cast<int>(jwt::VerificationErrc::InvalidIssuer));
485
486  return 0;
487}
488```
489
490## Error Codes & Exceptions
491The library as we saw earlier supports error reporting via both exceptions and error_code.
492
493<strong>Error codes:</strong>
494
495The error codes are divided into different categories:
496- Algorithm Errors
497
498  Used for reporting errors at the time of encoding / signature creation.
499  ```cpp
500  enum class AlgorithmErrc
501  {
502    SigningErr = 1,
503    VerificationErr,
504    KeyNotFoundErr,
505    NoneAlgorithmUsed, // Not an actual error!
506  };
507  ```
508
509  <strong>NOTE:</strong> <code>NoneAlgorithmUsed</code> will be set in the error_code, but it usually should not be treated as a hard error when NONE algorithm is used intentionally.
510
511- Decode Errors
512
513  Used for reporting errors at the time of decoding. Different categories of decode errors are:
514  ```cpp
515  enum class DecodeErrc
516  {
517    // No algorithms provided in decode API
518    EmptyAlgoList = 1,
519    // The JWT signature has incorrect format
520    SignatureFormatError,
521    // The JSON library failed to parse
522    JsonParseError,
523    // Algorithm field in header is missing
524    AlgHeaderMiss,
525    // Type field in header is missing
526    TypHeaderMiss,
527    // Unexpected type field value
528    TypMismatch,
529    // Found duplicate claims
530    DuplClaims,
531    // Key/Secret not passed as decode argument
532    KeyNotPresent,
533    // Key/secret passed as argument for NONE algorithm.
534    // Not a hard error.
535    KeyNotRequiredForNoneAlg,
536  };
537  ```
538
539- Verification errors
540
541  Used for reporting verification errors when the verification falg is set to true in decode API.
542  Different categories of decode errors are:
543  ```cpp
544  enum class VerificationErrc
545  {
546    //Algorithms provided does not match with header
547    InvalidAlgorithm = 1,
548    //Token is expired at the time of decoding
549    TokenExpired,
550    //The issuer specified does not match with payload
551    InvalidIssuer,
552    //The subject specified does not match with payload
553    InvalidSubject,
554    //The field IAT is not present or is of invalid type
555    InvalidIAT,
556    //Checks for the existence of JTI
557    //if validate_jti is passed in decode
558    InvalidJTI,
559    //The audience specified dowes not match with payload
560    InvalidAudience,
561    //Decoded before nbf time
562    ImmatureSignature,
563    //Signature match error
564    InvalidSignature,
565    // Invalid value type used for known claims
566    TypeConversionError,
567  };
568  ```
569
570<strong>Exceptions:</strong>
571There are exception types created for almost all the error codes above.
572
573- MemoryAllocationException
574
575  Derived from <code>std::bad_alloc</code>. Thrown for memory allocation errors in OpenSSL C API.
576
577- SigningError
578
579  Derived from <code>std::runtime_error</code>. Thrown for failures in OpenSSL APIs while signing.
580
581- DecodeError
582
583  Derived from <code>std::runtime_error</code>. Base class for all decoding related exceptions.
584
585  - SignatureFormatError
586
587    Thrown if the format of the signature is not as expected.
588
589  - KeyNotPresentError
590
591    Thrown if key/secret is not passed in with the decode API if the algorithm used is something other than "NONE".
592
593- VerificationError
594
595  Derived from <code>std::runtime_error</code>. Base class exception for all kinds of verification errors. Verification errors are thrown only when the verify decode parameter is set to true.
596
597  - InvalidAlgorithmError
598  - TokenExpiredError
599  - InvalidIssuerError
600  - InvalidAudienceError
601  - InvalidSubjectError
602  - InvalidIATError
603  - InvalidJTIError
604  - ImmatureSignatureError
605  - InvalidSignatureError
606  - TypeConversionError
607
608  NOTE: See the error code section for explanation on above verification errors or checkout <code>exceptions.hpp</code> header for more details.
609
610
611## Additional Header Data
612Generally 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 <a href="https://tools.ietf.org/html/rfc7515">RFC-7515</a>.
613
614The library provides APIs to do that as well.
615
616```cpp
617#include <cassert>
618#include <iostream>
619#include "jwt/jwt.hpp"
620
621int main() {
622  using namespace jwt::params;
623
624  jwt::jwt_object obj{
625      headers({
626        {"alg", "none"},
627        {"typ", "jwt"},
628        }),
629      payload({
630        {"iss", "arun.muralidharan"},
631        {"sub", "nsfw"},
632        {"x-pld", "not my ex"}
633      })
634  };
635
636  bool ret = obj.header().add_header("kid", 1234567);
637  assert (ret);
638
639  ret = obj.header().add_header("crit", std::array<std::string, 1>{"exp"});
640  assert (ret);
641
642  std::error_code ec;
643  auto enc_str = obj.signature();
644
645  auto dec_obj = jwt::decode(enc_str, algorithms({"none"}), ec, verify(false));
646
647  // Should not be a hard error in general
648  assert (ec.value() == static_cast<int>(jwt::AlgorithmErrc::NoneAlgorithmUsed));
649}
650```
651
652
653## Things for improvement
654Many things!
655Encoding and decoding JWT is fairly a simple task and could be done in a single source file. I have tried my best to get the APIs and design correct in how much ever time I could give for this project. Still, there are quite a few places (or all the places :( ? ) where things are not correct or may not be the best approach.
656
657With C++, it is pretty easy to go overboard and create something very difficult or something very straightforward (not worth to be a library). My intention was to make a sane library easier for end users to use while also making the life of someone reading the source have fairly good time debugging some issue.
658
659Things one may have questions about
660- There is a string_view implementation. Why not use <code>boost::string_ref</code> ?
661
662  Sorry, I love boost! But, do not want it to be part of dependency.
663  Having said that, I can use some MACRO to use <code>boost::string_ref</code> or C++17 string_view if available. Perhaps in future.
664
665- You are not using the stack allocator or the shart string anywhere. Why to include it then ?
666
667  I will be using it in few places where I am sure I need not use `std::string` especially in the signing code.
668
669- Why the complete `nlohmann JSON` is part of your library ?
670
671  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.
672
673- Am I bound to use `nlohmann JSON` ? Can I use some other JSON library ?
674
675  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.
676
677- Error codes and exceptions....heh?
678
679  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.
680
681- Where to find more about the usage ?
682
683  Checkout the tests. It has examples for all the algorithms which are supported.
684
685- Support for C++11 seems trivial based on the changes required. Why not support C+11 then ?
686
687  Its 2018 now! If I ever start getting requests to have support for C++11, then I will surely consider it.
688
689- The Metaprogramming concept checks for Sequence and Mapping looks sad.
690
691  Yeah I know. Just hacked something very basic.
692
693
694## License
695
696MIT License
697
698Copyright (c) 2017 Arun Muralidharan
699
700Permission is hereby granted, free of charge, to any person obtaining a copy
701of this software and associated documentation files (the "Software"), to deal
702in the Software without restriction, including without limitation the rights
703to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
704copies of the Software, and to permit persons to whom the Software is
705furnished to do so, subject to the following conditions:
706
707The above copyright notice and this permission notice shall be included in all
708copies or substantial portions of the Software.
709
710THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
711IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
712FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
713AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
714LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
715OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
716SOFTWARE.
717