JWT-CPP
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
- GitHub Repository
- JWT-CPP Official Documentation
- JWT.io
- RFC 7519 - JSON Web Token
- OpenSSL Documentation
- C++ Reference
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;
}