From a78bd849cf3107cd2ebd539f239f1e2f44c0b74b Mon Sep 17 00:00:00 2001 From: cjserio Date: Sat, 9 Dec 2023 07:43:25 -0500 Subject: [PATCH 01/15] Implemented JsonCPP Traits --- include/jwt-cpp/traits/jsoncpp/defaults.h | 88 +++++++++++++++ include/jwt-cpp/traits/jsoncpp/traits.h | 125 ++++++++++++++++++++++ 2 files changed, 213 insertions(+) create mode 100644 include/jwt-cpp/traits/jsoncpp/defaults.h create mode 100644 include/jwt-cpp/traits/jsoncpp/traits.h diff --git a/include/jwt-cpp/traits/jsoncpp/defaults.h b/include/jwt-cpp/traits/jsoncpp/defaults.h new file mode 100644 index 000000000..05929b0f3 --- /dev/null +++ b/include/jwt-cpp/traits/jsoncpp/defaults.h @@ -0,0 +1,88 @@ +#ifndef JWT_CPP_JSONCPP_DEFAULTS_H +#define JWT_CPP_JSONCPP_DEFAULTS_H + +#ifndef JWT_DISABLE_PICOJSON +#define JWT_DISABLE_PICOJSON +#endif + +#include "traits.h" + +namespace jwt { + /** + * \brief a class to store a generic [jsoncpp](https://github.com/open-source-parsers/jsoncpp) value as claim + * + * This type is the specialization of the \ref basic_claim class which + * uses the standard template types. + */ + using claim = basic_claim; + + /** + * Create a verifier using the default clock + * \return verifier instance + */ + inline verifier verify() { + return verify(default_clock{}); + } + + /** + * Return a builder instance to create a new token + */ + inline builder create() { return builder(); } + +#ifndef JWT_DISABLE_BASE64 + /** + * Decode a token + * \param token Token to decode + * \return Decoded token + * \throw std::invalid_argument Token is not in correct format + * \throw std::runtime_error Base64 decoding failed or invalid json + */ + inline decoded_jwt decode(const std::string& token) { + return decoded_jwt(token); + } +#endif + + /** + * Decode a token + * \tparam Decode is callabled, taking a string_type and returns a string_type. + * It should ensure the padding of the input and then base64url decode and + * return the results. + * \param token Token to decode + * \param decode The token to parse + * \return Decoded token + * \throw std::invalid_argument Token is not in correct format + * \throw std::runtime_error Base64 decoding failed or invalid json + */ + template + decoded_jwt decode(const std::string& token, Decode decode) { + return decoded_jwt(token, decode); + } + + /** + * Parse a jwk + * \param token JWK Token to parse + * \return Parsed JWK + * \throw std::runtime_error Token is not in correct format + */ + inline jwk parse_jwk(const traits::jsoncpp::string_type& token) { + return jwk(token); + } + + /** + * Parse a jwks + * \param token JWKs Token to parse + * \return Parsed JWKs + * \throw std::runtime_error Token is not in correct format + */ + inline jwks parse_jwks(const traits::jsoncpp::string_type& token) { + return jwks(token); + } + + /** + * This type is the specialization of the \ref verify_ops::verify_context class which + * uses the standard template types. + */ + using verify_context = verify_ops::verify_context; +} // namespace jwt + +#endif // JWT_CPP_JSONCPP_DEFAULTS_H diff --git a/include/jwt-cpp/traits/jsoncpp/traits.h b/include/jwt-cpp/traits/jsoncpp/traits.h new file mode 100644 index 000000000..ae6fc2b78 --- /dev/null +++ b/include/jwt-cpp/traits/jsoncpp/traits.h @@ -0,0 +1,125 @@ +#ifndef JWT_CPP_JSONCPP_TRAITS_H +#define JWT_CPP_JSONCPP_TRAITS_H + +#include "jwt-cpp/jwt.h" +#include "json/json.h" + +namespace jwt { + namespace traits { + struct jsoncpp { + using value_type = Json::Value; + using string_type = std::string; + class array_type: public Json::Value { + public: + using value_type = Json::Value; + + array_type() = default; + array_type(const array_type&) = default; + explicit array_type(const Json::Value& o) : Json::Value(o) {} + array_type(array_type&&) = default; + explicit array_type(Json::Value&& o) : Json::Value(o) {} + ~array_type() = default; + array_type& operator=(const array_type& o) = default; + array_type& operator=(array_type&& o) noexcept = default; + }; + using number_type = double; + using integer_type = Json::Value::Int; + using boolean_type = bool; + class object_type : public Json::Value { + public: + using key_type = std::string; + using mapped_type = Json::Value; + using size_type = size_t; + + object_type() = default; + object_type(const object_type&) = default; + explicit object_type(const Json::Value& o) : Json::Value(o) {} + object_type(object_type&&) = default; + explicit object_type(Json::Value&& o) : Json::Value(o) {} + ~object_type() = default; + object_type& operator=(const object_type& o) = default; + object_type& operator=(object_type&& o) noexcept = default; + + // Add missing C++11 element access + const mapped_type& at(const key_type& key) const { + Json::Value const* found = find(key.data(), key.data() + key.length()); + if (!found) + throw std::out_of_range("invalid key"); + return *found; + } + + size_type count(const key_type& key) const { + return this->isMember(key) ? 1 : 0; + } + }; + + // Translation between the implementation notion of type, to the jwt::json::type equivilant + static jwt::json::type get_type(const value_type &val) { + using jwt::json::type; + + if (val.isArray()) + return type::array; + else if (val.isString()) + return type::string; + else if (val.isNumeric()) + return type::number; + else if (val.isInt()) + return type::integer; + else if (val.isBool()) + return type::boolean; + else if (val.isObject()) + return type::object; + + throw std::logic_error("invalid type"); + } + + static integer_type as_integer(const value_type& val) { + switch (val.type()) { + case Json::intValue: return val.asInt64(); + case Json::uintValue: return static_cast(val.asUInt64()); + default: throw std::bad_cast(); + } + } + + static boolean_type as_boolean(const value_type& val) { + if (!val.isBool()) throw std::bad_cast(); + return val.asBool(); + } + + static number_type as_number(const value_type& val) { + if (!val.isNumeric()) throw std::bad_cast(); + return val.asDouble(); + } + + static string_type as_string(const value_type& val) { + if (!val.isString()) throw std::bad_cast(); + return val.asString(); + } + + static object_type as_object(const value_type& val) { + if (!val.isObject()) throw std::bad_cast(); + return object_type(val); + } + + static array_type as_array(const value_type& val) { + if (!val.isArray()) throw std::bad_cast(); + return array_type(val); + } + + static bool parse(value_type &val, string_type str) { + Json::Reader reader; + return reader.parse(str, val); + } + + static string_type serialize(const value_type &val) { + Json::StreamWriterBuilder builder; + builder["commentStyle"] = "None"; + builder["indentation"] = ""; + std::unique_ptr writer(builder.newStreamWriter()); + return Json::writeString(builder, val); + } + }; + } // namespace traits +} // namespace jwt + +#endif From 9aeaf94b9bf13895d5ffe89135c4e809b3ad5870 Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Sat, 9 Dec 2023 15:50:42 -0500 Subject: [PATCH 02/15] quick pass to add CI --- .../open-source-parsers-jsoncpp/action.yml | 18 +++++++ .github/workflows/lint.yml | 2 + .github/workflows/traits.yml | 6 +++ example/traits/CMakeLists.txt | 6 +++ .../traits/open-source-parsers-jsoncpp.cpp | 47 +++++++++++++++++++ .../defaults.h | 0 .../traits.h | 2 +- 7 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 .github/actions/install/open-source-parsers-jsoncpp/action.yml create mode 100644 example/traits/open-source-parsers-jsoncpp.cpp rename include/jwt-cpp/traits/{jsoncpp => open-source-parsers-jsoncpp}/defaults.h (100%) rename include/jwt-cpp/traits/{jsoncpp => open-source-parsers-jsoncpp}/traits.h (99%) diff --git a/.github/actions/install/open-source-parsers-jsoncpp/action.yml b/.github/actions/install/open-source-parsers-jsoncpp/action.yml new file mode 100644 index 000000000..d44c1e1ef --- /dev/null +++ b/.github/actions/install/open-source-parsers-jsoncpp/action.yml @@ -0,0 +1,18 @@ +name: Install open-source-parsers/jsoncpp +description: Install open-source-parsers/jsoncpp for building test application +inputs: + version: + description: The desired open-source-parsers/jsoncpp version to install + required: false + default: "1.9.6" +runs: + using: composite + steps: + - run: | + cd /tmp + wget https://github.com/open-source-parsers/jsoncpp/archive/${{ inputs.version }}.tar.gz + tar -zxf /tmp/${{ inputs.version }}.tar.gz + cd jsoncpp-${{ inputs.version }} + cmake . + sudo cmake --install . + shell: bash diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index bff4a71e1..688ccbdc2 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -75,6 +75,7 @@ jobs: - { name: "danielaparker_jsoncons", library: "jsoncons", url: "https://github.com/danielaparker/jsoncons", disable_pico: true } - { name: "kazuho_picojson", library: "picojson", url: "https://github.com/kazuho/picojson", disable_pico: false } - { name: "nlohmann_json", library: "JSON for Modern C++", url: "https://github.com/nlohmann/json", disable_pico: true } + - { name: "osp_jsoncpp", library: "jsoncpp", url: "https://github.com/open-source-parsers/jsoncpp", disable_pico: true } name: render-defaults (${{ matrix.traits.name }}) steps: - uses: actions/checkout@v3 @@ -99,6 +100,7 @@ jobs: - { name: "danielaparker_jsoncons", suite: "JsonconsTest" } # - { name: "kazuho_picojson", suite: "PicoJsonTest" } # Currently the default everything tests against this! - { name: "nlohmann_json", suite: "NlohmannTest" } + - { name: "osp_jsoncpp", suite: "OspJsoncppTest" } name: render-tests (${{ matrix.traits.name }}) steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/traits.yml b/.github/workflows/traits.yml index a86568f47..da44ac484 100644 --- a/.github/workflows/traits.yml +++ b/.github/workflows/traits.yml @@ -17,6 +17,7 @@ jobs: - { name: "boost-json", tag: "1.78.0", version: "v1.80.0" } - { name: "nlohmann-json", tag: "3.11.2", version: "v3.11.2" } - { name: "kazuho-picojson", tag: "111c9be5188f7350c2eac9ddaedd8cca3d7bf394", version: "111c9be" } + - { name: "open-source-parsers-jsoncpp", tag: "1.9.5", version: "v1.9.5" } steps: - uses: actions/checkout@v3 - uses: lukka/get-cmake@latest @@ -50,6 +51,11 @@ jobs: with: version: ${{matrix.target.tag}} + - if: matrix.target.name == 'open-source-parsers-jsoncpp' + uses: ./.github/actions/install/open-source-parsers-jsoncpp + with: + version: ${{matrix.target.tag}} + - name: test working-directory: example/traits run: | diff --git a/example/traits/CMakeLists.txt b/example/traits/CMakeLists.txt index 42f88d54f..dcc77bf60 100644 --- a/example/traits/CMakeLists.txt +++ b/example/traits/CMakeLists.txt @@ -28,3 +28,9 @@ if(TARGET kazuho_picojson) add_executable(kazuho-picojson kazuho-picojson.cpp) target_link_libraries(kazuho-picojson jwt-cpp::jwt-cpp kazuho_picojson) endif() + +find_package(jsoncpp REQUIRED) +if(TARGET jsoncpp) + add_executable(open-source-parsers-jsoncpp open-source-parsers-jsoncpp.cpp) + target_link_libraries(open-source-parsers-jsoncpp jsoncpp jwt-cpp::jwt-cpp) +endif() diff --git a/example/traits/open-source-parsers-jsoncpp.cpp b/example/traits/open-source-parsers-jsoncpp.cpp new file mode 100644 index 000000000..46ddcd78f --- /dev/null +++ b/example/traits/open-source-parsers-jsoncpp.cpp @@ -0,0 +1,47 @@ +#include "jwt-cpp/traits/open-source-parsers-jsoncpp/traits.h" + +#include +#include + +int main() { + using sec = std::chrono::seconds; + using min = std::chrono::minutes; + using traits = jwt::traits::nlohmann_json; + using claim = jwt::basic_claim; + + claim from_raw_json; + std::istringstream iss{R"##({"api":{"array":[1,2,3],"null":null}})##"}; + iss >> from_raw_json; + + claim::set_t list{"once", "twice"}; + std::vector big_numbers{727663072ULL, 770979831ULL, 427239169ULL, 525936436ULL}; + + const auto time = jwt::date::clock::now(); + const auto token = jwt::create() + .set_type("JWT") + .set_issuer("auth.mydomain.io") + .set_audience("mydomain.io") + .set_issued_at(time) + .set_not_before(time) + .set_expires_at(time + min{2} + sec{15}) + .set_payload_claim("boolean", true) + .set_payload_claim("integer", 12345) + .set_payload_claim("precision", 12.3456789) + .set_payload_claim("strings", list) + .set_payload_claim("array", {big_numbers.begin(), big_numbers.end()}) + .set_payload_claim("object", from_raw_json) + .sign(jwt::algorithm::none{}); + const auto decoded = jwt::decode(token); + + const auto array = traits::as_array(decoded.get_payload_claim("object").to_json()["api"]["array"]); + std::cout << "payload /object/api/array = " << array << std::endl; + + jwt::verify() + .allow_algorithm(jwt::algorithm::none{}) + .with_issuer("auth.mydomain.io") + .with_audience("mydomain.io") + .with_claim("object", from_raw_json) + .verify(decoded); + + return 0; +} diff --git a/include/jwt-cpp/traits/jsoncpp/defaults.h b/include/jwt-cpp/traits/open-source-parsers-jsoncpp/defaults.h similarity index 100% rename from include/jwt-cpp/traits/jsoncpp/defaults.h rename to include/jwt-cpp/traits/open-source-parsers-jsoncpp/defaults.h diff --git a/include/jwt-cpp/traits/jsoncpp/traits.h b/include/jwt-cpp/traits/open-source-parsers-jsoncpp/traits.h similarity index 99% rename from include/jwt-cpp/traits/jsoncpp/traits.h rename to include/jwt-cpp/traits/open-source-parsers-jsoncpp/traits.h index ae6fc2b78..aa8e8fdec 100644 --- a/include/jwt-cpp/traits/jsoncpp/traits.h +++ b/include/jwt-cpp/traits/open-source-parsers-jsoncpp/traits.h @@ -6,7 +6,7 @@ namespace jwt { namespace traits { - struct jsoncpp { + struct osp_jsoncpp { using value_type = Json::Value; using string_type = std::string; class array_type: public Json::Value { From 64b66a881605cfc2edae691b45be6351bda41e94 Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Sat, 9 Dec 2023 15:55:42 -0500 Subject: [PATCH 03/15] correct find_packge to use config and not be required --- example/traits/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/traits/CMakeLists.txt b/example/traits/CMakeLists.txt index dcc77bf60..9413d008f 100644 --- a/example/traits/CMakeLists.txt +++ b/example/traits/CMakeLists.txt @@ -29,7 +29,7 @@ if(TARGET kazuho_picojson) target_link_libraries(kazuho-picojson jwt-cpp::jwt-cpp kazuho_picojson) endif() -find_package(jsoncpp REQUIRED) +find_package(jsoncpp CONFIG) if(TARGET jsoncpp) add_executable(open-source-parsers-jsoncpp open-source-parsers-jsoncpp.cpp) target_link_libraries(open-source-parsers-jsoncpp jsoncpp jwt-cpp::jwt-cpp) From 1eef3228ebdae57948a49f99674291599ee8cde1 Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Sat, 9 Dec 2023 15:56:22 -0500 Subject: [PATCH 04/15] typo in default version --- .github/actions/install/open-source-parsers-jsoncpp/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/install/open-source-parsers-jsoncpp/action.yml b/.github/actions/install/open-source-parsers-jsoncpp/action.yml index d44c1e1ef..c4e57acf1 100644 --- a/.github/actions/install/open-source-parsers-jsoncpp/action.yml +++ b/.github/actions/install/open-source-parsers-jsoncpp/action.yml @@ -4,7 +4,7 @@ inputs: version: description: The desired open-source-parsers/jsoncpp version to install required: false - default: "1.9.6" + default: "1.9.5" runs: using: composite steps: From dcfc0226e67c0376899f43a069d62cae8e64dde6 Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Sat, 9 Dec 2023 16:04:46 -0500 Subject: [PATCH 05/15] jsoncpp forces out of source builds --- .../actions/install/open-source-parsers-jsoncpp/action.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/actions/install/open-source-parsers-jsoncpp/action.yml b/.github/actions/install/open-source-parsers-jsoncpp/action.yml index c4e57acf1..fa6a2bc16 100644 --- a/.github/actions/install/open-source-parsers-jsoncpp/action.yml +++ b/.github/actions/install/open-source-parsers-jsoncpp/action.yml @@ -13,6 +13,9 @@ runs: wget https://github.com/open-source-parsers/jsoncpp/archive/${{ inputs.version }}.tar.gz tar -zxf /tmp/${{ inputs.version }}.tar.gz cd jsoncpp-${{ inputs.version }} - cmake . + # https://github.com/open-source-parsers/jsoncpp/blob/69098a18b9af0c47549d9a271c054d13ca92b006/include/PreventInSourceBuilds.cmake#L8 + mkdir build + cd build + cmake .. sudo cmake --install . shell: bash From 5e6230f037ab89bcee0b7b3d18639eba9fcc69dc Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Sat, 9 Dec 2023 16:49:22 -0500 Subject: [PATCH 06/15] disable tests and shared library --- .github/actions/install/open-source-parsers-jsoncpp/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/install/open-source-parsers-jsoncpp/action.yml b/.github/actions/install/open-source-parsers-jsoncpp/action.yml index fa6a2bc16..3f4cb5dc9 100644 --- a/.github/actions/install/open-source-parsers-jsoncpp/action.yml +++ b/.github/actions/install/open-source-parsers-jsoncpp/action.yml @@ -16,6 +16,6 @@ runs: # https://github.com/open-source-parsers/jsoncpp/blob/69098a18b9af0c47549d9a271c054d13ca92b006/include/PreventInSourceBuilds.cmake#L8 mkdir build cd build - cmake .. + cmake .. -DJSONCPP_WITH_TESTS=OFF -DBUILD_SHARED_LIBS=OFF sudo cmake --install . shell: bash From 5efe7c0aafccf97b08bc35385868f05dbd021fc4 Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Sat, 9 Dec 2023 16:51:27 -0500 Subject: [PATCH 07/15] add missing build step --- .github/actions/install/open-source-parsers-jsoncpp/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/install/open-source-parsers-jsoncpp/action.yml b/.github/actions/install/open-source-parsers-jsoncpp/action.yml index 3f4cb5dc9..966f00450 100644 --- a/.github/actions/install/open-source-parsers-jsoncpp/action.yml +++ b/.github/actions/install/open-source-parsers-jsoncpp/action.yml @@ -17,5 +17,6 @@ runs: mkdir build cd build cmake .. -DJSONCPP_WITH_TESTS=OFF -DBUILD_SHARED_LIBS=OFF + cmake --build . sudo cmake --install . shell: bash From de26fe12b276631bad8e0f6395b6ca793c134891 Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Sat, 9 Dec 2023 17:08:36 -0500 Subject: [PATCH 08/15] try a different target + build less "they" do not maintain the CMake scripts so this will be fun https://github.com/open-source-parsers/jsoncpp/issues/455#issuecomment-478251641 --- .../actions/install/open-source-parsers-jsoncpp/action.yml | 2 +- example/traits/CMakeLists.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/actions/install/open-source-parsers-jsoncpp/action.yml b/.github/actions/install/open-source-parsers-jsoncpp/action.yml index 966f00450..3a341f2b0 100644 --- a/.github/actions/install/open-source-parsers-jsoncpp/action.yml +++ b/.github/actions/install/open-source-parsers-jsoncpp/action.yml @@ -16,7 +16,7 @@ runs: # https://github.com/open-source-parsers/jsoncpp/blob/69098a18b9af0c47549d9a271c054d13ca92b006/include/PreventInSourceBuilds.cmake#L8 mkdir build cd build - cmake .. -DJSONCPP_WITH_TESTS=OFF -DBUILD_SHARED_LIBS=OFF + cmake .. -DJSONCPP_WITH_TESTS=OFF -DBUILD_SHARED_LIBS=OFF -DBUILD_OBJECT_LIBS=OFF cmake --build . sudo cmake --install . shell: bash diff --git a/example/traits/CMakeLists.txt b/example/traits/CMakeLists.txt index 9413d008f..4734fabc7 100644 --- a/example/traits/CMakeLists.txt +++ b/example/traits/CMakeLists.txt @@ -30,7 +30,7 @@ if(TARGET kazuho_picojson) endif() find_package(jsoncpp CONFIG) -if(TARGET jsoncpp) +if(TARGET jsoncpp_static) add_executable(open-source-parsers-jsoncpp open-source-parsers-jsoncpp.cpp) - target_link_libraries(open-source-parsers-jsoncpp jsoncpp jwt-cpp::jwt-cpp) + target_link_libraries(open-source-parsers-jsoncpp jsoncpp_static jwt-cpp::jwt-cpp) endif() From 0cfd157fe479fcd67c2c64a23672fa48e8751442 Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Sat, 9 Dec 2023 17:17:35 -0500 Subject: [PATCH 09/15] fix name for traits (missed replaces) --- .../traits/open-source-parsers-jsoncpp.cpp | 2 +- .../open-source-parsers-jsoncpp/defaults.h | 28 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/example/traits/open-source-parsers-jsoncpp.cpp b/example/traits/open-source-parsers-jsoncpp.cpp index 46ddcd78f..9be7504e5 100644 --- a/example/traits/open-source-parsers-jsoncpp.cpp +++ b/example/traits/open-source-parsers-jsoncpp.cpp @@ -6,7 +6,7 @@ int main() { using sec = std::chrono::seconds; using min = std::chrono::minutes; - using traits = jwt::traits::nlohmann_json; + using traits = jwt::traits::osp_jsoncpp; using claim = jwt::basic_claim; claim from_raw_json; diff --git a/include/jwt-cpp/traits/open-source-parsers-jsoncpp/defaults.h b/include/jwt-cpp/traits/open-source-parsers-jsoncpp/defaults.h index 05929b0f3..0892c553f 100644 --- a/include/jwt-cpp/traits/open-source-parsers-jsoncpp/defaults.h +++ b/include/jwt-cpp/traits/open-source-parsers-jsoncpp/defaults.h @@ -9,25 +9,25 @@ namespace jwt { /** - * \brief a class to store a generic [jsoncpp](https://github.com/open-source-parsers/jsoncpp) value as claim + * \brief a class to store a generic [osp_jsoncpp](https://github.com/open-source-parsers/osp_jsoncpp) value as claim * * This type is the specialization of the \ref basic_claim class which * uses the standard template types. */ - using claim = basic_claim; + using claim = basic_claim; /** * Create a verifier using the default clock * \return verifier instance */ - inline verifier verify() { - return verify(default_clock{}); + inline verifier verify() { + return verify(default_clock{}); } /** * Return a builder instance to create a new token */ - inline builder create() { return builder(); } + inline builder create() { return builder(); } #ifndef JWT_DISABLE_BASE64 /** @@ -37,8 +37,8 @@ namespace jwt { * \throw std::invalid_argument Token is not in correct format * \throw std::runtime_error Base64 decoding failed or invalid json */ - inline decoded_jwt decode(const std::string& token) { - return decoded_jwt(token); + inline decoded_jwt decode(const std::string& token) { + return decoded_jwt(token); } #endif @@ -54,8 +54,8 @@ namespace jwt { * \throw std::runtime_error Base64 decoding failed or invalid json */ template - decoded_jwt decode(const std::string& token, Decode decode) { - return decoded_jwt(token, decode); + decoded_jwt decode(const std::string& token, Decode decode) { + return decoded_jwt(token, decode); } /** @@ -64,8 +64,8 @@ namespace jwt { * \return Parsed JWK * \throw std::runtime_error Token is not in correct format */ - inline jwk parse_jwk(const traits::jsoncpp::string_type& token) { - return jwk(token); + inline jwk parse_jwk(const traits::osp_jsoncpp::string_type& token) { + return jwk(token); } /** @@ -74,15 +74,15 @@ namespace jwt { * \return Parsed JWKs * \throw std::runtime_error Token is not in correct format */ - inline jwks parse_jwks(const traits::jsoncpp::string_type& token) { - return jwks(token); + inline jwks parse_jwks(const traits::osp_jsoncpp::string_type& token) { + return jwks(token); } /** * This type is the specialization of the \ref verify_ops::verify_context class which * uses the standard template types. */ - using verify_context = verify_ops::verify_context; + using verify_context = verify_ops::verify_context; } // namespace jwt #endif // JWT_CPP_JSONCPP_DEFAULTS_H From d95031e7ae513f3bf1fd18801049d8af6f045b03 Mon Sep 17 00:00:00 2001 From: cjserio Date: Sun, 10 Dec 2023 08:23:00 -0500 Subject: [PATCH 10/15] Added missing array_type constructor so that basic_claims can be formed from sets of values --- example/traits/open-source-parsers-jsoncpp.cpp | 2 +- .../jwt-cpp/traits/open-source-parsers-jsoncpp/traits.h | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/example/traits/open-source-parsers-jsoncpp.cpp b/example/traits/open-source-parsers-jsoncpp.cpp index 9be7504e5..a46de941d 100644 --- a/example/traits/open-source-parsers-jsoncpp.cpp +++ b/example/traits/open-source-parsers-jsoncpp.cpp @@ -27,7 +27,7 @@ int main() { .set_payload_claim("boolean", true) .set_payload_claim("integer", 12345) .set_payload_claim("precision", 12.3456789) - .set_payload_claim("strings", list) + .set_payload_claim("strings", claim(list)) .set_payload_claim("array", {big_numbers.begin(), big_numbers.end()}) .set_payload_claim("object", from_raw_json) .sign(jwt::algorithm::none{}); diff --git a/include/jwt-cpp/traits/open-source-parsers-jsoncpp/traits.h b/include/jwt-cpp/traits/open-source-parsers-jsoncpp/traits.h index aa8e8fdec..d347add50 100644 --- a/include/jwt-cpp/traits/open-source-parsers-jsoncpp/traits.h +++ b/include/jwt-cpp/traits/open-source-parsers-jsoncpp/traits.h @@ -18,6 +18,14 @@ namespace jwt { explicit array_type(const Json::Value& o) : Json::Value(o) {} array_type(array_type&&) = default; explicit array_type(Json::Value&& o) : Json::Value(o) {} + template + array_type(Iterator begin, Iterator end) { + for (Iterator it = begin; it != end; ++it) { + Json::Value value; + value = *it; + this->append(value); + } + } ~array_type() = default; array_type& operator=(const array_type& o) = default; array_type& operator=(array_type&& o) noexcept = default; From e940eb9df119d835f8c2882d874d3b6d5e51d481 Mon Sep 17 00:00:00 2001 From: Christopher McArthur Date: Tue, 12 Dec 2023 10:19:39 -0500 Subject: [PATCH 11/15] Linters and more tests + fetchcontent gtest --- .github/workflows/lint.yml | 4 +- CMakeLists.txt | 10 +- .../traits/open-source-parsers-jsoncpp.cpp | 2 +- .../open-source-parsers-jsoncpp/defaults.h | 32 +-- .../open-source-parsers-jsoncpp/traits.h | 237 +++++++++--------- tests/CMakeLists.txt | 23 +- tests/traits/OspJsoncppTest.cpp | 127 ++++++++++ 7 files changed, 288 insertions(+), 147 deletions(-) create mode 100644 tests/traits/OspJsoncppTest.cpp diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 688ccbdc2..27526c1bc 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -75,7 +75,7 @@ jobs: - { name: "danielaparker_jsoncons", library: "jsoncons", url: "https://github.com/danielaparker/jsoncons", disable_pico: true } - { name: "kazuho_picojson", library: "picojson", url: "https://github.com/kazuho/picojson", disable_pico: false } - { name: "nlohmann_json", library: "JSON for Modern C++", url: "https://github.com/nlohmann/json", disable_pico: true } - - { name: "osp_jsoncpp", library: "jsoncpp", url: "https://github.com/open-source-parsers/jsoncpp", disable_pico: true } + - { name: "open_source_parsers_jsoncpp", library: "jsoncpp", url: "https://github.com/open-source-parsers/jsoncpp", disable_pico: true } name: render-defaults (${{ matrix.traits.name }}) steps: - uses: actions/checkout@v3 @@ -100,7 +100,7 @@ jobs: - { name: "danielaparker_jsoncons", suite: "JsonconsTest" } # - { name: "kazuho_picojson", suite: "PicoJsonTest" } # Currently the default everything tests against this! - { name: "nlohmann_json", suite: "NlohmannTest" } - - { name: "osp_jsoncpp", suite: "OspJsoncppTest" } + - { name: "open_source_parsers_jsoncpp", suite: "OspJsoncppTest" } name: render-tests (${{ matrix.traits.name }}) steps: - uses: actions/checkout@v3 diff --git a/CMakeLists.txt b/CMakeLists.txt index 5cd876f50..badf330e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,7 @@ set(JWT_SSL_LIBRARY_OPTIONS OpenSSL LibreSSL wolfSSL) set(JWT_SSL_LIBRARY OpenSSL CACHE STRING "Determines which SSL library to build with") set_property(CACHE JWT_SSL_LIBRARY PROPERTY STRINGS ${JWT_SSL_LIBRARY_OPTIONS}) -set(JWT_JSON_TRAITS_OPTIONS boost-json danielaparker-jsoncons kazuho-picojson nlohmann-json) +set(JWT_JSON_TRAITS_OPTIONS boost-json danielaparker-jsoncons kazuho-picojson nlohmann-json open-source-parsers-jsoncpp) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") @@ -71,11 +71,9 @@ find_package(nlohmann_json CONFIG) if(NOT nlohmann_json_FOUND) message(STATUS "jwt-cpp: using FetchContent for nlohmann json") include(FetchContent) - FetchContent_Declare(nlohmann_json - URL https://github.com/nlohmann/json/releases/download/v3.11.2/json.tar.xz - URL_MD5 127794b2c82c0c5693805feaa2a703e2 - ) - FetchContent_MakeAvailable(nlohmann_json) + fetchcontent_declare(nlohmann_json URL https://github.com/nlohmann/json/releases/download/v3.11.2/json.tar.xz + URL_MD5 127794b2c82c0c5693805feaa2a703e2) + fetchcontent_makeavailable(nlohmann_json) endif() set(JWT_INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/include) diff --git a/example/traits/open-source-parsers-jsoncpp.cpp b/example/traits/open-source-parsers-jsoncpp.cpp index a46de941d..893669114 100644 --- a/example/traits/open-source-parsers-jsoncpp.cpp +++ b/example/traits/open-source-parsers-jsoncpp.cpp @@ -6,7 +6,7 @@ int main() { using sec = std::chrono::seconds; using min = std::chrono::minutes; - using traits = jwt::traits::osp_jsoncpp; + using traits = jwt::traits::open_source_parsers_jsoncpp; using claim = jwt::basic_claim; claim from_raw_json; diff --git a/include/jwt-cpp/traits/open-source-parsers-jsoncpp/defaults.h b/include/jwt-cpp/traits/open-source-parsers-jsoncpp/defaults.h index 0892c553f..955544ee6 100644 --- a/include/jwt-cpp/traits/open-source-parsers-jsoncpp/defaults.h +++ b/include/jwt-cpp/traits/open-source-parsers-jsoncpp/defaults.h @@ -1,5 +1,5 @@ -#ifndef JWT_CPP_JSONCPP_DEFAULTS_H -#define JWT_CPP_JSONCPP_DEFAULTS_H +#ifndef JWT_CPP_OPEN_SOURCE_PARSERS_JSONCPP_DEFAULTS_H +#define JWT_CPP_OPEN_SOURCE_PARSERS_JSONCPP_DEFAULTS_H #ifndef JWT_DISABLE_PICOJSON #define JWT_DISABLE_PICOJSON @@ -9,25 +9,25 @@ namespace jwt { /** - * \brief a class to store a generic [osp_jsoncpp](https://github.com/open-source-parsers/osp_jsoncpp) value as claim + * \brief a class to store a generic [open_source_parsers_jsoncpp](https://github.com/open-source-parsers/open_source_parsers_jsoncpp) value as claim * * This type is the specialization of the \ref basic_claim class which * uses the standard template types. */ - using claim = basic_claim; + using claim = basic_claim; /** * Create a verifier using the default clock * \return verifier instance */ - inline verifier verify() { - return verify(default_clock{}); + inline verifier verify() { + return verify(default_clock{}); } /** * Return a builder instance to create a new token */ - inline builder create() { return builder(); } + inline builder create() { return builder(); } #ifndef JWT_DISABLE_BASE64 /** @@ -37,8 +37,8 @@ namespace jwt { * \throw std::invalid_argument Token is not in correct format * \throw std::runtime_error Base64 decoding failed or invalid json */ - inline decoded_jwt decode(const std::string& token) { - return decoded_jwt(token); + inline decoded_jwt decode(const std::string& token) { + return decoded_jwt(token); } #endif @@ -54,8 +54,8 @@ namespace jwt { * \throw std::runtime_error Base64 decoding failed or invalid json */ template - decoded_jwt decode(const std::string& token, Decode decode) { - return decoded_jwt(token, decode); + decoded_jwt decode(const std::string& token, Decode decode) { + return decoded_jwt(token, decode); } /** @@ -64,8 +64,8 @@ namespace jwt { * \return Parsed JWK * \throw std::runtime_error Token is not in correct format */ - inline jwk parse_jwk(const traits::osp_jsoncpp::string_type& token) { - return jwk(token); + inline jwk parse_jwk(const traits::open_source_parsers_jsoncpp::string_type& token) { + return jwk(token); } /** @@ -74,15 +74,15 @@ namespace jwt { * \return Parsed JWKs * \throw std::runtime_error Token is not in correct format */ - inline jwks parse_jwks(const traits::osp_jsoncpp::string_type& token) { - return jwks(token); + inline jwks parse_jwks(const traits::open_source_parsers_jsoncpp::string_type& token) { + return jwks(token); } /** * This type is the specialization of the \ref verify_ops::verify_context class which * uses the standard template types. */ - using verify_context = verify_ops::verify_context; + using verify_context = verify_ops::verify_context; } // namespace jwt #endif // JWT_CPP_JSONCPP_DEFAULTS_H diff --git a/include/jwt-cpp/traits/open-source-parsers-jsoncpp/traits.h b/include/jwt-cpp/traits/open-source-parsers-jsoncpp/traits.h index d347add50..1d4c0c7d6 100644 --- a/include/jwt-cpp/traits/open-source-parsers-jsoncpp/traits.h +++ b/include/jwt-cpp/traits/open-source-parsers-jsoncpp/traits.h @@ -6,126 +6,123 @@ namespace jwt { namespace traits { - struct osp_jsoncpp { - using value_type = Json::Value; - using string_type = std::string; - class array_type: public Json::Value { - public: - using value_type = Json::Value; - - array_type() = default; - array_type(const array_type&) = default; - explicit array_type(const Json::Value& o) : Json::Value(o) {} - array_type(array_type&&) = default; - explicit array_type(Json::Value&& o) : Json::Value(o) {} - template - array_type(Iterator begin, Iterator end) { - for (Iterator it = begin; it != end; ++it) { - Json::Value value; - value = *it; - this->append(value); - } - } - ~array_type() = default; - array_type& operator=(const array_type& o) = default; - array_type& operator=(array_type&& o) noexcept = default; - }; - using number_type = double; - using integer_type = Json::Value::Int; - using boolean_type = bool; - class object_type : public Json::Value { - public: - using key_type = std::string; - using mapped_type = Json::Value; - using size_type = size_t; - - object_type() = default; - object_type(const object_type&) = default; - explicit object_type(const Json::Value& o) : Json::Value(o) {} - object_type(object_type&&) = default; - explicit object_type(Json::Value&& o) : Json::Value(o) {} - ~object_type() = default; - object_type& operator=(const object_type& o) = default; - object_type& operator=(object_type&& o) noexcept = default; - - // Add missing C++11 element access - const mapped_type& at(const key_type& key) const { - Json::Value const* found = find(key.data(), key.data() + key.length()); - if (!found) - throw std::out_of_range("invalid key"); - return *found; - } - - size_type count(const key_type& key) const { - return this->isMember(key) ? 1 : 0; - } - }; - - // Translation between the implementation notion of type, to the jwt::json::type equivilant - static jwt::json::type get_type(const value_type &val) { - using jwt::json::type; - - if (val.isArray()) - return type::array; - else if (val.isString()) - return type::string; - else if (val.isNumeric()) - return type::number; - else if (val.isInt()) - return type::integer; - else if (val.isBool()) - return type::boolean; - else if (val.isObject()) - return type::object; - - throw std::logic_error("invalid type"); - } - - static integer_type as_integer(const value_type& val) { - switch (val.type()) { - case Json::intValue: return val.asInt64(); - case Json::uintValue: return static_cast(val.asUInt64()); - default: throw std::bad_cast(); - } - } - - static boolean_type as_boolean(const value_type& val) { - if (!val.isBool()) throw std::bad_cast(); - return val.asBool(); - } - - static number_type as_number(const value_type& val) { - if (!val.isNumeric()) throw std::bad_cast(); - return val.asDouble(); - } - - static string_type as_string(const value_type& val) { - if (!val.isString()) throw std::bad_cast(); - return val.asString(); - } - - static object_type as_object(const value_type& val) { - if (!val.isObject()) throw std::bad_cast(); - return object_type(val); - } - - static array_type as_array(const value_type& val) { - if (!val.isArray()) throw std::bad_cast(); - return array_type(val); - } - - static bool parse(value_type &val, string_type str) { - Json::Reader reader; - return reader.parse(str, val); - } - - static string_type serialize(const value_type &val) { - Json::StreamWriterBuilder builder; - builder["commentStyle"] = "None"; - builder["indentation"] = ""; - std::unique_ptr writer(builder.newStreamWriter()); - return Json::writeString(builder, val); - } + struct open_source_parsers_jsoncpp { + using value_type = Json::Value; + using string_type = std::string; + class array_type : public Json::Value { + public: + using value_type = Json::Value; + + array_type() = default; + array_type(const array_type&) = default; + explicit array_type(const Json::Value& o) : Json::Value(o) {} + array_type(array_type&&) = default; + explicit array_type(Json::Value&& o) : Json::Value(o) {} + template + array_type(Iterator begin, Iterator end) { + for (Iterator it = begin; it != end; ++it) { + Json::Value value; + value = *it; + this->append(value); + } + } + ~array_type() = default; + array_type& operator=(const array_type& o) = default; + array_type& operator=(array_type&& o) noexcept = default; + }; + using number_type = double; + using integer_type = Json::Value::Int; + using boolean_type = bool; + class object_type : public Json::Value { + public: + using key_type = std::string; + using mapped_type = Json::Value; + using size_type = size_t; + + object_type() = default; + object_type(const object_type&) = default; + explicit object_type(const Json::Value& o) : Json::Value(o) {} + object_type(object_type&&) = default; + explicit object_type(Json::Value&& o) : Json::Value(o) {} + ~object_type() = default; + object_type& operator=(const object_type& o) = default; + object_type& operator=(object_type&& o) noexcept = default; + + // Add missing C++11 element access + const mapped_type& at(const key_type& key) const { + Json::Value const* found = find(key.data(), key.data() + key.length()); + if (!found) throw std::out_of_range("invalid key"); + return *found; + } + + size_type count(const key_type& key) const { return this->isMember(key) ? 1 : 0; } + }; + + // Translation between the implementation notion of type, to the jwt::json::type equivilant + static jwt::json::type get_type(const value_type& val) { + using jwt::json::type; + + if (val.isArray()) + return type::array; + else if (val.isString()) + return type::string; + else if (val.isNumeric()) + return type::number; + else if (val.isInt()) + return type::integer; + else if (val.isBool()) + return type::boolean; + else if (val.isObject()) + return type::object; + + throw std::logic_error("invalid type"); + } + + static integer_type as_integer(const value_type& val) { + switch (val.type()) { + case Json::intValue: return val.asInt64(); + case Json::uintValue: return static_cast(val.asUInt64()); + default: throw std::bad_cast(); + } + } + + static boolean_type as_boolean(const value_type& val) { + if (!val.isBool()) throw std::bad_cast(); + return val.asBool(); + } + + static number_type as_number(const value_type& val) { + if (!val.isNumeric()) throw std::bad_cast(); + return val.asDouble(); + } + + static string_type as_string(const value_type& val) { + if (!val.isString()) throw std::bad_cast(); + return val.asString(); + } + + static object_type as_object(const value_type& val) { + if (!val.isObject()) throw std::bad_cast(); + return object_type(val); + } + + static array_type as_array(const value_type& val) { + if (!val.isArray()) throw std::bad_cast(); + return array_type(val); + } + + static bool parse(value_type& val, string_type str) { + Json::Reader reader; + return reader.parse(str, val); + } + + static string_type serialize(const value_type& val) { + Json::StreamWriterBuilder builder; + builder["commentStyle"] = "None"; + builder["indentation"] = ""; + std::unique_ptr writer(builder.newStreamWriter()); + return Json::writeString(builder, val); + } }; } // namespace traits } // namespace jwt diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cb7eaddc8..36331cd17 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,7 +11,20 @@ include(GoogleTest) if(HUNTER_ENABLED) hunter_add_package(GTest) endif() -find_package(GTest REQUIRED) + +find_package(GTest) +if(NOT TARGET GTest::gtest_main) + message(STATUS "jwt-cpp: using FetchContent for GTest") + include(FetchContent) + FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip + ) + # https://google.github.io/googletest/quickstart-cmake.html + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) + +endif() set(TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/BaseTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ClaimTest.cpp @@ -30,6 +43,11 @@ if(TARGET boost_json) list(APPEND TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/traits/BoostJsonTest.cpp) endif() +find_package(jsoncpp CONFIG) +if(TARGET jsoncpp_static) + list(APPEND TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/traits/OspJsoncppTest.cpp) +endif() + add_executable(jwt-cpp-test ${TEST_SOURCES}) # NOTE: Don't use space inside a generator expression here, because the function prematurely breaks the expression into @@ -44,7 +62,8 @@ if(HUNTER_ENABLED) # Define a compile define to bypass openssl error tests target_compile_definitions(jwt-cpp-test PRIVATE HUNTER_ENABLED=1) else() - target_link_libraries(jwt-cpp-test PRIVATE GTest::GTest GTest::Main) + # https://github.com/google/googletest/blob/eb80f759d595874a5e905a3342bd8e2af4c0a12d/googletest/README.md?plain=1#L62-L64 + target_link_libraries(jwt-cpp-test PRIVATE GTest::gtest_main) if(TARGET jsoncons) target_link_libraries(jwt-cpp-test PRIVATE jsoncons) endif() diff --git a/tests/traits/OspJsoncppTest.cpp b/tests/traits/OspJsoncppTest.cpp new file mode 100644 index 000000000..12062e328 --- /dev/null +++ b/tests/traits/OspJsoncppTest.cpp @@ -0,0 +1,127 @@ +#include "jwt-cpp/traits/osp-jsoncpp/traits.h" + +#include + +TEST(OspJsoncppTest, BasicClaims) { + const auto string = jwt::basic_claim(jwt::traits::open_source_parsers_jsoncpp::string_type("string")); + ASSERT_EQ(string.get_type(), jwt::json::type::string); + + const auto array = + jwt::basic_claim(std::set{"string", "string"}); + ASSERT_EQ(array.get_type(), jwt::json::type::array); + + const auto integer = jwt::basic_claim(159816816); + ASSERT_EQ(integer.get_type(), jwt::json::type::integer); +} + +TEST(OspJsoncppTest, AudienceAsString) { + jwt::traits::open_source_parsers_jsoncpp::string_type token = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0ZXN0In0.WZnM3SIiSRHsbO3O7Z2bmIzTJ4EC32HRBKfLznHhrh4"; + auto decoded = jwt::decode(token); + + ASSERT_TRUE(decoded.has_algorithm()); + ASSERT_TRUE(decoded.has_type()); + ASSERT_FALSE(decoded.has_content_type()); + ASSERT_FALSE(decoded.has_key_id()); + ASSERT_FALSE(decoded.has_issuer()); + ASSERT_FALSE(decoded.has_subject()); + ASSERT_TRUE(decoded.has_audience()); + ASSERT_FALSE(decoded.has_expires_at()); + ASSERT_FALSE(decoded.has_not_before()); + ASSERT_FALSE(decoded.has_issued_at()); + ASSERT_FALSE(decoded.has_id()); + + ASSERT_EQ("HS256", decoded.get_algorithm()); + ASSERT_EQ("JWT", decoded.get_type()); + auto aud = decoded.get_audience(); + ASSERT_EQ(1, aud.size()); + ASSERT_EQ("test", *aud.begin()); +} + +TEST(OspJsoncppTest, SetArray) { + std::vector vect = {100, 20, 10}; + auto token = jwt::create() + .set_payload_claim("test", jwt::basic_claim(vect.begin(), vect.end())) + .sign(jwt::algorithm::none{}); + ASSERT_EQ(token, "eyJhbGciOiJub25lIn0.eyJ0ZXN0IjpbMTAwLDIwLDEwXX0."); +} + +TEST(OspJsoncppTest, SetObject) { + std::istringstream iss{"{\"api-x\": [1]}"}; + jwt::basic_claim object; + iss >> object; + ASSERT_EQ(object.get_type(), jwt::json::type::object); + + auto token = jwt::create() + .set_payload_claim("namespace", object) + .sign(jwt::algorithm::hs256("test")); + ASSERT_EQ(token, + "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lc3BhY2UiOnsiYXBpLXgiOlsxXX19.F8I6I2RcSF98bKa0IpIz09fRZtHr1CWnWKx2za-tFQA"); +} + +TEST(OspJsoncppTest, VerifyTokenHS256) { + jwt::traits::open_source_parsers_jsoncpp::string_type token = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE"; + + const auto decoded_token = jwt::decode(token); + const auto verify = + jwt::verify().allow_algorithm(jwt::algorithm::hs256{"secret"}).with_issuer("auth0"); + verify.verify(decoded_token); +} + +TEST(OspJsoncppTest, VerifyTokenExpirationValid) { + const auto token = jwt::create() + .set_issuer("auth0") + .set_issued_at(std::chrono::system_clock::now()) + .set_expires_at(std::chrono::system_clock::now() + std::chrono::seconds{3600}) + .sign(jwt::algorithm::hs256{"secret"}); + + const auto decoded_token = jwt::decode(token); + const auto verify = + jwt::verify().allow_algorithm(jwt::algorithm::hs256{"secret"}).with_issuer("auth0"); + verify.verify(decoded_token); +} + +TEST(OspJsoncppTest, VerifyTokenExpired) { + const auto token = jwt::create() + .set_issuer("auth0") + .set_issued_at(std::chrono::system_clock::now() - std::chrono::seconds{3601}) + .set_expires_at(std::chrono::system_clock::now() - std::chrono::seconds{1}) + .sign(jwt::algorithm::hs256{"secret"}); + + const auto decoded_token = jwt::decode(token); + const auto verify = + jwt::verify().allow_algorithm(jwt::algorithm::hs256{"secret"}).with_issuer("auth0"); + ASSERT_THROW(verify.verify(decoded_token), jwt::error::token_verification_exception); + + std::error_code ec; + ASSERT_NO_THROW(verify.verify(decoded_token, ec)); + ASSERT_TRUE(!(!ec)); + ASSERT_EQ(ec.category(), jwt::error::token_verification_error_category()); + ASSERT_EQ(ec.value(), static_cast(jwt::error::token_verification_error::token_expired)); +} + +TEST(OspJsoncppTest, VerifyArray) { + jwt::traits::open_source_parsers_jsoncpp::string_type token = "eyJhbGciOiJub25lIn0.eyJ0ZXN0IjpbMTAwLDIwLDEwXX0."; + const auto decoded_token = jwt::decode(token); + + std::vector vect = {100, 20, 10}; + jwt::basic_claim array_claim(vect.begin(), vect.end()); + const auto verify = + jwt::verify().allow_algorithm(jwt::algorithm::none{}).with_claim("test", array_claim); + ASSERT_NO_THROW(verify.verify(decoded_token)); +} + +TEST(OspJsoncppTest, VerifyObject) { + jwt::traits::open_source_parsers_jsoncpp::string_type token = + "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lc3BhY2UiOnsiYXBpLXgiOlsxXX19.F8I6I2RcSF98bKa0IpIz09fRZtHr1CWnWKx2za-tFQA"; + const auto decoded_token = jwt::decode(token); + + jwt::basic_claim object_claim; + std::istringstream iss{"{\"api-x\": [1]}"}; + iss >> object_claim; + const auto verify = jwt::verify() + .allow_algorithm(jwt::algorithm::hs256("test")) + .with_claim("namespace", object_claim); + ASSERT_NO_THROW(verify.verify(decoded_token)); +} From 1de31d348b6826561dff948b3d765c6bdba82040 Mon Sep 17 00:00:00 2001 From: Christopher McArthur Date: Tue, 12 Dec 2023 17:13:51 -0500 Subject: [PATCH 12/15] linter --- tests/CMakeLists.txt | 9 +++------ tests/traits/OspJsoncppTest.cpp | 32 +++++++++++++++++++------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 36331cd17..0b88b5e74 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,14 +16,11 @@ find_package(GTest) if(NOT TARGET GTest::gtest_main) message(STATUS "jwt-cpp: using FetchContent for GTest") include(FetchContent) - FetchContent_Declare( - googletest - URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip - ) + fetchcontent_declare(googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip) # https://google.github.io/googletest/quickstart-cmake.html set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - FetchContent_MakeAvailable(googletest) - + fetchcontent_makeavailable(googletest) endif() set(TEST_SOURCES diff --git a/tests/traits/OspJsoncppTest.cpp b/tests/traits/OspJsoncppTest.cpp index 12062e328..f0bd3d533 100644 --- a/tests/traits/OspJsoncppTest.cpp +++ b/tests/traits/OspJsoncppTest.cpp @@ -1,13 +1,14 @@ -#include "jwt-cpp/traits/osp-jsoncpp/traits.h" +#include "jwt-cpp/traits/open-source-parsers-jsoncpp/traits.h" #include TEST(OspJsoncppTest, BasicClaims) { - const auto string = jwt::basic_claim(jwt::traits::open_source_parsers_jsoncpp::string_type("string")); + const auto string = jwt::basic_claim( + jwt::traits::open_source_parsers_jsoncpp::string_type("string")); ASSERT_EQ(string.get_type(), jwt::json::type::string); - const auto array = - jwt::basic_claim(std::set{"string", "string"}); + const auto array = jwt::basic_claim( + std::set{"string", "string"}); ASSERT_EQ(array.get_type(), jwt::json::type::array); const auto integer = jwt::basic_claim(159816816); @@ -41,7 +42,8 @@ TEST(OspJsoncppTest, AudienceAsString) { TEST(OspJsoncppTest, SetArray) { std::vector vect = {100, 20, 10}; auto token = jwt::create() - .set_payload_claim("test", jwt::basic_claim(vect.begin(), vect.end())) + .set_payload_claim( + "test", jwt::basic_claim(vect.begin(), vect.end())) .sign(jwt::algorithm::none{}); ASSERT_EQ(token, "eyJhbGciOiJub25lIn0.eyJ0ZXN0IjpbMTAwLDIwLDEwXX0."); } @@ -64,8 +66,9 @@ TEST(OspJsoncppTest, VerifyTokenHS256) { "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE"; const auto decoded_token = jwt::decode(token); - const auto verify = - jwt::verify().allow_algorithm(jwt::algorithm::hs256{"secret"}).with_issuer("auth0"); + const auto verify = jwt::verify() + .allow_algorithm(jwt::algorithm::hs256{"secret"}) + .with_issuer("auth0"); verify.verify(decoded_token); } @@ -77,8 +80,9 @@ TEST(OspJsoncppTest, VerifyTokenExpirationValid) { .sign(jwt::algorithm::hs256{"secret"}); const auto decoded_token = jwt::decode(token); - const auto verify = - jwt::verify().allow_algorithm(jwt::algorithm::hs256{"secret"}).with_issuer("auth0"); + const auto verify = jwt::verify() + .allow_algorithm(jwt::algorithm::hs256{"secret"}) + .with_issuer("auth0"); verify.verify(decoded_token); } @@ -90,8 +94,9 @@ TEST(OspJsoncppTest, VerifyTokenExpired) { .sign(jwt::algorithm::hs256{"secret"}); const auto decoded_token = jwt::decode(token); - const auto verify = - jwt::verify().allow_algorithm(jwt::algorithm::hs256{"secret"}).with_issuer("auth0"); + const auto verify = jwt::verify() + .allow_algorithm(jwt::algorithm::hs256{"secret"}) + .with_issuer("auth0"); ASSERT_THROW(verify.verify(decoded_token), jwt::error::token_verification_exception); std::error_code ec; @@ -107,8 +112,9 @@ TEST(OspJsoncppTest, VerifyArray) { std::vector vect = {100, 20, 10}; jwt::basic_claim array_claim(vect.begin(), vect.end()); - const auto verify = - jwt::verify().allow_algorithm(jwt::algorithm::none{}).with_claim("test", array_claim); + const auto verify = jwt::verify() + .allow_algorithm(jwt::algorithm::none{}) + .with_claim("test", array_claim); ASSERT_NO_THROW(verify.verify(decoded_token)); } From 2c3d6ce9fd513060b4a8bd2df3c678a3fff855ab Mon Sep 17 00:00:00 2001 From: Christopher McArthur Date: Tue, 12 Dec 2023 17:14:30 -0500 Subject: [PATCH 13/15] support library names with many separators --- .github/actions/render/defaults/action.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/actions/render/defaults/action.yml b/.github/actions/render/defaults/action.yml index af28f7930..5dc1b2988 100644 --- a/.github/actions/render/defaults/action.yml +++ b/.github/actions/render/defaults/action.yml @@ -47,6 +47,11 @@ runs: library_url: LIBRARY_URL, disable_default_traits: disableDefault, }) - const outputDir = path.join('include', 'jwt-cpp', 'traits', TRAITS_NAME.replace('_', '-')) + // https://dmitripavlutin.com/replace-all-string-occurrences-javascript/ + function replaceAll(string, search, replace) { + return string.split(search).join(replace); + } + + const outputDir = path.join('include', 'jwt-cpp', 'traits', replaceAll(TRAITS_NAME, '_', '-')) fs.mkdirSync(outputDir, { recursive: true }) fs.writeFileSync(path.join(outputDir, 'defaults.h'), content) From 17060d0066fd14b35fc78050bf3f03ed05dc8311 Mon Sep 17 00:00:00 2001 From: Christopher McArthur Date: Tue, 12 Dec 2023 17:17:04 -0500 Subject: [PATCH 14/15] linters --- .../traits/open-source-parsers-jsoncpp/defaults.h | 14 +++++++++----- tests/traits/OspJsoncppTest.cpp | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/include/jwt-cpp/traits/open-source-parsers-jsoncpp/defaults.h b/include/jwt-cpp/traits/open-source-parsers-jsoncpp/defaults.h index 955544ee6..57dce4984 100644 --- a/include/jwt-cpp/traits/open-source-parsers-jsoncpp/defaults.h +++ b/include/jwt-cpp/traits/open-source-parsers-jsoncpp/defaults.h @@ -9,7 +9,7 @@ namespace jwt { /** - * \brief a class to store a generic [open_source_parsers_jsoncpp](https://github.com/open-source-parsers/open_source_parsers_jsoncpp) value as claim + * \brief a class to store a generic [jsoncpp](https://github.com/open-source-parsers/jsoncpp) value as claim * * This type is the specialization of the \ref basic_claim class which * uses the standard template types. @@ -27,7 +27,9 @@ namespace jwt { /** * Return a builder instance to create a new token */ - inline builder create() { return builder(); } + inline builder create() { + return builder(); + } #ifndef JWT_DISABLE_BASE64 /** @@ -64,7 +66,8 @@ namespace jwt { * \return Parsed JWK * \throw std::runtime_error Token is not in correct format */ - inline jwk parse_jwk(const traits::open_source_parsers_jsoncpp::string_type& token) { + inline jwk + parse_jwk(const traits::open_source_parsers_jsoncpp::string_type& token) { return jwk(token); } @@ -74,7 +77,8 @@ namespace jwt { * \return Parsed JWKs * \throw std::runtime_error Token is not in correct format */ - inline jwks parse_jwks(const traits::open_source_parsers_jsoncpp::string_type& token) { + inline jwks + parse_jwks(const traits::open_source_parsers_jsoncpp::string_type& token) { return jwks(token); } @@ -85,4 +89,4 @@ namespace jwt { using verify_context = verify_ops::verify_context; } // namespace jwt -#endif // JWT_CPP_JSONCPP_DEFAULTS_H +#endif // JWT_CPP_OPEN_SOURCE_PARSERS_JSONCPP_DEFAULTS_H diff --git a/tests/traits/OspJsoncppTest.cpp b/tests/traits/OspJsoncppTest.cpp index f0bd3d533..1b65fa5ab 100644 --- a/tests/traits/OspJsoncppTest.cpp +++ b/tests/traits/OspJsoncppTest.cpp @@ -1,4 +1,4 @@ -#include "jwt-cpp/traits/open-source-parsers-jsoncpp/traits.h" +#include "jwt-cpp/traits/open-source_parsers_jsoncpp/traits.h" #include From fb4cf64ae0a25b431fb620e16878a39d77bd77f1 Mon Sep 17 00:00:00 2001 From: Christopher McArthur Date: Tue, 12 Dec 2023 17:23:32 -0500 Subject: [PATCH 15/15] run clang format after rendering defaults --- .github/workflows/lint.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 27526c1bc..a166c63ad 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -85,6 +85,7 @@ jobs: library_name: ${{ matrix.traits.library }} library_url: ${{ matrix.traits.url }} disable_default_traits: ${{ matrix.traits.disable_pico }} + - run: clang-format -i include/jwt-cpp/traits/**/*.h - run: git add include/jwt-cpp/traits/* - uses: ./.github/actions/process-linting-results with: