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

Make AAD B2C support global properties. #25799

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 @@ -2,8 +2,6 @@
// Licensed under the MIT License.
package com.azure.spring.cloud.autoconfigure.aad.b2c.implementation;

import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;
Expand Down Expand Up @@ -51,20 +49,20 @@ public class AADB2CAuthorizationRequestResolver implements OAuth2AuthorizationRe
* @param repository the client registration repository
* @param properties the AAD B2C properties
*/
public AADB2CAuthorizationRequestResolver(@NonNull ClientRegistrationRepository repository,
@NonNull AADB2CProperties properties) {
public AADB2CAuthorizationRequestResolver(ClientRegistrationRepository repository,
AADB2CProperties properties) {
this.properties = properties;
this.passwordResetUserFlow = this.properties.getPasswordReset();
this.defaultResolver = new DefaultOAuth2AuthorizationRequestResolver(repository, REQUEST_BASE_URI);
}

@Override
public OAuth2AuthorizationRequest resolve(@NonNull HttpServletRequest request) {
public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
return resolve(request, getRegistrationId(request));
}

@Override
public OAuth2AuthorizationRequest resolve(@NonNull HttpServletRequest request, String registrationId) {
public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId) {
if (StringUtils.hasText(passwordResetUserFlow) && isForgotPasswordAuthorizationRequest(request)) {
final OAuth2AuthorizationRequest authRequest = defaultResolver.resolve(request, passwordResetUserFlow);
return getB2CAuthorizationRequest(authRequest, passwordResetUserFlow);
Expand All @@ -79,7 +77,7 @@ public OAuth2AuthorizationRequest resolve(@NonNull HttpServletRequest request, S
return null;
}

private OAuth2AuthorizationRequest getB2CAuthorizationRequest(@Nullable OAuth2AuthorizationRequest request,
private OAuth2AuthorizationRequest getB2CAuthorizationRequest(OAuth2AuthorizationRequest request,
String userFlow) {
Assert.hasText(userFlow, "User flow should contain text.");

Expand Down Expand Up @@ -111,7 +109,7 @@ private String getRegistrationId(HttpServletRequest request) {

// Handle the forgot password of sign-up-or-in page cannot redirect user to password-reset page.
// The B2C service will enhance that, and then related code will be removed.
private boolean isForgotPasswordAuthorizationRequest(@NonNull HttpServletRequest request) {
private boolean isForgotPasswordAuthorizationRequest(HttpServletRequest request) {
final String error = request.getParameter("error");
final String description = request.getParameter("error_description");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,49 +5,31 @@
import com.azure.spring.cloud.autoconfigure.aad.b2c.AADB2COidcLoginConfigurer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.lang.NonNull;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;

/**
* When the configuration matches the {@link AADB2CConditions.CommonCondition.WebAppMode} condition,
* configure the necessary beans for AAD B2C authentication and authorization,
* Configure the necessary beans for AAD B2C authentication and authorization,
* and import {@link AADB2COAuth2ClientConfiguration} class for AAD B2C OAuth2 client support.
*/
@Configuration
@ConditionalOnProperty(value = "spring.cloud.azure.active-directory.b2c.enabled", havingValue = "true")
@Conditional({ AADB2CConditions.CommonCondition.class, AADB2CConditions.UserFlowCondition.class })
@EnableConfigurationProperties(AADB2CProperties.class)
@Import(AADB2COAuth2ClientConfiguration.class)
@Conditional(AADB2CConditions.UserFlowCondition.class)
@Import({AADB2CPropertiesConfiguration.class, AADB2COAuth2ClientConfiguration.class})
public class AADB2CAutoConfiguration {

private final ClientRegistrationRepository repository;
private final AADB2CProperties properties;

/**
* Creates a new instance of {@link AADB2CAutoConfiguration}.
*
* @param repository the client registration repository
* @param properties the AAD B2C properties
*/
public AADB2CAutoConfiguration(@NonNull ClientRegistrationRepository repository,
@NonNull AADB2CProperties properties) {
this.repository = repository;
this.properties = properties;
}

/**
* Declare AADB2CAuthorizationRequestResolver bean.
*
* @return AADB2CAuthorizationRequestResolver bean
*/
@Bean
@ConditionalOnMissingBean
public AADB2CAuthorizationRequestResolver b2cOAuth2AuthorizationRequestResolver() {
public AADB2CAuthorizationRequestResolver b2cOAuth2AuthorizationRequestResolver(
ClientRegistrationRepository repository, AADB2CProperties properties) {
return new AADB2CAuthorizationRequestResolver(repository, properties);
}

Expand All @@ -58,7 +40,7 @@ public AADB2CAuthorizationRequestResolver b2cOAuth2AuthorizationRequestResolver(
*/
@Bean
@ConditionalOnMissingBean
public AADB2CLogoutSuccessHandler b2cLogoutSuccessHandler() {
public AADB2CLogoutSuccessHandler b2cLogoutSuccessHandler(AADB2CProperties properties) {
return new AADB2CLogoutSuccessHandler(properties);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@
// Licensed under the MIT License.
package com.azure.spring.cloud.autoconfigure.aad.b2c.implementation;

import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.annotation.ConditionContext;
Expand All @@ -20,39 +17,6 @@
*/
public final class AADB2CConditions {

/**
* Web application or web resource server scenario condition.
*/
static final class CommonCondition extends AnyNestedCondition {
CommonCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}

/**
* Web application scenario condition.
*/
@ConditionalOnWebApplication
@ConditionalOnProperty(
prefix = AADB2CProperties.PREFIX,
value = {
"client-id",
"client-secret"
}
)
static class WebAppMode {

}

/**
* Web resource server scenario condition.
*/
@ConditionalOnWebApplication
@ConditionalOnProperty(prefix = AADB2CProperties.PREFIX, value = { "tenant-id" })
static class WebApiMode {

}
}

/**
* OAuth2 client beans condition.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

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

/**
* Properties used for authorize.
*/
public class AADB2CCredentialProperties {

/**
* Client id to use when performing service principal authentication with Azure.
*/
private String clientId;

/**
* Client secret to use when performing service principal authentication with Azure.
*/
private String clientSecret;

public String getClientId() {
return clientId;
}

public void setClientId(String clientId) {
this.clientId = clientId;
}

public String getClientSecret() {
return clientSecret;
}

public void setClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the MIT License.
package com.azure.spring.cloud.autoconfigure.aad.b2c.implementation;

import org.springframework.lang.NonNull;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;

Expand All @@ -23,7 +22,7 @@ public class AADB2CLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
*
* @param properties the AAD B2C properties
*/
public AADB2CLogoutSuccessHandler(@NonNull AADB2CProperties properties) {
public AADB2CLogoutSuccessHandler(AADB2CProperties properties) {
this.properties = properties;

super.setDefaultTargetUrl(getAADB2CEndSessionUrl());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;
import org.springframework.context.annotation.Import;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
Expand All @@ -33,8 +32,8 @@
*/
@Configuration
@ConditionalOnProperty(value = "spring.cloud.azure.active-directory.b2c.enabled", havingValue = "true")
@Conditional({ AADB2CConditions.CommonCondition.class, AADB2CConditions.ClientRegistrationCondition.class })
@EnableConfigurationProperties(AADB2CProperties.class)
@Conditional(AADB2CConditions.ClientRegistrationCondition.class)
@Import(AADB2CPropertiesConfiguration.class)
@ConditionalOnClass({ OAuth2LoginAuthenticationFilter.class })
public class AADB2COAuth2ClientConfiguration {

Expand All @@ -46,7 +45,7 @@ public class AADB2COAuth2ClientConfiguration {
*
* @param properties the AAD B2C properties
*/
public AADB2COAuth2ClientConfiguration(@NonNull AADB2CProperties properties) {
public AADB2COAuth2ClientConfiguration(AADB2CProperties properties) {
this.properties = properties;
}

Expand Down Expand Up @@ -79,12 +78,12 @@ public ClientRegistrationRepository clientRegistrationRepository() {
private ClientRegistration buildUserFlowClientRegistration(Map.Entry<String, String> client) {
return ClientRegistration.withRegistrationId(client.getValue()) // Use flow as registration Id.
.clientName(client.getKey())
.clientId(properties.getClientId())
.clientSecret(properties.getClientSecret())
.clientId(properties.getCredential().getClientId())
.clientSecret(properties.getCredential().getClientSecret())
.clientAuthenticationMethod(ClientAuthenticationMethod.POST)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri(properties.getReplyUrl())
.scope(properties.getClientId(), "openid", "offline_access")
.scope(properties.getCredential().getClientId(), "openid", "offline_access")
.authorizationUri(AADB2CURL.getAuthorizationUrl(properties.getBaseUri()))
.tokenUri(AADB2CURL.getTokenUrl(properties.getBaseUri(), client.getValue()))
.jwkSetUri(AADB2CURL.getJwkSetUrl(properties.getBaseUri(), client.getValue()))
Expand All @@ -108,13 +107,13 @@ private ClientRegistration buildClientRegistration(Map.Entry<String, Authorizati
}
return ClientRegistration.withRegistrationId(client.getKey())
.clientName(client.getKey())
.clientId(properties.getClientId())
.clientSecret(properties.getClientSecret())
.clientId(properties.getCredential().getClientId())
.clientSecret(properties.getCredential().getClientSecret())
.clientAuthenticationMethod(ClientAuthenticationMethod.POST)
.authorizationGrantType(authGrantType)
.scope(client.getValue().getScopes())
.tokenUri(AADB2CURL.getAADTokenUrl(properties.getTenantId()))
.jwkSetUri(AADB2CURL.getAADJwkSetUrl(properties.getTenantId()))
.tokenUri(AADB2CURL.getAADTokenUrl(properties.getProfile().getTenantId()))
.jwkSetUri(AADB2CURL.getAADJwkSetUrl(properties.getProfile().getTenantId()))
.build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

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

/**
* Profile of Azure cloud environment.
*/
public class AADB2CProfileProperties {
/**
* Azure Tenant ID.
*/
private String tenantId; // tenantId can not set to "common" here, otherwise we can not know whether it's set by customer or it is the default value.

public void setTenantId(String tenantId) {
this.tenantId = tenantId;
}

public String getTenantId() {
return tenantId;
}
}
Loading