diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 69563b1..3befc91 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -74,6 +74,8 @@ add_library(${target} ${firebase_admin_SRC} ${firebase_admin_HEADERS} ${firebase_admin_HEADERS_PRIVATE} + jwt/defaults.h + jwt/traits.h ) set_compiler_flags(${target}) diff --git a/src/googlecloudoauth2.cpp b/src/googlecloudoauth2.cpp index 81999d2..5324f6b 100644 --- a/src/googlecloudoauth2.cpp +++ b/src/googlecloudoauth2.cpp @@ -10,7 +10,8 @@ #include -#include "jwt-cpp/jwt.h" +#define JWT_DISABLE_PICOJSON +#include "jwt/defaults.h" Q_LOGGING_CATEGORY(GC_OAUTH, "gc.oauth", QtInfoMsg) diff --git a/src/jwt/defaults.h b/src/jwt/defaults.h new file mode 100644 index 0000000..c4e713d --- /dev/null +++ b/src/jwt/defaults.h @@ -0,0 +1,88 @@ +#ifndef JWT_CPP_QT_JSON_DEFAULTS_H +#define JWT_CPP_QT_JSON_DEFAULTS_H + +#include "traits.h" + +namespace jwt { +/** + * \brief a class to store a generic Qt JSON value as claim + * * This type is the specialization of the \ref basic_claim class which + * uses the qt_json. + */ +using claim = basic_claim; + +/** + * Create a verifier using the default clock + * \return verifier instance + */ +inline verifier verify() { + return verify(default_clock{}); +} + +/** + * Create a builder using the default clock + * \return builder instance to create a new token + */ +inline builder create() { + return builder(default_clock{}); +} + +#ifndef JWT_DISABLE_BASE64 +/** + * Decode a token + * \param token Token to decode + * \return Decoded token + * \throw std::invalid_argument Token is not in correct format + * \throw std::runtime_error Base64 decoding failed or invalid json + */ +inline decoded_jwt decode(const std::string &token) { + return decoded_jwt(token); +} +#endif + +/** + * Decode a token + * \tparam Decode is callable, taking a QString and returns a QString. + * It should ensure the padding of the input and then base64url decode and + * return the results. + * \param token Token to decode + * \param decode The token to parse + * \return Decoded token + * \throw std::invalid_argument Token is not in correct format + * \throw std::runtime_error Base64 decoding failed or invalid json + */ +template +decoded_jwt decode(const std::string &token, Decode decode) { + return decoded_jwt(token, decode); +} + +/** + * Parse a jwk + * \param token JWK Token to parse + * \return Parsed JWK + * \throw std::runtime_error Token is not in correct format + */ +inline jwk +parse_jwk(const traits::qt_json::string_type &token) { + return jwk(token); +} + +/** + * Parse a jwks + * \param token JWKs Token to parse + * \return Parsed JWKs + * \throw std::runtime_error Token is not in correct format + */ +inline jwks +parse_jwks(const traits::qt_json::string_type &token) { + return jwks(token); +} + +/** + * This type is the specialization of the \ref verify_ops::verify_context class + * which uses the qt_json. + */ +using verify_context = verify_ops::verify_context; +} // namespace jwt + +#endif // JWT_CPP_QT_JSON_DEFAULTS_H diff --git a/src/jwt/traits.h b/src/jwt/traits.h new file mode 100644 index 0000000..8ec3226 --- /dev/null +++ b/src/jwt/traits.h @@ -0,0 +1,203 @@ +#ifndef JWT_CPP_QT_JSON_TRAITS_H +#define JWT_CPP_QT_JSON_TRAITS_H + +#include +#include +#include +#include +#include +#include +#include + +#define JWT_DISABLE_PICOJSON +#include + +namespace jwt { +/** + * \brief Namespace containing all the json_trait implementations for a + * jwt::basic_claim. + */ +namespace traits { + +struct qt_json { + // Type Specifications + using string_type = + std::string; // current limitation of traits implementation + using number_type = double; + using integer_type = qint64; + using boolean_type = bool; + + struct value_type : QJsonValue { + using QJsonValue::QJsonValue; // Inherit constructors from QJsonValue + + value_type(const std::string &value) + : QJsonValue{QString::fromStdString(value)} {} + }; + + struct object_type : QJsonObject { + using QJsonObject::QJsonObject; // Inherit constructors from QJsonObject + + // Add missing C++11 member types + // using value_type = key_value_type; // Enable optional jwt-cpp methods + // using mapped_type = key_value_type::value_type; + using size_type = size_t; // for implementing count + + // object_type() = default; + // object_type(const object_type&) = default; + explicit object_type(const QJsonObject &o) : QJsonObject(o) {} + // object_type(object_type&&) = default; + explicit object_type(QJsonObject &&o) : QJsonObject(o) {} + // ~object_type() = default; + // object_type& operator=(const object_type& o) = default; + // object_type& operator=(object_type&& o) noexcept = default; + + // Add missing C++11 subscription operator + QJsonValue operator[](const std::string &key) { + QJsonValueRef ref = QJsonObject::operator[](QString::fromStdString(key)); + return ref; + } + + // Add missing C++11 element access + QJsonValue at(const std::string &key) const { + auto it = constFind(QString::fromStdString(key)); + if (it != constEnd()) { + return it.value(); + } + + throw std::out_of_range("invalid key"); + } + + // Add missing C++11 lookup method + size_type count(const std::string &key) const { + return contains(QString::fromStdString(key)) ? 0 : 1; + } + }; + + struct array_type : QJsonArray { + using QJsonArray::QJsonArray; // Inherit constructors from QJsonArray + + explicit array_type(const QJsonArray &o) : QJsonArray(o) {} + explicit array_type(QJsonArray &&o) : QJsonArray(o) {} + }; + + // Translation between Qt's JSON type and jwt::json::type equivalent + static jwt::json::type get_type(const value_type &val) { + using jwt::json::type; + + switch (val.type()) { + case QJsonValue::Object: + return type::object; + case QJsonValue::Array: + return type::array; + case QJsonValue::String: + return type::string; + case QJsonValue::Double: + return (std::trunc(val.toDouble()) == val.toDouble()) ? type::integer + : type::number; + case QJsonValue::Bool: + return type::boolean; + default: + throw std::logic_error("invalid type"); + } + } + + // Define conversions to and from std::string to satisfy the requirement + // static std::string to_std_string(const QString &str) { + // return str.toStdString(); + // } + + // static QString from_std_string(const std::string &str) { + // return QString::fromStdString(str); + // } + + // // Helper function for substring to replace std::string::substr + // static QString substring(const QString &str, integer_type pos, integer_type + // len) { + // return str.mid(pos, len); // Equivalent to std::string::substr for + // QString + // } + + // // Helper function to concatenate QStrings + // static QString concat_strings(const QString &lhs, const QString &rhs) { + // return lhs + rhs; + // } + + // Helper function that returns the actual string type to be used in templates + // where `std::string` is required. + // static std::string get_compatible_string(const string_type &qtStr) { + // return to_std_string(qtStr); + // } + + // Conversion from generic value to specific type + static object_type as_object(const value_type &val) { + if (!val.isObject()) + throw std::logic_error("Not an object type"); + return object_type(val.toObject()); + } + + static array_type as_array(const value_type &val) { + if (!val.isArray()) + throw std::logic_error("Not an array type"); + return array_type(val.toArray()); + } + + static string_type as_string(const value_type &val) { + if (!val.isString()) + throw std::logic_error("Not a string type"); + return val.toString().toStdString(); + } + + static number_type as_number(const value_type &val) { + if (!val.isDouble()) + throw std::logic_error("Not a number type"); + return val.toDouble(); + } + + static integer_type as_integer(const value_type &val) { + if (!val.isDouble() || std::trunc(val.toDouble()) != val.toDouble()) + throw std::logic_error("Not an integer type"); + return static_cast(val.toDouble()); + } + + static boolean_type as_boolean(const value_type &val) { + if (!val.isBool()) + throw std::logic_error("Not a boolean type"); + return val.toBool(); + } + + // Serialization and parsing + static bool parse(value_type &val, const string_type &str) { + QJsonParseError error; + QJsonDocument doc = + QJsonDocument::fromJson(QByteArray::fromStdString(str), &error); + if (error.error != QJsonParseError::NoError) + return false; + + if (doc.isObject()) { + val = value_type(doc.object()); + } else if (doc.isArray()) { + val = value_type(doc.array()); + } else { + return false; + } + return true; + } + + static string_type serialize(const value_type &val) { + QJsonDocument doc; + if (val.isObject()) { + doc = QJsonDocument(val.toObject()); + } else if (val.isArray()) { + doc = QJsonDocument(val.toArray()); + } else { + throw std::logic_error("Only objects and arrays can be serialized"); + } + return doc.toJson(QJsonDocument::Compact).toStdString(); + } +}; + +} // namespace traits + +} // namespace jwt + +#endif // JWT_CPP_QT_JSON_TRAITS_H