Skip to content

Commit

Permalink
gh-184 : (CONFIGURATION)
Browse files Browse the repository at this point in the history
Add parameters to token request
  • Loading branch information
ch4mpy committed Feb 5, 2024
1 parent 015d4ce commit 7187709
Show file tree
Hide file tree
Showing 17 changed files with 865 additions and 519 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@
import com.c4_soft.springaddons.security.oidc.starter.properties.SpringAddonsOidcProperties;
import com.c4_soft.springaddons.security.oidc.starter.reactive.ReactiveSpringAddonsOidcBeans;
import com.c4_soft.springaddons.security.oidc.starter.reactive.client.ReactiveSpringAddonsOidcClientBeans;
import com.c4_soft.springaddons.security.oidc.starter.reactive.client.ReactiveSpringAddonsOidcClientWithLoginBeans;
import com.c4_soft.springaddons.security.oidc.starter.reactive.resourceserver.ReactiveSpringAddonsOidcResourceServerBeans;
import com.c4_soft.springaddons.security.oidc.starter.synchronised.client.SpringAddonsOidcClientBeans;
import com.c4_soft.springaddons.security.oidc.starter.synchronised.client.SpringAddonsOidcClientWithLoginBeans;
import com.c4_soft.springaddons.security.oidc.starter.synchronised.resourceserver.SpringAddonsOidcResourceServerBeans;

import reactor.core.publisher.Mono;
Expand All @@ -59,8 +61,10 @@
exclude = {
ReactiveSpringAddonsOidcResourceServerBeans.class,
ReactiveSpringAddonsOidcClientBeans.class,
ReactiveSpringAddonsOidcClientWithLoginBeans.class,
SpringAddonsOidcResourceServerBeans.class,
SpringAddonsOidcClientBeans.class })
SpringAddonsOidcClientBeans.class,
SpringAddonsOidcClientWithLoginBeans.class })
@SpringBootTest(classes = { WebfluxJwtOauthentication.class, MessageService.class })
@Import({ AddonsWebfluxTestConf.class })
@ImportAutoConfiguration({ SpringAddonsOidcProperties.class, ReactiveSpringAddonsOidcBeans.class, AuthenticationFactoriesTestConf.class })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import com.c4_soft.springaddons.security.oauth2.test.webmvc.AddonsWebmvcComponentTest;
import com.c4_soft.springaddons.security.oauth2.test.webmvc.AutoConfigureAddonsWebmvcMinimalSecurity;
import com.c4_soft.springaddons.security.oauth2.test.webmvc.AutoConfigureAddonsWebmvcResourceServerSecurity;
import com.c4_soft.springaddons.security.oidc.starter.reactive.client.ReactiveSpringAddonsOidcClientBeans;
import com.c4_soft.springaddons.security.oidc.starter.reactive.client.ReactiveSpringAddonsOidcClientWithLoginBeans;
import com.c4_soft.springaddons.security.oidc.starter.reactive.resourceserver.ReactiveSpringAddonsOidcResourceServerBeans;

/**
Expand All @@ -27,6 +27,6 @@
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@AutoConfigureAddonsWebfluxMinimalSecurity
@ImportAutoConfiguration({ ReactiveSpringAddonsOidcClientBeans.class, AddonsWebfluxTestConf.class })
@ImportAutoConfiguration({ ReactiveSpringAddonsOidcClientWithLoginBeans.class, AddonsWebfluxTestConf.class })
public @interface AutoConfigureAddonsWebfluxClientSecurity {
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;

import com.c4_soft.springaddons.security.oauth2.test.webflux.AutoConfigureAddonsWebfluxClientSecurity;
import com.c4_soft.springaddons.security.oidc.starter.synchronised.client.SpringAddonsOidcClientBeans;
import com.c4_soft.springaddons.security.oidc.starter.synchronised.client.SpringAddonsOidcClientWithLoginBeans;

/**
* <p>
* Auto-configures {@link SpringAddonsOidcClientBeans} as well as what is already configured by {@link AutoConfigureAddonsWebmvcMinimalSecurity}. To be used to
* Auto-configures {@link SpringAddonsOidcClientWithLoginBeans} as well as what is already configured by {@link AutoConfigureAddonsWebmvcMinimalSecurity}. To be used to
* test controllers but not services or repositories (web context is not desired in that case).
* </p>
*
Expand All @@ -23,6 +23,6 @@
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@AutoConfigureAddonsWebmvcMinimalSecurity
@ImportAutoConfiguration({ SpringAddonsOidcClientBeans.class })
@ImportAutoConfiguration({ SpringAddonsOidcClientWithLoginBeans.class })
public @interface AutoConfigureAddonsWebmvcClientSecurity {
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ public URI getPostLogoutRedirectUri() {
*/
private Map<String, List<RequestParam>> authorizationRequestParams = new HashMap<>();

