Skip to content

Commit

Permalink
Add OAuth2ClientManagerBuilder FactoryBean
Browse files Browse the repository at this point in the history
  • Loading branch information
sjohnr committed Jun 8, 2023
1 parent 0409db3 commit 67d6fa4
Show file tree
Hide file tree
Showing 10 changed files with 334 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.security.config.annotation.web.configuration;

import java.util.function.Supplier;

import org.springframework.web.client.RestOperations;

/**
* @author Steve Riesenberg
*/
@FunctionalInterface
public interface OAuth2RestOperationsSupplier extends Supplier<RestOperations> {

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,22 @@
import java.time.Instant;
import java.util.function.Function;

import org.springframework.core.convert.converter.Converter;
import org.springframework.http.RequestEntity;
import org.springframework.lang.Nullable;
import org.springframework.security.oauth2.client.endpoint.DefaultJwtBearerTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.DefaultRefreshTokenTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
import org.springframework.security.oauth2.core.OAuth2Token;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.util.Assert;
import org.springframework.web.client.RestOperations;

/**
* An implementation of an {@link OAuth2AuthorizedClientProvider} for the
Expand Down Expand Up @@ -171,4 +176,78 @@ public void setClock(Clock clock) {
this.clock = clock;
}

public static JwtBearerGrantBuilder builder() {
return new JwtBearerGrantBuilder();
}

public static final class JwtBearerGrantBuilder implements OAuth2AuthorizedClientProviderBuilder.Builder {

private OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> accessTokenResponseClient;

private Converter<JwtBearerGrantRequest, RequestEntity<?>> requestEntityConverter;

private RestOperations restOperations;

private Duration clockSkew;

private Clock clock;

private JwtBearerGrantBuilder() {
}

public JwtBearerGrantBuilder accessTokenResponseClient(OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> accessTokenResponseClient) {
this.accessTokenResponseClient = accessTokenResponseClient;
return this;
}

public JwtBearerGrantBuilder requestEntityConverter(Converter<JwtBearerGrantRequest, RequestEntity<?>> requestEntityConverter) {
this.requestEntityConverter = requestEntityConverter;
return this;
}

public JwtBearerGrantBuilder restOperations(RestOperations restOperations) {
this.restOperations = restOperations;
return this;
}

public JwtBearerGrantBuilder clockSkew(Duration clockSkew) {
this.clockSkew = clockSkew;
return this;
}

public JwtBearerGrantBuilder clock(Clock clock) {
this.clock = clock;
return this;
}

@Override
public OAuth2AuthorizedClientProvider build() {
JwtBearerOAuth2AuthorizedClientProvider authorizedClientProvider =
new JwtBearerOAuth2AuthorizedClientProvider();
if (this.accessTokenResponseClient == null && (this.requestEntityConverter != null
|| this.restOperations != null)) {
DefaultJwtBearerTokenResponseClient accessTokenResponseClient =
new DefaultJwtBearerTokenResponseClient();
if (this.requestEntityConverter != null) {
accessTokenResponseClient.setRequestEntityConverter(this.requestEntityConverter);
}
if (this.restOperations != null) {
accessTokenResponseClient.setRestOperations(this.restOperations);
}
this.accessTokenResponseClient = accessTokenResponseClient;
}
if (this.accessTokenResponseClient != null) {
authorizedClientProvider.setAccessTokenResponseClient(this.accessTokenResponseClient);
}
if (this.clockSkew != null) {
authorizedClientProvider.setClockSkew(this.clockSkew);
}
if (this.clock != null) {
authorizedClientProvider.setClock(this.clock);
}
return authorizedClientProvider;
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.security.oauth2.client;

import java.util.function.Consumer;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.security.oauth2.client.endpoint.DefaultClientCredentialsTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.DefaultPasswordTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.DefaultRefreshTokenTokenResponseClient;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.util.Assert;
import org.springframework.web.client.RestOperations;

/**
* @author Steve Riesenberg
*/
public final class OAuth2AuthorizedClientManagerBuilder implements FactoryBean<OAuth2AuthorizedClientManager> {

private final ClientRegistrationRepository clientRegistrationRepository;

private final OAuth2AuthorizedClientRepository authorizedClientRepository;

// @formatter:off
private OAuth2AuthorizedClientProviderBuilder authorizedClientProviderBuilder =
OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.clientCredentials()
.password();
// @formatter:on

private OAuth2AuthorizedClientManagerBuilder(ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
this.clientRegistrationRepository = clientRegistrationRepository;
this.authorizedClientRepository = authorizedClientRepository;
}

public OAuth2AuthorizedClientManagerBuilder providers(OAuth2AuthorizedClientProviderBuilder authorizedClientProviderBuilder) {
Assert.notNull(authorizedClientProviderBuilder, "authorizedClientProviderBuilder cannot be null");
this.authorizedClientProviderBuilder = authorizedClientProviderBuilder;
return this;
}

public OAuth2AuthorizedClientManagerBuilder providers(Consumer<OAuth2AuthorizedClientProviderBuilder> builderConsumer) {
Assert.notNull(builderConsumer, "consumer cannot be null");
builderConsumer.accept(this.authorizedClientProviderBuilder);
return this;
}

public OAuth2AuthorizedClientManagerBuilder provider(OAuth2AuthorizedClientProvider authorizedClientProvider) {
Assert.notNull(authorizedClientProvider, "authorizedClientProvider cannot be null");
this.authorizedClientProviderBuilder.provider(authorizedClientProvider);
return this;
}

public OAuth2AuthorizedClientManagerBuilder restOperations(RestOperations restOperations) {
Assert.notNull(restOperations, "restOperations cannot be null");
// @formatter:off
this.authorizedClientProviderBuilder
.refreshToken((configurer) -> configurer.accessTokenResponseClient(
DefaultRefreshTokenTokenResponseClient.builder()
.restOperations(restOperations)
.build()))
.clientCredentials((configurer) -> configurer.accessTokenResponseClient(
DefaultClientCredentialsTokenResponseClient.builder()
.restOperations(restOperations)
.build()))
.password((configurer) -> configurer.accessTokenResponseClient(
DefaultPasswordTokenResponseClient.builder()
.restOperations(restOperations)
.build()));
// @formatter:on
return this;
}

public DefaultOAuth2AuthorizedClientManager build() {
DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(
this.clientRegistrationRepository, this.authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProviderBuilder.build());
return authorizedClientManager;
}

@Override
public OAuth2AuthorizedClientManager getObject() {
return build();
}

@Override
public Class<?> getObjectType() {
return OAuth2AuthorizedClientManager.class;
}

public static OAuth2AuthorizedClientManagerBuilder builder(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
Assert.notNull(authorizedClientRepository, "authorizedClientRepository cannot be null");
return new OAuth2AuthorizedClientManagerBuilder(clientRegistrationRepository, authorizedClientRepository);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,11 @@ public void setRestOperations(RestOperations restOperations) {
this.restOperations = restOperations;
}

public static OAuth2AccessTokenResponseClient.Builder<OAuth2AuthorizationCodeGrantRequest> builder() {
DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient =
new DefaultAuthorizationCodeTokenResponseClient();
return new DefaultOAuth2AccessTokenResponseClientBuilder<>(accessTokenResponseClient,
accessTokenResponseClient::setRequestEntityConverter, accessTokenResponseClient::setRestOperations);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,11 @@ public void setRestOperations(RestOperations restOperations) {
this.restOperations = restOperations;
}

public static OAuth2AccessTokenResponseClient.Builder<OAuth2ClientCredentialsGrantRequest> builder() {
DefaultClientCredentialsTokenResponseClient accessTokenResponseClient =
new DefaultClientCredentialsTokenResponseClient();
return new DefaultOAuth2AccessTokenResponseClientBuilder<>(accessTokenResponseClient,
accessTokenResponseClient::setRequestEntityConverter, accessTokenResponseClient::setRestOperations);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,10 @@ public void setRestOperations(RestOperations restOperations) {
this.restOperations = restOperations;
}

public static OAuth2AccessTokenResponseClient.Builder<JwtBearerGrantRequest> builder() {
DefaultJwtBearerTokenResponseClient accessTokenResponseClient = new DefaultJwtBearerTokenResponseClient();
return new DefaultOAuth2AccessTokenResponseClientBuilder<>(accessTokenResponseClient,
accessTokenResponseClient::setRequestEntityConverter, accessTokenResponseClient::setRestOperations);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.security.oauth2.client.endpoint;

import java.util.function.Consumer;

import org.springframework.core.convert.converter.Converter;
import org.springframework.http.RequestEntity;
import org.springframework.web.client.RestOperations;

/**
* @author Steve Riesenberg
*/
final class DefaultOAuth2AccessTokenResponseClientBuilder<T extends AbstractOAuth2AuthorizationGrantRequest>
implements OAuth2AccessTokenResponseClient.Builder<T> {

private final OAuth2AccessTokenResponseClient<T> accessTokenResponseClient;

private final Consumer<Converter<T, RequestEntity<?>>> requestEntityConverterSetter;

private final Consumer<RestOperations> restOperationsSetter;

DefaultOAuth2AccessTokenResponseClientBuilder(OAuth2AccessTokenResponseClient<T> accessTokenResponseClient,
Consumer<Converter<T, RequestEntity<?>>> requestEntityConverterSetter,
Consumer<RestOperations> restOperationsSetter) {
this.accessTokenResponseClient = accessTokenResponseClient;
this.requestEntityConverterSetter = requestEntityConverterSetter;
this.restOperationsSetter = restOperationsSetter;
}

@Override
public OAuth2AccessTokenResponseClient.Builder<T> requestEntityConverter(Converter<T, RequestEntity<?>> requestEntityConverter) {
this.requestEntityConverterSetter.accept(requestEntityConverter);
return this;
}

@Override
public OAuth2AccessTokenResponseClient.Builder<T> restOperations(RestOperations restOperations) {
this.restOperationsSetter.accept(restOperations);
return this;
}

@Override
public OAuth2AccessTokenResponseClient<T> build() {
return this.accessTokenResponseClient;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,11 @@ public void setRestOperations(RestOperations restOperations) {
this.restOperations = restOperations;
}

public static OAuth2AccessTokenResponseClient.Builder<OAuth2PasswordGrantRequest> builder() {
DefaultPasswordTokenResponseClient accessTokenResponseClient = new DefaultPasswordTokenResponseClient();
return new DefaultOAuth2AccessTokenResponseClientBuilder<>(accessTokenResponseClient,
accessTokenResponseClient::setRequestEntityConverter,
accessTokenResponseClient::setRestOperations);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,11 @@ public void setRestOperations(RestOperations restOperations) {
this.restOperations = restOperations;
}

public static OAuth2AccessTokenResponseClient.Builder<OAuth2RefreshTokenGrantRequest> builder() {
DefaultRefreshTokenTokenResponseClient accessTokenResponseClient =
new DefaultRefreshTokenTokenResponseClient();
return new DefaultOAuth2AccessTokenResponseClientBuilder<>(accessTokenResponseClient,
accessTokenResponseClient::setRequestEntityConverter, accessTokenResponseClient::setRestOperations);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@

package org.springframework.security.oauth2.client.endpoint;

import org.springframework.core.convert.converter.Converter;
import org.springframework.http.RequestEntity;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.web.client.RestOperations;

/**
* A strategy for &quot;exchanging&quot; an authorization grant credential (e.g. an
Expand Down Expand Up @@ -55,4 +58,14 @@ public interface OAuth2AccessTokenResponseClient<T extends AbstractOAuth2Authori
*/
OAuth2AccessTokenResponse getTokenResponse(T authorizationGrantRequest);

interface Builder<T extends AbstractOAuth2AuthorizationGrantRequest> {

Builder<T> requestEntityConverter(Converter<T, RequestEntity<?>> requestEntityConverter);

Builder<T> restOperations(RestOperations restOperations);

OAuth2AccessTokenResponseClient<T> build();

}

}

0 comments on commit 67d6fa4

Please sign in to comment.