Pistache Auth

Authentication LibraryC++HTTPREST APIPistacheWeb ServerAuthenticationSecurity

Authentication Library

Pistache Auth

Overview

Pistache Auth is a REST API authentication system using the high-performance C++ HTTP framework Pistache.

Details

Pistache Auth is an authentication system implementation based on the Pistache HTTP framework. Pistache is a modern, high-performance HTTP and REST framework written in C++17, provided under the Apache License 2.0. It provides asynchronous APIs, allows building APIs with multi-threaded HTTP servers, and includes HTTP client, HTTP router, REST description DSL, and type-safe headers and MIME type implementations. HTTPS support enables SSL/TLS connections, and authentication can be implemented through session mechanisms or HTTP built-in authentication mechanisms. However, the project hasn't yet reached 1.0 release and is unstable but usable. Most of the code is production-ready and can be used for RESTful API development without issues, though the HTTP client has some bugs.

Pros and Cons

Pros

  • High Performance: Optimized performance through C++17
  • Asynchronous Processing: Efficient request processing through asynchronous APIs
  • Modern API: Clear and pleasant API through low-level HTTP abstraction
  • HTTPS Support: Secure communication through built-in HTTPS functionality
  • Type Safety: Safety ensured through C++ type system
  • Lightweight: Minimal dependencies required
  • REST Focused: Optimized for RESTful API construction

Cons

  • Pre-release: Unstable parts due to being below 1.0
  • C++ Knowledge: Deep understanding of C++17 required
  • Authentication Limitations: Incomplete authentication logic in OpenAPI code generation
  • Documentation: Limited detailed documentation
  • Community: Relatively small community
  • Debug Difficulty: C++ level debugging required

Key Links

Code Examples

Basic HTTP Server Configuration

// main.cpp
#include <pistache/net.h>
#include <pistache/http.h>
#include <pistache/peer.h>
#include <pistache/http_headers.h>
#include <pistache/cookie.h>
#include <pistache/router.h>
#include <pistache/endpoint.h>

using namespace Pistache;

class AuthHandler {
public:
    void onRequest(const Rest::Request& request, Http::ResponseWriter response) {
        // Basic authentication check
        if (!checkBasicAuth(request)) {
            response.send(Http::Code::Unauthorized, "Authentication required");
            return;
        }
        
        response.send(Http::Code::Ok, "Hello, authenticated user!");
    }

private:
    bool checkBasicAuth(const Rest::Request& request) {
        auto auth_header = request.headers().tryGet<Http::Header::Authorization>();
        if (!auth_header) {
            return false;
        }
        
        // Basic authentication implementation
        std::string auth_string = auth_header->value();
        return validateBasicAuth(auth_string);
    }
    
    bool validateBasicAuth(const std::string& auth_string) {
        // Check "Basic " prefix
        if (auth_string.substr(0, 6) != "Basic ") {
            return false;
        }
        
        // Base64 decode and user verification
        std::string credentials = decodeBase64(auth_string.substr(6));
        return validateCredentials(credentials);
    }
};

int main() {
    Http::Endpoint server(Address(Ipv4::any(), Port(8080)));
    auto opts = Http::Endpoint::options().threads(1);
    server.init(opts);
    
    AuthHandler handler;
    server.setHandler(Http::make_handler<Http::Handler>([&](const Http::Request& req, Http::ResponseWriter response) {
        handler.onRequest(req, std::move(response));
    }));
    
    server.serve();
}

JWT Token Authentication

// jwt_auth.cpp
#include <pistache/router.h>
#include <jwt-cpp/jwt.h>
#include <nlohmann/json.hpp>

class JWTAuthenticator {
private:
    std::string secret_key;
    
public:
    JWTAuthenticator(const std::string& key) : secret_key(key) {}
    
    std::string generateToken(const std::string& username, const std::string& role) {
        auto token = jwt::create()
            .set_issuer("pistache-auth")
            .set_type("JWT")
            .set_payload_claim("username", jwt::claim(username))
            .set_payload_claim("role", jwt::claim(role))
            .set_issued_at(std::chrono::system_clock::now())
            .set_expires_at(std::chrono::system_clock::now() + std::chrono::hours{24})
            .sign(jwt::algorithm::hs256{secret_key});
        
        return token;
    }
    
