mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-06-30 10:34:21 +00:00
WIP
This commit is contained in:
@@ -1,132 +1,6 @@
|
||||
#include "AuthMiddleware.h"
|
||||
#include "DashboardAuthService.h"
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
#include <string>
|
||||
#include <cctype>
|
||||
|
||||
// Helper to extract cookie value from header
|
||||
static std::string ExtractCookieValue(const std::string& cookieHeader, const std::string& cookieName) {
|
||||
std::string searchStr = cookieName + "=";
|
||||
size_t pos = cookieHeader.find(searchStr);
|
||||
|
||||
if (pos == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
size_t valueStart = pos + searchStr.length();
|
||||
size_t valueEnd = cookieHeader.find(";", valueStart);
|
||||
|
||||
if (valueEnd == std::string::npos) {
|
||||
valueEnd = cookieHeader.length();
|
||||
}
|
||||
|
||||
std::string value = cookieHeader.substr(valueStart, valueEnd - valueStart);
|
||||
|
||||
// URL decode the value
|
||||
std::string decoded;
|
||||
for (size_t i = 0; i < value.length(); ++i) {
|
||||
if (value[i] == '%' && i + 2 < value.length()) {
|
||||
std::string hex = value.substr(i + 1, 2);
|
||||
char* endptr;
|
||||
int charCode = static_cast<int>(std::strtol(hex.c_str(), &endptr, 16));
|
||||
if (endptr - hex.c_str() == 2) {
|
||||
decoded += static_cast<char>(charCode);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
decoded += value[i];
|
||||
}
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
std::string AuthMiddleware::ExtractTokenFromQueryString(const std::string& queryString) {
|
||||
if (queryString.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Parse query string to find token parameter
|
||||
// Expected format: "?token=eyJhbGc..."
|
||||
std::string tokenPrefix = "token=";
|
||||
size_t tokenPos = queryString.find(tokenPrefix);
|
||||
|
||||
if (tokenPos == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Extract token value (from "token=" to next "&" or end of string)
|
||||
size_t valueStart = tokenPos + tokenPrefix.length();
|
||||
size_t valueEnd = queryString.find("&", valueStart);
|
||||
|
||||
if (valueEnd == std::string::npos) {
|
||||
valueEnd = queryString.length();
|
||||
}
|
||||
|
||||
return queryString.substr(valueStart, valueEnd - valueStart);
|
||||
}
|
||||
|
||||
std::string AuthMiddleware::ExtractTokenFromCookies(const std::string& cookieHeader) {
|
||||
if (cookieHeader.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Extract dashboardToken cookie value
|
||||
return ExtractCookieValue(cookieHeader, "dashboardToken");
|
||||
}
|
||||
|
||||
std::string AuthMiddleware::ExtractTokenFromAuthHeader(const std::string& authHeader) {
|
||||
if (authHeader.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Check for "Bearer <token>" format
|
||||
if (authHeader.substr(0, 7) == "Bearer ") {
|
||||
return authHeader.substr(7);
|
||||
}
|
||||
|
||||
// Check for "Token <token>" format
|
||||
if (authHeader.substr(0, 6) == "Token ") {
|
||||
return authHeader.substr(6);
|
||||
}
|
||||
|
||||
// If no prefix, assume raw token
|
||||
return authHeader;
|
||||
}
|
||||
#include "AuthTokenHandler.h"
|
||||
|
||||
bool AuthMiddleware::Process(HTTPContext& context, HTTPReply& reply) {
|
||||
// Try to extract token from various sources (in priority order)
|
||||
std::string token = ExtractTokenFromQueryString(context.queryString);
|
||||
|
||||
if (token.empty()) {
|
||||
const std::string& cookieHeader = context.GetHeader("Cookie");
|
||||
token = ExtractTokenFromCookies(cookieHeader);
|
||||
}
|
||||
|
||||
if (token.empty()) {
|
||||
const std::string& authHeader = context.GetHeader("Authorization");
|
||||
token = ExtractTokenFromAuthHeader(authHeader);
|
||||
}
|
||||
|
||||
// If we found a token, try to verify it
|
||||
if (!token.empty()) {
|
||||
std::string username;
|
||||
uint8_t gmLevel{};
|
||||
|
||||
if (DashboardAuthService::VerifyToken(token, username, gmLevel)) {
|
||||
context.isAuthenticated = true;
|
||||
context.authenticatedUser = username;
|
||||
context.gmLevel = gmLevel;
|
||||
LOG_DEBUG("User %s authenticated via API token (GM level %d)", username.c_str(), gmLevel);
|
||||
return true;
|
||||
} else {
|
||||
LOG_DEBUG("Invalid authentication token provided");
|
||||
return true; // Continue - let routes decide if auth is required
|
||||
}
|
||||
}
|
||||
|
||||
// No token found - continue without authentication
|
||||
// Routes can use RequireAuthMiddleware to enforce authentication
|
||||
return true;
|
||||
return AuthTokenHandler::ProcessHTTPContext(context, reply);
|
||||
}
|
||||
|
||||
@@ -23,12 +23,6 @@ public:
|
||||
|
||||
bool Process(HTTPContext& context, HTTPReply& reply) override;
|
||||
std::string GetName() const override { return "AuthMiddleware"; }
|
||||
|
||||
private:
|
||||
// Extract token from various sources
|
||||
static std::string ExtractTokenFromQueryString(const std::string& queryString);
|
||||
static std::string ExtractTokenFromCookies(const std::string& cookieHeader);
|
||||
static std::string ExtractTokenFromAuthHeader(const std::string& authHeader);
|
||||
};
|
||||
|
||||
#endif // !__AUTHMIDDLEWARE_H__
|
||||
|
||||
186
dDashboardServer/auth/AuthTokenHandler.cpp
Normal file
186
dDashboardServer/auth/AuthTokenHandler.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
#include "AuthTokenHandler.h"
|
||||
#include "DashboardAuthService.h"
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
#include "HTTPContext.h"
|
||||
#include "Web.h"
|
||||
|
||||
// Helper to extract cookie value from header
|
||||
static std::string ExtractCookieValue(const std::string& cookieHeader, const std::string& cookieName) {
|
||||
std::string searchStr = cookieName + "=";
|
||||
size_t pos = cookieHeader.find(searchStr);
|
||||
|
||||
if (pos == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
size_t valueStart = pos + searchStr.length();
|
||||
size_t valueEnd = cookieHeader.find(";", valueStart);
|
||||
|
||||
if (valueEnd == std::string::npos) {
|
||||
valueEnd = cookieHeader.length();
|
||||
}
|
||||
|
||||
std::string value = cookieHeader.substr(valueStart, valueEnd - valueStart);
|
||||
|
||||
// URL decode the value
|
||||
std::string decoded;
|
||||
for (size_t i = 0; i < value.length(); ++i) {
|
||||
if (value[i] == '%' && i + 2 < value.length()) {
|
||||
std::string hex = value.substr(i + 1, 2);
|
||||
char* endptr;
|
||||
int charCode = static_cast<int>(std::strtol(hex.c_str(), &endptr, 16));
|
||||
if (endptr - hex.c_str() == 2) {
|
||||
decoded += static_cast<char>(charCode);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
decoded += value[i];
|
||||
}
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
std::string AuthTokenHandler::ExtractTokenFromQueryString(const std::string& queryString) {
|
||||
if (queryString.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Parse query string to find token parameter
|
||||
// Expected format: "?token=eyJhbGc..."
|
||||
std::string tokenPrefix = "token=";
|
||||
size_t tokenPos = queryString.find(tokenPrefix);
|
||||
|
||||
if (tokenPos == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Extract token value (from "token=" to next "&" or end of string)
|
||||
size_t valueStart = tokenPos + tokenPrefix.length();
|
||||
size_t valueEnd = queryString.find("&", valueStart);
|
||||
|
||||
if (valueEnd == std::string::npos) {
|
||||
valueEnd = queryString.length();
|
||||
}
|
||||
|
||||
return queryString.substr(valueStart, valueEnd - valueStart);
|
||||
}
|
||||
|
||||
std::string AuthTokenHandler::ExtractTokenFromCookieHeader(const std::string& cookieHeader) {
|
||||
if (cookieHeader.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Extract dashboardToken cookie value
|
||||
return ExtractCookieValue(cookieHeader, "dashboardToken");
|
||||
}
|
||||
|
||||
std::string AuthTokenHandler::ExtractTokenFromAuthHeader(const std::string& authHeader) {
|
||||
if (authHeader.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Check for "Bearer <token>" format
|
||||
if (authHeader.length() >= 7 && authHeader.substr(0, 7) == "Bearer ") {
|
||||
return authHeader.substr(7);
|
||||
}
|
||||
|
||||
// Check for "Token <token>" format
|
||||
if (authHeader.length() >= 6 && authHeader.substr(0, 6) == "Token ") {
|
||||
return authHeader.substr(6);
|
||||
}
|
||||
|
||||
// If no prefix, assume raw token
|
||||
return authHeader;
|
||||
}
|
||||
|
||||
std::string AuthTokenHandler::ExtractToken(
|
||||
const std::string& queryString,
|
||||
const std::string& cookieHeader,
|
||||
const std::string& authHeader
|
||||
) {
|
||||
// Try in priority order: query string, cookie, auth header
|
||||
std::string token = ExtractTokenFromQueryString(queryString);
|
||||
|
||||
if (!token.empty()) {
|
||||
return token;
|
||||
}
|
||||
|
||||
token = ExtractTokenFromCookieHeader(cookieHeader);
|
||||
|
||||
if (!token.empty()) {
|
||||
return token;
|
||||
}
|
||||
|
||||
token = ExtractTokenFromAuthHeader(authHeader);
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
AuthTokenHandler::TokenValidationResult AuthTokenHandler::ValidateToken(const std::string& token) {
|
||||
TokenValidationResult result;
|
||||
|
||||
if (token.empty()) {
|
||||
result.isValid = false;
|
||||
result.errorMessage = "No token provided";
|
||||
return result;
|
||||
}
|
||||
|
||||
// Verify JWT token
|
||||
std::string username;
|
||||
uint8_t gmLevel = 0;
|
||||
|
||||
if (!DashboardAuthService::VerifyToken(token, username, gmLevel)) {
|
||||
result.isValid = false;
|
||||
result.errorMessage = "Invalid or expired token";
|
||||
LOG_DEBUG("Token validation failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
result.isValid = true;
|
||||
result.username = username;
|
||||
result.gmLevel = gmLevel;
|
||||
LOG_DEBUG("Token validated successfully for user: %s (GM Level: %d)", username.c_str(), gmLevel);
|
||||
return result;
|
||||
}
|
||||
|
||||
AuthTokenHandler::TokenValidationResult AuthTokenHandler::ExtractAndValidateToken(
|
||||
const std::string& queryString,
|
||||
const std::string& cookieHeader,
|
||||
const std::string& authHeader
|
||||
) {
|
||||
TokenValidationResult result;
|
||||
|
||||
// Extract token from any source
|
||||
std::string token = ExtractToken(queryString, cookieHeader, authHeader);
|
||||
|
||||
if (token.empty()) {
|
||||
result.isValid = false;
|
||||
result.errorMessage = "No authentication token found";
|
||||
return result;
|
||||
}
|
||||
|
||||
// Validate the token
|
||||
return ValidateToken(token);
|
||||
}
|
||||
|
||||
bool AuthTokenHandler::ProcessHTTPContext(HTTPContext& context, HTTPReply& reply) {
|
||||
// Extract and validate token from all available sources
|
||||
const std::string& queryString = context.queryString;
|
||||
const std::string& cookieHeader = context.GetHeader("Cookie");
|
||||
const std::string& authHeader = context.GetHeader("Authorization");
|
||||
|
||||
auto result = ExtractAndValidateToken(queryString, cookieHeader, authHeader);
|
||||
|
||||
if (result.isValid) {
|
||||
context.isAuthenticated = true;
|
||||
context.authenticatedUser = result.username;
|
||||
context.gmLevel = result.gmLevel;
|
||||
LOG_DEBUG("User %s authenticated via API token (GM level %d)", result.username.c_str(), result.gmLevel);
|
||||
return true;
|
||||
} else {
|
||||
LOG_DEBUG("Authentication token validation failed: %s", result.errorMessage.c_str());
|
||||
return true; // Continue - let routes decide if auth is required
|
||||
}
|
||||
}
|
||||
90
dDashboardServer/auth/AuthTokenHandler.h
Normal file
90
dDashboardServer/auth/AuthTokenHandler.h
Normal file
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* Centralized authentication token handler
|
||||
* Consolidates token extraction from multiple sources and validation
|
||||
* Used by both HTTP API routes and WebSocket connections
|
||||
*/
|
||||
class AuthTokenHandler {
|
||||
public:
|
||||
/**
|
||||
* Result of token extraction and validation
|
||||
*/
|
||||
struct TokenValidationResult {
|
||||
bool isValid{false};
|
||||
std::string username{};
|
||||
uint8_t gmLevel{0};
|
||||
std::string errorMessage{};
|
||||
};
|
||||
|
||||
/**
|
||||
* Extract token from query string
|
||||
* Expected format: "?token=eyJhbGc..." or "?token=xyz&other=abc"
|
||||
* @param queryString The query string from the request
|
||||
* @return The token value, or empty string if not found
|
||||
*/
|
||||
static std::string ExtractTokenFromQueryString(const std::string& queryString);
|
||||
|
||||
/**
|
||||
* Extract token from Cookie header
|
||||
* Looks for "dashboardToken=<value>" in the cookie string
|
||||
* @param cookieHeader The Cookie header value
|
||||
* @return The token value, or empty string if not found
|
||||
*/
|
||||
static std::string ExtractTokenFromCookieHeader(const std::string& cookieHeader);
|
||||
|
||||
/**
|
||||
* Extract token from Authorization header
|
||||
* Supports "Bearer <token>", "Token <token>", or raw token
|
||||
* @param authHeader The Authorization header value
|
||||
* @return The token value, or empty string if not found
|
||||
*/
|
||||
static std::string ExtractTokenFromAuthHeader(const std::string& authHeader);
|
||||
|
||||
/**
|
||||
* Extract token from any available source
|
||||
* Tries in priority order: query string, cookie, auth header
|
||||
* @param queryString The query string
|
||||
* @param cookieHeader The Cookie header
|
||||
* @param authHeader The Authorization header
|
||||
* @return The first token found, or empty string
|
||||
*/
|
||||
static std::string ExtractToken(
|
||||
const std::string& queryString,
|
||||
const std::string& cookieHeader,
|
||||
const std::string& authHeader
|
||||
);
|
||||
|
||||
/**
|
||||
* Validate a token and extract user information
|
||||
* Checks JWT signature, expiration, and user permissions
|
||||
* @param token The JWT token
|
||||
* @return TokenValidationResult with validity status and user info
|
||||
*/
|
||||
static TokenValidationResult ValidateToken(const std::string& token);
|
||||
|
||||
/**
|
||||
* Convenience method: Extract and validate token in one call
|
||||
* @param queryString Query string from request
|
||||
* @param cookieHeader Cookie header from request
|
||||
* @param authHeader Authorization header from request
|
||||
* @return TokenValidationResult with validity status and user info
|
||||
*/
|
||||
static TokenValidationResult ExtractAndValidateToken(
|
||||
const std::string& queryString,
|
||||
const std::string& cookieHeader,
|
||||
const std::string& authHeader
|
||||
);
|
||||
|
||||
/**
|
||||
* Process authentication for HTTP middleware use
|
||||
* Extracts and validates token from request, sets HTTPContext properties
|
||||
* @param context HTTP request context (modified to include auth info)
|
||||
* @param reply HTTP reply (not modified unless validation fails silently)
|
||||
* @return true to continue middleware chain, false to stop
|
||||
*/
|
||||
static bool ProcessHTTPContext(class HTTPContext& context, class HTTPReply& reply);
|
||||
};
|
||||
@@ -2,6 +2,7 @@ set(DASHBOARDAUTH_SOURCES
|
||||
"JWTUtils.cpp"
|
||||
"DashboardAuthService.cpp"
|
||||
"AuthMiddleware.cpp"
|
||||
"AuthTokenHandler.cpp"
|
||||
"RequireAuthMiddleware.cpp"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user