#include "jwt.h" #include #include "utils.h" #include static int64_t milliseconds() { timeval tv{}; gettimeofday(&tv,nullptr); return ((tv.tv_sec * 1000000l) + tv.tv_usec) / 1000; } std::string http::auth::jwt::secretKey; void http::auth::jwt::generateSecretKey() { secretKey.clear(); std::random_device rd; std::mt19937 generator(rd()); std::uniform_int_distribution<> distribution('!', '~'); for (size_t i = 0; i < 32; ++i) { secretKey += static_cast(distribution(generator)); } } http::auth::jwt::Jwt http::auth::jwt::Jwt::fromCookies(const std::string &cookie) { auto pc = utils::parseCookies(cookie); Jwt t; if (pc.find("auth") != pc.end()) { const auto auth = pc.at("auth"); int firstDot = -1; int secondDot = -1; for (size_t i = 0; i < auth.size(); i++) { if (auth[i] == '.') { if (firstDot < 0) { firstDot = static_cast(i); } else if (secondDot < 0) { secondDot = static_cast(i); } else { // так быть не должно return t; } } } if (firstDot < 0 || secondDot < 0 || secondDot - firstDot == 0) { // так тоже быть не должно return t; } try { t.payload = auth.substr(0, firstDot); t.signature = auth.substr(secondDot + 1); t.lastUpdate = std::stol(auth.substr(firstDot + 1, secondDot - firstDot - 1)); // теперь проверим, что сигнатура верная, только тогда декодируем строку юзера auto realSignature = utils::sha256(t.payload + std::to_string(t.lastUpdate) + secretKey); if (t.signature != realSignature) { t.payload.clear(); } else { t.payload = utils::b64Decode(t.payload); } } catch (std::exception& e) { t.payload.clear(); t.lastUpdate = 0; t.signature.clear(); } } return t; } http::auth::jwt::Jwt http::auth::jwt::Jwt::fromUser(const std::string &user) { Jwt t; t.payload = user; t.lastUpdate = milliseconds(); return t; } bool http::auth::jwt::Jwt::isValid() { if (payload.empty() || signature.empty()) { return false; } // проверка сигнатуры не нужна, она была на стадии парсинга куки // auto realSignature = utils::sha256(utils::b64Encode(this->payload) + std::to_string(this->lastUpdate) + secretKey); // if (signature != realSignature) { // return false; // } const auto currTime = milliseconds(); return currTime <= lastUpdate + SESSION_LIVE_MS && currTime >= lastUpdate; } std::string http::auth::jwt::Jwt::getUsername() { return payload; } std::string http::auth::jwt::Jwt::asCookie(bool isSecure) { this->lastUpdate = milliseconds(); const auto uTime = std::to_string(this->lastUpdate); const auto encodedPayload = utils::b64Encode(payload); signature = utils::sha256(encodedPayload + uTime + secretKey); const auto val = encodedPayload + "." + uTime + "." + signature; std::string cookie = "auth="; cookie += val; cookie += ";Path=/; Max-Age="; cookie += std::to_string(SESSION_LIVE_MS / 1000); if (isSecure) { cookie += "; Secure"; } cookie += "; HttpOnly; SameSite=Lax"; return cookie; } bool http::auth::jwt::Jwt::needUpdate() const { return milliseconds() >= lastUpdate + SESSION_UPDATE_THRESHOLD; } http::auth::jwt::Jwt::~Jwt() = default;