JWT-CPP

Authentication LibraryC++JWTThalhammerHeader-onlyEncryptionOpenSSL

Authentication Library

JWT-CPP

Overview

JWT-CPP is a header-only library for creating and validating JSON Web Tokens (JWT) in C++11, developed by Thalhammer and providing comprehensive implementation of JWT standards.

Details

JWT-CPP is a header-only library that enables comprehensive JSON Web Token (JWT) operations in C++11 environments. Developed and maintained by Thalhammer, it is publicly available on GitHub.

The library's key features include support for all JWT standard algorithms (HMAC, RSA, ECDSA, EdDSA, PSS), JWT token creation and signing using various cryptographic algorithms, JWT token decoding for claim access, JWT token verification including signature and claim validation, and JSON Web Keys (JWK) and JSON Web Key Sets (JWKS) operations.

Architecturally, the library is built around core components: jwt::create, jwt::decode, and jwt::verify. It adopts a trait-based approach supporting integration with multiple JSON libraries including PicoJSON (default), nlohmann/json, Boost.JSON, jsoncons, and jsoncpp, allowing various data types to be handled as claims through the jwt::basic_claim class.

For SSL/TLS implementation, it supports multiple SSL implementations including OpenSSL, LibreSSL, and wolfSSL, enabling use across a wide range of environments. The library achieves high performance while minimizing external dependencies through its header-only design.

Time handling uses std::chrono::system_clock::time_point for date claims, with set_issued_now() and set_expires_in() methods helping avoid timezone issues.

Pros and Cons

Pros

  • Header-only: Minimal external dependencies, easy integration
  • Standards Compliant: Supports all JWT specification algorithms
  • High Performance: Native C++ high-speed implementation
  • Flexible JSON Integration: Can integrate with multiple JSON libraries
  • Multi-SSL Support: Compatible with OpenSSL, LibreSSL, wolfSSL
  • Active Maintenance: Continuous development and community support
  • Rich Algorithms: HMAC, RSA, ECDSA, EdDSA, PSS support

Cons

  • C++ Only: Available only in C++ environments
  • Learning Curve: Requires understanding of JWT specifications and C++ templates
  • Compile Time: Header-only nature may increase compile time for large projects
  • Debug Difficulty: Template metaprogramming makes error messages complex
  • Configuration Complexity: Supporting multiple SSL implementations can make configuration complex
  • Documentation: Some advanced features lack comprehensive documentation

Key Links

Usage Examples

Basic Setup

#include <jwt-cpp/jwt.h>
#include <iostream>
#include <string>

// In CMakeLists.txt or via pkg-config:
// find_package(jwt-cpp REQUIRED)
// target_link_libraries(your_target jwt-cpp::jwt-cpp)

Hello World (Basic JWT Token Operations)

#include <iostream>
#include <jwt-cpp/jwt.h>

int main() {
    try {
        // Create JWT token
        auto token = jwt::create()
            .set_issuer("myapp")
            .set_type("JWS")
            .set_payload_claim("username", jwt::claim(std::string("testuser")))
            .set_payload_claim("role", jwt::claim(std::string("user")))
            .set_issued_now()
            .set_expires_in(std::chrono::hours{1})
            .sign(jwt::algorithm::hs256{"my-secret-key"});
            
        std::cout << "Generated Token: " << token << std::endl;

        // Decode JWT token
        auto decoded = jwt::decode(token);
        std::cout << "Issuer: " << decoded.get_issuer() << std::endl;
        std::cout << "Username: " << decoded.get_payload_claim("username").as_string() << std::endl;
        std::cout << "Role: " << decoded.get_payload_claim("role").as_string() << std::endl;

        // Verify JWT token
        auto verifier = jwt::verify()
            .allow_algorithm(jwt::algorithm::hs256{"my-secret-key"})
            .with_issuer("myapp");
            
        verifier.verify(decoded);
        std::cout << "Token is valid!" << std::endl;

    } catch (const jwt::token_verification_exception& e) {
        std::cout << "Token verification failed: " << e.what() << std::endl;
    } catch (const std::exception& e) {
        std::cout << "Error: " << e.what() << std::endl;
    }

    return 0;
}