/**
* Additional parameters to send with token request, mapped by client registration IDs
*/
private Map<String, List<RequestParam>> tokenRequestParams = new HashMap<>();

/**
* Logout properties for OpenID Providers which do not implement the RP-Initiated Logout spec
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
import org.springframework.context.annotation.Conditional;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;

import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsOidcClientCondition;
import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsClientWithLoginCondition;

public class DefaultGrantedAuthoritiesMapperCondition extends AllNestedConditions {
DefaultGrantedAuthoritiesMapperCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}

@Conditional(IsOidcClientCondition.class)
@Conditional(IsClientWithLoginCondition.class)
static class SpringAddonsOidcClientEnabled {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;

public class IsOidcClientCondition extends AnyNestedCondition {
public class IsClientWithLoginCondition extends AnyNestedCondition {

public IsOidcClientCondition() {
public IsClientWithLoginCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package com.c4_soft.springaddons.security.oidc.starter.reactive.client;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.StreamSupport;

import org.springframework.security.oauth2.client.AuthorizationCodeReactiveOAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.ClientCredentialsReactiveOAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.DelegatingReactiveOAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.OAuth2AuthorizationContext;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.RefreshTokenReactiveOAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.endpoint.WebClientReactiveClientCredentialsTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.WebClientReactiveRefreshTokenTokenResponseClient;
import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import com.c4_soft.springaddons.security.oidc.starter.properties.SpringAddonsOidcProperties;

import reactor.core.publisher.Mono;

/**
* <p>
* An alternative {@link ReactiveOAuth2AuthorizedClientProvider} to {@link DelegatingReactiveOAuth2AuthorizedClientProvider} keeping a different provider for
* each client registration. This allows to define for each a set of extra parameters to add to token requests.
* </p>
*
* @author Jerome Wacongne ch4mp&#64;c4-soft.com
*/
public final class PerRegistrationReactiveOAuth2AuthorizedClientProvider implements ReactiveOAuth2AuthorizedClientProvider {

private final Map<String, ReactiveOAuth2AuthorizedClientProvider> providers;

public PerRegistrationReactiveOAuth2AuthorizedClientProvider(
SpringAddonsOidcProperties addonsProperties,
Map<String, ReactiveOAuth2AuthorizedClientProvider> providers) {
this.providers = new HashMap<>(providers);
}

public PerRegistrationReactiveOAuth2AuthorizedClientProvider(
InMemoryReactiveClientRegistrationRepository clientRegistrationRepo,
SpringAddonsOidcProperties addonsProperties,
Map<String, ReactiveOAuth2AuthorizedClientProvider> customProviders) {
this.providers = new HashMap<>(customProviders);
StreamSupport.stream(clientRegistrationRepo.spliterator(), false).forEach(reg -> {
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(reg.getAuthorizationGrantType())) {
this.providers.putIfAbsent(reg.getRegistrationId(), new AuthorizationCodeReactiveOAuth2AuthorizedClientProvider());
} else if (AuthorizationGrantType.CLIENT_CREDENTIALS.equals(reg.getAuthorizationGrantType())) {
this.providers.putIfAbsent(reg.getRegistrationId(), new ClientCredentialsReactiveOAuth2AuthorizedClientProvider());
} else if (AuthorizationGrantType.REFRESH_TOKEN.equals(reg.getAuthorizationGrantType())) {
this.providers.putIfAbsent(reg.getRegistrationId(), new RefreshTokenReactiveOAuth2AuthorizedClientProvider());
} else {
throw new UnsupportedGrantTypeException(reg.getAuthorizationGrantType());
}

final var tokenParams = addonsProperties.getClient().getTokenRequestParams().getOrDefault(reg.getRegistrationId(), List.of());
if (tokenParams.isEmpty()) {
return;
}
final MultiValueMap<String, String> extraParameters = new LinkedMultiValueMap<>(tokenParams.size());
for (final var param : tokenParams) {
extraParameters.add(param.getName(), param.getValue());
}

final var delegate = this.providers.get(reg.getRegistrationId());
if (delegate instanceof ClientCredentialsReactiveOAuth2AuthorizedClientProvider clientCredentialsProvider) {
final var clientCredentialsResponseClient = new WebClientReactiveClientCredentialsTokenResponseClient();
clientCredentialsResponseClient.addParametersConverter(source -> extraParameters);

clientCredentialsProvider.setAccessTokenResponseClient(clientCredentialsResponseClient);

} else if (delegate instanceof RefreshTokenReactiveOAuth2AuthorizedClientProvider refreshTokenProvider) {
final var refreshTokenResponseClient = new WebClientReactiveRefreshTokenTokenResponseClient();
refreshTokenResponseClient.addParametersConverter(source -> extraParameters);

refreshTokenProvider.setAccessTokenResponseClient(refreshTokenResponseClient);
}
});
}

