Core Interfaces / Classes

ClientRegistration

OAuth2AccessToken 是向 OAuth 2.0 或 OpenID Connect 1.0 提供程序注册的客户端的表示形式。

ClientRegistration is a representation of a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.

客户端注册保存了信息,例如客户端 ID、客户端机密、授权授予类型、重定向 URI、范围、授权 URI、令牌 URI 和其他详细信息。

A client registration holds information, such as client id, client secret, authorization grant type, redirect URI, scope(s), authorization URI, token URI, and other details.

ClientRegistration 及其属性被定义如下:

ClientRegistration and its properties are defined as follows:

public final class ClientRegistration {
	private String registrationId;	1
	private String clientId;	2
	private String clientSecret;	3
	private ClientAuthenticationMethod clientAuthenticationMethod;	4
	private AuthorizationGrantType authorizationGrantType;	5
	private String redirectUri;	6
	private Set<String> scopes;	7
	private ProviderDetails providerDetails;
	private String clientName;	8

	public class ProviderDetails {
		private String authorizationUri;	9
		private String tokenUri;	10
		private UserInfoEndpoint userInfoEndpoint;
		private String jwkSetUri;	11
		private String issuerUri;	12
		private Map<String, Object> configurationMetadata;  13

		public class UserInfoEndpoint {
			private String uri;	14
			private AuthenticationMethod authenticationMethod;  15
			private String userNameAttributeName;	16

		}
	}
}
1 registrationId: The ID that uniquely identifies the ClientRegistration.
2 clientId: The client identifier.
3 clientSecret: The client secret.
4 clientAuthenticationMethod: The method used to authenticate the Client with the Provider. The supported values are client_secret_basic, client_secret_post, private_key_jwt, client_secret_jwt and none (public clients).
5 authorizationGrantType: The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials, password, as well as, extension grant type urn:ietf:params:oauth:grant-type:jwt-bearer.
6 redirectUri: The client’s registered redirect URI that the Authorization Server redirects the end-user’s user-agent to after the end-user has authenticated and authorized access to the client.
7 scopes: The scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.
8 clientName: A descriptive name used for the client. The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.
9 authorizationUri: The Authorization Endpoint URI for the Authorization Server.
10 tokenUri: The Token Endpoint URI for the Authorization Server.
11 jwkSetUri: The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID Token and optionally the UserInfo Response.
12 issuerUri: Returns the issuer identifier uri for the OpenID Connect 1.0 provider or the OAuth 2.0 Authorization Server.
13 configurationMetadata: The OpenID Provider Configuration Information. This information will only be available if the Spring Boot 2.x property spring.security.oauth2.client.provider.[providerId].issuerUri is configured.
14 (userInfoEndpoint)uri: The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user.
15 (userInfoEndpoint)authenticationMethod: The authentication method used when sending the access token to the UserInfo Endpoint. The supported values are header, form and query.
16 userNameAttributeName: The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.

可以使用 OpenID Connect 提供程序的 Configuration endpoint或授权服务器的 Metadata endpoint的发现来最初配置 ClientRegistration

A ClientRegistration can be initially configured using discovery of an OpenID Connect Provider’s Configuration endpoint or an Authorization Server’s Metadata endpoint.

ClientRegistrations 提供了通过这种方式配置 ClientRegistration 的便捷方法,如以下示例所示:

ClientRegistrations provides convenience methods for configuring a ClientRegistration in this way, as can be seen in the following example:

  • Java

  • Kotlin

ClientRegistration clientRegistration =
	ClientRegistrations.fromIssuerLocation("https://idp.example.com/issuer").build();
val clientRegistration = ClientRegistrations.fromIssuerLocation("https://idp.example.com/issuer").build()

或者,您可以使用 ClientRegistrations.fromOidcIssuerLocation() 仅查询 OpenID Connect 提供者的配置端点。

As an alternative, you can use ClientRegistrations.fromOidcIssuerLocation() to only query the OpenID Connect Provider’s Configuration endpoint.

ReactiveClientRegistrationRepository

ReactiveClientRegistrationRepository 用作 OAuth 2.0/OpenID Connect 1.0 ClientRegistration 的存储库。

The ReactiveClientRegistrationRepository serves as a repository for OAuth 2.0 / OpenID Connect 1.0 ClientRegistration(s).

