@@ -119,9 +119,28 @@ std::unique_ptr<IAccessTokenProcessor> IAccessTokenProcessor::parseTokenProcesso
119119
120120 return std::make_unique<AzureAccessTokenProcessor>(name, cache_lifetime, email_regex_str, tenant_id_str);
121121 }
122+ else if (provider == " openid" )
123+ {
124+ bool is_auto = config.hasProperty (prefix + " .configuration_endpoint" );
125+ bool is_manual = config.hasProperty (prefix + " .userinfo_endpoint" ) &&
126+ config.hasProperty (prefix + " .token_introspection_endpoint" ) &&
127+ (config.hasProperty (prefix + " .userinfo_endpoint" ) == config.hasProperty (prefix + " .token_introspection_endpoint" ));
128+
129+ if (is_auto && !is_manual)
130+ {
131+ return std::make_unique<OpenIDAccessTokenProcessor>(name, cache_lifetime, email_regex_str, config.getString (prefix + " .configuration_endpoint" ));
132+ }
133+ else if (!is_auto && is_manual)
134+ {
135+ return std::make_unique<OpenIDAccessTokenProcessor>(name, cache_lifetime, email_regex_str, config.getString (prefix + " .userinfo_endpoint" ), config.getString (prefix + " .token_introspection_endpoint" ));
136+ }
137+
138+ throw Exception (ErrorCodes::INVALID_CONFIG_PARAMETER, " Could not parse access token processor {}: "
139+ " Either configuration_endpoint or both userinfo_endpoint and token_introspection_endpoint shall be specified" , name);
140+ }
122141 else
123142 throw Exception (ErrorCodes::INVALID_CONFIG_PARAMETER,
124- " Could not parse access token processor {}: unknown provider {}" , name, provider);
143+ " Could not parse access token processor {}: unknown provider type {}" , name, provider);
125144 }
126145
127146 throw Exception (ErrorCodes::INVALID_CONFIG_PARAMETER,
@@ -316,4 +335,57 @@ String AzureAccessTokenProcessor::validateTokenAndGetUsername(const String & tok
316335 return getValueByKey (user_info_json, " sub" );
317336}
318337
338+ OpenIDAccessTokenProcessor::OpenIDAccessTokenProcessor (const String & name_,
339+ const UInt64 cache_invalidation_interval_,
340+ const String & email_regex_str,
341+ const String & openid_config_endpoint_)
342+ : IAccessTokenProcessor(name_, cache_invalidation_interval_, email_regex_str)
343+ {
344+ const picojson::object openid_config = getObjectFromURI (Poco::URI (openid_config_endpoint_));
345+
346+ if (!openid_config.contains (" userinfo_endpoint" ) || !openid_config.contains (" introspection_endpoint" ))
347+ throw Exception (ErrorCodes::AUTHENTICATION_FAILED, " {}: Cannot extract userinfo_endpoint or introspection_endpoint from OIDC configuration, consider manual configuration." , name);
348+ }
349+
350+ bool OpenIDAccessTokenProcessor::resolveAndValidate (const TokenCredentials & credentials)
351+ {
352+ const String & token = credentials.getToken ();
353+
354+ try
355+ {
356+ String username = validateTokenAndGetUsername (token);
357+ if (!username.empty ())
358+ {
359+ // / Credentials are passed as const everywhere up the flow, so we have to comply,
360+ // / in this case const_cast looks acceptable.
361+ const_cast <TokenCredentials &>(credentials).setUserName (username);
362+ }
363+ else
364+ LOG_TRACE (getLogger (" AccessTokenProcessor" ), " {}: Failed to get username with token" , name);
365+
366+ }
367+ catch (...)
368+ {
369+ return false ;
370+ }
371+
372+ return true ;
373+
374+ // / TODO: add proper groups functionality
375+ // try
376+ // {
377+ // const_cast<TokenCredentials &>(credentials).setExpiresAt(jwt::decode(token).get_expires_at());
378+ // }
379+ // catch (...) {
380+ // LOG_TRACE(getLogger("AccessTokenProcessor"),
381+ // "{}: No expiration data found in a valid token, will use default cache lifetime", name);
382+ // }
383+ }
384+
385+ String OpenIDAccessTokenProcessor::validateTokenAndGetUsername (const String & token) const
386+ {
387+ picojson::object user_info_json = getObjectFromURI (userinfo_endpoint, token);
388+ return getValueByKey (user_info_json, " sub" );
389+ }
390+
319391}
0 commit comments