Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update AAD auto-config conditional and split OAuth2AuthorizedClientManager configuration #26964

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
chenrujun marked this conversation as resolved.
Show resolved Hide resolved
@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 {
chenrujun marked this conversation as resolved.
Show resolved Hide resolved

@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