客户端注册信息最终由相关的授权服务器存储和拥有。该存储库提供检索一组主客户端注册信息的功能,该信息与授权服务器一起存储。

Client registration information is ultimately stored and owned by the associated Authorization Server. This repository provides the ability to retrieve a sub-set of the primary client registration information, which is stored with the Authorization Server.

Spring Boot 2.x 自动配置将 `spring.security.oauth2.client.registration.[registrationId]`下每个属性绑定到 `ClientRegistration`的一个实例,然后在 `ReactiveClientRegistrationRepository`中组合每个 `ClientRegistration`实例。

Spring Boot 2.x auto-configuration binds each of the properties under spring.security.oauth2.client.registration.[registrationId] to an instance of ClientRegistration and then composes each of the ClientRegistration instance(s) within a ReactiveClientRegistrationRepository.

ReactiveClientRegistrationRepository 的默认实现是 InMemoryReactiveClientRegistrationRepository

The default implementation of ReactiveClientRegistrationRepository is InMemoryReactiveClientRegistrationRepository.

自动配置还将 ReactiveClientRegistrationRepository 注册为 ApplicationContext 中的 @Bean,以便在应用程序需要时可以使用依赖注入。

The auto-configuration also registers the ReactiveClientRegistrationRepository as a @Bean in the ApplicationContext so that it is available for dependency-injection, if needed by the application.

下面的清单显示了一个示例:

The following listing shows an example:

  • Java

  • Kotlin

@Controller
public class OAuth2ClientController {

	@Autowired
	private ReactiveClientRegistrationRepository clientRegistrationRepository;

	@GetMapping("/")
	public Mono<String> index() {
		return this.clientRegistrationRepository.findByRegistrationId("okta")
				...
				.thenReturn("index");
	}
}
@Controller
class OAuth2ClientController {

    @Autowired
    private lateinit var clientRegistrationRepository: ReactiveClientRegistrationRepository

    @GetMapping("/")
    fun index(): Mono<String> {
        return this.clientRegistrationRepository.findByRegistrationId("okta")
            ...
            .thenReturn("index")
    }
}

OAuth2AuthorizedClient

OAuth2AuthorizedClient 是授权客户端的一个表示。当最终用户(资源所有者)授予客户端访问其受保护资源的授权时,客户端被认为是已授权的。

OAuth2AuthorizedClient is a representation of an Authorized Client. A client is considered to be authorized when the end-user (Resource Owner) has granted authorization to the client to access its protected resources.

OAuth2AuthorizedClient 的作用是将 OAuth2AccessToken(和可选的 OAuth2RefreshToken)关联到 ClientRegistration(客户端)和资源所有者(即授予授权的 Principal 最终用户)。

OAuth2AuthorizedClient serves the purpose of associating an OAuth2AccessToken (and optional OAuth2RefreshToken) to a ClientRegistration (client) and resource owner, who is the Principal end-user that granted the authorization.

ServerOAuth2AuthorizedClientRepository / ReactiveOAuth2AuthorizedClientService

ServerOAuth2AuthorizedClientRepository 负责在 Web 请求之间保留 OAuth2AuthorizedClient。相对而言,ReactiveOAuth2AuthorizedClientService 的主要角色是管理应用程序级别的 OAuth2AuthorizedClient

ServerOAuth2AuthorizedClientRepository is responsible for persisting OAuth2AuthorizedClient(s) between web requests. Whereas, the primary role of ReactiveOAuth2AuthorizedClientService is to manage OAuth2AuthorizedClient(s) at the application-level.

从开发者的角度来看,ServerOAuth2AuthorizedClientRepositoryReactiveOAuth2AuthorizedClientService 提供了查找与一个客户端相关联的 OAuth2AccessToken 的能力,以便可以将其用于发起受保护的资源请求。

From a developer perspective, the ServerOAuth2AuthorizedClientRepository or ReactiveOAuth2AuthorizedClientService provides the capability to lookup an OAuth2AccessToken associated with a client so that it may be used to initiate a protected resource request.

下面的清单显示了一个示例:

The following listing shows an example:

  • Java

  • Kotlin

@Controller
public class OAuth2ClientController {

	@Autowired
	private ReactiveOAuth2AuthorizedClientService authorizedClientService;

