@@ -30,17 +30,23 @@ namespace
3030 return jsonValue.get <picojson::object>();
3131 }
3232
33- template <typename ValueType = std::string>
34- ValueType getValueByKey (const picojson::object & jsonObject, const std::string & key) {
33+ template <typename ValueType = std::string, bool throw_on_exception = true >
34+ std::optional< ValueType> getValueByKey (const picojson::object & jsonObject, const std::string & key) {
3535 auto it = jsonObject.find (key); // Find the key in the object
3636 if (it == jsonObject.end ())
3737 {
38- throw std::runtime_error (" Key not found: " + key);
38+ if constexpr (throw_on_exception)
39+ throw std::runtime_error (" Key not found: " + key);
40+ else
41+ return std::nullopt ;
3942 }
4043
4144 const picojson::value & value = it->second ;
4245 if (!value.is <ValueType>()) {
43- throw std::runtime_error (" Value for key '" + key + " ' has incorrect type." );
46+ if constexpr (throw_on_exception)
47+ throw std::runtime_error (" Value for key '" + key + " ' has incorrect type." );
48+ else
49+ return std::nullopt ;
4450 }
4551
4652 return value.get <ValueType>();
@@ -94,11 +100,9 @@ bool GoogleTokenProcessor::resolveAndValidate(const TokenCredentials & credentia
94100 throw Exception (ErrorCodes::AUTHENTICATION_FAILED,
95101 " {}: Specified username_claim {} not found in token" , processor_name, username_claim);
96102
97- bool has_email = user_info_json.contains (" email" );
98- if (has_email)
99- user_info[" email" ] = getValueByKey (user_info_json, " email" );
103+ user_info[" email" ] = getValueByKey<std::string, false >(user_info_json, " email" ).value_or (" " );
100104
101- user_info[username_claim] = getValueByKey (user_info_json, username_claim);
105+ user_info[username_claim] = getValueByKey (user_info_json, username_claim). value () ;
102106
103107 String user_name = user_info[username_claim];
104108
@@ -109,11 +113,11 @@ bool GoogleTokenProcessor::resolveAndValidate(const TokenCredentials & credentia
109113
110114 auto token_info = getObjectFromURI (Poco::URI (" https://www.googleapis.com/oauth2/v3/tokeninfo" ), token);
111115 if (token_info.contains (" exp" ))
112- const_cast <TokenCredentials &>(credentials).setExpiresAt (std::chrono::system_clock::from_time_t ((getValueByKey<time_t >(token_info, " exp" ))));
116+ const_cast <TokenCredentials &>(credentials).setExpiresAt (std::chrono::system_clock::from_time_t ((getValueByKey<time_t >(token_info, " exp" ). value () )));
113117
114118 // / Groups info can only be retrieved if user email is known.
115119 // / If no email found in user info, we skip this step and there are no external roles for the user.
116- if (has_email )
120+ if (!user_info[ " email " ]. empty () )
117121 {
118122 std::set<String> external_groups_names;
119123 const Poco::URI get_groups_uri = Poco::URI (" https://cloudidentity.googleapis.com/v1/groups/-/memberships:searchDirectGroups?query=member_key_id==" + user_info[" email" ] + " '" );
@@ -139,10 +143,13 @@ bool GoogleTokenProcessor::resolveAndValidate(const TokenCredentials & credentia
139143 }
140144
141145 auto group_data = group.get <picojson::object>();
142- String group_name = getValueByKey (group_data[" groupKey" ].get <picojson::object>(), " id" );
143- external_groups_names.insert (group_name);
144- LOG_TRACE (getLogger (" TokenAuthentication" ),
145- " {}: User {}: new external group {}" , processor_name, user_name, group_name);
146+ String group_name = getValueByKey<std::string, false >(group_data[" groupKey" ].get <picojson::object>(), " id" ).value_or (" " );
147+ if (!group_name.empty ())
148+ {
149+ external_groups_names.insert (group_name);
150+ LOG_TRACE (getLogger (" TokenAuthentication" ),
151+ " {}: User {}: new external group {}" , processor_name, user_name, group_name);
152+ }
146153 }
147154
148155 const_cast <TokenCredentials &>(credentials).setGroups (external_groups_names);
@@ -172,7 +179,7 @@ bool AzureTokenProcessor::resolveAndValidate(const TokenCredentials & credential
172179 try
173180 {
174181 picojson::object user_info_json = getObjectFromURI (Poco::URI (" https://graph.microsoft.com/oidc/userinfo" ), token);
175- String username = getValueByKey (user_info_json, username_claim);
182+ String username = getValueByKey (user_info_json, username_claim). value () ;
176183 if (!username.empty ())
177184 {
178185 // / Credentials are passed as const everywhere up the flow, so we have to comply,
@@ -227,9 +234,12 @@ bool AzureTokenProcessor::resolveAndValidate(const TokenCredentials & credential
227234 if (!group_data.contains (" displayName" ))
228235 continue ;
229236
230- String group_name = getValueByKey (group_data, " displayName" );
231- external_groups_names.insert (group_name);
232- LOG_TRACE (getLogger (" TokenAuthentication" ), " {}: User {}: new external group {}" , processor_name, credentials.getUserName (), group_name);
237+ String group_name = getValueByKey<std::string, false >(group_data, " displayName" ).value_or (" " );
238+ if (!group_name.empty ())
239+ {
240+ external_groups_names.insert (group_name);
241+ LOG_TRACE (getLogger (" TokenAuthentication" ), " {}: User {}: new external group {}" , processor_name, credentials.getUserName (), group_name);
242+ }
233243 }
234244 }
235245 catch (const Exception & e)
@@ -285,7 +295,7 @@ OpenIdTokenProcessor::OpenIdTokenProcessor(const String & processor_name_,
285295 if (!openid_config.contains (" userinfo_endpoint" ) || !openid_config.contains (" introspection_endpoint" ))
286296 throw Exception (ErrorCodes::AUTHENTICATION_FAILED, " {}: Cannot extract userinfo_endpoint or introspection_endpoint from OIDC configuration, consider manual configuration." , processor_name);
287297
288- if (! openid_config.contains (" jwks_uri" ))
298+ if (openid_config.contains (" jwks_uri" ))
289299 {
290300 LOG_TRACE (getLogger (" TokenAuthentication" ), " {}: JWKS URI set, local JWT processing will be attempted" , processor_name_);
291301 jwt_validator.emplace (processor_name_ + " jwks_val" ,
@@ -294,7 +304,7 @@ OpenIdTokenProcessor::OpenIdTokenProcessor(const String & processor_name_,
294304 groups_claim_,
295305 " " ,
296306 verifier_leeway_,
297- getValueByKey (openid_config, " jwks_uri" ),
307+ getValueByKey (openid_config, " jwks_uri" ). value () ,
298308 jwks_cache_lifetime_);
299309 }
300310}
@@ -311,7 +321,7 @@ bool OpenIdTokenProcessor::resolveAndValidate(const TokenCredentials & credentia
311321 {
312322 auto decoded_token = jwt::decode (token);
313323 user_info_json = decoded_token.get_payload_json ();
314- username = getValueByKey (user_info_json, username_claim);
324+ username = getValueByKey (user_info_json, username_claim). value () ;
315325
316326 // / TODO: Now we work only with Keycloak -- and it provides expires_at in token itself. Need to add actual token introspection logic for other OIDC providers.
317327 if (decoded_token.has_expires_at ())
@@ -329,7 +339,7 @@ bool OpenIdTokenProcessor::resolveAndValidate(const TokenCredentials & credentia
329339 try
330340 {
331341 user_info_json = getObjectFromURI (userinfo_endpoint, token);
332- username = getValueByKey (user_info_json, username_claim);
342+ username = getValueByKey (user_info_json, username_claim). value () ;
333343 }
334344 catch (...)
335345 {
0 commit comments