HMAC (Symmetric Key) Signing and Verification

#include <jwt-cpp/jwt.h>
#include <iostream>
#include <chrono>

class HMACJWTService {
private:
    std::string secret;
    
public:
    HMACJWTService(const std::string& secret_key) : secret(secret_key) {}
    
    std::string createToken(const std::string& userId, 
                           const std::string& email, 
                           const std::vector<std::string>& roles) {
        try {
            auto token = jwt::create()
                .set_issuer("secure-app")
                .set_subject(userId)
                .set_audience("api-users")
                .set_payload_claim("email", jwt::claim(email))
                .set_payload_claim("roles", jwt::claim(roles))
                .set_issued_now()
                .set_expires_in(std::chrono::hours{24})
                .set_not_before(std::chrono::system_clock::now())
                .sign(jwt::algorithm::hs256{secret});
                
            return token;
        } catch (const std::exception& e) {
            throw std::runtime_error("Token creation failed: " + std::string(e.what()));
        }
    }
    
    bool validateToken(const std::string& token, std::string& userId, std::string& email) {
        try {
            auto decoded = jwt::decode(token);
            
            // Create verifier
            auto verifier = jwt::verify()
                .allow_algorithm(jwt::algorithm::hs256{secret})
                .with_issuer("secure-app")
                .with_audience("api-users");
                
            verifier.verify(decoded);
            
            // Extract claims
            userId = decoded.get_subject();
            email = decoded.get_payload_claim("email").as_string();
            
            return true;
        } catch (const jwt::token_verification_exception& e) {
            std::cerr << "Token validation failed: " << e.what() << std::endl;
            return false;
        }
    }
    
    std::vector<std::string> getRoles(const std::string& token) {
        try {
            auto decoded = jwt::decode(token);
            auto roles_claim = decoded.get_payload_claim("roles");
            
            std::vector<std::string> roles;
            if (roles_claim.get_type() == jwt::json::type::array) {
                for (const auto& role : roles_claim.as_array()) {
                    if (role.get_type() == jwt::json::type::string) {
                        roles.push_back(role.as_string());
                    }
                }
            }
            return roles;
        } catch (const std::exception& e) {
            std::cerr << "Failed to get roles: " << e.what() << std::endl;
            return {};
        }
    }
};

// Usage example
int main() {
    HMACJWTService jwtService("your-256-bit-secret-key");
    
    // Create token
    std::vector<std::string> roles = {"user", "reader"};
    std::string token = jwtService.createToken("user123", "[email protected]", roles);
    std::cout << "Created token: " << token << std::endl;
    
    // Validate token
    std::string userId, email;
    if (jwtService.validateToken(token, userId, email)) {
        std::cout << "Valid token - User ID: " << userId << ", Email: " << email << std::endl;
        
        auto userRoles = jwtService.getRoles(token);
        std::cout << "Roles: ";
        for (const auto& role : userRoles) {
            std::cout << role << " ";
        }
        std::cout << std::endl;
    }
    
    return 0;
}

RSA (Asymmetric Key) Signing and Verification

#include <jwt-cpp/jwt.h>
#include <iostream>
#include <fstream>
#include <sstream>

class RSAJWTService {
private:
    std::string private_key;
    std::string public_key;
    
    std::string readFile(const std::string& filepath) {
        std::ifstream file(filepath);
        if (!file.is_open()) {
            throw std::runtime_error("Could not open file: " + filepath);
        }
        
        std::stringstream buffer;
        buffer << file.rdbuf();
        return buffer.str();
    }
    
public:
    RSAJWTService(const std::string& private_key_path, const std::string& public_key_path) {
        private_key = readFile(private_key_path);
        public_key = readFile(public_key_path);
    }
    
    std::string createToken(const std::string& userId, const std::string& email) {
        try {
            auto token = jwt::create()
                .set_issuer("rsa-service")
                .set_subject(userId)
                .set_audience("api-clients")
                .set_payload_claim("email", jwt::claim(email))
                .set_payload_claim("scope", jwt::claim(std::string("read write")))
                .set_issued_now()
                .set_expires_in(std::chrono::hours{2})
                .sign(jwt::algorithm::rs256{public_key, private_key, "", ""});
                
            return token;
        } catch (const std::exception& e) {
            throw std::runtime_error("RSA Token creation failed: " + std::string(e.what()));
        }
    }
    
