Pistache Auth
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
- Pistache GitHub
- Pistache Official Site
- Pistache Documentation
- OpenAPI Generator Pistache
- LinuxLinks Pistache Introduction
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();
}
};