	@GetMapping("/")
	public Mono<String> index(Authentication authentication) {
		return this.authorizedClientService.loadAuthorizedClient("okta", authentication.getName())
				.map(OAuth2AuthorizedClient::getAccessToken)
				...
				.thenReturn("index");
	}
}
@Controller
class OAuth2ClientController {

    @Autowired
    private lateinit var authorizedClientService: ReactiveOAuth2AuthorizedClientService

    @GetMapping("/")
    fun index(authentication: Authentication): Mono<String> {
        return this.authorizedClientService.loadAuthorizedClient<OAuth2AuthorizedClient>("okta", authentication.name)
            .map { it.accessToken }
            ...
            .thenReturn("index")
    }
}

Spring Boot 2.x 自动配置在 ApplicationContext 中注册一个 ServerOAuth2AuthorizedClientRepository 和/或 ReactiveOAuth2AuthorizedClientService @Bean。但是,应用程序可以选择覆盖和注册自定义 ServerOAuth2AuthorizedClientRepositoryReactiveOAuth2AuthorizedClientService @Bean

Spring Boot 2.x auto-configuration registers an ServerOAuth2AuthorizedClientRepository and/or ReactiveOAuth2AuthorizedClientService @Bean in the ApplicationContext. However, the application may choose to override and register a custom ServerOAuth2AuthorizedClientRepository or ReactiveOAuth2AuthorizedClientService @Bean.

ReactiveOAuth2AuthorizedClientService 的默认实现为 InMemoryReactiveOAuth2AuthorizedClientService,它在内存中储存 OAuth2AuthorizedClient

The default implementation of ReactiveOAuth2AuthorizedClientService is InMemoryReactiveOAuth2AuthorizedClientService, which stores OAuth2AuthorizedClient(s) in-memory.

另外地,R2DBC 实现 R2dbcReactiveOAuth2AuthorizedClientService 还可以被配置为在一个数据库中保留 OAuth2AuthorizedClient

Alternatively, the R2DBC implementation R2dbcReactiveOAuth2AuthorizedClientService may be configured for persisting OAuth2AuthorizedClient(s) in a database.

R2dbcReactiveOAuth2AuthorizedClientService 取决于 OAuth 2.0 Client Schema 中描述的表定义。

R2dbcReactiveOAuth2AuthorizedClientService depends on the table definition described in OAuth 2.0 Client Schema.

ReactiveOAuth2AuthorizedClientManager / ReactiveOAuth2AuthorizedClientProvider

ReactiveOAuth2AuthorizedClientManager 负责 OAuth2AuthorizedClient 的整体管理。

The ReactiveOAuth2AuthorizedClientManager is responsible for the overall management of OAuth2AuthorizedClient(s).

主要职责包括:

The primary responsibilities include:

  • Authorizing (or re-authorizing) an OAuth 2.0 Client, using a ReactiveOAuth2AuthorizedClientProvider.

  • Delegating the persistence of an OAuth2AuthorizedClient, typically using a ReactiveOAuth2AuthorizedClientService or ServerOAuth2AuthorizedClientRepository.

  • Delegating to a ReactiveOAuth2AuthorizationSuccessHandler when an OAuth 2.0 Client has been successfully authorized (or re-authorized).

  • Delegating to a ReactiveOAuth2AuthorizationFailureHandler when an OAuth 2.0 Client fails to authorize (or re-authorize).

一个 ReactiveOAuth2AuthorizedClientProvider 实现了了一个授权(或重新授权) OAuth 2.0 客户端的策略。这些实现通常会实现一个授权许可类型,例如 authorization_codeclient_credentials 等。

A ReactiveOAuth2AuthorizedClientProvider implements a strategy for authorizing (or re-authorizing) an OAuth 2.0 Client. Implementations will typically implement an authorization grant type, eg. authorization_code, client_credentials, etc.

ReactiveOAuth2AuthorizedClientManager 的默认实现为 DefaultReactiveOAuth2AuthorizedClientManager,它与一个 ReactiveOAuth2AuthorizedClientProvider 关联在一起,该 ReactiveOAuth2AuthorizedClientProvider 可能支持多个授权许可类型,使用基于委托的组合。ReactiveOAuth2AuthorizedClientProviderBuilder 可以在配置和构建基于委托的组合时使用。