    bool validateToken(const std::string& token) {
        try {
            auto decoded = jwt::decode(token);
            
            auto verifier = jwt::verify()
                .allow_algorithm(jwt::algorithm::rs256{public_key, "", "", ""})
                .with_issuer("rsa-service")
                .with_audience("api-clients");
                
            verifier.verify(decoded);
            return true;
        } catch (const jwt::token_verification_exception& e) {
            std::cerr << "RSA Token validation failed: " << e.what() << std::endl;
            return false;
        }
    }
    
    void printTokenInfo(const std::string& token) {
        try {
            auto decoded = jwt::decode(token);
            
            std::cout << "Token Information:" << std::endl;
            std::cout << "  Algorithm: " << decoded.get_algorithm() << std::endl;
            std::cout << "  Type: " << decoded.get_type() << std::endl;
            std::cout << "  Issuer: " << decoded.get_issuer() << std::endl;
            std::cout << "  Subject: " << decoded.get_subject() << std::endl;
            std::cout << "  Audience: ";
            for (const auto& aud : decoded.get_audience()) {
                std::cout << aud << " ";
            }
            std::cout << std::endl;
            
            auto issued_at = decoded.get_issued_at();
            auto expires_at = decoded.get_expires_at();
            
            std::time_t iat = std::chrono::system_clock::to_time_t(issued_at);
            std::time_t exp = std::chrono::system_clock::to_time_t(expires_at);
            
            std::cout << "  Issued At: " << std::ctime(&iat);
            std::cout << "  Expires At: " << std::ctime(&exp);
            
        } catch (const std::exception& e) {
            std::cerr << "Failed to print token info: " << e.what() << std::endl;
        }
    }
};

