From 9666878959ac75c87568c41896f270fab8edaf6f Mon Sep 17 00:00:00 2001 From: Ali Girayhan Ozbay Date: Tue, 12 Aug 2025 11:57:00 +0100 Subject: [PATCH 01/17] add reflect json traits --- include/jwt-cpp/traits/reflect-json/traits.h | 100 +++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 include/jwt-cpp/traits/reflect-json/traits.h diff --git a/include/jwt-cpp/traits/reflect-json/traits.h b/include/jwt-cpp/traits/reflect-json/traits.h new file mode 100644 index 000000000..3e78ca0fb --- /dev/null +++ b/include/jwt-cpp/traits/reflect-json/traits.h @@ -0,0 +1,100 @@ +// jwt-cpp/traits/reflect-json/traits.h +#pragma once + +// Ensure picojson specialization is disabled so we use basic_claim +#ifndef JWT_DISABLE_PICOJSON +#define JWT_DISABLE_PICOJSON +#endif + +#include "jwt-cpp/jwt.h" + +#include // rfl::json::read / write +#include // rfl::Generic, rfl::Object +#include +#include +#include + +namespace jwt { +namespace traits { + +struct reflect_json { + // --- Type specification expected by jwt-cpp + using value_type = rfl::Generic; + using object_type = rfl::Generic::Object; // rfl::Object + using array_type = rfl::Generic::Array; // std::vector + using string_type = std::string; + using number_type = double; + using integer_type = long; // matches Generic’s integer alternative + using boolean_type = bool; + + template + struct variant_overloaded : Ts... + { + using Ts::operator()...; + }; + + // --- Type inspection: map rfl::Generic's variant alt to jwt::json::type + static jwt::json::type get_type(const value_type& val) { + return std::visit(variant_overloaded{ + [](boolean_type const&) -> jwt::json::type { return jwt::json::type::boolean; }, + [](integer_type const&) -> jwt::json::type { return jwt::json::type::integer; }, + [](number_type const&) -> jwt::json::type { return jwt::json::type::number; }, + [](string_type const&) -> jwt::json::type { return jwt::json::type::string; }, + [](array_type const&) -> jwt::json::type { return jwt::json::type::array; }, + [](object_type const&) -> jwt::json::type { return jwt::json::type::object; }, + [](std::nullopt_t const&) -> jwt::json::type { throw std::logic_error("invalid type"); }, + }, + val.get()); + } + + // --- Conversions (throwing on type mismatch, as jwt-cpp expects) + static object_type as_object(const value_type& val) { + const auto& v = val.get(); + if (!std::holds_alternative(v)) throw std::bad_cast(); + return std::get(v); + } + + static array_type as_array(const value_type& val) { + const auto& v = val.get(); + if (!std::holds_alternative(v)) throw std::bad_cast(); + return std::get(v); + } + + static string_type as_string(const value_type& val) { + const auto& v = val.get(); + if (!std::holds_alternative(v)) throw std::bad_cast(); + return std::get(v); + } + + static integer_type as_integer(const value_type& val) { + const auto& v = val.get(); + if (!std::holds_alternative(v)) throw std::bad_cast(); + return std::get(v); + } + + static boolean_type as_boolean(const value_type& val) { + const auto& v = val.get(); + if (!std::holds_alternative(v)) throw std::bad_cast(); + return std::get(v); + } + + static number_type as_number(const value_type& val) { + const auto& v = val.get(); + if (!std::holds_alternative(v)) throw std::bad_cast(); + return std::get(v); + } + + // --- Parse / Serialize + static bool parse(value_type& out, string_type json) { + auto res = rfl::json::read(json); + if (res) { out = *res; return true; } + return false; + } + + static std::string serialize(const value_type& val) { + return rfl::json::write(val); + } +}; + +} // namespace traits +} // namespace jwt From f864cb55a2a2786b672bb6368d7748dc7c54736e Mon Sep 17 00:00:00 2001 From: Ali Girayhan Ozbay Date: Tue, 12 Aug 2025 12:40:06 +0100 Subject: [PATCH 02/17] jwt-cpp defaults --- .../jwt-cpp/traits/reflect-json/defaults.h | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 include/jwt-cpp/traits/reflect-json/defaults.h diff --git a/include/jwt-cpp/traits/reflect-json/defaults.h b/include/jwt-cpp/traits/reflect-json/defaults.h new file mode 100644 index 000000000..3f500d9b0 --- /dev/null +++ b/include/jwt-cpp/traits/reflect-json/defaults.h @@ -0,0 +1,63 @@ +#ifndef JWT_CPP_REFLECT_JSON_DEFAULTS_H +#define JWT_CPP_REFLECT_JSON_DEFAULTS_H + +#ifndef JWT_DISABLE_PICOJSON +#define JWT_DISABLE_PICOJSON +#endif + +#include "traits.h" + +namespace jwt { + +/** + * \brief a class to store a generic reflect-cpp (rfl::Generic) value as claim + * + * This type is the specialization of the \ref basic_claim class which + * uses the reflect-cpp JSON traits. + */ +using claim = basic_claim; + +/** Create a verifier using the default clock */ +inline verifier verify() { + return verify(default_clock{}); +} + +/** Create a builder using the default clock */ +inline builder create() { + return builder(default_clock{}); +} + +#ifndef JWT_DISABLE_BASE64 +/** Decode a token (uses jwt-cpp’s built-in base64 if not disabled) */ +inline decoded_jwt decode(const std::string& token) { + return decoded_jwt(token); +} +#endif + +/** + * Decode a token with a custom base64url decoder. + * \tparam Decode: callable taking/returning string-like, performing base64url decode. + */ +template +decoded_jwt decode(const std::string& token, Decode decode) { + return decoded_jwt(token, decode); +} + +/** Parse a JWK */ +inline jwk +parse_jwk(const traits::reflect_json::string_type& token) { + return jwk(token); +} + +/** Parse a JWKS */ +inline jwks +parse_jwks(const traits::reflect_json::string_type& token) { + return jwks(token); +} + +/** Verify context type alias (for advanced verification ops) */ +using verify_context = verify_ops::verify_context; + +} // namespace jwt + +#endif // JWT_CPP_REFLECT_JSON_DEFAULTS_H From fa6d47df05978c1db0aff4d3bd248d976eb2c340 Mon Sep 17 00:00:00 2001 From: Ali Girayhan Ozbay Date: Tue, 12 Aug 2025 15:28:58 +0100 Subject: [PATCH 03/17] fix clang tidy warnings --- include/jwt-cpp/traits/reflect-json/traits.h | 45 ++++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/include/jwt-cpp/traits/reflect-json/traits.h b/include/jwt-cpp/traits/reflect-json/traits.h index 3e78ca0fb..2c384d1b9 100644 --- a/include/jwt-cpp/traits/reflect-json/traits.h +++ b/include/jwt-cpp/traits/reflect-json/traits.h @@ -8,11 +8,10 @@ #include "jwt-cpp/jwt.h" -#include // rfl::json::read / write #include // rfl::Generic, rfl::Object -#include +#include // rfl::json::read / write #include -#include +#include namespace jwt { namespace traits { @@ -24,7 +23,7 @@ struct reflect_json { using array_type = rfl::Generic::Array; // std::vector using string_type = std::string; using number_type = double; - using integer_type = long; // matches Generic’s integer alternative + using integer_type = int64_t; // matches Generic’s integer alternative using boolean_type = bool; template @@ -49,43 +48,43 @@ struct reflect_json { // --- Conversions (throwing on type mismatch, as jwt-cpp expects) static object_type as_object(const value_type& val) { - const auto& v = val.get(); - if (!std::holds_alternative(v)) throw std::bad_cast(); - return std::get(v); + const auto& variant = val.get(); + if (!std::holds_alternative(variant)) throw std::bad_cast(); + return std::get(variant); } static array_type as_array(const value_type& val) { - const auto& v = val.get(); - if (!std::holds_alternative(v)) throw std::bad_cast(); - return std::get(v); + const auto& variant = val.get(); + if (!std::holds_alternative(variant)) throw std::bad_cast(); + return std::get(variant); } static string_type as_string(const value_type& val) { - const auto& v = val.get(); - if (!std::holds_alternative(v)) throw std::bad_cast(); - return std::get(v); + const auto& variant = val.get(); + if (!std::holds_alternative(variant)) throw std::bad_cast(); + return std::get(variant); } static integer_type as_integer(const value_type& val) { - const auto& v = val.get(); - if (!std::holds_alternative(v)) throw std::bad_cast(); - return std::get(v); + const auto& variant = val.get(); + if (!std::holds_alternative(variant)) throw std::bad_cast(); + return std::get(variant); } static boolean_type as_boolean(const value_type& val) { - const auto& v = val.get(); - if (!std::holds_alternative(v)) throw std::bad_cast(); - return std::get(v); + const auto& variant = val.get(); + if (!std::holds_alternative(variant)) throw std::bad_cast(); + return std::get(variant); } static number_type as_number(const value_type& val) { - const auto& v = val.get(); - if (!std::holds_alternative(v)) throw std::bad_cast(); - return std::get(v); + const auto& variant = val.get(); + if (!std::holds_alternative(variant)) throw std::bad_cast(); + return std::get(variant); } // --- Parse / Serialize - static bool parse(value_type& out, string_type json) { + static bool parse(value_type& out, string_type const& json) { auto res = rfl::json::read(json); if (res) { out = *res; return true; } return false; From 76099fd978e7bdc739bdc30d9ded9d9d82219010 Mon Sep 17 00:00:00 2001 From: Ali Girayhan Ozbay Date: Tue, 12 Aug 2025 15:32:26 +0100 Subject: [PATCH 04/17] fix clang tidy warnings --- include/jwt-cpp/traits/reflect-json/traits.h | 23 +++++++------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/include/jwt-cpp/traits/reflect-json/traits.h b/include/jwt-cpp/traits/reflect-json/traits.h index 2c384d1b9..9235f5f0b 100644 --- a/include/jwt-cpp/traits/reflect-json/traits.h +++ b/include/jwt-cpp/traits/reflect-json/traits.h @@ -1,29 +1,26 @@ -// jwt-cpp/traits/reflect-json/traits.h #pragma once -// Ensure picojson specialization is disabled so we use basic_claim #ifndef JWT_DISABLE_PICOJSON #define JWT_DISABLE_PICOJSON #endif #include "jwt-cpp/jwt.h" -#include // rfl::Generic, rfl::Object -#include // rfl::json::read / write +#include +#include #include #include -namespace jwt { -namespace traits { +namespace jwt::traits { struct reflect_json { - // --- Type specification expected by jwt-cpp + using value_type = rfl::Generic; - using object_type = rfl::Generic::Object; // rfl::Object - using array_type = rfl::Generic::Array; // std::vector + using object_type = rfl::Generic::Object; + using array_type = rfl::Generic::Array; using string_type = std::string; using number_type = double; - using integer_type = int64_t; // matches Generic’s integer alternative + using integer_type = int64_t; using boolean_type = bool; template @@ -32,7 +29,6 @@ struct reflect_json { using Ts::operator()...; }; - // --- Type inspection: map rfl::Generic's variant alt to jwt::json::type static jwt::json::type get_type(const value_type& val) { return std::visit(variant_overloaded{ [](boolean_type const&) -> jwt::json::type { return jwt::json::type::boolean; }, @@ -46,7 +42,6 @@ struct reflect_json { val.get()); } - // --- Conversions (throwing on type mismatch, as jwt-cpp expects) static object_type as_object(const value_type& val) { const auto& variant = val.get(); if (!std::holds_alternative(variant)) throw std::bad_cast(); @@ -83,7 +78,6 @@ struct reflect_json { return std::get(variant); } - // --- Parse / Serialize static bool parse(value_type& out, string_type const& json) { auto res = rfl::json::read(json); if (res) { out = *res; return true; } @@ -95,5 +89,4 @@ struct reflect_json { } }; -} // namespace traits -} // namespace jwt +} // namespace jwt::traits From 051e6bf3a7089ba50da2f1a22cd048b0820da3f3 Mon Sep 17 00:00:00 2001 From: Ali Girayhan Ozbay Date: Tue, 12 Aug 2025 15:36:30 +0100 Subject: [PATCH 05/17] rename directory --- include/jwt-cpp/traits/{reflect-json => reflect-cpp}/defaults.h | 0 include/jwt-cpp/traits/{reflect-json => reflect-cpp}/traits.h | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename include/jwt-cpp/traits/{reflect-json => reflect-cpp}/defaults.h (100%) rename include/jwt-cpp/traits/{reflect-json => reflect-cpp}/traits.h (100%) diff --git a/include/jwt-cpp/traits/reflect-json/defaults.h b/include/jwt-cpp/traits/reflect-cpp/defaults.h similarity index 100% rename from include/jwt-cpp/traits/reflect-json/defaults.h rename to include/jwt-cpp/traits/reflect-cpp/defaults.h diff --git a/include/jwt-cpp/traits/reflect-json/traits.h b/include/jwt-cpp/traits/reflect-cpp/traits.h similarity index 100% rename from include/jwt-cpp/traits/reflect-json/traits.h rename to include/jwt-cpp/traits/reflect-cpp/traits.h From ca4c97e6d23dffc23e7ab6b7e5f41fa773a19d77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20Girayhan=20=C3=96zbay?= Date: Sun, 17 Aug 2025 19:28:34 +0100 Subject: [PATCH 06/17] add tests --- CMakeLists.txt | 4 +- include/jwt-cpp/traits/reflect-cpp/defaults.h | 83 +++++----- include/jwt-cpp/traits/reflect-cpp/traits.h | 151 +++++++++--------- tests/CMakeLists.txt | 43 +++++ tests/traits/ReflectCppTest.cpp | 145 +++++++++++++++++ 5 files changed, 304 insertions(+), 122 deletions(-) create mode 100644 tests/traits/ReflectCppTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c22318810..7a0bd6a60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,11 +31,13 @@ cmake_dependent_option(JWT_EXTERNAL_NLOHMANN_JSON "Use find_package() to locate nlohman-json required for tests and examples" OFF "JWT_BUILD_EXAMPLES OR JWT_BUILD_TESTS" OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE BOOL "Export compile commands" FORCE) + 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 open-source-parsers-jsoncpp) +set(JWT_JSON_TRAITS_OPTIONS boost-json danielaparker-jsoncons kazuho-picojson nlohmann-json open-source-parsers-jsoncpp reflect-cpp) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") diff --git a/include/jwt-cpp/traits/reflect-cpp/defaults.h b/include/jwt-cpp/traits/reflect-cpp/defaults.h index 3f500d9b0..044af4a3e 100644 --- a/include/jwt-cpp/traits/reflect-cpp/defaults.h +++ b/include/jwt-cpp/traits/reflect-cpp/defaults.h @@ -1,5 +1,4 @@ -#ifndef JWT_CPP_REFLECT_JSON_DEFAULTS_H -#define JWT_CPP_REFLECT_JSON_DEFAULTS_H +#pragma once #ifndef JWT_DISABLE_PICOJSON #define JWT_DISABLE_PICOJSON @@ -9,55 +8,47 @@ namespace jwt { -/** - * \brief a class to store a generic reflect-cpp (rfl::Generic) value as claim - * - * This type is the specialization of the \ref basic_claim class which - * uses the reflect-cpp JSON traits. - */ -using claim = basic_claim; + /** + * \brief a class to store a generic reflect-cpp (rfl::Generic) value as claim + * + * This type is the specialization of the \ref basic_claim class which + * uses the reflect-cpp JSON traits. + */ + using claim = basic_claim; -/** Create a verifier using the default clock */ -inline verifier verify() { - return verify(default_clock{}); -} + /** Create a verifier using the default clock */ + inline verifier verify() { + return verify(default_clock{}); + } -/** Create a builder using the default clock */ -inline builder create() { - return builder(default_clock{}); -} + /** Create a builder using the default clock */ + inline builder create() { + return builder(default_clock{}); + } #ifndef JWT_DISABLE_BASE64 -/** Decode a token (uses jwt-cpp’s built-in base64 if not disabled) */ -inline decoded_jwt decode(const std::string& token) { - return decoded_jwt(token); -} + /** Decode a token (uses jwt-cpp’s built-in base64 if not disabled) */ + inline decoded_jwt decode(const std::string& token) { + return decoded_jwt(token); + } #endif -/** - * Decode a token with a custom base64url decoder. - * \tparam Decode: callable taking/returning string-like, performing base64url decode. - */ -template -decoded_jwt decode(const std::string& token, Decode decode) { - return decoded_jwt(token, decode); -} - -/** Parse a JWK */ -inline jwk -parse_jwk(const traits::reflect_json::string_type& token) { - return jwk(token); -} - -/** Parse a JWKS */ -inline jwks -parse_jwks(const traits::reflect_json::string_type& token) { - return jwks(token); -} - -/** Verify context type alias (for advanced verification ops) */ -using verify_context = verify_ops::verify_context; + template + decoded_jwt decode(const std::string& token, Decode decode) { + return decoded_jwt(token, decode); + } -} // namespace jwt + /** Parse a JWK */ + inline jwk parse_jwk(const traits::reflect_cpp::string_type& token) { + return jwk(token); + } + + /** Parse a JWKS */ + inline jwks parse_jwks(const traits::reflect_cpp::string_type& token) { + return jwks(token); + } -#endif // JWT_CPP_REFLECT_JSON_DEFAULTS_H + /** Verify context type alias (for advanced verification ops) */ + using verify_context = verify_ops::verify_context; + +} // namespace jwt diff --git a/include/jwt-cpp/traits/reflect-cpp/traits.h b/include/jwt-cpp/traits/reflect-cpp/traits.h index 9235f5f0b..915cf88e4 100644 --- a/include/jwt-cpp/traits/reflect-cpp/traits.h +++ b/include/jwt-cpp/traits/reflect-cpp/traits.h @@ -13,80 +13,81 @@ namespace jwt::traits { -struct reflect_json { - - using value_type = rfl::Generic; - using object_type = rfl::Generic::Object; - using array_type = rfl::Generic::Array; - using string_type = std::string; - using number_type = double; - using integer_type = int64_t; - using boolean_type = bool; - - template - struct variant_overloaded : Ts... - { - using Ts::operator()...; - }; - - static jwt::json::type get_type(const value_type& val) { - return std::visit(variant_overloaded{ - [](boolean_type const&) -> jwt::json::type { return jwt::json::type::boolean; }, - [](integer_type const&) -> jwt::json::type { return jwt::json::type::integer; }, - [](number_type const&) -> jwt::json::type { return jwt::json::type::number; }, - [](string_type const&) -> jwt::json::type { return jwt::json::type::string; }, - [](array_type const&) -> jwt::json::type { return jwt::json::type::array; }, - [](object_type const&) -> jwt::json::type { return jwt::json::type::object; }, - [](std::nullopt_t const&) -> jwt::json::type { throw std::logic_error("invalid type"); }, - }, - val.get()); - } - - static object_type as_object(const value_type& val) { - const auto& variant = val.get(); - if (!std::holds_alternative(variant)) throw std::bad_cast(); - return std::get(variant); - } - - static array_type as_array(const value_type& val) { - const auto& variant = val.get(); - if (!std::holds_alternative(variant)) throw std::bad_cast(); - return std::get(variant); - } - - static string_type as_string(const value_type& val) { - const auto& variant = val.get(); - if (!std::holds_alternative(variant)) throw std::bad_cast(); - return std::get(variant); - } - - static integer_type as_integer(const value_type& val) { - const auto& variant = val.get(); - if (!std::holds_alternative(variant)) throw std::bad_cast(); - return std::get(variant); - } - - static boolean_type as_boolean(const value_type& val) { - const auto& variant = val.get(); - if (!std::holds_alternative(variant)) throw std::bad_cast(); - return std::get(variant); - } - - static number_type as_number(const value_type& val) { - const auto& variant = val.get(); - if (!std::holds_alternative(variant)) throw std::bad_cast(); - return std::get(variant); - } - - static bool parse(value_type& out, string_type const& json) { - auto res = rfl::json::read(json); - if (res) { out = *res; return true; } - return false; - } - - static std::string serialize(const value_type& val) { - return rfl::json::write(val); - } -}; + struct reflect_cpp { + + using value_type = rfl::Generic; + using object_type = rfl::Generic::Object; + using array_type = rfl::Generic::Array; + using string_type = std::string; + using number_type = double; + using integer_type = int64_t; + using boolean_type = bool; + + template + struct variant_overloaded : Ts... { + using Ts::operator()...; + }; + + static jwt::json::type get_type(const value_type& val) { + return std::visit( + variant_overloaded{ + [](boolean_type const&) -> jwt::json::type { return jwt::json::type::boolean; }, + [](integer_type const&) -> jwt::json::type { return jwt::json::type::integer; }, + [](number_type const&) -> jwt::json::type { return jwt::json::type::number; }, + [](string_type const&) -> jwt::json::type { return jwt::json::type::string; }, + [](array_type const&) -> jwt::json::type { return jwt::json::type::array; }, + [](object_type const&) -> jwt::json::type { return jwt::json::type::object; }, + [](std::nullopt_t const&) -> jwt::json::type { throw std::logic_error("invalid type"); }, + }, + val.get()); + } + + static object_type as_object(const value_type& val) { + const auto& variant = val.get(); + if (!std::holds_alternative(variant)) throw std::bad_cast(); + return std::get(variant); + } + + static array_type as_array(const value_type& val) { + const auto& variant = val.get(); + if (!std::holds_alternative(variant)) throw std::bad_cast(); + return std::get(variant); + } + + static string_type as_string(const value_type& val) { + const auto& variant = val.get(); + if (!std::holds_alternative(variant)) throw std::bad_cast(); + return std::get(variant); + } + + static integer_type as_integer(const value_type& val) { + const auto& variant = val.get(); + if (!std::holds_alternative(variant)) throw std::bad_cast(); + return std::get(variant); + } + + static boolean_type as_boolean(const value_type& val) { + const auto& variant = val.get(); + if (!std::holds_alternative(variant)) throw std::bad_cast(); + return std::get(variant); + } + + static number_type as_number(const value_type& val) { + const auto& variant = val.get(); + if (!std::holds_alternative(variant)) throw std::bad_cast(); + return std::get(variant); + } + + static bool parse(value_type& out, string_type const& json) { + auto res = rfl::json::read(json); + if (res) { + out = *res; + return true; + } + return false; + } + + static std::string serialize(const value_type& val) { return rfl::json::write(val); } + }; } // namespace jwt::traits diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6c7b75504..6a26ed926 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -44,6 +44,34 @@ if(TARGET jsoncpp_static) list(APPEND TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/traits/OspJsoncppTest.cpp) endif() +# ---- reflect-cpp: prefer find_package, fall back to FetchContent (C++20+) ---- +find_package(reflectcpp CONFIG QUIET) +set(JWT_HAVE_REFLECTCPP OFF) +if(NOT TARGET reflectcpp::reflectcpp AND NOT TARGET reflectcpp) + # Only fetch if toolchain isn't explicitly set < C++20 + if(CMAKE_CXX_STANDARD AND CMAKE_CXX_STANDARD LESS 20) + message(STATUS "jwt-cpp: reflect-cpp requires C++20; skipping fetch and tests") + else() + message(STATUS "jwt-cpp: using FetchContent for reflect-cpp for tests") + FetchContent_Declare( + reflectcpp + GIT_REPOSITORY https://github.com/getml/reflect-cpp.git + GIT_TAG ca53b225bfc6887dd27e278820c34992cce37c6d + ) + # keep test deps lean + set(REFLECTCPP_BUILD_TESTS OFF CACHE BOOL "" FORCE) + set(REFLECTCPP_BUILD_BENCHMARKS OFF CACHE BOOL "" FORCE) + set(REFLECTCPP_ALL_FORMATS OFF CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(reflectcpp) + endif() +endif() + +# Add the reflect-cpp trait test source ONLY if we actually have it +if(TARGET reflectcpp::reflectcpp OR TARGET reflectcpp) + set(JWT_HAVE_REFLECTCPP ON) + list(APPEND TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/traits/ReflectCppTest.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 @@ -73,6 +101,21 @@ endif() target_link_libraries(jwt-cpp-test PRIVATE jwt-cpp nlohmann_json::nlohmann_json $<$>:${CMAKE_DL_LIBS}>) +# ---- reflect-cpp wiring (only if present) ---- +if(JWT_HAVE_REFLECTCPP) + # Only enforce C++20 if not already C++20 or higher + if(NOT CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD LESS 20) + target_compile_features(jwt-cpp-test PRIVATE cxx_std_20) + endif() + + # Link the reflect-cpp target (name depends on how it was provided) + if(TARGET reflectcpp::reflectcpp) + target_link_libraries(jwt-cpp-test PRIVATE reflectcpp::reflectcpp) + else() + target_link_libraries(jwt-cpp-test PRIVATE reflectcpp) + endif() +endif() + gtest_discover_tests(jwt-cpp-test) add_custom_target(jwt-cpp-test-run COMMAND jwt-cpp-test) diff --git a/tests/traits/ReflectCppTest.cpp b/tests/traits/ReflectCppTest.cpp new file mode 100644 index 000000000..930c1b3eb --- /dev/null +++ b/tests/traits/ReflectCppTest.cpp @@ -0,0 +1,145 @@ +#include "jwt-cpp/traits/reflect-cpp/defaults.h" + +#include + +#include +#include +#include +#include + +TEST(ReflectCppTest, BasicClaims) { + const auto string = jwt::basic_claim(jwt::traits::reflect_cpp::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(ReflectCppTest, AudienceAsString) { + jwt::traits::reflect_cpp::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(ReflectCppTest, 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(ReflectCppTest, SetObject) { + // Parse JSON into the trait's value_type + jwt::traits::reflect_cpp::value_type value; + ASSERT_TRUE(jwt::traits::reflect_cpp::parse(value, "{\"api-x\": [1]}")); + + // Wrap into a claim and verify type + jwt::basic_claim object(value); + ASSERT_EQ(object.get_type(), jwt::json::type::object); + + // Build a token using the reflect-cpp trait explicitly + auto token = jwt::create() + .set_payload_claim("namespace", object) + .sign(jwt::algorithm::hs256("test")); + + ASSERT_EQ(token, + "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lc3BhY2UiOnsiYXBpLXgiOlsxXX19.F8I6I2RcSF98bKa0IpIz09fRZtHr1CWnWKx2za-tFQA"); +} + +TEST(ReflectCppTest, VerifyTokenHS256) { + jwt::traits::reflect_cpp::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(ReflectCppTest, 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(ReflectCppTest, VerifyTokenExpirationInValid) { + const auto token = jwt::create() + .set_issuer("auth0") + .set_issued_now() + .set_expires_in(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(ReflectCppTest, 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(ReflectCppTest, VerifyArray) { + jwt::traits::reflect_cpp::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(ReflectCppTest, VerifyObject) { + jwt::traits::reflect_cpp::string_type token = + "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lc3BhY2UiOnsiYXBpLXgiOlsxXX19.F8I6I2RcSF98bKa0IpIz09fRZtHr1CWnWKx2za-tFQA"; + const auto decoded_token = jwt::decode(token); + + // Parse into reflect-cpp's JSON value, then wrap as a claim + jwt::traits::reflect_cpp::value_type value; + ASSERT_TRUE(jwt::traits::reflect_cpp::parse(value, "{\"api-x\": [1]}")); + jwt::basic_claim object_claim(value); + + // Use the reflect-cpp trait for verify(), too + const auto verify = jwt::verify() + .allow_algorithm(jwt::algorithm::hs256("test")) + .with_claim("namespace", object_claim); + + ASSERT_NO_THROW(verify.verify(decoded_token)); +} From 4ee3643830911e70550fb15543d821bf8821865e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20Girayhan=20=C3=96zbay?= Date: Sun, 17 Aug 2025 19:38:32 +0100 Subject: [PATCH 07/17] add tests --- tests/traits/ReflectCppTest.cpp | 79 ++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 31 deletions(-) diff --git a/tests/traits/ReflectCppTest.cpp b/tests/traits/ReflectCppTest.cpp index 930c1b3eb..8bcdf548b 100644 --- a/tests/traits/ReflectCppTest.cpp +++ b/tests/traits/ReflectCppTest.cpp @@ -2,11 +2,6 @@ #include -#include -#include -#include -#include - TEST(ReflectCppTest, BasicClaims) { const auto string = jwt::basic_claim(jwt::traits::reflect_cpp::string_type("string")); ASSERT_EQ(string.get_type(), jwt::json::type::string); @@ -43,10 +38,16 @@ TEST(ReflectCppTest, AudienceAsString) { } TEST(ReflectCppTest, 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{}); + // Build the array via the trait first + jwt::traits::reflect_cpp::array_type arr{100, 20, 10}; + + jwt::traits::reflect_cpp::value_type v(arr); + jwt::basic_claim array_claim(v); + + // Use the reflect-cpp trait for create() + auto token = + jwt::create().set_payload_claim("test", array_claim).sign(jwt::algorithm::none{}); + ASSERT_EQ(token, "eyJhbGciOiJub25lIn0.eyJ0ZXN0IjpbMTAwLDIwLDEwXX0."); } @@ -71,59 +72,75 @@ TEST(ReflectCppTest, SetObject) { TEST(ReflectCppTest, VerifyTokenHS256) { jwt::traits::reflect_cpp::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); + + const auto decoded = jwt::decode(token); + const auto verify = + jwt::verify().allow_algorithm(jwt::algorithm::hs256{"secret"}).with_issuer("auth0"); + + ASSERT_NO_THROW(verify.verify(decoded)); } TEST(ReflectCppTest, VerifyTokenExpirationValid) { - const auto token = jwt::create() + 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); + + const auto decoded = jwt::decode(token); + const auto verify = + jwt::verify().allow_algorithm(jwt::algorithm::hs256{"secret"}).with_issuer("auth0"); + + ASSERT_NO_THROW(verify.verify(decoded)); } TEST(ReflectCppTest, VerifyTokenExpirationInValid) { - const auto token = jwt::create() + const auto token = jwt::create() .set_issuer("auth0") .set_issued_now() .set_expires_in(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); + + const auto decoded = jwt::decode(token); + const auto verify = + jwt::verify().allow_algorithm(jwt::algorithm::hs256{"secret"}).with_issuer("auth0"); + + ASSERT_NO_THROW(verify.verify(decoded)); } TEST(ReflectCppTest, VerifyTokenExpired) { - const auto token = jwt::create() + 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); + const auto decoded = jwt::decode(token); + const auto verify = + jwt::verify().allow_algorithm(jwt::algorithm::hs256{"secret"}).with_issuer("auth0"); + + ASSERT_THROW(verify.verify(decoded), jwt::error::token_verification_exception); + std::error_code ec; - ASSERT_NO_THROW(verify.verify(decoded_token, ec)); - ASSERT_TRUE(!(!ec)); + ASSERT_NO_THROW(verify.verify(decoded, ec)); + ASSERT_TRUE(ec); // non-zero ASSERT_EQ(ec.category(), jwt::error::token_verification_error_category()); ASSERT_EQ(ec.value(), static_cast(jwt::error::token_verification_error::token_expired)); } TEST(ReflectCppTest, VerifyArray) { jwt::traits::reflect_cpp::string_type token = "eyJhbGciOiJub25lIn0.eyJ0ZXN0IjpbMTAwLDIwLDEwXX0."; - const auto decoded_token = jwt::decode(token); + const auto decoded = jwt::decode(token); - std::vector vect = {100, 20, 10}; - jwt::basic_claim array_claim(vect.begin(), vect.end()); + // Build array value_type via the trait + jwt::traits::reflect_cpp::array_type arr{100, 20, 10}; - const auto verify = jwt::verify().allow_algorithm(jwt::algorithm::none{}).with_claim("test", array_claim); - ASSERT_NO_THROW(verify.verify(decoded_token)); + jwt::basic_claim array_claim{jwt::traits::reflect_cpp::value_type(arr)}; + + const auto verify = + jwt::verify().allow_algorithm(jwt::algorithm::none{}).with_claim("test", array_claim); + + ASSERT_NO_THROW(verify.verify(decoded)); } TEST(ReflectCppTest, VerifyObject) { From abbdf9d58fc0f1f688dd02e8cba72d1328dc3df5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20Girayhan=20=C3=96zbay?= Date: Sun, 17 Aug 2025 19:40:27 +0100 Subject: [PATCH 08/17] add tests --- tests/traits/ReflectCppTest.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/tests/traits/ReflectCppTest.cpp b/tests/traits/ReflectCppTest.cpp index 8bcdf548b..8e8898e53 100644 --- a/tests/traits/ReflectCppTest.cpp +++ b/tests/traits/ReflectCppTest.cpp @@ -1,7 +1,9 @@ -#include "jwt-cpp/traits/reflect-cpp/defaults.h" +#include "jwt-cpp/traits/reflect-cpp/traits.h" #include +using jwt::algorithm::hs256; + TEST(ReflectCppTest, BasicClaims) { const auto string = jwt::basic_claim(jwt::traits::reflect_cpp::string_type("string")); ASSERT_EQ(string.get_type(), jwt::json::type::string); @@ -41,8 +43,8 @@ TEST(ReflectCppTest, SetArray) { // Build the array via the trait first jwt::traits::reflect_cpp::array_type arr{100, 20, 10}; - jwt::traits::reflect_cpp::value_type v(arr); - jwt::basic_claim array_claim(v); + jwt::traits::reflect_cpp::value_type value(arr); + jwt::basic_claim array_claim(value); // Use the reflect-cpp trait for create() auto token = @@ -121,11 +123,11 @@ TEST(ReflectCppTest, VerifyTokenExpired) { ASSERT_THROW(verify.verify(decoded), jwt::error::token_verification_exception); - std::error_code ec; - ASSERT_NO_THROW(verify.verify(decoded, ec)); - ASSERT_TRUE(ec); // non-zero - ASSERT_EQ(ec.category(), jwt::error::token_verification_error_category()); - ASSERT_EQ(ec.value(), static_cast(jwt::error::token_verification_error::token_expired)); + std::error_code errcode; + ASSERT_NO_THROW(verify.verify(decoded, errcode)); + ASSERT_TRUE(errcode); // non-zero + ASSERT_EQ(errcode.category(), jwt::error::token_verification_error_category()); + ASSERT_EQ(errcode.value(), static_cast(jwt::error::token_verification_error::token_expired)); } TEST(ReflectCppTest, VerifyArray) { @@ -154,9 +156,8 @@ TEST(ReflectCppTest, VerifyObject) { jwt::basic_claim object_claim(value); // Use the reflect-cpp trait for verify(), too - const auto verify = jwt::verify() - .allow_algorithm(jwt::algorithm::hs256("test")) - .with_claim("namespace", object_claim); + const auto verify = + jwt::verify().allow_algorithm(hs256("test")).with_claim("namespace", object_claim); ASSERT_NO_THROW(verify.verify(decoded_token)); } From 3a134941e7421051c01af525309c8306a2963b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20Girayhan=20=C3=96zbay?= Date: Sun, 17 Aug 2025 20:43:56 +0100 Subject: [PATCH 09/17] replace unnecessary copy with move --- include/jwt-cpp/traits/reflect-cpp/traits.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/jwt-cpp/traits/reflect-cpp/traits.h b/include/jwt-cpp/traits/reflect-cpp/traits.h index 915cf88e4..3358cc266 100644 --- a/include/jwt-cpp/traits/reflect-cpp/traits.h +++ b/include/jwt-cpp/traits/reflect-cpp/traits.h @@ -81,7 +81,7 @@ namespace jwt::traits { static bool parse(value_type& out, string_type const& json) { auto res = rfl::json::read(json); if (res) { - out = *res; + out = *std::move(res); return true; } return false; From f546ffed1c31502573417f514333e1eadae829f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20Girayhan=20=C3=96zbay?= Date: Tue, 19 Aug 2025 00:44:01 +0100 Subject: [PATCH 10/17] gate reflect cpp tests behind flag --- tests/CMakeLists.txt | 59 +++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6a26ed926..c89fe1618 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -44,31 +44,25 @@ if(TARGET jsoncpp_static) list(APPEND TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/traits/OspJsoncppTest.cpp) endif() -# ---- reflect-cpp: prefer find_package, fall back to FetchContent (C++20+) ---- -find_package(reflectcpp CONFIG QUIET) -set(JWT_HAVE_REFLECTCPP OFF) -if(NOT TARGET reflectcpp::reflectcpp AND NOT TARGET reflectcpp) - # Only fetch if toolchain isn't explicitly set < C++20 - if(CMAKE_CXX_STANDARD AND CMAKE_CXX_STANDARD LESS 20) - message(STATUS "jwt-cpp: reflect-cpp requires C++20; skipping fetch and tests") - else() - message(STATUS "jwt-cpp: using FetchContent for reflect-cpp for tests") - FetchContent_Declare( - reflectcpp - GIT_REPOSITORY https://github.com/getml/reflect-cpp.git - GIT_TAG ca53b225bfc6887dd27e278820c34992cce37c6d - ) +# ---- reflect-cpp trait tests toggle (OFF by default) ---- +option(JWT_ENABLE_REFLECTCPP_TESTS "Build reflect-cpp trait tests (requires C++20)" OFF) +if(JWT_ENABLE_REFLECTCPP_TESTS) + # Prefer an installed package + find_package(reflectcpp CONFIG QUIET) + + # If not found, always fetch at a pinned commit + if(NOT TARGET reflectcpp::reflectcpp) + message(STATUS "jwt-cpp: reflect-cpp not found; fetching for tests") + include(FetchContent) + FetchContent_Declare(reflectcpp GIT_REPOSITORY https://github.com/getml/reflect-cpp.git GIT_TAG ca53b225bfc6887dd27e278820c34992cce37c6d) # keep test deps lean set(REFLECTCPP_BUILD_TESTS OFF CACHE BOOL "" FORCE) set(REFLECTCPP_BUILD_BENCHMARKS OFF CACHE BOOL "" FORCE) set(REFLECTCPP_ALL_FORMATS OFF CACHE BOOL "" FORCE) FetchContent_MakeAvailable(reflectcpp) endif() -endif() -# Add the reflect-cpp trait test source ONLY if we actually have it -if(TARGET reflectcpp::reflectcpp OR TARGET reflectcpp) - set(JWT_HAVE_REFLECTCPP ON) + # Add the reflect-cpp trait test source list(APPEND TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/traits/ReflectCppTest.cpp) endif() @@ -97,24 +91,23 @@ else() if(TARGET jsoncpp_static) target_link_libraries(jwt-cpp-test PRIVATE jsoncpp_static) endif() -endif() -target_link_libraries(jwt-cpp-test PRIVATE jwt-cpp nlohmann_json::nlohmann_json - $<$>:${CMAKE_DL_LIBS}>) - -# ---- reflect-cpp wiring (only if present) ---- -if(JWT_HAVE_REFLECTCPP) - # Only enforce C++20 if not already C++20 or higher - if(NOT CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD LESS 20) - target_compile_features(jwt-cpp-test PRIVATE cxx_std_20) - endif() - - # Link the reflect-cpp target (name depends on how it was provided) - if(TARGET reflectcpp::reflectcpp) + if(JWT_ENABLE_REFLECTCPP_TESTS) + # Enforce language level on this target: + # - If CMAKE_CXX_STANDARD is undefined, require C++20 for this target. + # - If defined and <20, hard error. + # - If defined and >=20, request that exact level on this target. + if(NOT DEFINED CMAKE_CXX_STANDARD) + target_compile_features(jwt-cpp-test PRIVATE cxx_std_20) + else() + if(CMAKE_CXX_STANDARD LESS 20) + message(FATAL_ERROR "jwt-cpp: reflect-cpp tests require C++20, but CMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}.") + endif() + endif() target_link_libraries(jwt-cpp-test PRIVATE reflectcpp::reflectcpp) - else() - target_link_libraries(jwt-cpp-test PRIVATE reflectcpp) endif() endif() +target_link_libraries(jwt-cpp-test PRIVATE jwt-cpp nlohmann_json::nlohmann_json + $<$>:${CMAKE_DL_LIBS}>) gtest_discover_tests(jwt-cpp-test) add_custom_target(jwt-cpp-test-run COMMAND jwt-cpp-test) From 808c249ec801ffab77646a84c6e27dfcbdcb3116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20Girayhan=20=C3=96zbay?= Date: Sun, 24 Aug 2025 18:56:03 +0100 Subject: [PATCH 11/17] reflect cpp CI work --- .../actions/install/reflect-cpp/action.yml | 19 ++++ .github/workflows/cmake.yml | 34 +++++++ .github/workflows/traits.yml | 6 ++ example/traits/CMakeLists.txt | 15 ++++ example/traits/reflect-cpp.cpp | 89 +++++++++++++++++++ tests/CMakeLists.txt | 40 +++------ 6 files changed, 175 insertions(+), 28 deletions(-) create mode 100644 .github/actions/install/reflect-cpp/action.yml create mode 100644 example/traits/reflect-cpp.cpp diff --git a/.github/actions/install/reflect-cpp/action.yml b/.github/actions/install/reflect-cpp/action.yml new file mode 100644 index 000000000..4af20b61f --- /dev/null +++ b/.github/actions/install/reflect-cpp/action.yml @@ -0,0 +1,19 @@ +name: Install reflect-cpp +description: Build & install reflect-cpp at a specific commit +inputs: + version: + description: Commit/tag to checkout + required: true +runs: + using: composite + steps: + - shell: bash + run: | + set -eux + sudo apt-get update + sudo apt-get install -y ninja-build + git clone https://github.com/getml/reflect-cpp.git _thirdparty/reflect-cpp + cd _thirdparty/reflect-cpp + git checkout "${{ inputs.version }}" + cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=20 + cmake --build build --target install -j diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index d73187525..301ded858 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -273,3 +273,37 @@ jobs: run: | cd build ./tests/jwt-cpp-test + + reflect-cpp-tests: + name: Unit tests (reflect-cpp) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install toolchain & deps + run: | + sudo apt-get update + sudo apt-get install -y ninja-build g++ libssl-dev + + - name: Install reflect-cpp + run: | + git clone https://github.com/getml/reflect-cpp.git _thirdparty/reflect-cpp + cd _thirdparty/reflect-cpp + git checkout ca53b225bfc6887dd27e278820c34992cce37c6d + cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=20 + sudo cmake --build build --target install -j + + - name: Configure + run: | + cmake -S . -B build -G Ninja \ + -DJWT_BUILD_TESTS=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_CXX_STANDARD=20 \ + -DJWT_SSL_LIBRARY=OpenSSL + + - name: Build tests + run: cmake --build build --target jwt-cpp-test -j + + - name: Run tests + working-directory: build + run: ctest --output-on-failure diff --git a/.github/workflows/traits.yml b/.github/workflows/traits.yml index aedcb14af..60f56e767 100644 --- a/.github/workflows/traits.yml +++ b/.github/workflows/traits.yml @@ -18,6 +18,7 @@ jobs: - { name: "nlohmann-json", tag: "3.12.0", version: "v3.12.0" } - { name: "kazuho-picojson", tag: "111c9be5188f7350c2eac9ddaedd8cca3d7bf394", version: "111c9be" } - { name: "open-source-parsers-jsoncpp", tag: "1.9.6", version: "v1.9.6" } + - { name: "reflect-cpp", tag: "ca53b225bfc6887dd27e278820c34992cce37c6d", version: "ca53b22" } steps: - uses: actions/checkout@v4 - uses: lukka/get-cmake@latest @@ -56,6 +57,11 @@ jobs: with: version: ${{matrix.target.tag}} + - if: matrix.target.name == 'reflect-cpp' + uses: ./.github/actions/install/reflect-cpp + 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 4734fabc7..dda08bdd8 100644 --- a/example/traits/CMakeLists.txt +++ b/example/traits/CMakeLists.txt @@ -34,3 +34,18 @@ if(TARGET jsoncpp_static) add_executable(open-source-parsers-jsoncpp open-source-parsers-jsoncpp.cpp) target_link_libraries(open-source-parsers-jsoncpp jsoncpp_static jwt-cpp::jwt-cpp) endif() + +# Build the reflect-cpp trait example only when the dep exists AND we're on C++20 or higher. +find_package(reflectcpp CONFIG) +if(TARGET reflectcpp::reflectcpp AND (NOT DEFINED CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD GREATER_EQUAL 20)) + add_executable(example-traits-reflectcpp example/traits/reflect-cpp.cpp) + target_link_libraries(example-traits-reflectcpp PRIVATE jwt-cpp reflectcpp::reflectcpp) + + # If the project hasn't set a global standard, request C++20 just for this example. + if(NOT DEFINED CMAKE_CXX_STANDARD) + target_compile_features(example-traits-reflectcpp PRIVATE cxx_std_20) + endif() +elseif(TARGET reflectcpp::reflectcpp) + # reflect-cpp is present but the user explicitly chose a pre-C++20 standard → skip the example. + message(STATUS "jwt-cpp: skipping example-traits-reflectcpp (CMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} < 20)") +endif() diff --git a/example/traits/reflect-cpp.cpp b/example/traits/reflect-cpp.cpp new file mode 100644 index 000000000..2bdfe4407 --- /dev/null +++ b/example/traits/reflect-cpp.cpp @@ -0,0 +1,89 @@ +#include "jwt-cpp/jwt.h" +#include "jwt-cpp/traits/reflect-cpp/traits.h" // the trait adapter + +#include +#include +#include + +int main() { + using R = jwt::traits::reflect_cpp; + + try { + // Build a small JSON object claim using the trait's value_type + R::object_type ns_obj; + ns_obj["api-x"] = R::value_type(1); // {"api-x": 1} + + // Build an array claim: [100, 20, 10] + R::array_type arr; + arr.emplace_back(100); + arr.emplace_back(20); + arr.emplace_back(10); + + // Wrap the native JSON values into jwt::basic_claim + jwt::basic_claim ns_claim(R::value_type(ns_obj)); + jwt::basic_claim arr_claim(R::value_type(arr)); + + // Create a token using the reflect-cpp trait (HS256) + const std::string secret = "secret"; + const auto now = std::chrono::system_clock::now(); + + const auto token = jwt::create() + .set_type("JWT") + .set_algorithm("HS256") + .set_issuer("auth0") + .set_subject("demo") + .set_audience("example") + .set_issued_at(now) + .set_not_before(now - std::chrono::seconds{5}) + .set_expires_at(now + std::chrono::minutes{10}) + .set_payload_claim("namespace", ns_claim) + .set_payload_claim("numbers", arr_claim) + .sign(jwt::algorithm::hs256{secret}); + + std::cout << "token: " << token << "\n"; + + // Decode with the same trait + const auto decoded = jwt::decode(token); + + // Access header/payload fields + std::cout << "alg: " << decoded.get_algorithm() << "\n"; + std::cout << "typ: " << decoded.get_type() << "\n"; + if (decoded.has_issuer()) std::cout << "iss: " << decoded.get_issuer() << "\n"; + + // Verify with the same trait + const auto verifier = jwt::verify().allow_algorithm(jwt::algorithm::hs256{secret}).with_issuer("auth0"); + + verifier.verify(decoded); + std::cout << "verification: OK\n"; + + // Read back our custom claims using the trait accessors + if (decoded.has_payload_claim("namespace")) { + const auto c = decoded.get_payload_claim("namespace"); + if (c.get_type() == jwt::json::type::object) { + const auto& obj = c.as_object(); // R::object_type (map-like) + const auto it = obj.find("api-x"); + if (it != obj.end()) { + // it->second is R::value_type + // print integer if present + // (rfl::Generic can be visited, but jwt-cpp trait exposes typed helpers) + // Keep it simple and rely on serialization through the trait: + std::cout << "namespace.api-x present\n"; + } + } + } + + if (decoded.has_payload_claim("numbers")) { + const auto c = decoded.get_payload_claim("numbers"); + if (c.get_type() == jwt::json::type::array) { + const auto& a = c.as_array(); // R::array_type (vector-like) + std::cout << "numbers.size: " << a.size() << "\n"; + } + } + + } catch (const std::exception& e) { + std::cerr << "error: " << e.what() << "\n"; + return 1; + } + + return 0; +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c89fe1618..b81505478 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -44,26 +44,13 @@ if(TARGET jsoncpp_static) list(APPEND TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/traits/OspJsoncppTest.cpp) endif() -# ---- reflect-cpp trait tests toggle (OFF by default) ---- -option(JWT_ENABLE_REFLECTCPP_TESTS "Build reflect-cpp trait tests (requires C++20)" OFF) -if(JWT_ENABLE_REFLECTCPP_TESTS) - # Prefer an installed package - find_package(reflectcpp CONFIG QUIET) - - # If not found, always fetch at a pinned commit - if(NOT TARGET reflectcpp::reflectcpp) - message(STATUS "jwt-cpp: reflect-cpp not found; fetching for tests") - include(FetchContent) - FetchContent_Declare(reflectcpp GIT_REPOSITORY https://github.com/getml/reflect-cpp.git GIT_TAG ca53b225bfc6887dd27e278820c34992cce37c6d) - # keep test deps lean - set(REFLECTCPP_BUILD_TESTS OFF CACHE BOOL "" FORCE) - set(REFLECTCPP_BUILD_BENCHMARKS OFF CACHE BOOL "" FORCE) - set(REFLECTCPP_ALL_FORMATS OFF CACHE BOOL "" FORCE) - FetchContent_MakeAvailable(reflectcpp) +find_package(reflectcpp CONFIG QUIET) +if(TARGET reflectcpp::reflectcpp) + if(NOT DEFINED CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD GREATER_EQUAL 20) + list(APPEND TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/traits/ReflectCppTest.cpp) + else() + message(STATUS "jwt-cpp: skipping reflect-cpp tests (CMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} < 20)") endif() - - # Add the reflect-cpp trait test source - list(APPEND TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/traits/ReflectCppTest.cpp) endif() add_executable(jwt-cpp-test ${TEST_SOURCES}) @@ -75,6 +62,7 @@ set(JWT_TESTER_CLANG_FLAGS -Weverything -Wno-c++98-compat -Wno-global-constructo target_compile_options( jwt-cpp-test PRIVATE $<$:/W4> $<$:${JWT_TESTER_GCC_FLAGS}> $<$:${JWT_TESTER_CLANG_FLAGS}>) + if(HUNTER_ENABLED) target_link_libraries(jwt-cpp-test PRIVATE GTest::gtest GTest::gtest_main) # Define a compile define to bypass openssl error tests @@ -91,21 +79,17 @@ else() if(TARGET jsoncpp_static) target_link_libraries(jwt-cpp-test PRIVATE jsoncpp_static) endif() - if(JWT_ENABLE_REFLECTCPP_TESTS) - # Enforce language level on this target: - # - If CMAKE_CXX_STANDARD is undefined, require C++20 for this target. - # - If defined and <20, hard error. - # - If defined and >=20, request that exact level on this target. + + # reflect-cpp: link only when available and only for C++20 builds + if(TARGET reflectcpp::reflectcpp AND (NOT DEFINED CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD GREATER_EQUAL 20)) + # If the user didn't set a project-wide standard, request C++20 for this test target. if(NOT DEFINED CMAKE_CXX_STANDARD) target_compile_features(jwt-cpp-test PRIVATE cxx_std_20) - else() - if(CMAKE_CXX_STANDARD LESS 20) - message(FATAL_ERROR "jwt-cpp: reflect-cpp tests require C++20, but CMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}.") - endif() endif() target_link_libraries(jwt-cpp-test PRIVATE reflectcpp::reflectcpp) endif() endif() + target_link_libraries(jwt-cpp-test PRIVATE jwt-cpp nlohmann_json::nlohmann_json $<$>:${CMAKE_DL_LIBS}>) From 45375a827b96e324319453cf106a35b839858e6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20Girayhan=20=C3=96zbay?= Date: Sun, 24 Aug 2025 19:26:27 +0100 Subject: [PATCH 12/17] reflect-cpp example --- example/traits/CMakeLists.txt | 8 +- example/traits/reflect-cpp.cpp | 144 ++++++++++++++++----------------- 2 files changed, 73 insertions(+), 79 deletions(-) diff --git a/example/traits/CMakeLists.txt b/example/traits/CMakeLists.txt index dda08bdd8..a03feff98 100644 --- a/example/traits/CMakeLists.txt +++ b/example/traits/CMakeLists.txt @@ -38,14 +38,14 @@ endif() # Build the reflect-cpp trait example only when the dep exists AND we're on C++20 or higher. find_package(reflectcpp CONFIG) if(TARGET reflectcpp::reflectcpp AND (NOT DEFINED CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD GREATER_EQUAL 20)) - add_executable(example-traits-reflectcpp example/traits/reflect-cpp.cpp) - target_link_libraries(example-traits-reflectcpp PRIVATE jwt-cpp reflectcpp::reflectcpp) + add_executable(reflect-cpp reflect-cpp.cpp) + target_link_libraries(reflect-cpp PRIVATE jwt-cpp reflectcpp::reflectcpp) # If the project hasn't set a global standard, request C++20 just for this example. if(NOT DEFINED CMAKE_CXX_STANDARD) - target_compile_features(example-traits-reflectcpp PRIVATE cxx_std_20) + target_compile_features(reflect-cpp PRIVATE cxx_std_20) endif() elseif(TARGET reflectcpp::reflectcpp) # reflect-cpp is present but the user explicitly chose a pre-C++20 standard → skip the example. - message(STATUS "jwt-cpp: skipping example-traits-reflectcpp (CMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} < 20)") + message(STATUS "jwt-cpp: skipping reflect-cpp example (CMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} < 20)") endif() diff --git a/example/traits/reflect-cpp.cpp b/example/traits/reflect-cpp.cpp index 2bdfe4407..449df0e20 100644 --- a/example/traits/reflect-cpp.cpp +++ b/example/traits/reflect-cpp.cpp @@ -1,89 +1,83 @@ #include "jwt-cpp/jwt.h" -#include "jwt-cpp/traits/reflect-cpp/traits.h" // the trait adapter +#include "jwt-cpp/traits/reflect-cpp/traits.h" #include #include -#include int main() { - using R = jwt::traits::reflect_cpp; - - try { - // Build a small JSON object claim using the trait's value_type - R::object_type ns_obj; - ns_obj["api-x"] = R::value_type(1); // {"api-x": 1} - - // Build an array claim: [100, 20, 10] - R::array_type arr; - arr.emplace_back(100); - arr.emplace_back(20); - arr.emplace_back(10); - - // Wrap the native JSON values into jwt::basic_claim - jwt::basic_claim ns_claim(R::value_type(ns_obj)); - jwt::basic_claim arr_claim(R::value_type(arr)); - - // Create a token using the reflect-cpp trait (HS256) - const std::string secret = "secret"; - const auto now = std::chrono::system_clock::now(); - - const auto token = jwt::create() - .set_type("JWT") - .set_algorithm("HS256") - .set_issuer("auth0") - .set_subject("demo") - .set_audience("example") - .set_issued_at(now) - .set_not_before(now - std::chrono::seconds{5}) - .set_expires_at(now + std::chrono::minutes{10}) - .set_payload_claim("namespace", ns_claim) - .set_payload_claim("numbers", arr_claim) - .sign(jwt::algorithm::hs256{secret}); - - std::cout << "token: " << token << "\n"; - - // Decode with the same trait - const auto decoded = jwt::decode(token); - - // Access header/payload fields - std::cout << "alg: " << decoded.get_algorithm() << "\n"; - std::cout << "typ: " << decoded.get_type() << "\n"; - if (decoded.has_issuer()) std::cout << "iss: " << decoded.get_issuer() << "\n"; - - // Verify with the same trait - const auto verifier = jwt::verify().allow_algorithm(jwt::algorithm::hs256{secret}).with_issuer("auth0"); - - verifier.verify(decoded); - std::cout << "verification: OK\n"; - - // Read back our custom claims using the trait accessors - if (decoded.has_payload_claim("namespace")) { - const auto c = decoded.get_payload_claim("namespace"); - if (c.get_type() == jwt::json::type::object) { - const auto& obj = c.as_object(); // R::object_type (map-like) - const auto it = obj.find("api-x"); - if (it != obj.end()) { - // it->second is R::value_type - // print integer if present - // (rfl::Generic can be visited, but jwt-cpp trait exposes typed helpers) - // Keep it simple and rely on serialization through the trait: - std::cout << "namespace.api-x present\n"; - } - } + using sec = std::chrono::seconds; + using min = std::chrono::minutes; + using traits = jwt::traits::reflect_cpp; + using claim = jwt::basic_claim; + + // Load a raw JSON object into a claim (reflect-cpp: parse -> wrap) + claim from_raw_json; + { + traits::value_type value; + // Mirrors the nlohmann example’s JSON + const auto* const json_text = R"##({"api":{"array":[1,2,3],"null":null}})##"; + if (!traits::parse(value, json_text)) { + std::cerr << "failed to parse raw json\n"; + return 1; } + from_raw_json = claim{std::move(value)}; + } - if (decoded.has_payload_claim("numbers")) { - const auto c = decoded.get_payload_claim("numbers"); - if (c.get_type() == jwt::json::type::array) { - const auto& a = c.as_array(); // R::array_type (vector-like) - std::cout << "numbers.size: " << a.size() << "\n"; + claim::set_t list{"once", "twice"}; + std::vector big_numbers{727663072LL, 770979831LL, 427239169LL, 525936436LL}; + + // Build an array claim from the big_numbers vector + traits::array_type arr; + arr.reserve(big_numbers.size()); + for (auto val : big_numbers) { + arr.emplace_back(val); + } + claim array_claim{traits::value_type{arr}}; + claim strings_claim{list.begin(), list.end()}; + + 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", strings_claim) // <— fixed + .set_payload_claim("array", array_claim) + .set_payload_claim("object", from_raw_json) + .sign(jwt::algorithm::none{}); + + const auto decoded = jwt::decode(token); + + // Access payload /object/api/array using reflect-cpp's Result-returning get() + { + const auto obj_v = decoded.get_payload_claim("object").to_json(); // R::value_type + const auto obj = traits::as_object(obj_v); // rfl::Object + + if (auto api_res = obj.get("api"); api_res) { // rfl::Result + const auto api_obj = traits::as_object(api_res.value()); // nested object + + if (auto arr_res = api_obj.get("array"); arr_res) { + const auto& nested = traits::as_array(arr_res.value()); // vector-like + std::cout << "payload /object/api/array = " << rfl::json::write(nested) << '\n'; + } else { + std::cout << "payload /object/api/array missing\n"; } + } else { + std::cout << "payload /object/api missing\n"; } - - } catch (const std::exception& e) { - std::cerr << "error: " << e.what() << "\n"; - return 1; } + 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; } From d8f7c8f5b8cc54ccb3ecb2f04a09de719efb5d65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20Girayhan=20=C3=96zbay?= Date: Sun, 24 Aug 2025 19:39:18 +0100 Subject: [PATCH 13/17] reflect-cpp CI --- .github/actions/install/reflect-cpp/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/install/reflect-cpp/action.yml b/.github/actions/install/reflect-cpp/action.yml index 4af20b61f..fe8d0037d 100644 --- a/.github/actions/install/reflect-cpp/action.yml +++ b/.github/actions/install/reflect-cpp/action.yml @@ -16,4 +16,4 @@ runs: cd _thirdparty/reflect-cpp git checkout "${{ inputs.version }}" cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=20 - cmake --build build --target install -j + sudo cmake --build build --target install -j From 0803017991466462afb4c9450b2eb5e9c4ec3c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20Girayhan=20=C3=96zbay?= Date: Sun, 24 Aug 2025 20:27:40 +0100 Subject: [PATCH 14/17] reflect-cpp CI --- 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 a03feff98..1bfb0f514 100644 --- a/example/traits/CMakeLists.txt +++ b/example/traits/CMakeLists.txt @@ -39,7 +39,7 @@ endif() find_package(reflectcpp CONFIG) if(TARGET reflectcpp::reflectcpp AND (NOT DEFINED CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD GREATER_EQUAL 20)) add_executable(reflect-cpp reflect-cpp.cpp) - target_link_libraries(reflect-cpp PRIVATE jwt-cpp reflectcpp::reflectcpp) + target_link_libraries(reflect-cpp PRIVATE jwt-cpp::jwt-cpp reflectcpp::reflectcpp) # If the project hasn't set a global standard, request C++20 just for this example. if(NOT DEFINED CMAKE_CXX_STANDARD) From e4d7ce047f5035e1210dbb176f0cea808ca0f81b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20Girayhan=20=C3=96zbay?= Date: Tue, 26 Aug 2025 23:15:23 +0100 Subject: [PATCH 15/17] reflect-cpp CI --- .github/actions/install/reflect-cpp/action.yml | 4 +--- .github/workflows/cmake.yml | 17 +++++++---------- CMakeLists.txt | 2 -- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/.github/actions/install/reflect-cpp/action.yml b/.github/actions/install/reflect-cpp/action.yml index fe8d0037d..4a9875b39 100644 --- a/.github/actions/install/reflect-cpp/action.yml +++ b/.github/actions/install/reflect-cpp/action.yml @@ -10,10 +10,8 @@ runs: - shell: bash run: | set -eux - sudo apt-get update - sudo apt-get install -y ninja-build git clone https://github.com/getml/reflect-cpp.git _thirdparty/reflect-cpp cd _thirdparty/reflect-cpp git checkout "${{ inputs.version }}" - cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=20 + cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=20 sudo cmake --build build --target install -j diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 301ded858..6b8e3fddb 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -283,19 +283,16 @@ jobs: - name: Install toolchain & deps run: | sudo apt-get update - sudo apt-get install -y ninja-build g++ libssl-dev + sudo apt-get install -y g++ libssl-dev - name: Install reflect-cpp - run: | - git clone https://github.com/getml/reflect-cpp.git _thirdparty/reflect-cpp - cd _thirdparty/reflect-cpp - git checkout ca53b225bfc6887dd27e278820c34992cce37c6d - cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=20 - sudo cmake --build build --target install -j + uses: ./.github/actions/install/reflect-cpp + with: + version: ca53b225bfc6887dd27e278820c34992cce37c6d - name: Configure run: | - cmake -S . -B build -G Ninja \ + cmake -S . -B build \ -DJWT_BUILD_TESTS=ON \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_CXX_STANDARD=20 \ @@ -304,6 +301,6 @@ jobs: - name: Build tests run: cmake --build build --target jwt-cpp-test -j - - name: Run tests + - name: Run reflect-cpp tests working-directory: build - run: ctest --output-on-failure + run: ctest -j -R ReflectCppTest --output-on-failure diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a0bd6a60..cdd3533a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,8 +31,6 @@ cmake_dependent_option(JWT_EXTERNAL_NLOHMANN_JSON "Use find_package() to locate nlohman-json required for tests and examples" OFF "JWT_BUILD_EXAMPLES OR JWT_BUILD_TESTS" OFF) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE BOOL "Export compile commands" FORCE) - 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}) From 79ad997eb4f96d33765c73540777a41bac0d0c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20Girayhan=20=C3=96zbay?= Date: Thu, 28 Aug 2025 23:13:04 +0100 Subject: [PATCH 16/17] add reflect-cpp to traits.md --- docs/traits.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/traits.md b/docs/traits.md index caa12b8e5..844a0d961 100644 --- a/docs/traits.md +++ b/docs/traits.md @@ -11,12 +11,14 @@ For your convenience there are serval traits implementation which provide some p [![jsoncons][jsoncons]](https://github.com/danielaparker/jsoncons) [![boostjson][boostjson]](https://github.com/boostorg/json) [![jsoncpp][jsoncpp]](https://github.com/open-source-parsers/jsoncpp) +[![reflectcpp][reflectcpp]](https://github.com/getml/reflect-cpp) [picojson]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/traits/kazuho-picojson/shields.json [nlohmann]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/traits/nlohmann-json/shields.json [jsoncons]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/traits/danielaparker-jsoncons/shields.json [boostjson]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/traits/boost-json/shields.json [jsoncpp]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/traits/open-source-parsers-jsoncpp/shields.json +[reflectcpp]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/traits/getml-reflectcpp/shields.json In order to maintain compatibility, [picojson](https://github.com/kazuho/picojson) is still used to provide a specialized `jwt::claim` along with all helpers. Defining `JWT_DISABLE_PICOJSON` will remove this optional dependency. It's possible to directly include the traits defaults for the other JSON libraries. See the [traits examples](https://github.com/Thalhammer/jwt-cpp/tree/master/example/traits) for details. From 25372b60061faab6069da962dcf6d606882875b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ali=20Girayhan=20=C3=96zbay?= Date: Thu, 28 Aug 2025 23:21:57 +0100 Subject: [PATCH 17/17] add reflect-cpp to traits.md --- docs/traits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/traits.md b/docs/traits.md index 844a0d961..18c6dae8c 100644 --- a/docs/traits.md +++ b/docs/traits.md @@ -18,7 +18,7 @@ For your convenience there are serval traits implementation which provide some p [jsoncons]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/traits/danielaparker-jsoncons/shields.json [boostjson]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/traits/boost-json/shields.json [jsoncpp]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/traits/open-source-parsers-jsoncpp/shields.json -[reflectcpp]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/traits/getml-reflectcpp/shields.json +[reflectcpp]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Thalhammer/jwt-cpp/badges/traits/reflect-cpp/shields.json In order to maintain compatibility, [picojson](https://github.com/kazuho/picojson) is still used to provide a specialized `jwt::claim` along with all helpers. Defining `JWT_DISABLE_PICOJSON` will remove this optional dependency. It's possible to directly include the traits defaults for the other JSON libraries. See the [traits examples](https://github.com/Thalhammer/jwt-cpp/tree/master/example/traits) for details.