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;
 |