diff --git a/docs/traits.md b/docs/traits.md index 71eddb6af..a5681fba7 100644 --- a/docs/traits.md +++ b/docs/traits.md @@ -54,7 +54,8 @@ struct my_favorite_json_library_traits { static boolean_type as_boolean(const value_type &val); // serialization and parsing - static bool parse(value_type &val, string_type str); + template // could be the json string_type, or std::string_view for instance + static bool parse(value_type &val, const string_t& str); static string_type serialize(const value_type &val); // with no extra whitespace, padding or indentation }; ``` diff --git a/include/jwt-cpp/base.h b/include/jwt-cpp/base.h index 8e6fc311a..bf86be912 100644 --- a/include/jwt-cpp/base.h +++ b/include/jwt-cpp/base.h @@ -2,11 +2,11 @@ #define JWT_CPP_BASE_H #include -#include #include #include #include -#include + +#include "string_types.h" #ifdef __has_cpp_attribute #if __has_cpp_attribute(fallthrough) @@ -18,6 +18,11 @@ #define JWT_FALLTHROUGH #endif +#ifndef JWT_HAS_STRING_VIEW +#include +#include +#endif + namespace jwt { /** * \brief character maps when encoding and decoding @@ -30,19 +35,31 @@ namespace jwt { * base64-encoded as per [Section 4 of RFC4648](https://datatracker.ietf.org/doc/html/rfc4648#section-4) */ struct base64 { + +#define JWT_BASE_ALPHABET \ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', \ + 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', \ + 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' + +#ifdef JWT_HAS_STRING_VIEW + // From C++17 it's perfectly fine to have inline static variables. No ODR violation in this case. + static constexpr char kData[]{JWT_BASE_ALPHABET, '+', '/'}; + + static constexpr std::string_view kFill[]{"="}; +#else + // For pre C++17 standards, we need to use a method static const std::array& data() { - static constexpr std::array data{ - {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}}; - return data; + static constexpr std::array kData{{JWT_BASE_ALPHABET, '+', '/'}}; + return kData; } - static const std::string& fill() { - static std::string fill{"="}; - return fill; + + static const std::array& fill() { + static constexpr std::array kFill{"="}; + return kFill; } +#endif }; + /** * \brief valid list of character when working with [Base64URL](https://tools.ietf.org/html/rfc4648#section-5) * @@ -53,18 +70,24 @@ namespace jwt { * > [Section 5 of RFC 4648 RFC4648](https://tools.ietf.org/html/rfc4648#section-5), with all trailing '=' characters omitted */ struct base64url { + +#ifdef JWT_HAS_STRING_VIEW + static constexpr char kData[]{JWT_BASE_ALPHABET, '-', '_'}; + + static constexpr std::string_view kFill[]{"%3d"}; +#else + // For pre C++17 standards, we need to use a method static const std::array& data() { - static constexpr std::array data{ - {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'}}; - return data; + static constexpr std::array kData{{JWT_BASE_ALPHABET, '-', '_'}}; + return kData; } - static const std::string& fill() { - static std::string fill{"%3d"}; - return fill; + + static const std::array& fill() { + static constexpr std::array kFill{"%3d"}; + return kFill; } + +#endif }; namespace helper { /** @@ -74,26 +97,35 @@ namespace jwt { * This is useful in situations outside of JWT encoding/decoding and is provided as a helper */ struct base64url_percent_encoding { + +#ifdef JWT_HAS_STRING_VIEW + static constexpr char kData[]{JWT_BASE_ALPHABET, '-', '_'}; + + static constexpr std::string_view kFill[]{"%3D", "%3d"}; +#else + // For pre C++17 standards, we need to use a method static const std::array& data() { - static constexpr std::array data{ - {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'}}; - return data; + static constexpr std::array kData{{JWT_BASE_ALPHABET, '-', '_'}}; + return kData; } - static const std::initializer_list& fill() { - static std::initializer_list fill{"%3D", "%3d"}; - return fill; + + static const std::array& fill() { + static constexpr std::array kFill{"%3D", "%3d"}; + return kFill; } +#endif }; } // namespace helper - inline uint32_t index(const std::array& alphabet, char symbol) { - auto itr = std::find_if(alphabet.cbegin(), alphabet.cend(), [symbol](char c) { return c == symbol; }); - if (itr == alphabet.cend()) { throw std::runtime_error("Invalid input: not within alphabet"); } + template + inline uint32_t index(char_it alphabetBeg, char_it alphabetEnd, char symbol) { + if (symbol >= 'A' && symbol <= 'Z') { return static_cast(symbol - 'A'); } + if (symbol >= 'a' && symbol <= 'z') { return static_cast(26 + symbol - 'a'); } + if (symbol >= '0' && symbol <= '9') { return static_cast(52 + symbol - '0'); } + auto itr = std::find(std::next(alphabetBeg, 62U), alphabetEnd, symbol); + if (itr == alphabetEnd) { throw std::runtime_error("Invalid input: not within alphabet"); } - return std::distance(alphabet.cbegin(), itr); + return static_cast(std::distance(alphabetBeg, itr)); } } // namespace alphabet @@ -108,39 +140,44 @@ namespace jwt { size_t length = 0; padding() = default; - padding(size_t count, size_t length) : count(count), length(length) {} - padding operator+(const padding& p) { return padding(count + p.count, length + p.length); } + padding(size_t c, size_t l) : count(c), length(l) {} - friend bool operator==(const padding& lhs, const padding& rhs) { - return lhs.count == rhs.count && lhs.length == rhs.length; - } + padding operator+(const padding& p) const { return padding{count + p.count, length + p.length}; } }; - inline padding count_padding(const std::string& base, const std::vector& fills) { - for (const auto& fill : fills) { - if (base.size() < fill.size()) continue; - // Does the end of the input exactly match the fill pattern? - if (base.substr(base.size() - fill.size()) == fill) { - return padding{1, fill.length()} + - count_padding(base.substr(0, base.size() - fill.size()), fills); + inline std::size_t string_len(string_view str) { return str.size(); } + + template + padding count_padding(string_view base, str_input_it fillStart, str_input_it fillEnd) { + for (str_input_it fillIt = fillStart; fillIt != fillEnd; ++fillIt) { + std::size_t fillLen = string_len(*fillIt); + if (base.size() >= fillLen) { + std::size_t deltaLen = base.size() - fillLen; + // Does the end of the input exactly match the fill pattern? + if (base.substr(deltaLen) == *fillIt) { + return padding{1UL, fillLen} + count_padding(base.substr(0, deltaLen), fillStart, fillEnd); + } } } return {}; } - inline std::string encode(const std::string& bin, const std::array& alphabet, - const std::string& fill) { + inline std::string encode(string_view bin, const char* alphabet, string_view fill) { size_t size = bin.size(); std::string res; + res.reserve((4UL * size) / 3UL); + // clear incomplete bytes - size_t fast_size = size - size % 3; - for (size_t i = 0; i < fast_size;) { - uint32_t octet_a = static_cast(bin[i++]); - uint32_t octet_b = static_cast(bin[i++]); - uint32_t octet_c = static_cast(bin[i++]); + size_t mod = size % 3; + + size_t fast_size = size - mod; + for (size_t i = 0; i < fast_size; i += 3) { + uint32_t octet_a = static_cast(bin[i]); + uint32_t octet_b = static_cast(bin[i + 1]); + uint32_t octet_c = static_cast(bin[i + 2]); uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; @@ -152,8 +189,6 @@ namespace jwt { if (fast_size == size) return res; - size_t mod = size % 3; - uint32_t octet_a = fast_size < size ? static_cast(bin[fast_size++]) : 0; uint32_t octet_b = fast_size < size ? static_cast(bin[fast_size++]) : 0; uint32_t octet_c = fast_size < size ? static_cast(bin[fast_size++]) : 0; @@ -179,9 +214,10 @@ namespace jwt { return res; } - inline std::string decode(const std::string& base, const std::array& alphabet, - const std::vector& fill) { - const auto pad = count_padding(base, fill); + template + inline std::string decode(string_view base, char_it alphabetBeg, char_it alphabetEnd, + str_input_it fillStart, str_input_it fillEnd) { + const auto pad = count_padding(base, fillStart, fillEnd); if (pad.count > 2) throw std::runtime_error("Invalid input: too much fill"); const size_t size = base.size() - pad.length; @@ -191,7 +227,9 @@ namespace jwt { std::string res; res.reserve(out_size); - auto get_sextet = [&](size_t offset) { return alphabet::index(alphabet, base[offset]); }; + auto get_sextet = [&](size_t offset) { + return alphabet::index(alphabetBeg, alphabetEnd, base[offset]); + }; size_t fast_size = size - size % 4; for (size_t i = 0; i < fast_size;) { @@ -225,46 +263,64 @@ namespace jwt { return res; } - inline std::string decode(const std::string& base, const std::array& alphabet, - const std::string& fill) { - return decode(base, alphabet, std::vector{fill}); - } - - inline std::string pad(const std::string& base, const std::string& fill) { - std::string padding; - switch (base.size() % 4) { - case 1: padding += fill; JWT_FALLTHROUGH; - case 2: padding += fill; JWT_FALLTHROUGH; - case 3: padding += fill; JWT_FALLTHROUGH; + inline std::string pad(string_view base, string_view fill) { + std::string res(base); + switch (res.size() % 4) { + case 1: res += fill; JWT_FALLTHROUGH; + case 2: res += fill; JWT_FALLTHROUGH; + case 3: res += fill; JWT_FALLTHROUGH; default: break; } - - return base + padding; + return res; } - inline std::string trim(const std::string& base, const std::string& fill) { + inline std::string trim(string_view base, string_view fill) { auto pos = base.find(fill); - return base.substr(0, pos); + return static_cast(base.substr(0, pos)); } } // namespace details +#ifdef JWT_HAS_STRING_VIEW template - std::string encode(const std::string& bin) { - return details::encode(bin, T::data(), T::fill()); + std::string encode(string_view bin) { + return details::encode(bin, T::kData, T::kFill[0]); } template - std::string decode(const std::string& base) { - return details::decode(base, T::data(), T::fill()); + std::string decode(string_view base) { + return details::decode(base, std::begin(T::kData), std::end(T::kData), std::begin(T::kFill), + std::end(T::kFill)); } template - std::string pad(const std::string& base) { - return details::pad(base, T::fill()); + std::string pad(string_view base) { + return details::pad(base, T::kFill[0]); } template - std::string trim(const std::string& base) { - return details::trim(base, T::fill()); + std::string trim(string_view base) { + return details::trim(base, T::kFill[0]); } + +#else + template + std::string encode(string_view bin) { + return details::encode(bin, T::data().data(), T::fill()[0]); + } + template + std::string decode(string_view base) { + return details::decode(base, std::begin(T::data()), std::end(T::data()), std::begin(T::fill()), + std::end(T::fill())); + } + template + std::string pad(string_view base) { + return details::pad(base, T::fill()[0]); + } + template + std::string trim(string_view base) { + return details::trim(base, T::fill()[0]); + } +#endif } // namespace base } // namespace jwt +#undef JWT_BASE_ALPHABET + #endif diff --git a/include/jwt-cpp/jwt.h b/include/jwt-cpp/jwt.h index b2b998a2e..67296e0ad 100644 --- a/include/jwt-cpp/jwt.h +++ b/include/jwt-cpp/jwt.h @@ -12,6 +12,8 @@ #include "base.h" #endif +#include "string_types.h" + #include #include #include @@ -470,7 +472,7 @@ namespace jwt { return std::unique_ptr(BIO_new(BIO_s_mem()), BIO_free_all); } - inline std::unique_ptr make_mem_buf_bio(const std::string& data) { + inline std::unique_ptr make_mem_buf_bio(string_view data) { return std::unique_ptr( #if OPENSSL_VERSION_NUMBER <= 0x10100003L BIO_new_mem_buf(const_cast(data.data()), static_cast(data.size())), BIO_free_all @@ -496,8 +498,7 @@ namespace jwt { * \param pw Password used to decrypt certificate (leave empty if not encrypted) * \param ec error_code for error_detection (gets cleared if no error occurred) */ - inline std::string extract_pubkey_from_cert(const std::string& certstr, const std::string& pw, - std::error_code& ec) { + inline std::string extract_pubkey_from_cert(string_view certstr, const std::string& pw, std::error_code& ec) { ec.clear(); auto certbio = make_mem_buf_bio(certstr); auto keybio = make_mem_buf_bio(); @@ -537,7 +538,7 @@ namespace jwt { * \param pw Password used to decrypt certificate (leave empty if not encrypted) * \throw rsa_exception if an error occurred */ - inline std::string extract_pubkey_from_cert(const std::string& certstr, const std::string& pw = "") { + inline std::string extract_pubkey_from_cert(string_view certstr, const std::string& pw = "") { std::error_code ec; auto res = extract_pubkey_from_cert(certstr, pw, ec); error::throw_if_error(ec); @@ -550,14 +551,15 @@ namespace jwt { * \param cert_der_str String containing the certificate encoded as base64 DER * \param ec error_code for error_detection (gets cleared if no error occurs) */ - inline std::string convert_der_to_pem(const std::string& cert_der_str, std::error_code& ec) { + inline std::string convert_der_to_pem(string_view cert_der_str, std::error_code& ec) { ec.clear(); - auto c_str = reinterpret_cast(cert_der_str.c_str()); + auto c_str = reinterpret_cast(cert_der_str.data()); std::unique_ptr cert( d2i_X509(NULL, &c_str, static_cast(cert_der_str.size())), X509_free); auto certbio = make_mem_buf_bio(); + if (!cert || !certbio) { ec = error::rsa_error::create_mem_bio_failed; return {}; @@ -574,7 +576,6 @@ namespace jwt { ec = error::rsa_error::convert_to_pem_failed; return {}; } - return {ptr, static_cast(len)}; } @@ -584,7 +585,7 @@ namespace jwt { * This is useful when using with JWKs as x5c claim is encoded as base64 DER. More info * (here)[https://tools.ietf.org/html/rfc7517#section-4.7] * - * \tparam Decode is callabled, taking a string_type and returns a string_type. + * \tparam Decode is a callable, taking a string_type and returns a string_type. * It should ensure the padding of the input and then base64 decode and return * the results. * @@ -593,8 +594,7 @@ namespace jwt { * \param ec error_code for error_detection (gets cleared if no error occurs) */ template - std::string convert_base64_der_to_pem(const std::string& cert_base64_der_str, Decode decode, - std::error_code& ec) { + std::string convert_base64_der_to_pem(string_view cert_base64_der_str, Decode decode, std::error_code& ec) { ec.clear(); const auto decoded_str = decode(cert_base64_der_str); return convert_der_to_pem(decoded_str, ec); @@ -606,7 +606,7 @@ namespace jwt { * This is useful when using with JWKs as x5c claim is encoded as base64 DER. More info * (here)[https://tools.ietf.org/html/rfc7517#section-4.7] * - * \tparam Decode is callabled, taking a string_type and returns a string_type. + * \tparam Decode is a callable, taking a string_type and returns a string_type. * It should ensure the padding of the input and then base64 decode and return * the results. * @@ -615,7 +615,7 @@ namespace jwt { * \throw rsa_exception if an error occurred */ template - std::string convert_base64_der_to_pem(const std::string& cert_base64_der_str, Decode decode) { + std::string convert_base64_der_to_pem(string_view cert_base64_der_str, Decode decode) { std::error_code ec; auto res = convert_base64_der_to_pem(cert_base64_der_str, std::move(decode), ec); error::throw_if_error(ec); @@ -626,10 +626,9 @@ namespace jwt { * \brief Convert the certificate provided as DER to PEM. * * \param cert_der_str String containing the DER certificate - * \param decode The function to decode the cert * \throw rsa_exception if an error occurred */ - inline std::string convert_der_to_pem(const std::string& cert_der_str) { + inline std::string convert_der_to_pem(string_view cert_der_str) { std::error_code ec; auto res = convert_der_to_pem(cert_der_str, ec); error::throw_if_error(ec); @@ -646,11 +645,11 @@ namespace jwt { * \param cert_base64_der_str String containing the certificate encoded as base64 DER * \param ec error_code for error_detection (gets cleared if no error occurs) */ - inline std::string convert_base64_der_to_pem(const std::string& cert_base64_der_str, std::error_code& ec) { - auto decode = [](const std::string& token) { - return base::decode(base::pad(token)); - }; - return convert_base64_der_to_pem(cert_base64_der_str, std::move(decode), ec); + inline std::string convert_base64_der_to_pem(string_view cert_base64_der_str, std::error_code& ec) { + return convert_base64_der_to_pem( + cert_base64_der_str, + [](string_view token) { return base::decode(base::pad(token)); }, + ec); } /** @@ -662,7 +661,7 @@ namespace jwt { * \param cert_base64_der_str String containing the certificate encoded as base64 DER * \throw rsa_exception if an error occurred */ - inline std::string convert_base64_der_to_pem(const std::string& cert_base64_der_str) { + inline std::string convert_base64_der_to_pem(string_view cert_base64_der_str) { std::error_code ec; auto res = convert_base64_der_to_pem(cert_base64_der_str, ec); error::throw_if_error(ec); @@ -678,7 +677,7 @@ namespace jwt { * \param password Password used to decrypt certificate (leave empty if not encrypted) * \param ec error_code for error_detection (gets cleared if no error occurs) */ - inline evp_pkey_handle load_public_key_from_string(const std::string& key, const std::string& password, + inline evp_pkey_handle load_public_key_from_string(string_view key, const std::string& password, std::error_code& ec) { ec.clear(); auto pubkey_bio = make_mem_buf_bio(); @@ -704,7 +703,7 @@ namespace jwt { evp_pkey_handle pkey(PEM_read_bio_PUBKEY( pubkey_bio.get(), nullptr, nullptr, - (void*)password.data())); // NOLINT(google-readability-casting) requires `const_cast` + (void*)password.c_str())); // NOLINT(google-readability-casting) requires `const_cast` if (!pkey) ec = error::rsa_error::load_key_bio_read; return pkey; } @@ -718,7 +717,7 @@ namespace jwt { * \param password Password used to decrypt certificate or key (leave empty if not encrypted) * \throw rsa_exception if an error occurred */ - inline evp_pkey_handle load_public_key_from_string(const std::string& key, const std::string& password = "") { + inline evp_pkey_handle load_public_key_from_string(string_view key, const std::string& password = "") { std::error_code ec; auto res = load_public_key_from_string(key, password, ec); error::throw_if_error(ec); @@ -732,7 +731,7 @@ namespace jwt { * \param password Password used to decrypt key (leave empty if not encrypted) * \param ec error_code for error_detection (gets cleared if no error occurs) */ - inline evp_pkey_handle load_private_key_from_string(const std::string& key, const std::string& password, + inline evp_pkey_handle load_private_key_from_string(string_view key, const std::string& password, std::error_code& ec) { auto privkey_bio = make_mem_buf_bio(); if (!privkey_bio) { @@ -757,7 +756,7 @@ namespace jwt { * \param password Password used to decrypt key (leave empty if not encrypted) * \throw rsa_exception if an error occurred */ - inline evp_pkey_handle load_private_key_from_string(const std::string& key, const std::string& password = "") { + inline evp_pkey_handle load_private_key_from_string(string_view key, const std::string& password = "") { std::error_code ec; auto res = load_private_key_from_string(key, password, ec); error::throw_if_error(ec); @@ -773,7 +772,7 @@ namespace jwt { * \param password Password used to decrypt certificate (leave empty if not encrypted) * \param ec error_code for error_detection (gets cleared if no error occurs) */ - inline evp_pkey_handle load_public_ec_key_from_string(const std::string& key, const std::string& password, + inline evp_pkey_handle load_public_ec_key_from_string(string_view key, const std::string& password, std::error_code& ec) { ec.clear(); auto pubkey_bio = make_mem_buf_bio(); @@ -799,7 +798,7 @@ namespace jwt { evp_pkey_handle pkey(PEM_read_bio_PUBKEY( pubkey_bio.get(), nullptr, nullptr, - (void*)password.data())); // NOLINT(google-readability-casting) requires `const_cast` + (void*)password.c_str())); // NOLINT(google-readability-casting) requires `const_cast` if (!pkey) ec = error::ecdsa_error::load_key_bio_read; return pkey; } @@ -813,8 +812,7 @@ namespace jwt { * \param password Password used to decrypt certificate or key (leave empty if not encrypted) * \throw ecdsa_exception if an error occurred */ - inline evp_pkey_handle load_public_ec_key_from_string(const std::string& key, - const std::string& password = "") { + inline evp_pkey_handle load_public_ec_key_from_string(string_view key, const std::string& password = "") { std::error_code ec; auto res = load_public_ec_key_from_string(key, password, ec); error::throw_if_error(ec); @@ -828,7 +826,7 @@ namespace jwt { * \param password Password used to decrypt key (leave empty if not encrypted) * \param ec error_code for error_detection (gets cleared if no error occurs) */ - inline evp_pkey_handle load_private_ec_key_from_string(const std::string& key, const std::string& password, + inline evp_pkey_handle load_private_ec_key_from_string(string_view key, const std::string& password, std::error_code& ec) { auto privkey_bio = make_mem_buf_bio(); if (!privkey_bio) { @@ -853,8 +851,7 @@ namespace jwt { * \param password Password used to decrypt key (leave empty if not encrypted) * \throw ecdsa_exception if an error occurred */ - inline evp_pkey_handle load_private_ec_key_from_string(const std::string& key, - const std::string& password = "") { + inline evp_pkey_handle load_private_ec_key_from_string(string_view key, const std::string& password = "") { std::error_code ec; auto res = load_private_ec_key_from_string(key, password, ec); error::throw_if_error(ec); @@ -876,7 +873,7 @@ namespace jwt { #endif { std::string res(BN_num_bytes(bn), '\0'); - BN_bn2bin(bn, (unsigned char*)res.data()); // NOLINT(google-readability-casting) requires `const_cast` + BN_bn2bin(bn, reinterpret_cast(&res[0])); return res; } /** @@ -884,7 +881,7 @@ namespace jwt { * \param raw String to convert * \return BIGNUM representation */ - inline std::unique_ptr raw2bn(const std::string& raw) { + inline std::unique_ptr raw2bn(string_view raw) { return std::unique_ptr( BN_bin2bn(reinterpret_cast(raw.data()), static_cast(raw.size()), nullptr), BN_free); @@ -910,7 +907,7 @@ namespace jwt { /** * \brief Return an empty string */ - std::string sign(const std::string& /*unused*/, std::error_code& ec) const { + std::string sign(string_view /*unused*/, std::error_code& ec) const { ec.clear(); return {}; } @@ -921,12 +918,16 @@ namespace jwt { * \param signature Signature data to verify * \param ec error_code filled with details about the error */ - void verify(const std::string& /*unused*/, const std::string& signature, std::error_code& ec) const { + void verify(string_view /*unused*/, string_view signature, std::error_code& ec) const { ec.clear(); if (!signature.empty()) { ec = error::signature_verification_error::invalid_signature; } } /// Get algorithm name +#ifdef JWT_HAS_STRING_VIEW + std::string_view name() const { return "none"; } +#else std::string name() const { return "none"; } +#endif }; /** * \brief Base class for HMAC family of algorithms @@ -938,24 +939,24 @@ namespace jwt { * \param md Pointer to hash function * \param name Name of the algorithm */ - hmacsha(std::string key, const EVP_MD* (*md)(), std::string name) - : secret(std::move(key)), md(md), alg_name(std::move(name)) {} + hmacsha(string_view key, const EVP_MD* (*md)(), std::string name) + : secret(key), md(md), alg_name(std::move(name)) {} /** * Sign jwt data * \param data The data to sign * \param ec error_code filled with details on error * \return HMAC signature for the given data */ - std::string sign(const std::string& data, std::error_code& ec) const { + std::string sign(string_view data, std::error_code& ec) const { ec.clear(); std::string res(static_cast(EVP_MAX_MD_SIZE), '\0'); auto len = static_cast(res.size()); if (HMAC(md(), secret.data(), static_cast(secret.size()), reinterpret_cast(data.data()), static_cast(data.size()), - (unsigned char*)res.data(), // NOLINT(google-readability-casting) requires `const_cast` - &len) == nullptr) { + reinterpret_cast(&res[0]), &len) == nullptr) { ec = error::signature_generation_error::hmac_failed; - return {}; + res.clear(); + return res; } res.resize(len); return res; @@ -966,7 +967,7 @@ namespace jwt { * \param signature Signature provided by the jwt * \param ec Filled with details about failure. */ - void verify(const std::string& data, const std::string& signature, std::error_code& ec) const { + void verify(string_view data, string_view signature, std::error_code& ec) const { ec.clear(); auto res = sign(data, ec); if (ec) return; @@ -984,15 +985,15 @@ namespace jwt { * Returns the algorithm name provided to the constructor * \return algorithm's name */ - std::string name() const { return alg_name; } + string_view name() const { return alg_name; } private: - /// HMAC secrect - const std::string secret; + /// HMAC secret + std::string secret; /// HMAC hash generator const EVP_MD* (*md)(); /// algorithm's name - const std::string alg_name; + std::string alg_name; }; /** * \brief Base class for RSA family of algorithms @@ -1007,7 +1008,7 @@ namespace jwt { * \param md Pointer to hash function * \param name Name of the algorithm */ - rsa(const std::string& public_key, const std::string& private_key, const std::string& public_key_password, + rsa(string_view public_key, string_view private_key, const std::string& public_key_password, const std::string& private_key_password, const EVP_MD* (*md)(), std::string name) : md(md), alg_name(std::move(name)) { if (!private_key.empty()) { @@ -1023,7 +1024,7 @@ namespace jwt { * \param ec error_code filled with details on error * \return RSA signature for the given data */ - std::string sign(const std::string& data, std::error_code& ec) const { + std::string sign(string_view data, std::error_code& ec) const { ec.clear(); auto ctx = helper::make_evp_md_ctx(); if (!ctx) { @@ -1034,17 +1035,16 @@ namespace jwt { ec = error::signature_generation_error::signinit_failed; return {}; } - - std::string res(EVP_PKEY_size(pkey.get()), '\0'); - unsigned int len = 0; - if (!EVP_SignUpdate(ctx.get(), data.data(), data.size())) { ec = error::signature_generation_error::signupdate_failed; return {}; } - if (EVP_SignFinal(ctx.get(), (unsigned char*)res.data(), &len, pkey.get()) == 0) { + std::string res(EVP_PKEY_size(pkey.get()), '\0'); + unsigned int len = 0; + if (EVP_SignFinal(ctx.get(), reinterpret_cast(&res[0]), &len, pkey.get()) == 0) { ec = error::signature_generation_error::signfinal_failed; - return {}; + res.clear(); + return res; } res.resize(len); @@ -1056,7 +1056,7 @@ namespace jwt { * \param signature Signature provided by the jwt * \param ec Filled with details on failure */ - void verify(const std::string& data, const std::string& signature, std::error_code& ec) const { + void verify(string_view data, string_view signature, std::error_code& ec) const { ec.clear(); auto ctx = helper::make_evp_md_ctx(); if (!ctx) { @@ -1082,7 +1082,7 @@ namespace jwt { * Returns the algorithm name provided to the constructor * \return algorithm's name */ - std::string name() const { return alg_name; } + string_view name() const { return alg_name; } private: /// OpenSSL structure containing converted keys @@ -1090,7 +1090,7 @@ namespace jwt { /// Hash generator const EVP_MD* (*md)(); /// algorithm's name - const std::string alg_name; + std::string alg_name; }; /** * \brief Base class for ECDSA family of algorithms @@ -1107,7 +1107,7 @@ namespace jwt { * \param name Name of the algorithm * \param siglen The bit length of the signature */ - ecdsa(const std::string& public_key, const std::string& private_key, const std::string& public_key_password, + ecdsa(string_view public_key, string_view private_key, const std::string& public_key_password, const std::string& private_key_password, const EVP_MD* (*md)(), std::string name, size_t siglen) : md(md), alg_name(std::move(name)), signature_length(siglen) { if (!private_key.empty()) { @@ -1132,7 +1132,7 @@ namespace jwt { * \param ec error_code filled with details on error * \return ECDSA signature for the given data */ - std::string sign(const std::string& data, std::error_code& ec) const { + std::string sign(string_view data, std::error_code& ec) const { ec.clear(); auto ctx = helper::make_evp_md_ctx(); if (!ctx) { @@ -1154,9 +1154,10 @@ namespace jwt { return {}; } std::string res(len, '\0'); - if (!EVP_DigestSignFinal(ctx.get(), (unsigned char*)res.data(), &len)) { + if (!EVP_DigestSignFinal(ctx.get(), reinterpret_cast(&res[0]), &len)) { ec = error::signature_generation_error::signfinal_failed; - return {}; + res.clear(); + return res; } res.resize(len); @@ -1169,7 +1170,7 @@ namespace jwt { * \param signature Signature provided by the jwt * \param ec Filled with details on error */ - void verify(const std::string& data, const std::string& signature, std::error_code& ec) const { + void verify(string_view data, string_view signature, std::error_code& ec) const { ec.clear(); std::string der_signature = p1363_to_der_signature(signature, ec); if (ec) { return; } @@ -1208,7 +1209,7 @@ namespace jwt { * Returns the algorithm name provided to the constructor * \return algorithm's name */ - std::string name() const { return alg_name; } + string_view name() const { return alg_name; } private: static void check_public_key(EVP_PKEY* pkey) { @@ -1241,7 +1242,7 @@ namespace jwt { #endif } - std::string der_to_p1363_signature(const std::string& der_signature, std::error_code& ec) const { + std::string der_to_p1363_signature(string_view der_signature, std::error_code& ec) const { const unsigned char* possl_signature = reinterpret_cast(der_signature.data()); std::unique_ptr sig( d2i_ECDSA_SIG(nullptr, &possl_signature, static_cast(der_signature.length())), @@ -1269,12 +1270,13 @@ namespace jwt { return rr + rs; } - std::string p1363_to_der_signature(const std::string& signature, std::error_code& ec) const { + std::string p1363_to_der_signature(string_view signature, std::error_code& ec) const { ec.clear(); auto r = helper::raw2bn(signature.substr(0, signature.size() / 2)); auto s = helper::raw2bn(signature.substr(signature.size() / 2)); ECDSA_SIG* psig; + std::string der_signature; #ifdef JWT_OPENSSL_1_0_0 ECDSA_SIG sig; sig.r = r.get(); @@ -1284,7 +1286,7 @@ namespace jwt { std::unique_ptr sig(ECDSA_SIG_new(), ECDSA_SIG_free); if (!sig) { ec = error::signature_verification_error::create_context_failed; - return {}; + return der_signature; } ECDSA_SIG_set0(sig.get(), r.release(), s.release()); psig = sig.get(); @@ -1293,14 +1295,15 @@ namespace jwt { int length = i2d_ECDSA_SIG(psig, nullptr); if (length < 0) { ec = error::signature_verification_error::signature_encoding_failed; - return {}; + return der_signature; } - std::string der_signature(length, '\0'); + der_signature.assign(length, '\0'); unsigned char* psbuffer = (unsigned char*)der_signature.data(); length = i2d_ECDSA_SIG(psig, &psbuffer); if (length < 0) { ec = error::signature_verification_error::signature_encoding_failed; - return {}; + der_signature.clear(); + return der_signature; } der_signature.resize(length); return der_signature; @@ -1311,9 +1314,9 @@ namespace jwt { /// Hash generator function const EVP_MD* (*md)(); /// algorithm's name - const std::string alg_name; + std::string alg_name; /// Length of the resulting signature - const size_t signature_length; + size_t signature_length; }; #if !defined(JWT_OPENSSL_1_0_0) && !defined(JWT_OPENSSL_1_1_0) @@ -1336,7 +1339,7 @@ namespace jwt { * to decrypt private key pem. * \param name Name of the algorithm */ - eddsa(const std::string& public_key, const std::string& private_key, const std::string& public_key_password, + eddsa(string_view public_key, string_view private_key, const std::string& public_key_password, const std::string& private_key_password, std::string name) : alg_name(std::move(name)) { if (!private_key.empty()) { @@ -1352,7 +1355,7 @@ namespace jwt { * \param ec error_code filled with details on error * \return EdDSA signature for the given data */ - std::string sign(const std::string& data, std::error_code& ec) const { + std::string sign(string_view data, std::error_code& ec) const { ec.clear(); auto ctx = helper::make_evp_md_ctx(); if (!ctx) { @@ -1376,17 +1379,20 @@ namespace jwt { 1) { std::cout << ERR_error_string(ERR_get_error(), NULL) << std::endl; ec = error::signature_generation_error::signupdate_failed; - return {}; + res.clear(); + return res; } if (EVP_DigestSignFinal(ctx.get(), reinterpret_cast(&res[0]), &len) != 1) { ec = error::signature_generation_error::signfinal_failed; - return {}; + res.clear(); + return res; } #else if (EVP_DigestSign(ctx.get(), reinterpret_cast(&res[0]), &len, reinterpret_cast(data.data()), data.size()) != 1) { ec = error::signature_generation_error::signfinal_failed; - return {}; + res.clear(); + return res; } #endif @@ -1400,7 +1406,7 @@ namespace jwt { * \param signature Signature provided by the jwt * \param ec Filled with details on error */ - void verify(const std::string& data, const std::string& signature, std::error_code& ec) const { + void verify(string_view data, string_view signature, std::error_code& ec) const { ec.clear(); auto ctx = helper::make_evp_md_ctx(); if (!ctx) { @@ -1439,13 +1445,13 @@ namespace jwt { * Returns the algorithm name provided to the constructor * \return algorithm's name */ - std::string name() const { return alg_name; } + string_view name() const { return alg_name; } private: /// OpenSSL struct containing keys helper::evp_pkey_handle pkey; /// algorithm's name - const std::string alg_name; + std::string alg_name; }; #endif /** @@ -1461,7 +1467,7 @@ namespace jwt { * \param md Pointer to hash function * \param name Name of the algorithm */ - pss(const std::string& public_key, const std::string& private_key, const std::string& public_key_password, + pss(string_view public_key, string_view private_key, const std::string& public_key_password, const std::string& private_key_password, const EVP_MD* (*md)(), std::string name) : md(md), alg_name(std::move(name)) { if (!private_key.empty()) { @@ -1478,7 +1484,7 @@ namespace jwt { * \param ec error_code filled with details on error * \return ECDSA signature for the given data */ - std::string sign(const std::string& data, std::error_code& ec) const { + std::string sign(string_view data, std::error_code& ec) const { ec.clear(); auto md_ctx = helper::make_evp_md_ctx(); if (!md_ctx) { @@ -1508,13 +1514,11 @@ namespace jwt { } size_t size = EVP_PKEY_size(pkey.get()); - std::string res(size, 0x00); - if (EVP_DigestSignFinal( - md_ctx.get(), - (unsigned char*)res.data(), // NOLINT(google-readability-casting) requires `const_cast` - &size) <= 0) { + std::string res(size, '\0'); + if (EVP_DigestSignFinal(md_ctx.get(), reinterpret_cast(&res[0]), &size) <= 0) { ec = error::signature_generation_error::signfinal_failed; - return {}; + res.clear(); + return res; } return res; @@ -1526,7 +1530,7 @@ namespace jwt { * \param signature Signature provided by the jwt * \param ec Filled with error details */ - void verify(const std::string& data, const std::string& signature, std::error_code& ec) const { + void verify(string_view data, string_view signature, std::error_code& ec) const { ec.clear(); auto md_ctx = helper::make_evp_md_ctx(); @@ -1565,7 +1569,7 @@ namespace jwt { * Returns the algorithm name provided to the constructor * \return algorithm's name */ - std::string name() const { return alg_name; } + string_view name() const { return alg_name; } private: /// OpenSSL structure containing keys @@ -1573,7 +1577,7 @@ namespace jwt { /// Hash generator function const EVP_MD* (*md)(); /// algorithm's name - const std::string alg_name; + std::string alg_name; }; /** @@ -1584,7 +1588,7 @@ namespace jwt { * Construct new instance of algorithm * \param key HMAC signing key */ - explicit hs256(std::string key) : hmacsha(std::move(key), EVP_sha256, "HS256") {} + explicit hs256(string_view key) : hmacsha(key, EVP_sha256, "HS256") {} }; /** * HS384 algorithm @@ -1594,7 +1598,7 @@ namespace jwt { * Construct new instance of algorithm * \param key HMAC signing key */ - explicit hs384(std::string key) : hmacsha(std::move(key), EVP_sha384, "HS384") {} + explicit hs384(string_view key) : hmacsha(key, EVP_sha384, "HS384") {} }; /** * HS512 algorithm @@ -1604,7 +1608,7 @@ namespace jwt { * Construct new instance of algorithm * \param key HMAC signing key */ - explicit hs512(std::string key) : hmacsha(std::move(key), EVP_sha512, "HS512") {} + explicit hs512(string_view key) : hmacsha(key, EVP_sha512, "HS512") {} }; /** * RS256 algorithm @@ -1617,7 +1621,7 @@ namespace jwt { * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password to decrypt private key pem. */ - explicit rs256(const std::string& public_key, const std::string& private_key = "", + explicit rs256(string_view public_key, string_view private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : rsa(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "RS256") {} }; @@ -1632,7 +1636,7 @@ namespace jwt { * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password to decrypt private key pem. */ - explicit rs384(const std::string& public_key, const std::string& private_key = "", + explicit rs384(string_view public_key, string_view private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : rsa(public_key, private_key, public_key_password, private_key_password, EVP_sha384, "RS384") {} }; @@ -1647,7 +1651,7 @@ namespace jwt { * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password to decrypt private key pem. */ - explicit rs512(const std::string& public_key, const std::string& private_key = "", + explicit rs512(string_view public_key, string_view private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : rsa(public_key, private_key, public_key_password, private_key_password, EVP_sha512, "RS512") {} }; @@ -1664,7 +1668,7 @@ namespace jwt { * \param private_key_password Password * to decrypt private key pem. */ - explicit es256(const std::string& public_key, const std::string& private_key = "", + explicit es256(string_view public_key, string_view private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : ecdsa(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "ES256", 64) {} }; @@ -1681,7 +1685,7 @@ namespace jwt { * \param private_key_password Password * to decrypt private key pem. */ - explicit es384(const std::string& public_key, const std::string& private_key = "", + explicit es384(string_view public_key, string_view private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : ecdsa(public_key, private_key, public_key_password, private_key_password, EVP_sha384, "ES384", 96) {} }; @@ -1698,7 +1702,7 @@ namespace jwt { * \param private_key_password Password * to decrypt private key pem. */ - explicit es512(const std::string& public_key, const std::string& private_key = "", + explicit es512(string_view public_key, string_view private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : ecdsa(public_key, private_key, public_key_password, private_key_password, EVP_sha512, "ES512", 132) {} }; @@ -1714,7 +1718,7 @@ namespace jwt { * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password to decrypt private key pem. */ - explicit es256k(const std::string& public_key, const std::string& private_key = "", + explicit es256k(string_view public_key, string_view private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : ecdsa(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "ES256K", 64) {} }; @@ -1737,7 +1741,7 @@ namespace jwt { * \param private_key_password Password * to decrypt private key pem. */ - explicit ed25519(const std::string& public_key, const std::string& private_key = "", + explicit ed25519(string_view public_key, string_view private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : eddsa(public_key, private_key, public_key_password, private_key_password, "EdDSA") {} }; @@ -1759,7 +1763,7 @@ namespace jwt { * \param private_key_password Password * to decrypt private key pem. */ - explicit ed448(const std::string& public_key, const std::string& private_key = "", + explicit ed448(string_view public_key, string_view private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : eddsa(public_key, private_key, public_key_password, private_key_password, "EdDSA") {} }; @@ -1776,7 +1780,7 @@ namespace jwt { * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password to decrypt private key pem. */ - explicit ps256(const std::string& public_key, const std::string& private_key = "", + explicit ps256(string_view public_key, string_view private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "PS256") {} }; @@ -1791,7 +1795,7 @@ namespace jwt { * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password to decrypt private key pem. */ - explicit ps384(const std::string& public_key, const std::string& private_key = "", + explicit ps384(string_view public_key, string_view private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha384, "PS384") {} }; @@ -1806,7 +1810,7 @@ namespace jwt { * \param public_key_password Password to decrypt public key pem. * \param private_key_password Password to decrypt private key pem. */ - explicit ps512(const std::string& public_key, const std::string& private_key = "", + explicit ps512(string_view public_key, string_view private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "") : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha512, "PS512") {} }; @@ -1970,10 +1974,9 @@ namespace jwt { struct is_subcription_operator_signature< object_type, string_type, void_t().operator[](std::declval()))>> : std::true_type { - // TODO(prince-chrismc): I am not convienced this is meaningful anymore + // TODO(prince-chrismc): I am not convinced this is meaningful anymore static_assert( - value, - "object_type must implementate the subscription operator '[]' taking string_type as an argument"); + value, "object_type must implement the subscription operator '[]' taking string_type as an argument"); }; template @@ -2106,12 +2109,22 @@ namespace jwt { std::is_constructible::value, "string_type must be a std::string, convertible to a std::string, or construct a std::string."); + template + struct is_string_view_compatible { +#ifdef JWT_HAS_STRING_VIEW + static constexpr bool value = std::is_constructible::value && + std::is_convertible::value; +#else + static constexpr bool value = false; +#endif + }; + static_assert( details::is_valid_json_types::value, - "must staisfy json container requirements"); + "must satisfy json container requirements"); static_assert(details::is_valid_traits::value, "traits must satisfy requirements"); typename json_traits::value_type val; @@ -2126,7 +2139,11 @@ namespace jwt { basic_claim& operator=(basic_claim&&) = default; ~basic_claim() = default; - JWT_CLAIM_EXPLICIT basic_claim(typename json_traits::string_type s) : val(std::move(s)) {} + template::value, + bool>::type = true> + JWT_CLAIM_EXPLICIT basic_claim(string_type&& s) : val(std::forward(s)) {} + JWT_CLAIM_EXPLICIT basic_claim(const date& d) : val(typename json_traits::integer_type(std::chrono::system_clock::to_time_t(d))) {} JWT_CLAIM_EXPLICIT basic_claim(typename json_traits::array_type a) : val(std::move(a)) {} @@ -2247,12 +2264,9 @@ namespace jwt { using const_iterator = typename json_traits::object_type::const_iterator; map_of_claims() = default; - map_of_claims(const map_of_claims&) = default; - map_of_claims(map_of_claims&&) = default; - map_of_claims& operator=(const map_of_claims&) = default; - map_of_claims& operator=(map_of_claims&&) = default; - map_of_claims(typename json_traits::object_type json) : claims(std::move(json)) {} + map_of_claims(const typename json_traits::object_type& json) : claims(json) {} + map_of_claims(typename json_traits::object_type&& json) : claims(std::move(json)) {} iterator begin() { return claims.begin(); } iterator end() { return claims.end(); } @@ -2269,18 +2283,20 @@ namespace jwt { * \param str JSON data to be parse as an object * \return content as JSON object */ - static typename json_traits::object_type parse_claims(const typename json_traits::string_type& str) { + template + static typename json_traits::object_type parse_claims(const string_type& str) { typename json_traits::value_type val; if (!json_traits::parse(val, str)) throw error::invalid_json_exception(); return json_traits::as_object(val); - }; + } /** * Check if a claim is present in the map * \return true if claim was present, false otherwise */ - bool has_claim(const typename json_traits::string_type& name) const noexcept { + template + bool has_claim(const string_type& name) const noexcept { return claims.count(name) != 0; } @@ -2291,7 +2307,8 @@ namespace jwt { * \return Requested claim * \throw jwt::error::claim_not_present_exception if the claim was not present */ - basic_claim_t get_claim(const typename json_traits::string_type& name) const { + template + basic_claim_t get_claim(const string_type& name) const { if (!has_claim(name)) throw error::claim_not_present_exception(); return basic_claim_t{claims.at(name)}; } @@ -2533,7 +2550,7 @@ namespace jwt { /** * \brief Parses a given token * - * \tparam Decode is callabled, taking a string_type and returns a string_type. + * \tparam Decode is a callable, 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 The token to parse @@ -2635,16 +2652,73 @@ namespace jwt { typename json_traits::object_type header_claims; typename json_traits::object_type payload_claims; + private: + template::value, + bool>::type = true> + static typename json_traits::value_type json_value_from_str(string_type&& s) { + return typename json_traits::value_type(std::forward(s)); + } + + template::value, + bool>::type = true> + static typename json_traits::value_type json_value_from_str(string_type&& s) { + return typename json_traits::value_type(typename json_traits::string_type(std::forward(s))); + } + + // emulation of C++20 std::remove_cvref + template + struct remove_cvref { + using type = typename std::remove_cv::type>::type; + }; + public: builder() = default; + + /** + * Set a header claim. + * \param id Name of the claim + * \param c Claim to add + * \return *this to allow for method chaining + */ + template::value && + std::is_constructible::value && + !std::is_same, typename remove_cvref::type>::value, + bool>::type = true> + builder& set_header_claim(string_type&& id, value_type&& c) { + header_claims[typename json_traits::string_type(std::forward(id))] = + typename json_traits::value_type(std::forward(c)); + return *this; + } + + /** + * Set a header claim. + * \param id Name of the claim + * \param c Claim to add + * \return *this to allow for method chaining + */ + template::value, + bool>::type = true> + builder& set_header_claim(string_type&& id, const typename json_traits::value_type& c) { + header_claims[typename json_traits::string_type(std::forward(id))] = c; + return *this; + } + /** * Set a header claim. * \param id Name of the claim * \param c Claim to add * \return *this to allow for method chaining */ - builder& set_header_claim(const typename json_traits::string_type& id, typename json_traits::value_type c) { - header_claims[id] = std::move(c); + template::value, + bool>::type = true> + builder& set_header_claim(string_type&& id, typename json_traits::value_type&& c) { + header_claims[typename json_traits::string_type(std::forward(id))] = std::move(c); return *this; } @@ -2654,30 +2728,74 @@ namespace jwt { * \param c Claim to add * \return *this to allow for method chaining */ - builder& set_header_claim(const typename json_traits::string_type& id, basic_claim c) { - header_claims[id] = c.to_json(); + template::value, + bool>::type = true> + builder& set_header_claim(string_type&& id, const basic_claim& c) { + header_claims[typename json_traits::string_type(std::forward(id))] = c.to_json(); return *this; } + /** * Set a payload claim. * \param id Name of the claim * \param c Claim to add * \return *this to allow for method chaining */ - builder& set_payload_claim(const typename json_traits::string_type& id, typename json_traits::value_type c) { - payload_claims[id] = std::move(c); + template::value && + std::is_constructible::value && + !std::is_same, typename remove_cvref::type>::value, + bool>::type = true> + builder& set_payload_claim(string_type&& id, value_type&& c) { + payload_claims[typename json_traits::string_type(std::forward(id))] = + typename json_traits::value_type(std::forward(c)); return *this; } + /** * Set a payload claim. * \param id Name of the claim * \param c Claim to add * \return *this to allow for method chaining */ - builder& set_payload_claim(const typename json_traits::string_type& id, basic_claim c) { - payload_claims[id] = c.to_json(); + template::value, + bool>::type = true> + builder& set_payload_claim(string_type&& id, typename json_traits::value_type&& c) { + payload_claims[typename json_traits::string_type(std::forward(id))] = std::move(c); return *this; } + + /** + * Set a payload claim. + * \param id Name of the claim + * \param c Claim to add + * \return *this to allow for method chaining + */ + template::value, + bool>::type = true> + builder& set_payload_claim(string_type&& id, const typename json_traits::value_type& c) { + payload_claims[typename json_traits::string_type(std::forward(id))] = c; + return *this; + } + + /** + * Set a payload claim. + * \param id Name of the claim + * \param c Claim to add + * \return *this to allow for method chaining + */ + template::value, + bool>::type = true> + builder& set_payload_claim(string_type&& id, const basic_claim& c) { + payload_claims[typename json_traits::string_type(std::forward(id))] = c.to_json(); + return *this; + } + /** * \brief Set algorithm claim * You normally don't need to do this, as the algorithm is automatically set if you don't change it. @@ -2685,24 +2803,27 @@ namespace jwt { * \param str Name of algorithm * \return *this to allow for method chaining */ - builder& set_algorithm(typename json_traits::string_type str) { - return set_header_claim("alg", typename json_traits::value_type(str)); + template + builder& set_algorithm(string_type&& str) { + return set_header_claim("alg", std::forward(str)); } /** * Set type claim * \param str Type to set * \return *this to allow for method chaining */ - builder& set_type(typename json_traits::string_type str) { - return set_header_claim("typ", typename json_traits::value_type(str)); + template + builder& set_type(string_type&& str) { + return set_header_claim("typ", std::forward(str)); } /** * Set content type claim * \param str Type to set * \return *this to allow for method chaining */ - builder& set_content_type(typename json_traits::string_type str) { - return set_header_claim("cty", typename json_traits::value_type(str)); + template + builder& set_content_type(string_type&& str) { + return set_header_claim("cty", std::forward(str)); } /** * \brief Set key id claim @@ -2710,40 +2831,50 @@ namespace jwt { * \param str Key id to set * \return *this to allow for method chaining */ - builder& set_key_id(typename json_traits::string_type str) { - return set_header_claim("kid", typename json_traits::value_type(str)); + template + builder& set_key_id(string_type&& str) { + return set_header_claim("kid", std::forward(str)); } /** * Set issuer claim * \param str Issuer to set * \return *this to allow for method chaining */ - builder& set_issuer(typename json_traits::string_type str) { - return set_payload_claim("iss", typename json_traits::value_type(str)); + template + builder& set_issuer(string_type&& str) { + return set_payload_claim("iss", std::forward(str)); } /** * Set subject claim * \param str Subject to set * \return *this to allow for method chaining */ - builder& set_subject(typename json_traits::string_type str) { - return set_payload_claim("sub", typename json_traits::value_type(str)); + template + builder& set_subject(string_type&& str) { + return set_payload_claim("sub", std::forward(str)); } /** * Set audience claim * \param a Audience set * \return *this to allow for method chaining */ - builder& set_audience(typename json_traits::array_type a) { - return set_payload_claim("aud", typename json_traits::value_type(a)); + builder& set_audience(const typename json_traits::array_type& arr) { return set_payload_claim("aud", arr); } + /** + * Set audience claim + * \param a Audience set + * \return *this to allow for method chaining + */ + builder& set_audience(typename json_traits::array_type&& arr) { + return set_payload_claim("aud", std::move(arr)); } /** * Set audience claim * \param aud Single audience * \return *this to allow for method chaining */ - builder& set_audience(typename json_traits::string_type aud) { - return set_payload_claim("aud", typename json_traits::value_type(aud)); + template + builder& set_audience(string_type&& aud) { + return set_payload_claim("aud", std::forward(aud)); } /** * Set expires at claim @@ -2768,8 +2899,9 @@ namespace jwt { * \param str ID to set * \return *this to allow for method chaining */ - builder& set_id(const typename json_traits::string_type& str) { - return set_payload_claim("jti", typename json_traits::value_type(str)); + template + builder& set_id(string_type&& str) { + return set_payload_claim("jti", std::forward(str)); } /** @@ -2822,18 +2954,30 @@ namespace jwt { */ template typename json_traits::string_type sign(const Algo& algo, Encode encode, std::error_code& ec) const { - // make a copy such that a builder can be re-used - typename json_traits::object_type obj_header = header_claims; - if (header_claims.count("alg") == 0) obj_header["alg"] = typename json_traits::value_type(algo.name()); + typename json_traits::object_type obj_header; + const typename json_traits::object_type* pheader_claims; + if (header_claims.count("alg") == 0) { + // make a copy such that a builder can be re-used + obj_header = header_claims; + obj_header["alg"] = json_value_from_str(algo.name()); + pheader_claims = std::addressof(obj_header); + } else { + // no copy is needed because alg is already filled + pheader_claims = std::addressof(header_claims); + } - const auto header = encode(json_traits::serialize(typename json_traits::value_type(obj_header))); + const auto header = encode(json_traits::serialize(typename json_traits::value_type(*pheader_claims))); const auto payload = encode(json_traits::serialize(typename json_traits::value_type(payload_claims))); const auto token = header + "." + payload; auto signature = algo.sign(token, ec); - if (ec) return {}; + typename json_traits::string_type res; + if (ec) return res; - return token + "." + encode(signature); + res.append(token); + res.push_back('.'); + res.append(encode(signature)); + return res; } #ifndef JWT_DISABLE_BASE64 /** @@ -3017,24 +3161,22 @@ namespace jwt { } } - static std::string to_lower_unicode(const std::string& str, const std::locale& loc) { + static std::string to_lower_unicode(string_view str, const std::locale& loc) { std::mbstate_t state = std::mbstate_t(); const char* in_next = str.data(); const char* in_end = str.data() + str.size(); - std::wstring wide; - wide.reserve(str.size()); + std::wstring wide(str.size(), wchar_t{}); - while (in_next != in_end) { - wchar_t wc; - std::size_t result = std::mbrtowc(&wc, in_next, in_end - in_next, &state); - if (result == static_cast(-1)) { + for (auto wideIt = wide.begin(); in_next != in_end; ++wideIt) { + std::size_t result = std::mbrtowc(&(*wideIt), in_next, in_end - in_next, &state); + switch (result) { + case static_cast(-1): throw std::runtime_error("encoding error: " + std::string(std::strerror(errno))); - } else if (result == static_cast(-2)) { + case static_cast(-2): throw std::runtime_error("conversion error: next bytes constitute an incomplete, but so far " "valid, multibyte character."); + default: in_next += result; break; } - in_next += result; - wide.push_back(wc); } auto& f = std::use_facet>(loc); @@ -3076,15 +3218,13 @@ namespace jwt { private: struct algo_base { virtual ~algo_base() = default; - virtual void verify(const std::string& data, const std::string& sig, std::error_code& ec) = 0; + virtual void verify(string_view data, string_view sig, std::error_code& ec) = 0; }; template struct algo : public algo_base { T alg; explicit algo(T a) : alg(a) {} - void verify(const std::string& data, const std::string& sig, std::error_code& ec) override { - alg.verify(data, sig, ec); - } + void verify(string_view data, string_view sig, std::error_code& ec) override { alg.verify(data, sig, ec); } }; /// Required claims std::unordered_map claims; @@ -3255,7 +3395,7 @@ namespace jwt { */ template verifier& allow_algorithm(Algorithm alg) { - algs[alg.name()] = std::make_shared>(alg); + algs[static_cast(alg.name())] = std::make_shared>(alg); return *this; } @@ -3518,7 +3658,8 @@ namespace jwt { using iterator = typename jwt_vector_t::iterator; using const_iterator = typename jwt_vector_t::const_iterator; - JWT_CLAIM_EXPLICIT jwks(const typename json_traits::string_type& str) { + template + JWT_CLAIM_EXPLICIT jwks(const string_type& str) { typename json_traits::value_type parsed_val; if (!json_traits::parse(parsed_val, str)) throw error::invalid_json_exception(); diff --git a/include/jwt-cpp/string_types.h b/include/jwt-cpp/string_types.h new file mode 100644 index 000000000..8f7fa1cb1 --- /dev/null +++ b/include/jwt-cpp/string_types.h @@ -0,0 +1,21 @@ +#ifndef STRING_TYPES_H +#define STRING_TYPES_H + +#if __cplusplus >= 201703L + +#include + +#define JWT_HAS_STRING_VIEW +namespace jwt { + using string_view = std::string_view; +} + +#else + +#include +namespace jwt { + using string_view = const std::string&; +} +#endif + +#endif diff --git a/include/jwt-cpp/traits/boost-json/traits.h b/include/jwt-cpp/traits/boost-json/traits.h index 3b27d5f2f..4d7419aba 100644 --- a/include/jwt-cpp/traits/boost-json/traits.h +++ b/include/jwt-cpp/traits/boost-json/traits.h @@ -67,7 +67,8 @@ namespace jwt { return val.get_double(); } - static bool parse(value_type& val, string_type str) { + template + static bool parse(value_type& val, const string_t& str) { val = json::parse(str); return true; } diff --git a/include/jwt-cpp/traits/danielaparker-jsoncons/traits.h b/include/jwt-cpp/traits/danielaparker-jsoncons/traits.h index be56740ca..795ead400 100644 --- a/include/jwt-cpp/traits/danielaparker-jsoncons/traits.h +++ b/include/jwt-cpp/traits/danielaparker-jsoncons/traits.h @@ -108,7 +108,8 @@ namespace jwt { return val.as_bool(); } - static bool parse(json& val, const std::string& str) { + template + static bool parse(json& val, const string_t& str) { val = json::parse(str); return true; } diff --git a/include/jwt-cpp/traits/kazuho-picojson/traits.h b/include/jwt-cpp/traits/kazuho-picojson/traits.h index 2f96bfc01..311c61c19 100644 --- a/include/jwt-cpp/traits/kazuho-picojson/traits.h +++ b/include/jwt-cpp/traits/kazuho-picojson/traits.h @@ -64,7 +64,8 @@ namespace jwt { return val.get(); } - static bool parse(picojson::value& val, const std::string& str) { + template + static bool parse(picojson::value& val, const string_t& str) { return picojson::parse(val, str).empty(); } diff --git a/include/jwt-cpp/traits/nlohmann-json/traits.h b/include/jwt-cpp/traits/nlohmann-json/traits.h index 7cf486902..9babb4e7d 100644 --- a/include/jwt-cpp/traits/nlohmann-json/traits.h +++ b/include/jwt-cpp/traits/nlohmann-json/traits.h @@ -64,7 +64,8 @@ namespace jwt { return val.get(); } - static bool parse(json& val, std::string str) { + template + static bool parse(json& val, const string_t& str) { val = json::parse(str.begin(), str.end()); return true; } diff --git a/include/picojson/picojson.h b/include/picojson/picojson.h index 76742fe06..9acb49319 100644 --- a/include/picojson/picojson.h +++ b/include/picojson/picojson.h @@ -104,6 +104,8 @@ extern "C" { } while (0) #endif + + #ifdef _MSC_VER #define SNPRINTF _snprintf_s #pragma warning(push) @@ -117,6 +119,14 @@ extern "C" { namespace picojson { +// string_view +#if __cplusplus >= 201703L +#include +using string_view = std::string_view; +#else +using string_view = const std::string &; +#endif + enum { null_type, boolean_type, @@ -137,7 +147,13 @@ struct null {}; class value { public: typedef std::vector array; - typedef std::map object; + typedef std::map= 201703L + // allow map to use transparent type for comparisons + , + std::less<> +#endif + > object; union _storage { bool boolean_; double number_; @@ -161,7 +177,7 @@ class value { explicit value(int64_t i); #endif explicit value(double n); - explicit value(const std::string &s); + explicit value(string_view s); explicit value(const array &a); explicit value(const object &o); #if PICOJSON_USE_RVALUE_REFERENCE @@ -188,12 +204,12 @@ class value { #endif bool evaluate_as_boolean() const; const value &get(const size_t idx) const; - const value &get(const std::string &key) const; + const value &get(string_view key) const; value &get(const size_t idx); - value &get(const std::string &key); + value &get(string_view key); bool contains(const size_t idx) const; - bool contains(const std::string &key) const; + bool contains(string_view key) const; std::string to_str() const; template void serialize(Iter os, bool prettify = false) const; std::string serialize(bool prettify = false) const; @@ -209,8 +225,7 @@ class value { typedef value::array array; typedef value::object object; -inline value::value() : type_(null_type), u_() { -} +inline value::value() : type_(null_type), u_() {} inline value::value(int type, bool) : type_(type), u_() { switch (type) { @@ -257,7 +272,7 @@ inline value::value(double n) : type_(number_type), u_() { u_.number_ = n; } -inline value::value(const std::string &s) : type_(string_type), u_() { +inline value::value(string_view s) : type_(string_type), u_() { u_.string_ = new std::string(s); } @@ -452,14 +467,14 @@ inline value &value::get(const size_t idx) { return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null; } -inline const value &value::get(const std::string &key) const { +inline const value &value::get(string_view key) const { static value s_null; PICOJSON_ASSERT(is()); object::const_iterator i = u_.object_->find(key); return i != u_.object_->end() ? i->second : s_null; } -inline value &value::get(const std::string &key) { +inline value &value::get(string_view key) { static value s_null; PICOJSON_ASSERT(is()); object::iterator i = u_.object_->find(key); @@ -471,7 +486,7 @@ inline bool value::contains(const size_t idx) const { return idx < u_.array_->size(); } -inline bool value::contains(const std::string &key) const { +inline bool value::contains(string_view key) const { PICOJSON_ASSERT(is()); object::const_iterator i = u_.object_->find(key); return i != u_.object_->end(); @@ -522,7 +537,7 @@ inline std::string value::to_str() const { return std::string(); } -template void copy(const std::string &s, Iter oi) { +template void copy(string_view s, Iter oi) { std::copy(s.begin(), s.end(), oi); } @@ -556,7 +571,7 @@ template struct serialize_str_char { } }; -template void serialize_str(const std::string &s, Iter oi) { +template void serialize_str(string_view s, Iter oi) { *oi++ = '"'; serialize_str_char process_char = {oi}; std::for_each(s.begin(), s.end(), process_char); @@ -703,8 +718,8 @@ template class input { } return true; } - bool match(const std::string &pattern) { - for (std::string::const_iterator pi(pattern.begin()); pi != pattern.end(); ++pi) { + bool match(string_view pattern) { + for (auto pi(pattern.begin()); pi != pattern.end(); ++pi) { if (getc() != *pi) { ungetc(); return false; @@ -1124,7 +1139,7 @@ template inline Iter parse(value &out, const Iter &first, const return _parse(ctx, first, last, err); } -inline std::string parse(value &out, const std::string &s) { +inline std::string parse(value &out, string_view s) { std::string err; parse(out, s.begin(), s.end(), &err); return err; @@ -1139,7 +1154,7 @@ inline std::string parse(value &out, std::istream &is) { template struct last_error_t { static std::string s; }; template std::string last_error_t::s; -inline void set_last_error(const std::string &s) { +inline void set_last_error(string_view s) { last_error_t::s = s; } diff --git a/tests/BaseTest.cpp b/tests/BaseTest.cpp index 210798afc..590d52d42 100644 --- a/tests/BaseTest.cpp +++ b/tests/BaseTest.cpp @@ -1,46 +1,85 @@ #include "jwt-cpp/base.h" #include +namespace jwt { + namespace base { + namespace details { + inline bool operator==(const padding& lhs, const padding& rhs) { + return lhs.count == rhs.count && lhs.length == rhs.length; + } + } // namespace details + } // namespace base +} // namespace jwt +namespace { + jwt::base::details::padding count_padding(jwt::string_view base, std::initializer_list fills) { + return jwt::base::details::count_padding(base, fills.begin(), fills.end()); + } +} // namespace + TEST(BaseTest, Base64Index) { - ASSERT_EQ(0, jwt::alphabet::index(jwt::alphabet::base64::data(), 'A')); - ASSERT_EQ(32, jwt::alphabet::index(jwt::alphabet::base64::data(), 'g')); - ASSERT_EQ(62, jwt::alphabet::index(jwt::alphabet::base64::data(), '+')); +#ifdef JWT_HAS_STRING_VIEW + ASSERT_EQ( + 0, jwt::alphabet::index(std::begin(jwt::alphabet::base64::kData), std::end(jwt::alphabet::base64::kData), 'A')); + ASSERT_EQ(32, jwt::alphabet::index(std::begin(jwt::alphabet::base64::kData), std::end(jwt::alphabet::base64::kData), + 'g')); + ASSERT_EQ(62, jwt::alphabet::index(std::begin(jwt::alphabet::base64::kData), std::end(jwt::alphabet::base64::kData), + '+')); +#else + ASSERT_EQ(0, jwt::alphabet::index(std::begin(jwt::alphabet::base64::data()), + std::end(jwt::alphabet::base64::data()), 'A')); + ASSERT_EQ(32, jwt::alphabet::index(std::begin(jwt::alphabet::base64::data()), + std::end(jwt::alphabet::base64::data()), 'g')); + ASSERT_EQ(62, jwt::alphabet::index(std::begin(jwt::alphabet::base64::data()), + std::end(jwt::alphabet::base64::data()), '+')); +#endif } TEST(BaseTest, Base64URLIndex) { - ASSERT_EQ(0, jwt::alphabet::index(jwt::alphabet::base64url::data(), 'A')); - ASSERT_EQ(32, jwt::alphabet::index(jwt::alphabet::base64url::data(), 'g')); - ASSERT_EQ(62, jwt::alphabet::index(jwt::alphabet::base64url::data(), '-')); +#ifdef JWT_HAS_STRING_VIEW + ASSERT_EQ(0, jwt::alphabet::index(std::begin(jwt::alphabet::base64url::kData), + std::end(jwt::alphabet::base64url::kData), 'A')); + ASSERT_EQ(32, jwt::alphabet::index(std::begin(jwt::alphabet::base64url::kData), + std::end(jwt::alphabet::base64url::kData), 'g')); + ASSERT_EQ(62, jwt::alphabet::index(std::begin(jwt::alphabet::base64url::kData), + std::end(jwt::alphabet::base64url::kData), '-')); +#else + ASSERT_EQ(0, jwt::alphabet::index(std::begin(jwt::alphabet::base64url::data()), + std::end(jwt::alphabet::base64url::data()), 'A')); + ASSERT_EQ(32, jwt::alphabet::index(std::begin(jwt::alphabet::base64url::data()), + std::end(jwt::alphabet::base64url::data()), 'g')); + ASSERT_EQ(62, jwt::alphabet::index(std::begin(jwt::alphabet::base64url::data()), + std::end(jwt::alphabet::base64url::data()), '-')); +#endif } TEST(BaseTest, BaseDetailsCountPadding) { using jwt::base::details::padding; - ASSERT_EQ(padding{}, jwt::base::details::count_padding("ABC", {"~"})); - ASSERT_EQ((padding{3, 3}), jwt::base::details::count_padding("ABC~~~", {"~"})); - ASSERT_EQ((padding{5, 5}), jwt::base::details::count_padding("ABC~~~~~", {"~"})); - - ASSERT_EQ(padding{}, jwt::base::details::count_padding("ABC", {"~", "!"})); - ASSERT_EQ((padding{1, 1}), jwt::base::details::count_padding("ABC!", {"~", "!"})); - ASSERT_EQ((padding{1, 1}), jwt::base::details::count_padding("ABC~", {"~", "!"})); - ASSERT_EQ((padding{3, 3}), jwt::base::details::count_padding("ABC~~!", {"~", "!"})); - ASSERT_EQ((padding{3, 3}), jwt::base::details::count_padding("ABC!~~", {"~", "!"})); - ASSERT_EQ((padding{5, 5}), jwt::base::details::count_padding("ABC~~!~~", {"~", "!"})); - - ASSERT_EQ((padding{2, 6}), jwt::base::details::count_padding("MTIzNA%3d%3d", {"%3d", "%3D"})); - ASSERT_EQ((padding{2, 6}), jwt::base::details::count_padding("MTIzNA%3d%3D", {"%3d", "%3D"})); - ASSERT_EQ((padding{2, 6}), jwt::base::details::count_padding("MTIzNA%3D%3d", {"%3d", "%3D"})); - ASSERT_EQ((padding{2, 6}), jwt::base::details::count_padding("MTIzNA%3D%3D", {"%3d", "%3D"})); + ASSERT_EQ(padding{}, count_padding("ABC", {"~"})); + ASSERT_EQ((padding{3, 3}), count_padding("ABC~~~", {"~"})); + ASSERT_EQ((padding{5, 5}), count_padding("ABC~~~~~", {"~"})); + + ASSERT_EQ(padding{}, count_padding("ABC", {"~", "!"})); + ASSERT_EQ((padding{1, 1}), count_padding("ABC!", {"~", "!"})); + ASSERT_EQ((padding{1, 1}), count_padding("ABC~", {"~", "!"})); + ASSERT_EQ((padding{3, 3}), count_padding("ABC~~!", {"~", "!"})); + ASSERT_EQ((padding{3, 3}), count_padding("ABC!~~", {"~", "!"})); + ASSERT_EQ((padding{5, 5}), count_padding("ABC~~!~~", {"~", "!"})); + + ASSERT_EQ((padding{2, 6}), count_padding("MTIzNA%3d%3d", {"%3d", "%3D"})); + ASSERT_EQ((padding{2, 6}), count_padding("MTIzNA%3d%3D", {"%3d", "%3D"})); + ASSERT_EQ((padding{2, 6}), count_padding("MTIzNA%3D%3d", {"%3d", "%3D"})); + ASSERT_EQ((padding{2, 6}), count_padding("MTIzNA%3D%3D", {"%3d", "%3D"})); // Some fake scenarios - ASSERT_EQ(padding{}, jwt::base::details::count_padding("", {"~"})); - ASSERT_EQ(padding{}, jwt::base::details::count_padding("ABC", {"~", "~~!"})); - ASSERT_EQ(padding{}, jwt::base::details::count_padding("ABC!", {"~", "~~!"})); - ASSERT_EQ((padding{1, 1}), jwt::base::details::count_padding("ABC~", {"~", "~~!"})); - ASSERT_EQ((padding{1, 3}), jwt::base::details::count_padding("ABC~~!", {"~", "~~!"})); - ASSERT_EQ((padding{2, 2}), jwt::base::details::count_padding("ABC!~~", {"~", "~~!"})); - ASSERT_EQ((padding{3, 5}), jwt::base::details::count_padding("ABC~~!~~", {"~", "~~!"})); - ASSERT_EQ(padding{}, jwt::base::details::count_padding("ABC~~!~~", {})); + ASSERT_EQ(padding{}, count_padding("", {"~"})); + ASSERT_EQ(padding{}, count_padding("ABC", {"~", "~~!"})); + ASSERT_EQ(padding{}, count_padding("ABC!", {"~", "~~!"})); + ASSERT_EQ((padding{1, 1}), count_padding("ABC~", {"~", "~~!"})); + ASSERT_EQ((padding{1, 3}), count_padding("ABC~~!", {"~", "~~!"})); + ASSERT_EQ((padding{2, 2}), count_padding("ABC!~~", {"~", "~~!"})); + ASSERT_EQ((padding{3, 5}), count_padding("ABC~~!~~", {"~", "~~!"})); + ASSERT_EQ(padding{}, count_padding("ABC~~!~~", {})); } TEST(BaseTest, Base64Decode) { @@ -107,4 +146,4 @@ TEST(BaseTest, Base64TrimURL) { ASSERT_EQ("MTI", jwt::base::trim("MTI%3d")); ASSERT_EQ("MTIz", jwt::base::trim("MTIz")); ASSERT_EQ("MTIzNA", jwt::base::trim("MTIzNA%3d%3d")); -} +} \ No newline at end of file diff --git a/tests/ClaimTest.cpp b/tests/ClaimTest.cpp index e7cccc3b5..d940ef786 100644 --- a/tests/ClaimTest.cpp +++ b/tests/ClaimTest.cpp @@ -139,3 +139,37 @@ TEST(ClaimTest, MapOfClaim) { ASSERT_EQ(claims.get_claim("bool").as_boolean(), true); ASSERT_THROW(claims.get_claim("__missing__"), jwt::error::claim_not_present_exception); } + +#ifdef JWT_HAS_STRING_VIEW +TEST(ClaimTest, StringViewSupport) { + // Force all parameters to be string_views + std::string_view jwtType = "JWT"; + std::string_view claimKey = "access_key"; + std::string_view claimPayload = "accessKeyPayload"; + std::string_view alg = "hs512"; + std::string_view contentType = "mycontent"; + std::string_view keyId = "mykeyid"; + std::string_view issuer = "myissuer"; + std::string_view subject = "mysubject"; + std::string_view audience = "myaudience"; + std::string_view id = "myid"; + + auto jsonWebToken = jwt::create() + .set_algorithm(alg) + .set_type(jwtType) + .set_content_type(contentType) + .set_key_id(keyId) + .set_issuer(issuer) + .set_subject(subject) + .set_audience(audience) + .set_id(id) + .set_payload_claim(claimKey, jwt::claim(claimPayload)); + + std::string_view privateKey = "myprivatekey"; + + auto token = jsonWebToken.sign(jwt::algorithm::hs256{privateKey}); + ASSERT_EQ(token, "eyJhbGciOiJoczUxMiIsImN0eSI6Im15Y29udGVudCIsImtpZCI6Im15a2V5aWQiLCJ0eXAiOiJKV1QifQ." + "eyJhY2Nlc3Nfa2V5IjoiYWNjZXNzS2V5UGF5bG9hZCIsImF1ZCI6Im15YXVkaWVuY2UiLCJpc3MiOiJteWlzc3VlciIsImp0a" + "SI6Im15aWQiLCJzdWIiOiJteXN1YmplY3QifQ.9fd56G-dAxHh89Dl7wZftwsoSfFO_msUFYvA5q77fes"); +} +#endif \ No newline at end of file diff --git a/tests/TokenTest.cpp b/tests/TokenTest.cpp index a64e0d11c..07deb1799 100644 --- a/tests/TokenTest.cpp +++ b/tests/TokenTest.cpp @@ -119,6 +119,7 @@ TEST(TokenTest, CreateTokenES256) { } TEST(TokenTest, CreateTokenES256NoPrivate) { + ASSERT_THROW( []() { auto token = jwt::create().set_issuer("auth0").set_type("JWS").sign(