public PerRegistrationReactiveOAuth2AuthorizedClientProvider(
InMemoryReactiveClientRegistrationRepository clientRegistrationRepo,
SpringAddonsOidcProperties addonsProperties) {
this(clientRegistrationRepo, addonsProperties, Map.of());
}

@Override
public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext context) throws UnsupportedGrantTypeException {
if (context == null) {
return null;
}

final var provider = getDelegate(context.getClientRegistration().getRegistrationId());

return provider.authorize(context);
}

@SuppressWarnings("unchecked")
public <T extends ReactiveOAuth2AuthorizedClientProvider> T getDelegate(String registrationId) throws UnsupportedGrantTypeException {
final var provider = providers.get(registrationId);
return (T) provider;
}

public PerRegistrationReactiveOAuth2AuthorizedClientProvider setDelegate(String registrationId, ReactiveOAuth2AuthorizedClientProvider delegate) {
Assert.notNull(registrationId, "registrationId cannot be null");
Assert.notNull(delegate, "delegate cannot be null");
providers.put(registrationId, delegate);
return this;
}

static class UnsupportedGrantTypeException extends RuntimeException {
private static final long serialVersionUID = 5600617070203595919L;

public UnsupportedGrantTypeException(AuthorizationGrantType grantType) {
super(
"No OAuth2AuthorizedClientProvider registered for GrantType: %s. Consider adding one to SpringAddonsDelegatingOAuth2AuthorizedClientProvider in your conf."
.formatted(grantType));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@

import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsClientMultiTenancyEnabled;
import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsNotServlet;
import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsOidcClientCondition;
import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsClientWithLoginCondition;

import lombok.RequiredArgsConstructor;
import reactor.core.publisher.Mono;

@Conditional({ IsOidcClientCondition.class, IsNotServlet.class, IsClientMultiTenancyEnabled.class })
@Conditional({ IsClientWithLoginCondition.class, IsNotServlet.class, IsClientMultiTenancyEnabled.class })
@AutoConfiguration
@PropertySource(value = "classpath:/c4-spring-addons.properties", ignoreResourceNotFound = true)
public class ReactiveSpringAddonsAop {
Expand Down
Loading

0 comments on commit 7187709

Please sign in to comment.