// Usage example (requires RSA key files)
int main() {
    try {
        RSAJWTService rsaService("private_key.pem", "public_key.pem");
        
        std::string token = rsaService.createToken("user456", "[email protected]");
        std::cout << "RSA Token: " << token << std::endl;
        
        if (rsaService.validateToken(token)) {
            std::cout << "RSA Token is valid!" << std::endl;
            rsaService.printTokenInfo(token);
        }
        
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
    
    return 0;
}

Advanced Claim Processing and Custom Validation

#include <jwt-cpp/jwt.h>
#include <iostream>
#include <map>
#include <vector>
#include <chrono>

class AdvancedJWTService {
private:
    std::string secret;
    std::map<std::string, std::string> issuers;
    
public:
    AdvancedJWTService(const std::string& secret_key) : secret(secret_key) {
        // Set trusted issuers
        issuers["auth-service"] = "Authentication Service";
        issuers["user-service"] = "User Service";
        issuers["admin-service"] = "Admin Service";
    }
    
    std::string createAdvancedToken(const std::string& userId, 
                                  const std::string& issuer,
                                  const std::map<std::string, std::string>& custom_claims) {
        try {
            auto builder = jwt::create()
                .set_issuer(issuer)
                .set_subject(userId)
                .set_audience("multi-service")
                .set_issued_now()
                .set_expires_in(std::chrono::hours{8})
                .set_not_before(std::chrono::system_clock::now())
                .set_id(generateJTI()); // Unique JWT ID
            
            // Add custom claims
            for (const auto& claim : custom_claims) {
                builder.set_payload_claim(claim.first, jwt::claim(claim.second));
            }
            
            // Timestamp claim
            auto now = std::chrono::system_clock::now();
            auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
            builder.set_payload_claim("created_at", jwt::claim(timestamp));
            
            return builder.sign(jwt::algorithm::hs256{secret});
            
        } catch (const std::exception& e) {
            throw std::runtime_error("Advanced token creation failed: " + std::string(e.what()));
        }
    }
    
    struct TokenValidationResult {
        bool is_valid;
        std::string error_message;
        std::string user_id;
        std::string issuer;
        std::map<std::string, std::string> claims;
        std::chrono::system_clock::time_point expires_at;
    };
    
    TokenValidationResult validateAdvancedToken(const std::string& token) {
        TokenValidationResult result;
        result.is_valid = false;
        
        try {
            auto decoded = jwt::decode(token);
            
            // Issuer validation
            std::string issuer = decoded.get_issuer();
            if (issuers.find(issuer) == issuers.end()) {
                result.error_message = "Unknown issuer: " + issuer;
                return result;
            }
            
            // Basic validation
            auto verifier = jwt::verify()
                .allow_algorithm(jwt::algorithm::hs256{secret})
                .with_audience("multi-service")
                .with_issuer(issuer);
                
            verifier.verify(decoded);
            
            // Custom validation: token age check
            auto created_at_claim = decoded.get_payload_claim("created_at");
            if (created_at_claim.get_type() == jwt::json::type::number) {
                auto created_timestamp = created_at_claim.as_int();
                auto now_timestamp = std::chrono::duration_cast<std::chrono::seconds>(
                    std::chrono::system_clock::now().time_since_epoch()).count();
                
                // Reject tokens older than 24 hours
                if (now_timestamp - created_timestamp > 24 * 3600) {
                    result.error_message = "Token is too old";
                    return result;
                }
            }
            
            // Set result
            result.is_valid = true;
            result.user_id = decoded.get_subject();
            result.issuer = issuer;
            result.expires_at = decoded.get_expires_at();
            
            // Get all payload claims
            for (const auto& claim : decoded.get_payload_claims()) {
                if (claim.second.get_type() == jwt::json::type::string) {
                    result.claims[claim.first] = claim.second.as_string();
                }
            }
            
        } catch (const jwt::token_verification_exception& e) {
            result.error_message = "Verification failed: " + std::string(e.what());
        } catch (const std::exception& e) {
            result.error_message = "Validation error: " + std::string(e.what());
        }
        
        return result;
    }
    
    bool hasPermission(const std::string& token, const std::string& required_permission) {
        try {
            auto decoded = jwt::decode(token);
            auto permissions_claim = decoded.get_payload_claim("permissions");
            
            if (permissions_claim.get_type() == jwt::json::type::array) {
                for (const auto& perm : permissions_claim.as_array()) {
                    if (perm.get_type() == jwt::json::type::string && 
                        perm.as_string() == required_permission) {
                        return true;
                    }
                }
            }
            return false;
        } catch (const std::exception& e) {
            std::cerr << "Permission check failed: " << e.what() << std::endl;
            return false;
        }
    }
    
private:
    std::string generateJTI() {
        // Simple JWT ID generation (use UUID in actual implementation)
        auto now = std::chrono::system_clock::now();
        auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
            now.time_since_epoch()).count();
        return "jwt_" + std::to_string(timestamp);
    }
};

// Usage example
int main() {
    AdvancedJWTService advancedService("advanced-secret-key-256-bits");
    
    // Create token with custom claims
    std::map<std::string, std::string> claims = {
        {"department", "engineering"},
        {"permissions", "read,write,admin"},
        {"level", "senior"}
    };
    
    std::string token = advancedService.createAdvancedToken("user789", "auth-service", claims);
    std::cout << "Advanced Token: " << token << std::endl;
    
    // Advanced validation
    auto result = advancedService.validateAdvancedToken(token);
    if (result.is_valid) {
        std::cout << "Token is valid!" << std::endl;
        std::cout << "User ID: " << result.user_id << std::endl;
        std::cout << "Issuer: " << result.issuer << std::endl;
        std::cout << "Custom Claims:" << std::endl;
        for (const auto& claim : result.claims) {
            std::cout << "  " << claim.first << ": " << claim.second << std::endl;
        }
        
        // Permission check
        if (advancedService.hasPermission(token, "admin")) {
            std::cout << "User has admin permission" << std::endl;
        }
        
    } else {
        std::cout << "Token validation failed: " << result.error_message << std::endl;
    }
    
    return 0;
}

JSON Web Key Set (JWKS) Support

#include <jwt-cpp/jwt.h>
#include <iostream>
#include <string>

class JWKSJWTService {
private:
    std::string jwks_uri;
    
public:
    JWKSJWTService(const std::string& jwks_endpoint) : jwks_uri(jwks_endpoint) {}
    