    bool validateToken(const std::string& token, std::string& username) {
        try {
            auto decoded = jwt::decode(token);
            auto verifier = jwt::verify()
                .allow_algorithm(jwt::algorithm::hs256{secret_key})
                .with_issuer("pistache-auth");
            
            verifier.verify(decoded);
            username = decoded.get_payload_claim("username").as_string();
            return true;
        } catch (const std::exception& e) {
            return false;
        }
    }
};

class SecureEndpoint {
private:
    JWTAuthenticator authenticator;
    
public:
    SecureEndpoint(const std::string& secret) : authenticator(secret) {}
    
    void login(const Rest::Request& request, Http::ResponseWriter response) {
        auto json_body = nlohmann::json::parse(request.body());
        std::string username = json_body["username"];
        std::string password = json_body["password"];
        
        if (validateCredentials(username, password)) {
            std::string token = authenticator.generateToken(username, "user");
            nlohmann::json result = {
                {"token", token},
                {"message", "Login successful"}
            };
            
            response.headers().add<Http::Header::ContentType>(MIME(Application, Json));
            response.send(Http::Code::Ok, result.dump());
        } else {
            response.send(Http::Code::Unauthorized, "Invalid credentials");
        }
    }
    
    void protectedRoute(const Rest::Request& request, Http::ResponseWriter response) {
        auto auth_header = request.headers().tryGet<Http::Header::Authorization>();
        if (!auth_header) {
            response.send(Http::Code::Unauthorized, "Authorization header missing");
            return;
        }
        
        std::string auth_value = auth_header->value();
        if (auth_value.substr(0, 7) != "Bearer ") {
            response.send(Http::Code::Unauthorized, "Invalid authorization format");
            return;
        }
        
        std::string token = auth_value.substr(7);
        std::string username;
        
        if (authenticator.validateToken(token, username)) {
            nlohmann::json result = {
                {"message", "Access granted"},
                {"user", username}
            };
            
            response.headers().add<Http::Header::ContentType>(MIME(Application, Json));
            response.send(Http::Code::Ok, result.dump());
        } else {
            response.send(Http::Code::Unauthorized, "Invalid token");
        }
    }
    
private:
    bool validateCredentials(const std::string& username, const std::string& password) {
        // Verify user information from database or file
        return (username == "admin" && password == "password123");
    }
};

Router Configuration and Middleware

// router_auth.cpp
#include <pistache/router.h>

class AuthMiddleware {
public:
    static void authRequired(const Rest::Request& request, 
                           Http::ResponseWriter response, 
                           Rest::Route::Result& result) {
        auto auth_header = request.headers().tryGet<Http::Header::Authorization>();
        if (!auth_header) {
            response.send(Http::Code::Unauthorized, "Authentication required");
            result = Rest::Route::Result::Failure;
            return;
        }
        
        // Token validation logic
        if (!validateAuthToken(auth_header->value())) {
            response.send(Http::Code::Forbidden, "Invalid authentication");
            result = Rest::Route::Result::Failure;
            return;
        }
        
        result = Rest::Route::Result::Ok;
    }
    
private:
    static bool validateAuthToken(const std::string& token) {
        // Token validation implementation
        return !token.empty();
    }
};

class ApiServer {
private:
    std::shared_ptr<Http::Endpoint> httpEndpoint;
    Rest::Router router;
    
public:
    explicit ApiServer(Address addr) 
        : httpEndpoint(std::make_shared<Http::Endpoint>(addr)) {}
    
    void init(size_t thr = 2) {
        auto opts = Http::Endpoint::options()
            .threads(static_cast<int>(thr));
        httpEndpoint->init(opts);
        setupRoutes();
    }
    
    void start() {
        httpEndpoint->setHandler(router.handler());
        httpEndpoint->serve();
    }
    
private:
    void setupRoutes() {
        using namespace Rest;
        
        // Public endpoints
        Routes::Post(router, "/auth/login", Routes::bind(&ApiServer::login, this));
        Routes::Post(router, "/auth/register", Routes::bind(&ApiServer::registerUser, this));
        
        // Protected endpoints (middleware applied)
        auto protectedGroup = router.group("/api");
        protectedGroup.addMiddleware(AuthMiddleware::authRequired);
        
        Routes::Get(protectedGroup, "/profile", Routes::bind(&ApiServer::getProfile, this));
        Routes::Put(protectedGroup, "/profile", Routes::bind(&ApiServer::updateProfile, this));
        Routes::Delete(protectedGroup, "/account", Routes::bind(&ApiServer::deleteAccount, this));
    }
    