The default implementation of ReactiveOAuth2AuthorizedClientManager is DefaultReactiveOAuth2AuthorizedClientManager, which is associated with a ReactiveOAuth2AuthorizedClientProvider that may support multiple authorization grant types using a delegation-based composite. The ReactiveOAuth2AuthorizedClientProviderBuilder may be used to configure and build the delegation-based composite.

下面的代码展示了一个如何配置和构建一个 ReactiveOAuth2AuthorizedClientProvider 组合的示例,该组合为 authorization_coderefresh_tokenclient_credentialspassword 授权许可类型提供支持:

The following code shows an example of how to configure and build a ReactiveOAuth2AuthorizedClientProvider composite that provides support for the authorization_code, refresh_token, client_credentials and password authorization grant types:

  • Java

  • Kotlin

@Bean
public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
		ReactiveClientRegistrationRepository clientRegistrationRepository,
		ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {

	ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
			ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
					.authorizationCode()
					.refreshToken()
					.clientCredentials()
					.password()
					.build();

	DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
			new DefaultReactiveOAuth2AuthorizedClientManager(
					clientRegistrationRepository, authorizedClientRepository);
	authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

	return authorizedClientManager;
}
@Bean
fun authorizedClientManager(
        clientRegistrationRepository: ReactiveClientRegistrationRepository,
        authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {
    val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
            .authorizationCode()
            .refreshToken()
            .clientCredentials()
            .password()
            .build()
    val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientRepository)
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
    return authorizedClientManager
}

当一个授权尝试成功时,DefaultReactiveOAuth2AuthorizedClientManager 会委托给 ReactiveOAuth2AuthorizationSuccessHandler,后者(默认情况下)会通过 ServerOAuth2AuthorizedClientRepository 保存 OAuth2AuthorizedClient。如果重新授权失败,例如刷新令牌不再有效,则先前保存的 OAuth2AuthorizedClient 会通过 RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandlerServerOAuth2AuthorizedClientRepository 中删除。可以用 setAuthorizationSuccessHandler(ReactiveOAuth2AuthorizationSuccessHandler)setAuthorizationFailureHandler(ReactiveOAuth2AuthorizationFailureHandler) 自定义默认行为。

When an authorization attempt succeeds, the DefaultReactiveOAuth2AuthorizedClientManager will delegate to the ReactiveOAuth2AuthorizationSuccessHandler, which (by default) will save the OAuth2AuthorizedClient via the ServerOAuth2AuthorizedClientRepository. In the case of a re-authorization failure, eg. a refresh token is no longer valid, the previously saved OAuth2AuthorizedClient will be removed from the ServerOAuth2AuthorizedClientRepository via the RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler. The default behaviour may be customized via setAuthorizationSuccessHandler(ReactiveOAuth2AuthorizationSuccessHandler) and setAuthorizationFailureHandler(ReactiveOAuth2AuthorizationFailureHandler).

DefaultReactiveOAuth2AuthorizedClientManager 也与一个类型为 Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>>contextAttributesMapper 关联,该 contextAttributesMapper 负责将属性从 OAuth2AuthorizeRequest 映射到一个属性 Map,该属性将与 OAuth2AuthorizationContext 关联。当您需要向 ReactiveOAuth2AuthorizedClientProvider 提供所需(支持的)属性时,这会很有用,例如 PasswordReactiveOAuth2AuthorizedClientProvider 要求资源所有者的 usernamepasswordOAuth2AuthorizationContext.getAttributes() 中可用。

The DefaultReactiveOAuth2AuthorizedClientManager is also associated with a contextAttributesMapper of type Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>>, which is responsible for mapping attribute(s) from the OAuth2AuthorizeRequest to a Map of attributes to be associated to the OAuth2AuthorizationContext. This can be useful when you need to supply a ReactiveOAuth2AuthorizedClientProvider with required (supported) attribute(s), eg. the PasswordReactiveOAuth2AuthorizedClientProvider requires the resource owner’s username and password to be available in OAuth2AuthorizationContext.getAttributes().

以下代码显示了 contextAttributesMapper 的示例:

The following code shows an example of the contextAttributesMapper:

  • Java

  • Kotlin