    bool validateWithJWKS(const std::string& token) {
        try {
            auto decoded = jwt::decode(token);
            
            // Get key from JWKS (requires HTTP client in actual implementation)
            // This example is simplified
            std::string kid = decoded.get_key_id();
            
            // In actual JWKS implementation, retrieve key from remote based on kid
            std::cout << "Key ID: " << kid << std::endl;
            std::cout << "JWKS URI: " << jwks_uri << std::endl;
            
            // Simplified validation (actually retrieve key from JWKS and validate)
            auto verifier = jwt::verify()
                .allow_algorithm(jwt::algorithm::rs256{"public_key", "", "", ""});
                
            // verifier.verify(decoded);
            std::cout << "JWKS validation would be performed here" << std::endl;
            return true;
            
        } catch (const std::exception& e) {
            std::cerr << "JWKS validation failed: " << e.what() << std::endl;
            return false;
        }
    }
};

Error Handling and Security Best Practices

#include <jwt-cpp/jwt.h>
#include <iostream>
#include <chrono>

class SecureJWTService {
private:
    std::string secret;
    std::chrono::seconds max_token_age;
    
public:
    SecureJWTService(const std::string& secret_key, std::chrono::seconds max_age = std::chrono::hours{1}) 
        : secret(secret_key), max_token_age(max_age) {}
    
    enum class ValidationError {
        NONE,
        EXPIRED,
        INVALID_SIGNATURE,
        INVALID_ISSUER,
        INVALID_AUDIENCE,
        TOKEN_TOO_OLD,
        MALFORMED_TOKEN,
        UNKNOWN_ERROR
    };
    
    struct SecureValidationResult {
        bool is_valid;
        ValidationError error;
        std::string error_message;
        std::string user_id;
        std::chrono::system_clock::time_point expires_at;
    };
    
    SecureValidationResult secureValidate(const std::string& token, 
                                        const std::string& expected_issuer = "secure-app",
                                        const std::string& expected_audience = "api") {
        SecureValidationResult result;
        result.is_valid = false;
        result.error = ValidationError::UNKNOWN_ERROR;
        
        try {
            // Decode token
            auto decoded = jwt::decode(token);
            
            // Security check 1: Algorithm verification
            if (decoded.get_algorithm() != "HS256") {
                result.error = ValidationError::INVALID_SIGNATURE;
                result.error_message = "Unsupported algorithm: " + decoded.get_algorithm();
                return result;
            }
            
            // Security check 2: Token age verification
            auto now = std::chrono::system_clock::now();
            auto issued_at = decoded.get_issued_at();
            auto token_age = std::chrono::duration_cast<std::chrono::seconds>(now - issued_at);
            
            if (token_age > max_token_age) {
                result.error = ValidationError::TOKEN_TOO_OLD;
                result.error_message = "Token is too old";
                return result;
            }
            
            // Main validation
            auto verifier = jwt::verify()
                .allow_algorithm(jwt::algorithm::hs256{secret})
                .with_issuer(expected_issuer)
                .with_audience(expected_audience)
                .leeway(std::chrono::seconds{10}); // 10 second leeway
                
            verifier.verify(decoded);
            
            // Success
            result.is_valid = true;
            result.error = ValidationError::NONE;
            result.user_id = decoded.get_subject();
            result.expires_at = decoded.get_expires_at();
            
        } catch (const jwt::token_expired_exception& e) {
            result.error = ValidationError::EXPIRED;
            result.error_message = "Token expired";
        } catch (const jwt::signature_verification_exception& e) {
            result.error = ValidationError::INVALID_SIGNATURE;
            result.error_message = "Invalid signature";
        } catch (const jwt::token_verification_exception& e) {
            result.error = ValidationError::INVALID_ISSUER;
            result.error_message = e.what();
        } catch (const std::exception& e) {
            result.error = ValidationError::MALFORMED_TOKEN;
            result.error_message = "Malformed token: " + std::string(e.what());
        }
        
        return result;
    }
    
