-
-
Notifications
You must be signed in to change notification settings - Fork 243
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add jwks parsing (abandoned) #112
Changes from 15 commits
0366d4c
099c249
1e16c10
a6ccd9a
ffa40c9
901a6a7
df53292
7b370ce
e54dc12
b20a114
cda5166
ed5e307
ca30c1c
11a5717
3db45cb
9939c8c
c1b86f4
f5dad55
d72d592
609f6f6
e06fc33
d50c25e
c5fb84f
ca53842
ac48132
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
#include <iostream> | ||
#include <jwt-cpp/jwt.h> | ||
|
||
int main() | ||
{ | ||
std::string publicKey = R"({"keys": [{ | ||
"kid":"internal-gateway-jwt.api.sc.net", | ||
"alg": "RS256", | ||
"kty": "RSA", | ||
"use": "sig", | ||
"x5c": [ | ||
"MIIC+DCCAeCgAwIBAgIJBIGjYW6hFpn2MA0GCSqGSIb3DQEBBQUAMCMxITAfBgNVBAMTGGN1c3RvbWVyLWRlbW9zLmF1dGgwLmNvbTAeFw0xNjExMjIyMjIyMDVaFw0zMDA4MDEyMjIyMDVaMCMxITAfBgNVBAMTGGN1c3RvbWVyLWRlbW9zLmF1dGgwLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMnjZc5bm/eGIHq09N9HKHahM7Y31P0ul+A2wwP4lSpIwFrWHzxw88/7Dwk9QMc+orGXX95R6av4GF+Es/nG3uK45ooMVMa/hYCh0Mtx3gnSuoTavQEkLzCvSwTqVwzZ+5noukWVqJuMKNwjL77GNcPLY7Xy2/skMCT5bR8UoWaufooQvYq6SyPcRAU4BtdquZRiBT4U5f+4pwNTxSvey7ki50yc1tG49Per/0zA4O6Tlpv8x7Red6m1bCNHt7+Z5nSl3RX/QYyAEUX1a28VcYmR41Osy+o2OUCXYdUAphDaHo4/8rbKTJhlu8jEcc1KoMXAKjgaVZtG/v5ltx6AXY0CAwEAAaMvMC0wDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQUQxFG602h1cG+pnyvJoy9pGJJoCswDQYJKoZIhvcNAQEFBQADggEBAGvtCbzGNBUJPLICth3mLsX0Z4z8T8iu4tyoiuAshP/Ry/ZBnFnXmhD8vwgMZ2lTgUWwlrvlgN+fAtYKnwFO2G3BOCFw96Nm8So9sjTda9CCZ3dhoH57F/hVMBB0K6xhklAc0b5ZxUpCIN92v/w+xZoz1XQBHe8ZbRHaP1HpRM4M7DJk2G5cgUCyu3UBvYS41sHvzrxQ3z7vIePRA4WF4bEkfX12gvny0RsPkrbVMXX1Rj9t6V7QXrbPYBAO+43JvDGYawxYVvLhz+BJ45x50GFQmHszfY3BR9TPK8xmMmQwtIvLu1PMttNCs7niCYkSiUv2sc2mlq1i3IashGkkgmo=" | ||
], | ||
"n": "yeNlzlub94YgerT030codqEztjfU_S6X4DbDA_iVKkjAWtYfPHDzz_sPCT1Axz6isZdf3lHpq_gYX4Sz-cbe4rjmigxUxr-FgKHQy3HeCdK6hNq9ASQvMK9LBOpXDNn7mei6RZWom4wo3CMvvsY1w8tjtfLb-yQwJPltHxShZq5-ihC9irpLI9xEBTgG12q5lGIFPhTl_7inA1PFK97LuSLnTJzW0bj096v_TMDg7pOWm_zHtF53qbVsI0e3v5nmdKXdFf9BjIARRfVrbxVxiZHjU6zL6jY5QJdh1QCmENoejj_ytspMmGW7yMRxzUqgxcAqOBpVm0b-_mW3HoBdjQ", | ||
"e": "AQAB", | ||
"x5t": "NjVBRjY5MDlCMUIwNzU4RTA2QzZFMDQ4QzQ2MDAyQjVDNjk1RTM2Qg" | ||
} | ||
}, | ||
{ | ||
"kid":"internal-123456", | ||
"use":"sig", | ||
"x5c":["MIIG1TCCBL2gAwIBAgIIFvMVGp6t\/cMwDQYJKoZIhvcNAQELBQAwZjELMAkGA1UEBhMCR0IxIDAeBgNVBAoMF1N0YW5kYXJkIENoYXJ0ZXJlZCBCYW5rMTUwMwYDVQQDDCxTdGFuZGFyZCBDaGFydGVyZWQgQmFuayBTaWduaW5nIENBIEcxIC0gU0hBMjAeFw0xODEwMTAxMTI2MzVaFw0yMjEwMTAxMTI2MzVaMIG9MQswCQYDVQQGEwJTRzESMBAGA1UECAwJU2luZ2Fwb3JlMRIwEAYDVQQHDAlTaW5nYXBvcmUxIDAeBgNVBAoMF1N0YW5kYXJkIENoYXJ0ZXJlZCBCYW5rMRwwGgYDVQQLDBNGb3VuZGF0aW9uIFNlcnZpY2VzMSgwJgYDVQQDDB9pbnRlcm5hbC1nYXRld2F5LWp3dC5hcGkuc2MubmV0MRwwGgYJKoZIhvcNAQkBFg1BUElQU1NAc2MuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArVWBoIi3IJ4nOWXu7\/SDxczqMou1B+c4c2FdQrOXrK31HxAaz4WEtma9BLXFdFHJ5mCCPIvdUcVxxnCynqhMOkZ\/a7acQbUD9cDzI8isMB9JL7VooDw0CctxHxffjqQQVIEhC2Q7zsM1pQayR7cl+pbBlvHIoRxq2n1B0fFvfoiosjf4kDiCpgHdM+v5Hw9aVYmUbroHxmQWqhB0iRTJQPPLZqqQVC50A1Q\/96gkwoODyotc46Uy9wYEpdGrtDG\/thWay3fmMsjpWR0U25xFIrxTrfCGBblYpD7juukWWml2E9rtE2rHgUxbymxXjEw7xrMwcGrhOGyqwoBqJy1JVwIDAQABo4ICLTCCAikwZAYIKwYBBQUHAQEEWDBWMFQGCCsGAQUFBzABhkhodHRwOi8vY29yZW9jc3AuZ2xvYmFsLnN0YW5kYXJkY2hhcnRlcmVkLmNvbS9lamJjYS9wdWJsaWN3ZWIvc3RhdHVzL29jc3AwHQYDVR0OBBYEFIinW4BNDeVEFcuLf8YjZjtySoW9MAwGA1UdEwEB\/wQCMAAwHwYDVR0jBBgwFoAUfNZMoZi33nKrcmVU3TFVQnuEi\/4wggFCBgNVHR8EggE5MIIBNTCCATGggcKggb+GgbxodHRwOi8vY29yZWNybC5nbG9iYWwuc3RhbmRhcmRjaGFydGVyZWQuY29tL2VqYmNhL3B1YmxpY3dlYi93ZWJkaXN0L2NlcnRkaXN0P2NtZD1jcmwmaXNzdWVyPUNOPVN0YW5kYXJkJTIwQ2hhcnRlcmVkJTIwQmFuayUyMFNpZ25pbmclMjBDQSUyMEcxJTIwLSUyMFNIQTIsTz1TdGFuZGFyZCUyMENoYXJ0ZXJlZCUyMEJhbmssQz1HQqJqpGgwZjE1MDMGA1UEAwwsU3RhbmRhcmQgQ2hhcnRlcmVkIEJhbmsgU2lnbmluZyBDQSBHMSAtIFNIQTIxIDAeBgNVBAoMF1N0YW5kYXJkIENoYXJ0ZXJlZCBCYW5rMQswCQYDVQQGEwJHQjAOBgNVHQ8BAf8EBAMCBsAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMEMA0GCSqGSIb3DQEBCwUAA4ICAQBtsoRlDHuOTDChcWdfdVUtRgP0U0ijDSeJi8vULN1rgYnqqJc4PdJno50aiu9MGlxY02O7HW7ZVD6QEG\/pqHmZ0sbWpb\/fumMgZSjP65IcGuS53zgcNtLYnyXyEv+v5T\/CK3bk4Li6tUW3ScJPUwVWwP1E0\/u6aBSb5k\/h4lTwS1o88ybS5pJOg6XutXByp991QQrrs7tp7fKNynjNZbFuG3J1e09X+zTfJOpjaDUofQTkt8IyMRI6Cs4wI1eZA+dAIL8B0n8ze1mRl1FOJqgdZrAQjoqZkCTnc0Il5VY\/dUXxGVg6D9e5pfck3FWT107K9\/5EZoxytpqYXFCjMXi5hx4YjK17OUgm82mZhvqkNdzF8Yq2vFuB3LPfyelESq99xFLykvinrVm1NtZKeDTT1Jq\/VvZt6stO\/tovq1RfJJcznpYcwOzxlnhGR6E+hxuBx7aDJzGf0JaoRxQILH1B2XV9WDI3HPYQsP7XtriX+QUJ\/aly28QkV48RmaGYCsly43YZu1MKudSsw+dhnbZzRsg\/aes3dzGW2x137bQPtux7k2LCSpsTXgedhOys28YoGlsoe8kUv0myAU4Stt+I3mrwO3BKUn+tJggvlDiiiyT1tg2HiklyU\/2FxQkZRMeB0eRrXTpg3l9x2mpF+dDFxOMKszxwD2kgoEZgo6o58A=="], | ||
"n":"nr9UsxnPVd21iuiGcIJ_Qli2XVlAZe5VbELA1hO2-L4k5gI4fjHZ3ysUcautLpbOYogOQgsnlpsLrCmvNDvBDVzVp2nMbpguJlt12vHSP1fRJJpipGQ8qU-VaXsC4OjOQf3H9ojAU5Vfnl5gZ7kVCd8g4M29l-IRyNpxE-Ccxc2Y7molsCHT6GHLMMBVsd11JIOXMICJf4hz2YYkQ1t7C8SaB2RFRPuGO5Mn6mfAnwdmRera4TBz6_pIPPCgCbN8KOdJItWkr9F7Tjv_0nhh-ZVlQvbQ9PXHyKTj00g3IYUlbZIWHm0Ley__fzNZk2dyAAVjNA2QSzTZJc33MQx1pQ", | ||
"e":"AQAB", | ||
"x5t":"-qC0akuyiHTV5aFsKVWM9da7lzq6DLrj09I", | ||
"alg":"RS256", | ||
"kty":"RSA" | ||
} | ||
]})"; | ||
|
||
std::string token = "eyJraWQiOiJpbnRlcm5hbC1nYXRld2F5LWp3dC5hcGkuc2MubmV0IiwiYWxnIjoiUlMyNTYiLCJ0eXAiOiJKV1QifQ.eyJuYmYiOjE1Mzk3NjcwMTUsImlhdCI6MTUzOTc2Njk5MiwiaXNzIjoia29uZyIsImh0dHA6XC9cL3dzbzIub3JnXC9nYXRld2F5XC9zdWJzY3JpYmVyIjoidXZ0dXNlcjJAY2FyYm9uLnN1cGVyIiwib3JpZ2luYWxfaXNzIjoiaHR0cDpcL1wvd3NvMi5vcmdcL2dhdGV3YXkiLCJzdWIiOiJ1dnR1c2VyMkBjYXJib24uc3VwZXIiLCJodHRwOlwvXC93c28yLm9yZ1wvZ2F0ZXdheVwvZW5kdXNlciI6InV2dHVzZXIyQGNhcmJvbi5zdXBlciIsImp0aSI6IjI0NmJkZTlhLWQ4OGQtNGRlZC1hODhmLTRhMTNhOWJmODQ4ZiIsImh0dHA6XC9cL3dzbzIub3JnXC9nYXRld2F5XC9hcHBsaWNhdGlvbm5hbWUiOiJ1dnR1c2VyMl9hcHBfMSIsImV4cCI6MTUzOTc2NzkxNX0.foxbo6C30yr_wkF-5EkgtYUMG-4SXNfRsmewdT6MbE-RXVkIPkVk8kDP41yRXmnk4OxburCqawiGlzzEhfHoFf0qv0qZEmwEXSdcyRw-czZTs6ACjWYe8kejOCVmpvUrq01NgOhTwgVg6pv93BlcmNY--zytjx_9hlVm5SS1lZ0I21n45BIWu5JvBD51TZXEURb_XhL7RcF9I8mfzrRpB2fSHW38gj-nogsdOPA_y3S-hJKylmmaqmaQgTF-jP-gYr6eqKyGPVwc6fLZ5zqAup59SefdPEY23-WWmHzj968jlsDSEiCp_YiYTnF3tHVLFWDsrprYKwNb0_p95tBmPA"; | ||
|
||
auto decoded_jwt = jwt::decode(token); | ||
auto jwkskeys = jwt::parse_jwks_keys(publicKey); | ||
auto jwks = jwkskeys.get_jwks(decoded_jwt.get_key_id()); | ||
|
||
auto issuer = decoded_jwt.get_issuer(); | ||
auto x5c = jwks.get_x5c(); | ||
|
||
if (!x5c.empty() && !issuer.empty()) { | ||
auto verifier = jwt::verify() | ||
.allow_algorithm(jwt::algorithm::rs256(jwt::helper::convert_base64_der_to_pem(x5c), "", "", "")) | ||
.with_issuer(issuer) | ||
.leeway(10000000000UL); // value in seconds, add some to compensate timeout | ||
|
||
std::error_code err; | ||
verifier.verify(decoded_jwt, err); | ||
|
||
jwt::error::throw_if_error(err); | ||
dtiukalov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2668,6 +2668,162 @@ namespace jwt { | |
} | ||
} | ||
}; | ||
|
||
template<typename json_traits> | ||
class jwks { | ||
dtiukalov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
using basic_claim_t = basic_claim<json_traits>; | ||
template<typename> friend class jwks_keys; | ||
dtiukalov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
public: | ||
JWT_CLAIM_EXPLICIT jwks(const typename json_traits::string_type& jwks_token) { | ||
auto parse_claims = [](const typename json_traits::string_type& str) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this block seems to be copy pasted from the original 📓 for myself to look into a small refactor! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, and it costs an extra move, good point to refactor. |
||
using basic_claim_t = basic_claim<json_traits>; | ||
std::unordered_map<typename json_traits::string_type, basic_claim_t> res; | ||
typename json_traits::value_type val; | ||
if (!json_traits::parse(val, str)) | ||
throw std::runtime_error("Invalid json"); | ||
|
||
for (const auto& e : json_traits::as_object(val)) { | ||
res.emplace(e.first, basic_claim_t(e.second)); | ||
} | ||
|
||
return res; | ||
}; | ||
|
||
jwks_claims = parse_claims(jwks_token); | ||
} | ||
|
||
/** | ||
* Get algorithm claim | ||
* \return algorithm as string | ||
* \throw std::runtime_error If claim was not present | ||
* \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token) | ||
*/ | ||
typename json_traits::string_type get_algorithm() const { return get_jwks_claim("alg").as_string(); } | ||
|
||
/** | ||
* Get key id claim | ||
* \return key id as string | ||
* \throw std::runtime_error If claim was not present | ||
* \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token) | ||
*/ | ||
typename json_traits::string_type get_key_id() const { return get_jwks_claim("kid").as_string(); } | ||
|
||
/** | ||
* Get x5c claim | ||
* \return x5c as string | ||
* \throw std::runtime_error If claim was not present | ||
* \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token) | ||
*/ | ||
typename json_traits::string_type get_x5c() const { return json_traits::as_string(get_jwks_claim("x5c").as_array().front()); } //do not use serialize() instead | ||
|
||
/** | ||
* Check if algortihm is present ("alg") | ||
* \return true if present, false otherwise | ||
*/ | ||
bool has_algorithm() const noexcept { return has_jwks_claim("alg"); } | ||
|
||
/** | ||
* Check if key id is present ("kid") | ||
* \return true if present, false otherwise | ||
*/ | ||
bool has_key_id() const noexcept { return has_jwks_claim("kid"); } | ||
|
||
/** | ||
* Check if x5c is present ("x5c") | ||
* \return true if present, false otherwise | ||
*/ | ||
bool has_x5c_id() const noexcept { return has_jwks_claim("x5c"); } | ||
|
||
/** | ||
* Check if a jwks claim is present | ||
* \return true if claim was present, false otherwise | ||
*/ | ||
bool has_jwks_claim(const typename json_traits::string_type& name) const noexcept { return jwks_claims.count(name) != 0; } | ||
|
||
/** | ||
* Get jwks claim | ||
* \return Requested claim | ||
* \throw std::runtime_error If claim was not present | ||
*/ | ||
basic_claim_t get_jwks_claim(const typename json_traits::string_type& name) const { | ||
if (!has_jwks_claim(name)) { | ||
typename json_traits::string_type error_msg = "claim \""; | ||
error_msg += name; | ||
error_msg += "\" not found"; | ||
throw std::runtime_error(error_msg.c_str()); | ||
dtiukalov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
return jwks_claims.at(name); | ||
} | ||
|
||
bool empty() const noexcept { return jwks_claims.empty(); } | ||
|
||
protected: | ||
JWT_CLAIM_EXPLICIT jwks(const typename json_traits::value_type& claim) { | ||
for (const auto& e : json_traits::as_object(claim)) { | ||
jwks_claims.emplace(e.first, basic_claim_t(e.second)); | ||
} | ||
} | ||
|
||
private: | ||
std::unordered_map<typename json_traits::string_type, basic_claim_t> jwks_claims; | ||
}; | ||
|
||
template<typename json_traits> | ||
class jwks_keys { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if this "parser" could be a static method that returns a vector, this was the implementation is not limited on KIDs being used in all the keys Thoughts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. to_vector() is added |
||
using basic_claim_t = basic_claim<json_traits>; | ||
using jwks_t = jwks<json_traits>; | ||
|
||
public: | ||
|
||
JWT_CLAIM_EXPLICIT jwks_keys(const typename json_traits::string_type& jwks_keys_token) { | ||
auto parse_claims = [](const typename json_traits::string_type& str) { | ||
std::unordered_map<typename json_traits::string_type, jwks_t> res; | ||
typename json_traits::value_type val; | ||
if (!json_traits::parse(val, str)) | ||
throw std::runtime_error("Invalid json"); | ||
|
||
for (const auto& k : json_traits::as_object(val)) { | ||
for (const typename json_traits::value_type& item : json_traits::as_array(k.second)) { | ||
jwks_t jwks_entry(item); | ||
res.emplace(jwks_entry.get_key_id(), std::move(jwks_entry)); | ||
dtiukalov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
|
||
return res; | ||
}; | ||
|
||
jwks_keys_claims = parse_claims(jwks_keys_token); | ||
} | ||
|
||
/** | ||
* Get jwks | ||
* \return Requested jwks by key_id | ||
* \throw std::runtime_error If jwks was not present | ||
*/ | ||
jwks_t get_jwks(const typename json_traits::string_type& key_id) const { | ||
if (!has_jwks(key_id)) { | ||
typename json_traits::string_type error_msg = "jwks with key id \""; | ||
error_msg += key_id; | ||
error_msg += "\" not found"; | ||
throw std::runtime_error(error_msg.c_str()); | ||
} | ||
|
||
return jwks_keys_claims.at(key_id); | ||
} | ||
|
||
/** | ||
* Check if a jwks with the kid is present | ||
* \return true if jwks was present, false otherwise | ||
*/ | ||
bool has_jwks(const typename json_traits::string_type& key_id) const noexcept { return jwks_keys_claims.count(key_id) != 0; } | ||
|
||
bool empty() const noexcept { return jwks_keys_claims.empty(); } | ||
|
||
private: | ||
|
||
std::unordered_map<typename json_traits::string_type, jwks_t> jwks_keys_claims; | ||
}; | ||
|
||
/** | ||
* Create a verifier using the given clock | ||
|
@@ -2720,6 +2876,16 @@ namespace jwt { | |
decoded_jwt<json_traits> decode(const typename json_traits::string_type& token) { | ||
return decoded_jwt<json_traits>(token); | ||
} | ||
|
||
template<typename json_traits> | ||
jwks<json_traits> parse_jwks(const typename json_traits::string_type& token) { | ||
return jwks<json_traits>(token); | ||
} | ||
|
||
template<typename json_traits> | ||
jwks_keys<json_traits> parse_jwks_keys(const typename json_traits::string_type& token) { | ||
return jwks_keys<json_traits>(token); | ||
} | ||
|
||
#ifndef JWT_DISABLE_PICOJSON | ||
struct picojson_traits { | ||
|
@@ -2839,6 +3005,16 @@ namespace jwt { | |
decoded_jwt<picojson_traits> decode(const std::string& token, Decode decode) { | ||
return decoded_jwt<picojson_traits>(token, decode); | ||
} | ||
|
||
inline | ||
jwks<picojson_traits> parse_jwks(const picojson_traits::string_type& token) { | ||
return jwks<picojson_traits>(token); | ||
} | ||
|
||
inline | ||
jwks_keys<picojson_traits> parse_jwks_keys(const picojson_traits::string_type& token) { | ||
return jwks_keys<picojson_traits>(token); | ||
} | ||
#endif | ||
} // namespace jwt | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps, a simple token with no expiration is possible?