2626#include < openssl/bn.h>
2727#include < curl/curl.h>
2828
29- // Thread-local error storage
29+ // Thread-local error storage with context
3030static thread_local std::string g_last_error;
31+ static thread_local std::string g_last_error_context; // Additional context information
3132static thread_local mcp_auth_error_t g_last_error_code = MCP_AUTH_SUCCESS;
3233
3334// Global initialization state
@@ -38,12 +39,22 @@ static std::mutex g_init_mutex;
3839static void set_error (mcp_auth_error_t code, const std::string& message) {
3940 g_last_error_code = code;
4041 g_last_error = message;
42+ g_last_error_context.clear ();
43+ }
44+
45+ // Set error with additional context
46+ static void set_error_with_context (mcp_auth_error_t code, const std::string& message,
47+ const std::string& context) {
48+ g_last_error_code = code;
49+ g_last_error = message;
50+ g_last_error_context = context;
4151}
4252
4353// Clear error state
4454static void clear_error () {
4555 g_last_error_code = MCP_AUTH_SUCCESS;
4656 g_last_error.clear ();
57+ g_last_error_context.clear ();
4758}
4859
4960// ========================================================================
@@ -931,6 +942,10 @@ struct mcp_auth_client {
931942 std::string last_alg;
932943 std::string last_kid;
933944
945+ // Error context for debugging
946+ std::string last_error_context;
947+ mcp_auth_error_t last_error_code = MCP_AUTH_SUCCESS;
948+
934949 // JWKS cache
935950 std::vector<jwks_key> cached_keys;
936951 std::chrono::steady_clock::time_point cache_timestamp;
@@ -947,6 +962,16 @@ struct mcp_auth_validation_options {
947962 int64_t clock_skew = 60 ;
948963};
949964
965+ // Store error in client structure with context (moved after struct definition)
966+ static void set_client_error (mcp_auth_client_t client, mcp_auth_error_t code,
967+ const std::string& message, const std::string& context = " " ) {
968+ if (client) {
969+ client->last_error_code = code;
970+ client->last_error_context = context.empty () ? message : message + " (" + context + " )" ;
971+ }
972+ set_error_with_context (code, message, context);
973+ }
974+
950975struct mcp_auth_token_payload {
951976 std::string subject;
952977 std::string issuer;
@@ -1109,11 +1134,17 @@ static void invalidate_cache(mcp_auth_client_t client) {
11091134static bool fetch_and_cache_jwks (mcp_auth_client_t client) {
11101135 std::string jwks_json;
11111136 if (!fetch_jwks_json (client->jwks_uri , jwks_json)) {
1137+ set_client_error (client, MCP_AUTH_ERROR_JWKS_FETCH_FAILED,
1138+ " Failed to fetch JWKS" ,
1139+ " URI: " + client->jwks_uri + " , Error: " + g_last_error);
11121140 return false ;
11131141 }
11141142
11151143 std::vector<jwks_key> keys;
11161144 if (!parse_jwks (jwks_json, keys)) {
1145+ set_client_error (client, MCP_AUTH_ERROR_JWKS_FETCH_FAILED,
1146+ " Failed to parse JWKS response" ,
1147+ " URI: " + client->jwks_uri );
11171148 return false ;
11181149 }
11191150
@@ -1289,7 +1320,12 @@ mcp_auth_error_t mcp_auth_client_create(
12891320 }
12901321
12911322 if (!client || !jwks_uri || !issuer) {
1292- set_error (MCP_AUTH_ERROR_INVALID_PARAMETER, " Invalid parameter" );
1323+ std::string context = " Missing: " ;
1324+ if (!client) context += " client ptr, " ;
1325+ if (!jwks_uri) context += " jwks_uri, " ;
1326+ if (!issuer) context += " issuer" ;
1327+ set_error_with_context (MCP_AUTH_ERROR_INVALID_PARAMETER,
1328+ " Invalid parameters" , context);
12931329 return MCP_AUTH_ERROR_INVALID_PARAMETER;
12941330 }
12951331
@@ -1510,7 +1546,18 @@ mcp_auth_error_t mcp_auth_validate_token(
15101546 }
15111547
15121548 if (!client || !token || !result) {
1513- set_error (MCP_AUTH_ERROR_INVALID_PARAMETER, " Invalid parameter" );
1549+ std::string context = " Missing: " ;
1550+ if (!client) context += " client, " ;
1551+ if (!token) context += " token, " ;
1552+ if (!result) context += " result" ;
1553+
1554+ if (result) {
1555+ result->valid = false ;
1556+ result->error_code = MCP_AUTH_ERROR_INVALID_PARAMETER;
1557+ result->error_message = " Invalid parameters" ;
1558+ }
1559+ set_client_error (client, MCP_AUTH_ERROR_INVALID_PARAMETER,
1560+ " Invalid parameters for token validation" , context);
15141561 return MCP_AUTH_ERROR_INVALID_PARAMETER;
15151562 }
15161563
@@ -1606,9 +1653,13 @@ mcp_auth_error_t mcp_auth_validate_token(
16061653
16071654 if (payload_data.expiration > 0 ) {
16081655 if (now > payload_data.expiration + clock_skew) {
1609- set_error (MCP_AUTH_ERROR_EXPIRED_TOKEN, " JWT has expired" );
1656+ std::string context = " Token expired at " + std::to_string (payload_data.expiration ) +
1657+ " , current time: " + std::to_string (now) +
1658+ " , clock skew: " + std::to_string (clock_skew) + " s" ;
1659+ set_client_error (client, MCP_AUTH_ERROR_EXPIRED_TOKEN,
1660+ " JWT has expired" , context);
16101661 result->error_code = MCP_AUTH_ERROR_EXPIRED_TOKEN;
1611- result->error_message = safe_strdup (" JWT has expired" );
1662+ result->error_message = safe_strdup (( " JWT has expired [ " + context + " ] " ). c_str () );
16121663 return MCP_AUTH_ERROR_EXPIRED_TOKEN;
16131664 }
16141665 }
@@ -1954,10 +2005,46 @@ mcp_auth_error_t mcp_auth_get_last_error_code(void) {
19542005 return g_last_error_code;
19552006}
19562007
2008+ // Get last error with full context information
2009+ mcp_auth_error_t mcp_auth_get_last_error_full (char ** error_message) {
2010+ if (!error_message) {
2011+ return MCP_AUTH_ERROR_INVALID_PARAMETER;
2012+ }
2013+
2014+ std::string full_message = g_last_error;
2015+ if (!g_last_error_context.empty ()) {
2016+ full_message += " [Context: " + g_last_error_context + " ]" ;
2017+ }
2018+
2019+ *error_message = safe_strdup (full_message);
2020+ return g_last_error_code;
2021+ }
2022+
2023+ // Get error details from client
2024+ mcp_auth_error_t mcp_auth_client_get_last_error (mcp_auth_client_t client, char ** error_message) {
2025+ if (!client || !error_message) {
2026+ return MCP_AUTH_ERROR_INVALID_PARAMETER;
2027+ }
2028+
2029+ *error_message = safe_strdup (client->last_error_context );
2030+ return client->last_error_code ;
2031+ }
2032+
19572033void mcp_auth_clear_error (void ) {
19582034 clear_error ();
19592035}
19602036
2037+ // Clear error for a specific client
2038+ mcp_auth_error_t mcp_auth_client_clear_error (mcp_auth_client_t client) {
2039+ if (!client) {
2040+ return MCP_AUTH_ERROR_INVALID_PARAMETER;
2041+ }
2042+
2043+ client->last_error_code = MCP_AUTH_SUCCESS;
2044+ client->last_error_context .clear ();
2045+ return MCP_AUTH_SUCCESS;
2046+ }
2047+
19612048bool mcp_auth_has_error (void ) {
19622049 return g_last_error_code != MCP_AUTH_SUCCESS;
19632050}
@@ -2018,53 +2105,40 @@ bool mcp_auth_validate_scopes(
20182105}
20192106
20202107const char * mcp_auth_error_to_string (mcp_auth_error_t error_code) {
2021- // Thread-safe static strings for error descriptions
2022- static const char * const error_strings[] = {
2023- [MCP_AUTH_SUCCESS] = " Success" ,
2024- [MCP_AUTH_ERROR_INVALID_TOKEN] = " Invalid or malformed JWT token" ,
2025- [MCP_AUTH_ERROR_EXPIRED_TOKEN] = " JWT token has expired" ,
2026- [MCP_AUTH_ERROR_INVALID_SIGNATURE] = " JWT signature verification failed" ,
2027- [MCP_AUTH_ERROR_INVALID_ISSUER] = " Token issuer does not match expected value" ,
2028- [MCP_AUTH_ERROR_INVALID_AUDIENCE] = " Token audience does not match expected value" ,
2029- [MCP_AUTH_ERROR_INSUFFICIENT_SCOPE] = " Token lacks required scopes for operation" ,
2030- [MCP_AUTH_ERROR_JWKS_FETCH_FAILED] = " Failed to fetch JWKS from authorization server" ,
2031- [MCP_AUTH_ERROR_INVALID_KEY] = " No valid signing key found in JWKS" ,
2032- [MCP_AUTH_ERROR_NETWORK_ERROR] = " Network communication error" ,
2033- [MCP_AUTH_ERROR_INVALID_CONFIG] = " Invalid authentication configuration" ,
2034- [MCP_AUTH_ERROR_OUT_OF_MEMORY] = " Memory allocation failed" ,
2035- [MCP_AUTH_ERROR_INVALID_PARAMETER] = " Invalid parameter passed to function" ,
2036- [MCP_AUTH_ERROR_NOT_INITIALIZED] = " Authentication library not initialized" ,
2037- [MCP_AUTH_ERROR_INTERNAL_ERROR] = " Internal library error"
2038- };
2039-
2040- // Bounds checking
2041- if (error_code >= 0 ) {
2042- return error_strings[MCP_AUTH_SUCCESS];
2043- }
2044-
2045- int index = -error_code;
2046- if (index >= 1000 && index <= 1014 ) {
2047- // Map negative error codes to array indices
2048- switch (error_code) {
2049- case MCP_AUTH_ERROR_INVALID_TOKEN: return error_strings[MCP_AUTH_ERROR_INVALID_TOKEN];
2050- case MCP_AUTH_ERROR_EXPIRED_TOKEN: return error_strings[MCP_AUTH_ERROR_EXPIRED_TOKEN];
2051- case MCP_AUTH_ERROR_INVALID_SIGNATURE: return error_strings[MCP_AUTH_ERROR_INVALID_SIGNATURE];
2052- case MCP_AUTH_ERROR_INVALID_ISSUER: return error_strings[MCP_AUTH_ERROR_INVALID_ISSUER];
2053- case MCP_AUTH_ERROR_INVALID_AUDIENCE: return error_strings[MCP_AUTH_ERROR_INVALID_AUDIENCE];
2054- case MCP_AUTH_ERROR_INSUFFICIENT_SCOPE: return error_strings[MCP_AUTH_ERROR_INSUFFICIENT_SCOPE];
2055- case MCP_AUTH_ERROR_JWKS_FETCH_FAILED: return error_strings[MCP_AUTH_ERROR_JWKS_FETCH_FAILED];
2056- case MCP_AUTH_ERROR_INVALID_KEY: return error_strings[MCP_AUTH_ERROR_INVALID_KEY];
2057- case MCP_AUTH_ERROR_NETWORK_ERROR: return error_strings[MCP_AUTH_ERROR_NETWORK_ERROR];
2058- case MCP_AUTH_ERROR_INVALID_CONFIG: return error_strings[MCP_AUTH_ERROR_INVALID_CONFIG];
2059- case MCP_AUTH_ERROR_OUT_OF_MEMORY: return error_strings[MCP_AUTH_ERROR_OUT_OF_MEMORY];
2060- case MCP_AUTH_ERROR_INVALID_PARAMETER: return error_strings[MCP_AUTH_ERROR_INVALID_PARAMETER];
2061- case MCP_AUTH_ERROR_NOT_INITIALIZED: return error_strings[MCP_AUTH_ERROR_NOT_INITIALIZED];
2062- case MCP_AUTH_ERROR_INTERNAL_ERROR: return error_strings[MCP_AUTH_ERROR_INTERNAL_ERROR];
2063- default : break ;
2064- }
2108+ switch (error_code) {
2109+ case MCP_AUTH_SUCCESS:
2110+ return " Success" ;
2111+ case MCP_AUTH_ERROR_INVALID_TOKEN:
2112+ return " Invalid or malformed JWT token" ;
2113+ case MCP_AUTH_ERROR_EXPIRED_TOKEN:
2114+ return " JWT token has expired" ;
2115+ case MCP_AUTH_ERROR_INVALID_SIGNATURE:
2116+ return " JWT signature verification failed" ;
2117+ case MCP_AUTH_ERROR_INVALID_ISSUER:
2118+ return " Token issuer does not match expected value" ;
2119+ case MCP_AUTH_ERROR_INVALID_AUDIENCE:
2120+ return " Token audience does not match expected value" ;
2121+ case MCP_AUTH_ERROR_INSUFFICIENT_SCOPE:
2122+ return " Token lacks required scopes for operation" ;
2123+ case MCP_AUTH_ERROR_JWKS_FETCH_FAILED:
2124+ return " Failed to fetch JWKS from authorization server" ;
2125+ case MCP_AUTH_ERROR_INVALID_KEY:
2126+ return " No valid signing key found in JWKS" ;
2127+ case MCP_AUTH_ERROR_NETWORK_ERROR:
2128+ return " Network communication error" ;
2129+ case MCP_AUTH_ERROR_INVALID_CONFIG:
2130+ return " Invalid authentication configuration" ;
2131+ case MCP_AUTH_ERROR_OUT_OF_MEMORY:
2132+ return " Memory allocation failed" ;
2133+ case MCP_AUTH_ERROR_INVALID_PARAMETER:
2134+ return " Invalid parameter passed to function" ;
2135+ case MCP_AUTH_ERROR_NOT_INITIALIZED:
2136+ return " Authentication library not initialized" ;
2137+ case MCP_AUTH_ERROR_INTERNAL_ERROR:
2138+ return " Internal library error" ;
2139+ default :
2140+ return " Unknown error code" ;
20652141 }
2066-
2067- return " Unknown error code" ;
20682142}
20692143
20702144int mcp_auth_error_to_http_status (mcp_auth_error_t error_code) {
0 commit comments