    void login(const Rest::Request& request, Http::ResponseWriter response) {
        // Login processing
        response.send(Http::Code::Ok, "Login endpoint");
    }
    
    void registerUser(const Rest::Request& request, Http::ResponseWriter response) {
        // User registration processing
        response.send(Http::Code::Created, "User registered");
    }
    
    void getProfile(const Rest::Request& request, Http::ResponseWriter response) {
        // Profile retrieval (authenticated)
        response.send(Http::Code::Ok, "User profile data");
    }
    
    void updateProfile(const Rest::Request& request, Http::ResponseWriter response) {
        // Profile update (authenticated)
        response.send(Http::Code::Ok, "Profile updated");
    }
    
    void deleteAccount(const Rest::Request& request, Http::ResponseWriter response) {
        // Account deletion (authenticated)
        response.send(Http::Code::Ok, "Account deleted");
    }
};

HTTPS Configuration and Security

// https_server.cpp
#include <pistache/endpoint.h>
#include <pistache/http.h>
#include <pistache/ssl.h>

class SecureServer {
private:
    Http::Endpoint endpoint;
    
public:
    SecureServer(const Address& addr) : endpoint(addr) {}
    
    void init() {
        // HTTPS configuration
        auto opts = Http::Endpoint::options()
            .threads(4)
            .maxRequestSize(1024 * 1024)  // 1MB
            .maxResponseSize(1024 * 1024); // 1MB
            
        // SSL/TLS configuration
        if (enableSSL()) {
            opts.enableSSL("server.crt", "server.key");
        }
        
        endpoint.init(opts);
        setupSecurityHeaders();
    }
    
    void start() {
        endpoint.serve();
    }
    
private:
    bool enableSSL() {
        // Check SSL certificate existence
        return std::filesystem::exists("server.crt") && 
               std::filesystem::exists("server.key");
    }
    
    void setupSecurityHeaders() {
        endpoint.setHandler(Http::make_handler<Http::Handler>([this](const Http::Request& req, Http::ResponseWriter response) {
            // Add security headers
            response.headers().add<Http::Header::StrictTransportSecurity>("max-age=31536000; includeSubDomains");
            response.headers().add<Http::Header::ContentSecurityPolicy>("default-src 'self'");
            response.headers().addRaw("X-Frame-Options", "DENY");
            response.headers().addRaw("X-Content-Type-Options", "nosniff");
            response.headers().addRaw("X-XSS-Protection", "1; mode=block");
            
            handleRequest(req, std::move(response));
        }));
    }
    
    void handleRequest(const Http::Request& request, Http::ResponseWriter response) {
        // Request processing logic
        response.send(Http::Code::Ok, "Secure response");
    }
};

int main() {
    Address addr(Ipv4::loopback(), Port(8443));
    SecureServer server(addr);
    
    server.init();
    std::cout << "Secure server listening on https://localhost:8443" << std::endl;
    server.start();
    
    return 0;
}

Session Management

// session_manager.cpp
#include <unordered_map>
#include <random>
#include <sstream>

class SessionManager {
private:
    std::unordered_map<std::string, SessionData> sessions;
    std::random_device rd;
    std::mt19937 gen;
    
    struct SessionData {
        std::string username;
        std::chrono::system_clock::time_point created_at;
        std::chrono::system_clock::time_point last_access;
        std::unordered_map<std::string, std::string> data;
    };
    
public:
    SessionManager() : gen(rd()) {}
    
    std::string createSession(const std::string& username) {
        std::string session_id = generateSessionId();
        SessionData session;
        session.username = username;
        session.created_at = std::chrono::system_clock::now();
        session.last_access = session.created_at;
        
        sessions[session_id] = session;
        return session_id;
    }
    
    bool validateSession(const std::string& session_id) {
        auto it = sessions.find(session_id);
        if (it == sessions.end()) {
            return false;
        }
        
        // Session expiration check (24 hours)
        auto now = std::chrono::system_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::hours>(
            now - it->second.created_at);
            
        if (duration.count() > 24) {
            sessions.erase(it);
            return false;
        }
        
        // Update last access time
        it->second.last_access = now;
        return true;
    }
    
    void destroySession(const std::string& session_id) {
        sessions.erase(session_id);
    }
    
private:
    std::string generateSessionId() {
        std::uniform_int_distribution<> dis(0, 15);
        std::stringstream ss;
        
        for (int i = 0; i < 32; ++i) {
            ss << std::hex << dis(gen);
        }
        
        return ss.str();
    }
};