    std::string getErrorString(ValidationError error) {
        switch (error) {
            case ValidationError::NONE: return "No error";
            case ValidationError::EXPIRED: return "Token expired";
            case ValidationError::INVALID_SIGNATURE: return "Invalid signature";
            case ValidationError::INVALID_ISSUER: return "Invalid issuer";
            case ValidationError::INVALID_AUDIENCE: return "Invalid audience";
            case ValidationError::TOKEN_TOO_OLD: return "Token too old";
            case ValidationError::MALFORMED_TOKEN: return "Malformed token";
            case ValidationError::UNKNOWN_ERROR: return "Unknown error";
            default: return "Undefined error";
        }
    }
};

// Usage example
int main() {
    SecureJWTService secureService("secure-secret-key-256-bits", std::chrono::minutes{30});
    
    // Create normal token
    auto token = jwt::create()
        .set_issuer("secure-app")
        .set_subject("secureuser123")
        .set_audience("api")
        .set_issued_now()
        .set_expires_in(std::chrono::minutes{15})
        .sign(jwt::algorithm::hs256{"secure-secret-key-256-bits"});
    
    std::cout << "Secure Token: " << token << std::endl;
    
    // Secure validation
    auto result = secureService.secureValidate(token);
    
    if (result.is_valid) {
        std::cout << "Secure validation passed!" << std::endl;
        std::cout << "User ID: " << result.user_id << std::endl;
    } else {
        std::cout << "Secure validation failed!" << std::endl;
        std::cout << "Error: " << secureService.getErrorString(result.error) << std::endl;
        std::cout << "Details: " << result.error_message << std::endl;
    }
    
    return 0;
}

Performance Optimization and Benchmarking

#include <jwt-cpp/jwt.h>
#include <iostream>
#include <chrono>
#include <vector>

class PerformanceJWTService {
private:
    std::string secret;
    jwt::algorithm::hs256 algorithm;
    jwt::verifier<jwt::default_clock> cached_verifier;
    
public:
    PerformanceJWTService(const std::string& secret_key) 
        : secret(secret_key), 
          algorithm(secret_key),
          cached_verifier(jwt::verify().allow_algorithm(algorithm).with_issuer("perf-test")) {}
    
    // Fast token creation (using pre-built builder)
    std::string fastCreateToken(const std::string& userId) {
        return jwt::create()
            .set_issuer("perf-test")
            .set_subject(userId)
            .set_issued_now()
            .set_expires_in(std::chrono::hours{1})
            .sign(algorithm);
    }
    
    // Fast validation (using cached verifier)
    bool fastValidateToken(const std::string& token) {
        try {
            auto decoded = jwt::decode(token);
            cached_verifier.verify(decoded);
            return true;
        } catch (const jwt::token_verification_exception&) {
            return false;
        }
    }
    
    // Benchmark test
    void runBenchmark(int iterations = 10000) {
        std::cout << "Running JWT performance benchmark..." << std::endl;
        std::cout << "Iterations: " << iterations << std::endl;
        
        // Token creation benchmark
        auto start = std::chrono::high_resolution_clock::now();
        std::vector<std::string> tokens;
        tokens.reserve(iterations);
        
        for (int i = 0; i < iterations; ++i) {
            tokens.push_back(fastCreateToken("user" + std::to_string(i)));
        }
        
        auto end = std::chrono::high_resolution_clock::now();
        auto creation_time = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
        
        std::cout << "Token creation: " << creation_time.count() << " microseconds" << std::endl;
        std::cout << "Average per token: " << creation_time.count() / iterations << " microseconds" << std::endl;
        
        // Token validation benchmark
        start = std::chrono::high_resolution_clock::now();
        int valid_count = 0;
        
        for (const auto& token : tokens) {
            if (fastValidateToken(token)) {
                valid_count++;
            }
        }
        
        end = std::chrono::high_resolution_clock::now();
        auto validation_time = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
        
        std::cout << "Token validation: " << validation_time.count() << " microseconds" << std::endl;
        std::cout << "Average per token: " << validation_time.count() / iterations << " microseconds" << std::endl;
        std::cout << "Valid tokens: " << valid_count << "/" << iterations << std::endl;
    }
};

// Usage example
int main() {
    PerformanceJWTService perfService("performance-test-secret-key");
    
    // Run benchmark
    perfService.runBenchmark(1000);
    
    return 0;
}