119 lines
3.7 KiB
C++
119 lines
3.7 KiB
C++
#include "jwt.h"
|
|
#include <random>
|
|
#include "utils.h"
|
|
|
|
#include <sys/time.h>
|
|
|
|
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<char>(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<int>(i); }
|
|
else if (secondDot < 0) { secondDot = static_cast<int>(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;
|