@Bean
public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
		ReactiveClientRegistrationRepository clientRegistrationRepository,
		ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {

	ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
			ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
					.password()
					.refreshToken()
					.build();

	DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
			new DefaultReactiveOAuth2AuthorizedClientManager(
					clientRegistrationRepository, authorizedClientRepository);
	authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

	// Assuming the `username` and `password` are supplied as `ServerHttpRequest` parameters,
	// map the `ServerHttpRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
	authorizedClientManager.setContextAttributesMapper(contextAttributesMapper());

	return authorizedClientManager;
}

private Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> contextAttributesMapper() {
	return authorizeRequest -> {
		Map<String, Object> contextAttributes = Collections.emptyMap();
		ServerWebExchange exchange = authorizeRequest.getAttribute(ServerWebExchange.class.getName());
		ServerHttpRequest request = exchange.getRequest();
		String username = request.getQueryParams().getFirst(OAuth2ParameterNames.USERNAME);
		String password = request.getQueryParams().getFirst(OAuth2ParameterNames.PASSWORD);
		if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
			contextAttributes = new HashMap<>();

			// `PasswordReactiveOAuth2AuthorizedClientProvider` requires both attributes
			contextAttributes.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, username);
			contextAttributes.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, password);
		}
		return Mono.just(contextAttributes);
	};
}
@Bean
fun authorizedClientManager(
        clientRegistrationRepository: ReactiveClientRegistrationRepository,
        authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {
    val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
            .password()
            .refreshToken()
            .build()
    val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientRepository)
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

    // Assuming the `username` and `password` are supplied as `ServerHttpRequest` parameters,
    // map the `ServerHttpRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
    authorizedClientManager.setContextAttributesMapper(contextAttributesMapper())
    return authorizedClientManager
}

private fun contextAttributesMapper(): Function<OAuth2AuthorizeRequest, Mono<MutableMap<String, Any>>> {
    return Function { authorizeRequest ->
        var contextAttributes: MutableMap<String, Any> = mutableMapOf()
        val exchange: ServerWebExchange = authorizeRequest.getAttribute(ServerWebExchange::class.java.name)!!
        val request: ServerHttpRequest = exchange.request
        val username: String? = request.queryParams.getFirst(OAuth2ParameterNames.USERNAME)
        val password: String? = request.queryParams.getFirst(OAuth2ParameterNames.PASSWORD)
        if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
            contextAttributes = hashMapOf()

            // `PasswordReactiveOAuth2AuthorizedClientProvider` requires both attributes
            contextAttributes[OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME] = username!!
            contextAttributes[OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME] = password!!
        }
        Mono.just(contextAttributes)
    }
}

DefaultReactiveOAuth2AuthorizedClientManager 旨在在 ServerWebExchange 的上下文中使用 within。当在 ServerWebExchange 上下文 outside 操作时,请改用 AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager

The DefaultReactiveOAuth2AuthorizedClientManager is designed to be used within the context of a ServerWebExchange. When operating outside of a ServerWebExchange context, use AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager instead.

一个 service application 是何时使用 AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager 的常见使用案例。服务应用程序通常在后台运行,没有任何用户交互,并且通常在系统级帐户而不是用户帐户下运行。配置了 client_credentials 许可类型的 OAuth 2.0 客户端可以被视为一种服务应用程序。

A service application is a common use case for when to use an AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager. Service applications often run in the background, without any user interaction, and typically run under a system-level account instead of a user account. An OAuth 2.0 Client configured with the client_credentials grant type can be considered a type of service application.

下面的代码展示了一个如何配置一个为 client_credentials 许可类型提供支持的 AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager 的示例:

The following code shows an example of how to configure an AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager that provides support for the client_credentials grant type:

  • Java

  • Kotlin

@Bean
public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
		ReactiveClientRegistrationRepository clientRegistrationRepository,
		ReactiveOAuth2AuthorizedClientService authorizedClientService) {

	ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
			ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
					.clientCredentials()
					.build();

	AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =
			new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
					clientRegistrationRepository, authorizedClientService);
	authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

	return authorizedClientManager;
}
@Bean
fun authorizedClientManager(
        clientRegistrationRepository: ReactiveClientRegistrationRepository,
        authorizedClientService: ReactiveOAuth2AuthorizedClientService): ReactiveOAuth2AuthorizedClientManager {
    val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
            .clientCredentials()
            .build()
    val authorizedClientManager = AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientService)
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
    return authorizedClientManager
}