23#include "http/JwtAuthenticator.h"
27#include <mbedtls/md.h>
28#include <mbedtls/base64.h>
29#include <mbedtls/error.h>
30#include <nlohmann/json.hpp>
32using json = nlohmann::json;
34#ifndef MBEDTLS_SHA256_DIGEST_LENGTH
35#define MBEDTLS_SHA256_DIGEST_LENGTH 32
47 size_t encoded_len = ((input.length() + 2) / 3) * 4 + 1;
48 char *encoded_data =
new char[encoded_len];
51 int result = mbedtls_base64_encode(
reinterpret_cast<unsigned char *
>(encoded_data), encoded_len, &len,
52 reinterpret_cast<const unsigned char *
>(input.c_str()), input.length());
56 std::cerr <<
"Base64 encoding failed with error code: " << result << std::endl;
57 delete[] encoded_data;
61 std::string base64_str(encoded_data, len);
62 std::string base64url_str(base64_str);
63 std::replace(base64url_str.begin(), base64url_str.end(),
'+',
'-');
64 std::replace(base64url_str.begin(), base64url_str.end(),
'/',
'_');
65 base64url_str.erase(std::remove(base64url_str.begin(), base64url_str.end(),
'='), base64url_str.end());
67 delete[] encoded_data;
74 std::string base64_input = input;
75 std::replace(base64_input.begin(), base64_input.end(),
'-',
'+');
76 std::replace(base64_input.begin(), base64_input.end(),
'_',
'/');
77 while (base64_input.size() % 4 != 0)
82 for (
size_t i = 0; i < base64_input.size(); ++i)
84 if (
static_cast<unsigned char>(base64_input[i]) > 127)
86 std::cout <<
"Invalid character found in base64: " << (int)base64_input[i] << std::endl;
91 size_t decoded_len = base64_input.size() * 3 / 4 + 4;
92 unsigned char *decoded_data =
new unsigned char[decoded_len];
94 int ret = mbedtls_base64_decode(decoded_data, decoded_len, &decoded_len,
95 (
const unsigned char *)base64_input.c_str(), base64_input.size());
100 mbedtls_strerror(ret, error_msg,
sizeof(error_msg));
101 std::cerr <<
"Base64 decoding failed: " << error_msg << std::endl;
102 delete[] decoded_data;
106 output.assign(
reinterpret_cast<char *
>(decoded_data), decoded_len);
107 delete[] decoded_data;
114 static const std::string base64url_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
116 if (str.length() % 4 != 0)
123 if (base64url_chars.find(c) == std::string::npos)
136 mbedtls_md_context_t ctx;
138 mbedtls_md_init(&ctx);
139 const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
140 mbedtls_md_setup(&ctx, md_info, 1);
141 mbedtls_md_hmac_starts(&ctx,
reinterpret_cast<const unsigned char *
>(
secretKey.c_str()),
secretKey.size());
142 mbedtls_md_hmac_update(&ctx,
reinterpret_cast<const unsigned char *
>(message.c_str()), message.size());
143 mbedtls_md_hmac_finish(&ctx, hmac_output);
144 mbedtls_md_free(&ctx);
152 json header = {{
"alg",
"HS256"}, {
"typ",
"JWT"}};
156 {
"iat", std::time(0)},
157 {
"exp", std::time(0) + 3600}};
161 std::string message = encodedHeader +
"." + encodedPayload;
164 return encodedHeader +
"." + encodedPayload +
"." + signature;
170 size_t first_dot = token.find(
'.');
171 size_t second_dot = token.find(
'.', first_dot + 1);
172 if (first_dot == std::string::npos || second_dot == std::string::npos)
177 header = token.substr(0, first_dot);
178 payload = token.substr(first_dot + 1, second_dot - first_dot - 1);
179 signature = token.substr(second_dot + 1);
203 size_t output_len = length * 4 / 3 + 4;
204 char *base64_output =
new char[output_len];
205 size_t written_len = 0;
207 mbedtls_base64_encode((
unsigned char *)base64_output, output_len, &written_len, data, length);
209 std::string base64_result(base64_output, written_len);
210 std::replace(base64_result.begin(), base64_result.end(),
'+',
'-');
211 std::replace(base64_result.begin(), base64_result.end(),
'/',
'_');
212 base64_result.erase(std::remove(base64_result.end() - 1, base64_result.end(),
'='), base64_result.end());
214 delete[] base64_output;
215 return base64_result;
221 std::string header_payload = encoded_header +
"." + encoded_payload;
223 mbedtls_md_context_t ctx;
224 mbedtls_md_init(&ctx);
225 const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
226 mbedtls_md_setup(&ctx, md_info, 1);
227 mbedtls_md_hmac_starts(&ctx, (
const unsigned char *)
secretKey.c_str(),
secretKey.length());
228 mbedtls_md_hmac_update(&ctx, (
const unsigned char *)header_payload.c_str(), header_payload.length());
231 mbedtls_md_hmac_finish(&ctx, hmac_output);
232 mbedtls_md_free(&ctx);
235 return computed_signature == signature;
241 auto parsed = json::parse(payload,
nullptr,
false);
242 if (parsed.is_discarded() || !parsed.contains(
"exp") || !parsed[
"exp"].is_number_integer())
244 std::cerr <<
"Invalid or missing 'exp' in JWT payload." << std::endl;
248 long long exp_timestamp = parsed[
"exp"].get<
long long>();
249 if (exp_timestamp <= 0)
252 auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
253 return now >= exp_timestamp;
259 size_t first_dot = token.find(
'.');
260 size_t second_dot = token.find(
'.', first_dot + 1);
261 if (first_dot == std::string::npos || second_dot == std::string::npos)
264 std::string encoded_payload = token.substr(first_dot + 1, second_dot - first_dot - 1);
265 std::string decoded_payload;
275 size_t first_dot = token.find(
'.');
276 size_t second_dot = token.find(
'.', first_dot + 1);
277 if (first_dot == std::string::npos || second_dot == std::string::npos)
280 std::string encoded_header = token.substr(0, first_dot);
281 std::string encoded_payload = token.substr(first_dot + 1, second_dot - first_dot - 1);
282 std::string signature = token.substr(second_dot + 1);
284 std::string decoded_header, decoded_payload;
Macro-based debug trace system with optional SD file logging.
#define TRACE_INIT(MODULE_NAME)
Declare trace usage in a source file for a given module.
#define MBEDTLS_SHA256_DIGEST_LENGTH
JWT JwtAuthenticator for embedded applications.
bool validateJWT(const std::string &token, bool validateExpiry=false) const
Validate a JWT's signature and optionally its expiration.
std::string base64urlEncode(const std::string &input) const
Encode a string using base64url encoding.
std::string hmacSHA256(const std::string &message) const
Calculate HMAC-SHA256 for a given message.
bool isJWTPayloadExpired(const std::string &payload) const
Check if a decoded JWT payload is expired.
bool verifyJWTSignature(const std::string &encoded_header, const std::string &encoded_payload, const std::string &signature) const
Verify a JWT's signature using HMAC-SHA256.
std::string bytesToBase64url(const unsigned char *data, size_t length) const
Convert a byte buffer to a base64url string.
std::string generateJWT(const std::string &userId, const std::string &userName) const
Generate a JWT for a given user.
bool isJWTExpired(const std::string &token) const
Check if a JWT is expired based on the exp claim.
bool decodeJWT(const std::string &token, std::string &header, std::string &payload, std::string &signature) const
Decode a JWT into its components.
void init(const std::string &secret, int expirySeconds)
Initialize the JwtAuthenticator with a secret key and expiry time.
bool base64urlDecode(const std::string &input, std::string &output) const
Decode a base64url-encoded string.
JwtAuthenticator()
Get an instance of the JwtAuthenticator.
bool isBase64urlEncoded(const std::string &str) const
Check if a string is valid base64url.
std::string secretKey
Construct a new JwtAuthenticator object.
Delegates to user or system configuration.