#include "login.hpp" #include "sodium.h" #include "databasepool.h" #include "utils.hpp" #include namespace login { std::unordered_map sessions; bool is_logged_in(const crow::request& req) { std::string session_id = get_session_id(req); if (session_id.empty()) return false; auto it = sessions.find(session_id); if (it == sessions.end()) return false; return !it->second.user_id.empty(); } std::string hashPassword(const std::string& password) { // Allocate storage for the hash char hash[crypto_pwhash_STRBYTES]; // Hash the password using Argon2id if (crypto_pwhash_str( hash, password.c_str(), password.size(), crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE ) != 0) { std::cerr << "Out of memory while hashing password!" << std::endl; return ""; } return hash; } std::optional createUser(const std::string& username, const std::string& password){ auto db = Database(); if (!db.open()) return false; std::string password_hash = hashPassword(password); if(password_hash.empty()) return false; std::string insert_sql = "INSERT INTO users (username, password_hash) VALUES ('" + username + "', '" + password_hash + "');"; return db.insert(insert_sql); } bool verifyHashWithPassword(const std::string& hash, std::string const& password) { if (crypto_pwhash_str_verify(hash.c_str(), password.c_str(), password.size()) == 0) { return true; } else { return false; } } std::string get_session_id(const crow::request& req) { auto cookie_header = req.get_header_value("Cookie"); std::string prefix = "session_id="; auto pos = cookie_header.find(prefix); if (pos == std::string::npos) return ""; return cookie_header.substr(pos + prefix.size(), 32); } // Utility: generate random string std::string random_string(size_t length) { static const char charset[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; std::string result; result.resize(length); std::mt19937 rng(std::random_device{}()); std::uniform_int_distribution<> dist(0, sizeof(charset)-2); for (size_t i = 0; i < length; ++i) result[i] = charset[dist(rng)]; return result; } std::optional loginUser(const std::string& username, const std::string& password) { auto sql = "SELECT id, password_hash FROM users WHERE username = ? LIMIT 1;"; auto db = Database(); if (!db.open()){ return {}; } auto opt_pair = db.get(sql, {username}); if (opt_pair.has_value()) { if (verifyHashWithPassword(opt_pair.value().second, password)) { return opt_pair.value().first; } } return {}; } bool initDB() { auto db = Database(); if (!db.open()){ return false; } // Create a tables const char* create_sql_chars = "CREATE TABLE IF NOT EXISTS users (" "id INTEGER PRIMARY KEY," "username TEXT NOT NULL," "password_hash TEXT NOT NULL," "created_at DATETIME DEFAULT CURRENT_TIMESTAMP);"; if (!db.exec(create_sql_chars)){ CROW_LOG_ERROR << "Failed to create users table"; return false; } return true; } bool initLogin(crow::SimpleApp& app) { if (sodium_init() < 0) { CROW_LOG_ERROR << "Failed to Init Sodium"; return false; } if(!initDB()) { return false; } // createUser("lukas", "Trollar4928"); CROW_ROUTE(app, "/login").methods("GET"_method) ([](const crow::request& req){ std::string csrf = random_string(32); // store CSRF in a temporary session cookie std::string session_id = random_string(32); sessions[session_id] = Session{"", csrf}; crow::response res; res.body = "
" "" "" "" "
"; res.add_header("Set-Cookie", "session_id=" + session_id + "; HttpOnly; Secure; SameSite=Strict"); return res; }); CROW_ROUTE(app, "/login").methods("POST"_method) ([](const crow::request& req){ auto cookie_it = req.get_header_value("Cookie").find("session_id="); if (cookie_it == std::string::npos) return crow::response(401, "No session"); // extract session_id std::string session_id = req.get_header_value("Cookie").substr(cookie_it + 11, 32); auto it = sessions.find(session_id); if (it == sessions.end()) return crow::response(401, "Invalid session"); auto session = it->second; // parse form auto body = utils::parseBody(req.body); if (body.empty()) return crow::response(400); std::string csrf_token = body.at("csrf_token"); std::string username = body.at("username"); std::string password = body.at("password"); if (csrf_token != session.csrf_token) return crow::response(403, "CSRF failed"); std::optional userId = loginUser(username, password); if (!userId.has_value()) { return crow::response(401, "Invalid credentials"); } // set user id sessions[session_id].user_id = std::to_string(userId.value()); crow::response res; res.add_header("HX-Redirect", "/templates/dashboard.html"); // htmx redirect return res; }); return true; } }