Skip to content

Commit

Permalink
Update AAD auto-config conditional and split OAuth2AuthorizedClientMa…
Browse files Browse the repository at this point in the history
…nager configuration (#26964)
  • Loading branch information
moarychan authored Feb 14, 2022
1 parent 3a1c944 commit b5e9555
Show file tree
Hide file tree
Showing 17 changed files with 563 additions and 116 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
* spring.cloud.azure.active-directory.session-stateless=true}. Otherwise, {@link AADAuthenticationFilter} will be configured.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnExpression("${spring.cloud.azure.active-directory.enabled:false}")
@ConditionalOnMissingClass({ "org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken" })
@Import(AADPropertiesConfiguration.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.azure.spring.cloud.autoconfigure.aad.configuration.AADResourceServerConfiguration;
import com.azure.spring.cloud.autoconfigure.aad.configuration.AADWebApplicationConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

Expand All @@ -17,12 +18,16 @@
* </p>
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnProperty(value = "spring.cloud.azure.active-directory.enabled", havingValue = "true")
@Import({
AADPropertiesConfiguration.class,
AADWebApplicationConfiguration.class,
AADResourceServerConfiguration.class,
AADOAuth2ClientConfiguration.class
AADOAuth2ClientConfiguration.OAuth2ClientRepositoryConfiguration.class,
AADOAuth2ClientConfiguration.WebApplicationWithoutResourceServerOAuth2AuthorizedClientManagerConfiguration.class,
AADOAuth2ClientConfiguration.ResourceServerWithOBOOAuth2AuthorizedClientManagerConfiguration.class,
AADOAuth2ClientConfiguration.WebApplicationAndResourceServiceOAuth2AuthorizedClientManagerConfiguration.class
})
public class AADAutoConfiguration {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
package com.azure.spring.cloud.autoconfigure.aad.configuration;

import com.azure.spring.cloud.autoconfigure.aad.implementation.conditions.ClientRegistrationCondition;
import com.azure.spring.cloud.autoconfigure.aad.implementation.conditions.ResourceServerWithOBOCondition;
import com.azure.spring.cloud.autoconfigure.aad.implementation.conditions.WebApplicationAndResourceServerCondition;
import com.azure.spring.cloud.autoconfigure.aad.implementation.conditions.WebApplicationWithoutResourceServerCondition;
import com.azure.spring.cloud.autoconfigure.aad.implementation.oauth2.AADClientRegistrationRepository;
import com.azure.spring.cloud.autoconfigure.aad.implementation.oauth2.JacksonHttpSessionOAuth2AuthorizedClientRepository;
import com.azure.spring.cloud.autoconfigure.aad.implementation.webapi.AADOBOOAuth2AuthorizedClientProvider;
Expand All @@ -28,60 +31,146 @@
* </p>
*/
@Configuration(proxyBeanMethods = false)
@Conditional(ClientRegistrationCondition.class)
public class AADOAuth2ClientConfiguration {

/**
* Declare ClientRegistrationRepository bean.
*
* @param properties the AAD authentication properties
* @return ClientRegistrationRepository bean
* OAuth2 client configuration for AAD.
*/
@Bean
@ConditionalOnMissingBean
public ClientRegistrationRepository clientRegistrationRepository(AADAuthenticationProperties properties) {
return new AADClientRegistrationRepository(properties);
@Configuration(proxyBeanMethods = false)
@Conditional(ClientRegistrationCondition.class)
public static class OAuth2ClientRepositoryConfiguration {

/**
* Declare ClientRegistrationRepository bean.
*
* @param properties the AAD authentication properties
* @return ClientRegistrationRepository bean
*/
@Bean
@ConditionalOnMissingBean
public ClientRegistrationRepository clientRegistrationRepository(AADAuthenticationProperties properties) {
return new AADClientRegistrationRepository(properties);
}

/**
* Declare OAuth2AuthorizedClientRepository bean.
*
* @return OAuth2AuthorizedClientRepository bean
*/
@Bean
@ConditionalOnMissingBean
public OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository() {
return new JacksonHttpSessionOAuth2AuthorizedClientRepository();
}
}

/**
* Declare OAuth2AuthorizedClientRepository bean.
*
* @return OAuth2AuthorizedClientRepository bean
* Web application scenario, OAuth2AuthorizedClientManager configuration for AAD.
*/
@Bean
@ConditionalOnMissingBean
public OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository() {
return new JacksonHttpSessionOAuth2AuthorizedClientRepository();
@Configuration(proxyBeanMethods = false)
@Conditional(WebApplicationWithoutResourceServerCondition.class)
public static class WebApplicationWithoutResourceServerOAuth2AuthorizedClientManagerConfiguration {

/**
* Declare OAuth2AuthorizedClientManager bean for Resource Server with OBO scenario.
*
* @param clientRegistrations the client registration repository
* @param authorizedClients the OAuth2 authorized client repository
* @return OAuth2AuthorizedClientManager bean
*/
@Bean
@ConditionalOnMissingBean
public OAuth2AuthorizedClientManager authorizedClientManager(ClientRegistrationRepository clientRegistrations,
OAuth2AuthorizedClientRepository authorizedClients) {
DefaultOAuth2AuthorizedClientManager manager =
new DefaultOAuth2AuthorizedClientManager(clientRegistrations, authorizedClients);
AADAzureDelegatedOAuth2AuthorizedClientProvider azureDelegatedProvider =
new AADAzureDelegatedOAuth2AuthorizedClientProvider(
new RefreshTokenOAuth2AuthorizedClientProvider(),
authorizedClients);
OAuth2AuthorizedClientProvider authorizedClientProviders =
OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.clientCredentials()
.password()
.provider(azureDelegatedProvider)
.build();
manager.setAuthorizedClientProvider(authorizedClientProviders);
return manager;
}
}

/**
* Declare OAuth2AuthorizedClientManager bean.
*
* @param clientRegistrations the client registration repository
* @param authorizedClients the OAuth2 authorized client repository
* @return OAuth2AuthorizedClientManager bean
* Resource server with OBO scenario, OAuth2AuthorizedClientManager configuration for AAD.
*/
@Bean
@ConditionalOnMissingBean
public OAuth2AuthorizedClientManager authorizedClientManager(ClientRegistrationRepository clientRegistrations,
OAuth2AuthorizedClientRepository authorizedClients) {
DefaultOAuth2AuthorizedClientManager manager =
new DefaultOAuth2AuthorizedClientManager(clientRegistrations, authorizedClients);
AADAzureDelegatedOAuth2AuthorizedClientProvider azureDelegatedProvider =
new AADAzureDelegatedOAuth2AuthorizedClientProvider(
new RefreshTokenOAuth2AuthorizedClientProvider(),
authorizedClients);
AADOBOOAuth2AuthorizedClientProvider oboProvider = new AADOBOOAuth2AuthorizedClientProvider();
OAuth2AuthorizedClientProvider authorizedClientProviders =
OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.clientCredentials()
.password()
.provider(azureDelegatedProvider)
.provider(oboProvider)
.build();
manager.setAuthorizedClientProvider(authorizedClientProviders);
return manager;
@Configuration(proxyBeanMethods = false)
@Conditional(ResourceServerWithOBOCondition.class)
public static class ResourceServerWithOBOOAuth2AuthorizedClientManagerConfiguration {

/**
* Declare OAuth2AuthorizedClientManager bean for Resource Server with OBO scenario.
*
* @param clientRegistrations the client registration repository
* @param authorizedClients the OAuth2 authorized client repository
* @return OAuth2AuthorizedClientManager bean
*/
@Bean
@ConditionalOnMissingBean
public OAuth2AuthorizedClientManager authorizedClientManager(ClientRegistrationRepository clientRegistrations,
OAuth2AuthorizedClientRepository authorizedClients) {
DefaultOAuth2AuthorizedClientManager manager =
new DefaultOAuth2AuthorizedClientManager(clientRegistrations, authorizedClients);
AADOBOOAuth2AuthorizedClientProvider oboProvider = new AADOBOOAuth2AuthorizedClientProvider();
OAuth2AuthorizedClientProvider authorizedClientProviders =
OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.clientCredentials()
.password()
.provider(oboProvider)
.build();
manager.setAuthorizedClientProvider(authorizedClientProviders);
return manager;
}
}

/**
* Web application and resource server scenario, OAuth2AuthorizedClientManager configuration for AAD.
*/
@Configuration(proxyBeanMethods = false)
@Conditional(WebApplicationAndResourceServerCondition.class)
public static class WebApplicationAndResourceServiceOAuth2AuthorizedClientManagerConfiguration {

/**
* Declare OAuth2AuthorizedClientManager bean.
*
* @param clientRegistrations the client registration repository
* @param authorizedClients the OAuth2 authorized client repository
* @return OAuth2AuthorizedClientManager bean
*/
@Bean
@ConditionalOnMissingBean
public OAuth2AuthorizedClientManager authorizedClientManager(ClientRegistrationRepository clientRegistrations,
OAuth2AuthorizedClientRepository authorizedClients) {
DefaultOAuth2AuthorizedClientManager manager =
new DefaultOAuth2AuthorizedClientManager(clientRegistrations, authorizedClients);
AADAzureDelegatedOAuth2AuthorizedClientProvider azureDelegatedProvider =
new AADAzureDelegatedOAuth2AuthorizedClientProvider(
new RefreshTokenOAuth2AuthorizedClientProvider(),
authorizedClients);
AADOBOOAuth2AuthorizedClientProvider oboProvider = new AADOBOOAuth2AuthorizedClientProvider();
OAuth2AuthorizedClientProvider authorizedClientProviders =
OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.clientCredentials()
.password()
.provider(azureDelegatedProvider)
.provider(oboProvider)
.build();
manager.setAuthorizedClientProvider(authorizedClientProviders);
return manager;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.spring.cloud.autoconfigure.aad.implementation.conditions;

import com.azure.spring.cloud.autoconfigure.aad.properties.AADApplicationType;
import com.azure.spring.cloud.autoconfigure.aad.properties.AADAuthenticationProperties;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

import java.util.Optional;

/**
* Abstract class condition for each application type scenario.
*/
public abstract class AbstractApplicationTypeCondition extends SpringBootCondition {

/**
* Check the applicationType satisfies the target application type.
* @param applicationType the target application type.
* @return true if the applicationType satisfies the target type condition.
*/
abstract boolean isTargetApplicationType(AADApplicationType applicationType);

private boolean isNotTargetApplicationType(AADApplicationType applicationType) {
return !isTargetApplicationType(applicationType);
}

/**
* Return the condition title name.
* @return the condition title.
*/
abstract String getConditionTitle();

@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage.forCondition(getConditionTitle());
AADAuthenticationProperties properties =
Binder.get(context.getEnvironment())
.bind("spring.cloud.azure.active-directory", AADAuthenticationProperties.class)
.orElse(null);
if (properties == null) {
return ConditionOutcome.noMatch(message.notAvailable("aad authorization properties"));
}

// Bind properties will not execute AADAuthenticationProperties#afterPropertiesSet()
AADApplicationType applicationType = Optional.ofNullable(properties.getApplicationType())
.orElseGet(AADApplicationType::inferApplicationTypeByDependencies);
if (isNotTargetApplicationType(applicationType)) {
return ConditionOutcome.noMatch(
message.because("spring.cloud.azure.active-directory.application-type=" + applicationType));
}
return ConditionOutcome.match(
message.foundExactly("spring.cloud.azure.active-directory.application-type=" + applicationType));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,25 @@
package com.azure.spring.cloud.autoconfigure.aad.implementation.conditions;

import com.azure.spring.cloud.autoconfigure.aad.properties.AADApplicationType;
import com.azure.spring.cloud.autoconfigure.aad.properties.AADAuthenticationProperties;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.context.annotation.Condition;

import java.util.Optional;

import static com.azure.spring.cloud.autoconfigure.aad.properties.AADApplicationType.WEB_APPLICATION;
import static com.azure.spring.cloud.autoconfigure.aad.properties.AADApplicationType.RESOURCE_SERVER;
import static com.azure.spring.cloud.autoconfigure.aad.properties.AADApplicationType.RESOURCE_SERVER_WITH_OBO;
import static com.azure.spring.cloud.autoconfigure.aad.properties.AADApplicationType.WEB_APPLICATION_AND_RESOURCE_SERVER;

/**
* Resource server or all in scenario condition.
* {@link Condition} that checks for resource server scenario.
*/
public final class ResourceServerCondition extends SpringBootCondition {
public final class ResourceServerCondition extends AbstractApplicationTypeCondition {

@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage.forCondition("AAD Resource Server Condition");
AADAuthenticationProperties properties =
Binder.get(context.getEnvironment())
.bind("spring.cloud.azure.active-directory", AADAuthenticationProperties.class)
.orElse(null);
if (properties == null) {
return ConditionOutcome.noMatch(message.notAvailable("aad authorization properties"));
}
boolean isTargetApplicationType(AADApplicationType applicationType) {
return applicationType == RESOURCE_SERVER || applicationType == RESOURCE_SERVER_WITH_OBO
|| applicationType == WEB_APPLICATION_AND_RESOURCE_SERVER;
}

// Bind properties will not execute AADAuthenticationProperties#afterPropertiesSet()
AADApplicationType applicationType = Optional.ofNullable(properties.getApplicationType())
.orElseGet(AADApplicationType::inferApplicationTypeByDependencies);
if (applicationType == null || applicationType == WEB_APPLICATION) {
return ConditionOutcome.noMatch(
message.because("spring.cloud.azure.active-directory.application-type=" + applicationType));
}
return ConditionOutcome.match(
message.foundExactly("spring.cloud.azure.active-directory.application-type=" + applicationType));
@Override
protected String getConditionTitle() {
return "AAD Resource Server Condition";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.spring.cloud.autoconfigure.aad.implementation.conditions;

import com.azure.spring.cloud.autoconfigure.aad.properties.AADApplicationType;
import org.springframework.context.annotation.Condition;

import static com.azure.spring.cloud.autoconfigure.aad.properties.AADApplicationType.RESOURCE_SERVER_WITH_OBO;

/**
* {@link Condition} that checks for resource server with OBO scenario.
*/
public final class ResourceServerWithOBOCondition extends AbstractApplicationTypeCondition {

@Override
boolean isTargetApplicationType(AADApplicationType applicationType) {
return applicationType == RESOURCE_SERVER_WITH_OBO;
}

@Override
protected String getConditionTitle() {
return "AAD Resource Server with OBO Condition";
}
}
Loading

0 comments on commit b5e9555

Please sign in to comment.