@@ -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,
@@ -224,9 +231,15 @@ bool AzureTokenProcessor::resolveAndValidate(const TokenCredentials & credential
224231 }
225232
226233 auto group_data = group.get <picojson::object>();
227- String group_name = getValueByKey (group_data, " id" );
228- external_groups_names.insert (group_name);
229- LOG_TRACE (getLogger (" TokenAuthentication" ), " {}: User {}: new external group {}" , processor_name, credentials.getUserName (), group_name);
234+ if (!group_data.contains (" displayName" ))
235+ continue ;
236+
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+ }
230243 }
231244 }
232245 catch (const Exception & e)
@@ -282,7 +295,7 @@ OpenIdTokenProcessor::OpenIdTokenProcessor(const String & processor_name_,
282295 if (!openid_config.contains (" userinfo_endpoint" ) || !openid_config.contains (" introspection_endpoint" ))
283296 throw Exception (ErrorCodes::AUTHENTICATION_FAILED, " {}: Cannot extract userinfo_endpoint or introspection_endpoint from OIDC configuration, consider manual configuration." , processor_name);
284297
285- if (! openid_config.contains (" jwks_uri" ))
298+ if (openid_config.contains (" jwks_uri" ))
286299 {
287300 LOG_TRACE (getLogger (" TokenAuthentication" ), " {}: JWKS URI set, local JWT processing will be attempted" , processor_name_);
288301 jwt_validator.emplace (processor_name_ + " jwks_val" ,
@@ -291,7 +304,7 @@ OpenIdTokenProcessor::OpenIdTokenProcessor(const String & processor_name_,
291304 groups_claim_,
292305 " " ,
293306 verifier_leeway_,
294- getValueByKey (openid_config, " jwks_uri" ),
307+ getValueByKey (openid_config, " jwks_uri" ). value () ,
295308 jwks_cache_lifetime_);
296309 }
297310}
@@ -308,7 +321,7 @@ bool OpenIdTokenProcessor::resolveAndValidate(const TokenCredentials & credentia
308321 {
309322 auto decoded_token = jwt::decode (token);
310323 user_info_json = decoded_token.get_payload_json ();
311- username = getValueByKey (user_info_json, username_claim);
324+ username = getValueByKey (user_info_json, username_claim). value () ;
312325
313326 // / 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.
314327 if (decoded_token.has_expires_at ())
@@ -326,7 +339,7 @@ bool OpenIdTokenProcessor::resolveAndValidate(const TokenCredentials & credentia
326339 try
327340 {
328341 user_info_json = getObjectFromURI (userinfo_endpoint, token);
329- username = getValueByKey (user_info_json, username_claim);
342+ username = getValueByKey (user_info_json, username_claim). value () ;
330343 }
331344 catch (...)
332345 {
0 commit comments