From 05402ed62a1b4a0412824f795c9fd09fd5ed0c0f Mon Sep 17 00:00:00 2001 From: Yi Liu Date: Wed, 6 May 2020 17:31:20 +0800 Subject: [PATCH 01/11] migrate aad-b2c --- eng/versioning/version_data.txt | 1 + .../README.md | 238 ++++++++++++++++++ .../pom.xml | 119 +++++++++ .../src/main/resources/aadb2c.enable.config | 1 + .../AADB2CAuthorizationRequestResolver.java | 115 +++++++++ .../btoc/AADB2CAutoConfiguration.java | 140 +++++++++++ .../btoc/AADB2CConfigurationException.java | 14 ++ .../btoc/AADB2CLogoutSuccessHandler.java | 36 +++ .../btoc/AADB2COidcLoginConfigurer.java | 32 +++ .../autoconfigure/btoc/AADB2CProperties.java | 199 +++++++++++++++ .../spring/autoconfigure/btoc/AADB2CURL.java | 65 +++++ sdk/spring/ci.yml | 4 +- sdk/spring/pom.xml | 1 + 13 files changed, 964 insertions(+), 1 deletion(-) create mode 100644 sdk/spring/azure-spring-boot-starter-active-directory-b2c/README.md create mode 100644 sdk/spring/azure-spring-boot-starter-active-directory-b2c/pom.xml create mode 100644 sdk/spring/azure-spring-boot-starter-active-directory-b2c/src/main/resources/aadb2c.enable.config create mode 100644 sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAuthorizationRequestResolver.java create mode 100644 sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAutoConfiguration.java create mode 100644 sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CConfigurationException.java create mode 100644 sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CLogoutSuccessHandler.java create mode 100644 sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2COidcLoginConfigurer.java create mode 100644 sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CProperties.java create mode 100644 sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CURL.java diff --git a/eng/versioning/version_data.txt b/eng/versioning/version_data.txt index 4470658aae31..45f7715135f0 100644 --- a/eng/versioning/version_data.txt +++ b/eng/versioning/version_data.txt @@ -43,3 +43,4 @@ com.microsoft.azure:azure-media;1.0.0-beta.1;1.0.0-beta.1 com.microsoft.azure:azure-spring-boot;2.2.4;2.2.5-beta.1 com.microsoft.azure:azure-spring-boot-starter;2.2.4;2.2.5-beta.1 com.microsoft.azure:azure-active-directory-spring-boot-starter;2.2.4;2.2.5-beta.1 +com.microsoft.azure:azure-active-directory-b2c-spring-boot-starter;2.2.4;2.2.5-beta.1 diff --git a/sdk/spring/azure-spring-boot-starter-active-directory-b2c/README.md b/sdk/spring/azure-spring-boot-starter-active-directory-b2c/README.md new file mode 100644 index 000000000000..8f0873bab62c --- /dev/null +++ b/sdk/spring/azure-spring-boot-starter-active-directory-b2c/README.md @@ -0,0 +1,238 @@ +# Azure AAD B2C Spring Boot Starter client library for Java +## Overview + +Azure Active Directory (Azure AD) B2C is an identity management service that enables you to customize and control how +customers sign up, sign in, and manage their profiles when using your applications. Azure AD B2C enables these actions +while protecting the identities of your customers at the same time. + +## Prerequisites + +The following prerequisites are required in order to complete the steps in this article: + +* A supported Java Development Kit (JDK). For more information about the JDKs available for use when developing on Azure, see . +* [Apache Maven](http://maven.apache.org/), version 3.0 or later. +* Azure subscription. + +If you don't have an Azure subscription, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. + +## Getting started + +### Create the Active Directory instance + +1. Log into . + +2. Click **+Create a resource**, then **Identity**, and then **Azure Active Directory B2C**. + +3. Enter your **Organization name** and your **Initial domain name**, record the **domain name** as your +`${your-tenant-name}` and click **Create**. + +4. Select your account name on the top-right of the Azure portal toolbar, then click **Switch directory**. + +5. Select your new Azure Active Directory from the drop-down menu. + +6. Search `b2c` and click `Azure AD B2C` service. + +### Add an application registration for your Spring Boot app + +1. Select **Azure AD B2C** from the portal menu, click **Applications**, and then click **Add**. + +2. Specify your application **Name**, add `http://localhost:8080/home` for the **Reply URL**, record the +**Application ID** as your `${your-client-id}` and then click **Save**. + +3. Select **Keys** from your application, click **Generate key** to generate `${your-client-secret}` and then **Save**. + +4. Select **User flows** on your left, and then **Click** **New user flow **. + +5. Choose **Sign up or in**, **Profile editing** and **Password reset** to create user flows +respectively. Specify your user flow **Name** and **User attributes and claims**, click **Create**. + +## Examples +### Configure and compile your app + +1. Extract the files from the project archive you created and downloaded earlier in this tutorial into a directory. + +2. Navigate to the parent folder for your project, and open the `pom.xml` Maven project file in a text editor. + +3. Add the dependencies for Spring OAuth2 security to the `pom.xml`: + + ```xml + + com.azure + azure-spring-boot-starter-active-directory-b2c + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity5 + + ``` + +4. Save and close the *pom.xml* file. + +5. Navigate to the *src/main/resources* folder in your project and open the *application.yml* file in a text editor. + +6. Specify the settings for your app registration using the values you created earlier; for example: + + ```yaml + azure: + activedirectory: + b2c: + tenant: ${your-tenant-name} + client-id: ${your-client-id} + client-secret: ${your-client-secret} + reply-url: ${your-reply-url-from-aad} # should be absolute url. + logout-success-url: ${you-logout-success-url} + user-flows: + sign-up-or-sign-in: ${your-sign-up-or-in-user-flow} + profile-edit: ${your-profile-edit-user-flow} # optional + password-reset: ${your-password-reset-user-flow} # optional + ``` + Where: + + | Parameter | Description | + |---|---| + | `azure.activedirectory.b2c.tenant` | Contains your AD B2C's `${your-tenant-name` from earlier. | + | `azure.activedirectory.b2c.client-id` | Contains the `${your-client-id}` from your application that you completed earlier. | + | `azure.activedirectory.b2c.client-secret` | Contains the `${your-client-secret}` from your application that you completed earlier. | + | `azure.activedirectory.b2c.reply-url` | Contains one of the **Reply URL** from your application that you completed earlier. | + | `azure.activedirectory.b2c.logout-success-url` | Specify the URL when your application logout successfully. | + | `azure.activedirectory.b2c.user-flows` | Contains the name of the user flows that you completed earlier. + +7. Save and close the *application.yml* file. + +8. Create a folder named *controller* in the Java source folder for your application. + +9. Create a new Java file named *HelloController.java* in the *controller* folder and open it in a text editor. + +10. Enter the following code, then save and close the file: + + ```java + package sample.aad.controller; + + import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; + import org.springframework.security.oauth2.core.user.OAuth2User; + import org.springframework.stereotype.Controller; + import org.springframework.ui.Model; + import org.springframework.web.bind.annotation.GetMapping; + + @Controller + public class WebController { + + private void initializeModel(Model model, OAuth2AuthenticationToken token) { + if (token != null) { + final OAuth2User user = token.getPrincipal(); + + model.addAttribute("grant_type", user.getAuthorities()); + model.addAllAttributes(user.getAttributes()); + } + } + + @GetMapping(value = "/") + public String index(Model model, OAuth2AuthenticationToken token) { + initializeModel(model, token); + + return "home"; + } + + @GetMapping(value = "/greeting") + public String greeting(Model model, OAuth2AuthenticationToken token) { + initializeModel(model, token); + + return "greeting"; + } + + @GetMapping(value = "/home") + public String home(Model model, OAuth2AuthenticationToken token) { + initializeModel(model, token); + + return "home"; + } + } + ``` + +11. Create a folder named *security* in the Java source folder for your application. + +12. Create a new Java file named *WebSecurityConfig.java* in the *security* folder and open it in a text editor. + +13. Enter the following code, then save and close the file: + + ```java + package sample.aad.security; + + import com.microsoft.azure.spring.autoconfigure.btoc.AADB2COidcLoginConfigurer; + import org.springframework.security.config.annotation.web.builders.HttpSecurity; + import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; + import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + + @EnableWebSecurity + public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { + + private final AADB2COidcLoginConfigurer configurer; + + public WebSecurityConfiguration(AADB2COidcLoginConfigurer configurer) { + this.configurer = configurer; + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest() + .authenticated() + .and() + .apply(configurer) + ; + } + } + ``` +14. Copy the `greeting.html` and `home.html` from [Azure AD B2C Spring Boot Sample](https://github.com/Microsoft/azure-spring-boot/tree/master/azure-spring-boot-samples/azure-active-directory-b2c-oidc-spring-boot-sample/src/main/resources/templates), and replace the +`${your-profile-edit-user-flow}` and `${your-password-reset-user-flow}` with your user flow name +respectively that completed earlier. + +### Build and test your app + +1. Open a command prompt and change directory to the folder where your app's *pom.xml* file is located. + +2. Build your Spring Boot application with Maven and run it; for example: + + ```shell + mvn clean package + mvn spring-boot:run + ``` + +3. After your application is built and started by Maven, open in a web browser; +you should be redirected to login page. + +4. Click linke with name of `${your-sign-up-or-in}` user flow, you should be rediected Azure AD B2C to start the authentication process. + +4. After you have logged in successfully, you should see the sample `home page` from the browser. + +### Allow telemetry + +Microsoft would like to collect data about how users use this Spring boot starter. Microsoft uses this information to improve our tooling experience. Participation is voluntary. If you don't want to participate, just simply disable it by setting below configuration in `application.properties`. + +``` +azure.activedirectory.b2c.allow-telemetry=false +``` + +When telemetry is enabled, an HTTP request will be sent to URL `https://dc.services.visualstudio.com/v2/track`. So please make sure it's not blocked by your firewall. + +Find more information about Azure Service Privacy Statement, please check [Microsoft Online Services Privacy Statement](https://www.microsoft.com/en-us/privacystatement/OnlineServices/Default.aspx). + +## Key concepts + +## Troubleshooting + +## Next steps + +## Contributing + +## Summary + +In this documentation, you created a new Java web application using the Azure Active Directory B2C starter, +configured a new Azure AD B2C tenant and registered a new application in it, and then configured your +application to use the Spring annotations and classes to protect the web app. + diff --git a/sdk/spring/azure-spring-boot-starter-active-directory-b2c/pom.xml b/sdk/spring/azure-spring-boot-starter-active-directory-b2c/pom.xml new file mode 100644 index 000000000000..3f47d023fa6c --- /dev/null +++ b/sdk/spring/azure-spring-boot-starter-active-directory-b2c/pom.xml @@ -0,0 +1,119 @@ + + + 4.0.0 + + + com.azure + azure-client-sdk-parent + 1.7.0 + ../../parents/azure-client-sdk-parent + + + com.microsoft.azure + azure-active-directory-b2c-spring-boot-starter + 2.2.5-beta.1 + + Azure AD B2C Spring Security Integration Spring Boot Starter + Spring Boot Starter for Azure AD B2C and Spring Security Integration + + + + org.springframework.boot + spring-boot-starter + 2.2.0.RELEASE + + + + org.springframework.boot + spring-boot-starter-validation + 2.2.0.RELEASE + + + + com.microsoft.azure + azure-spring-boot + 2.2.5-beta.1 + + + + + org.springframework + spring-web + 5.2.0.RELEASE + + + + javax.validation + validation-api + 2.0.1.Final + + + + + org.springframework.security + spring-security-core + 5.2.0.RELEASE + + + + org.springframework.security + spring-security-web + 5.2.0.RELEASE + + + + org.springframework.security + spring-security-config + 5.2.0.RELEASE + + + + org.springframework.security + spring-security-oauth2-core + 5.2.0.RELEASE + + + + org.springframework.security + spring-security-oauth2-client + 5.2.0.RELEASE + + + + org.springframework.security + spring-security-oauth2-jose + 5.2.0.RELEASE + + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M3 + + + + + com.microsoft.azure:* + javax.validation:validation-api:[2.0.1.Final] + org.springframework:spring-web:[5.2.0.RELEASE] + org.springframework.boot:spring-boot-starter:[2.2.0.RELEASE] + org.springframework.boot:spring-boot-starter-validation:[2.2.0.RELEASE] + org.springframework.security:spring-security-config:[5.2.0.RELEASE] + org.springframework.security:spring-security-core:[5.2.0.RELEASE] + org.springframework.security:spring-security-oauth2-client:[5.2.0.RELEASE] + org.springframework.security:spring-security-oauth2-core:[5.2.0.RELEASE] + org.springframework.security:spring-security-oauth2-jose:[5.2.0.RELEASE] + org.springframework.security:spring-security-web:[5.2.0.RELEASE] + + + + + + + + diff --git a/sdk/spring/azure-spring-boot-starter-active-directory-b2c/src/main/resources/aadb2c.enable.config b/sdk/spring/azure-spring-boot-starter-active-directory-b2c/src/main/resources/aadb2c.enable.config new file mode 100644 index 000000000000..2995a4d0e749 --- /dev/null +++ b/sdk/spring/azure-spring-boot-starter-active-directory-b2c/src/main/resources/aadb2c.enable.config @@ -0,0 +1 @@ +dummy \ No newline at end of file diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAuthorizationRequestResolver.java b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAuthorizationRequestResolver.java new file mode 100644 index 000000000000..7f413832cb0e --- /dev/null +++ b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAuthorizationRequestResolver.java @@ -0,0 +1,115 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.microsoft.azure.spring.autoconfigure.btoc; + +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver; +import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter; +import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; + +public class AADB2CAuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver { + + private static final String REQUEST_BASE_URI = + OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI; + + private static final String REGISTRATION_ID_NAME = "registrationId"; + + private static final String PARAMETER_X_CLIENT_SKU = "x-client-SKU"; + + private static final String AAD_B2C_USER_AGENT = "spring-boot-starter"; + + private static final String MATCHER_PATTERN = String.format("%s/{%s}", REQUEST_BASE_URI, REGISTRATION_ID_NAME); + + private static final AntPathRequestMatcher REQUEST_MATCHER = new AntPathRequestMatcher(MATCHER_PATTERN); + + private final OAuth2AuthorizationRequestResolver defaultResolver; + + private final String passwordResetUserFlow; + + public AADB2CAuthorizationRequestResolver(@NonNull ClientRegistrationRepository repository) { + this.passwordResetUserFlow = null; + this.defaultResolver = new DefaultOAuth2AuthorizationRequestResolver(repository, REQUEST_BASE_URI); + } + + public AADB2CAuthorizationRequestResolver(@NonNull ClientRegistrationRepository repository, + @Nullable String passwordResetUserFlow) { + this.passwordResetUserFlow = passwordResetUserFlow; + this.defaultResolver = new DefaultOAuth2AuthorizationRequestResolver(repository, REQUEST_BASE_URI); + } + + @Override + public OAuth2AuthorizationRequest resolve(@NonNull HttpServletRequest request) { + return resolve(request, getRegistrationId(request)); + } + + @Override + public OAuth2AuthorizationRequest resolve(@NonNull HttpServletRequest request, String registrationId) { + if (StringUtils.hasText(passwordResetUserFlow) && isForgotPasswordAuthorizationRequest(request)) { + final OAuth2AuthorizationRequest authRequest = defaultResolver.resolve(request, passwordResetUserFlow); + return getB2CAuthorizationRequest(authRequest, passwordResetUserFlow); + } + + if (StringUtils.hasText(registrationId) && REQUEST_MATCHER.matches(request)) { + return getB2CAuthorizationRequest(defaultResolver.resolve(request), registrationId); + } + + // Return null may not be the good practice, but we need to align with oauth2.client.web + // DefaultOAuth2AuthorizationRequestResolver. + return null; + } + + private void cleanupSecurityContextAuthentication() { + SecurityContextHolder.getContext().setAuthentication(null); + } + + private OAuth2AuthorizationRequest getB2CAuthorizationRequest(@Nullable OAuth2AuthorizationRequest request, + String userFlow) { + Assert.hasText(userFlow, "User flow should contain text."); + + if (request == null) { + return null; + } + + cleanupSecurityContextAuthentication(); + + final Map parameters = new HashMap<>(request.getAdditionalParameters()); + + parameters.put("p", userFlow); + parameters.put(PARAMETER_X_CLIENT_SKU, AAD_B2C_USER_AGENT); + + return OAuth2AuthorizationRequest.from(request).additionalParameters(parameters).build(); + } + + private String getRegistrationId(HttpServletRequest request) { + if (REQUEST_MATCHER.matches(request)) { + return REQUEST_MATCHER.extractUriTemplateVariables(request).get(REGISTRATION_ID_NAME); + } + + return null; + } + + // 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) { + final String error = request.getParameter("error"); + final String description = request.getParameter("error_description"); + + if ("access_denied".equals(error)) { + Assert.hasText(description, "description should contain text."); + return description.startsWith("AADB2C90118:"); + } + + return false; + } +} diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAutoConfiguration.java b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAutoConfiguration.java new file mode 100644 index 000000000000..002d0e565eff --- /dev/null +++ b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAutoConfiguration.java @@ -0,0 +1,140 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.microsoft.azure.spring.autoconfigure.btoc; + +import com.microsoft.azure.telemetry.TelemetrySender; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnResource; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.lang.NonNull; +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; +import org.springframework.security.oauth2.core.AuthorizationGrantType; +import org.springframework.security.oauth2.core.ClientAuthenticationMethod; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.microsoft.azure.spring.autoconfigure.btoc.AADB2CProperties.PREFIX; +import static com.microsoft.azure.spring.autoconfigure.btoc.AADB2CProperties.USER_FLOW_SIGN_UP_OR_SIGN_IN; +import static com.microsoft.azure.telemetry.TelemetryData.SERVICE_NAME; +import static com.microsoft.azure.telemetry.TelemetryData.TENANT_NAME; +import static com.microsoft.azure.telemetry.TelemetryData.getClassPackageSimpleName; + +@Configuration +@ConditionalOnWebApplication +@ConditionalOnResource(resources = "classpath:aadb2c.enable.config") +@ConditionalOnProperty( + prefix = PREFIX, + value = { + "tenant", + "client-id", + "client-secret", + "reply-url", + USER_FLOW_SIGN_UP_OR_SIGN_IN + } +) +@EnableConfigurationProperties(AADB2CProperties.class) +public class AADB2CAutoConfiguration { + + private final ClientRegistrationRepository repository; + + private final AADB2CProperties properties; + + public AADB2CAutoConfiguration(@NonNull ClientRegistrationRepository repository, + @NonNull AADB2CProperties properties) { + this.repository = repository; + this.properties = properties; + } + + @Bean + @ConditionalOnMissingBean + public AADB2CAuthorizationRequestResolver b2cOAuth2AuthorizationRequestResolver() { + return new AADB2CAuthorizationRequestResolver(repository, properties.getUserFlows().getPasswordReset()); + } + + @Bean + @ConditionalOnMissingBean + public AADB2CLogoutSuccessHandler b2cLogoutSuccessHandler() { + return new AADB2CLogoutSuccessHandler(properties); + } + + @Bean + @ConditionalOnMissingBean + public AADB2COidcLoginConfigurer b2cLoginConfigurer(AADB2CLogoutSuccessHandler handler, + AADB2CAuthorizationRequestResolver resolver) { + return new AADB2COidcLoginConfigurer(properties, handler, resolver); + } + + @PostConstruct + private void sendTelemetry() { + if (properties.isAllowTelemetry()) { + final Map events = new HashMap<>(); + final TelemetrySender sender = new TelemetrySender(); + + events.put(SERVICE_NAME, getClassPackageSimpleName(AADB2CAutoConfiguration.class)); + events.put(TENANT_NAME, properties.getTenant()); + + sender.send(ClassUtils.getUserClass(getClass()).getSimpleName(), events); + } + } + + @Configuration + @ConditionalOnResource(resources = "classpath:aadb2c.enable.config") + @ConditionalOnProperty(prefix = PREFIX, value = "oidc-enabled", havingValue = "true", matchIfMissing = true) + public static class AADB2COidcAutoConfiguration { + + private final AADB2CProperties properties; + + public AADB2COidcAutoConfiguration(@NonNull AADB2CProperties properties) { + this.properties = properties; + } + + private void addB2CClientRegistration(@NonNull List registrations, String userFlow) { + if (StringUtils.hasText(userFlow)) { + registrations.add(b2cClientRegistration(userFlow)); + } + } + + @Bean + @ConditionalOnMissingBean + public ClientRegistrationRepository clientRegistrationRepository() { + final List registrations = new ArrayList<>(); + + addB2CClientRegistration(registrations, properties.getUserFlows().getSignUpOrSignIn()); + addB2CClientRegistration(registrations, properties.getUserFlows().getProfileEdit()); + addB2CClientRegistration(registrations, properties.getUserFlows().getPasswordReset()); + + return new InMemoryClientRegistrationRepository(registrations); + } + + private ClientRegistration b2cClientRegistration(String userFlow) { + Assert.hasText(userFlow, "User flow should contains text."); + + return ClientRegistration.withRegistrationId(userFlow) // Use flow as registration Id. + .clientId(properties.getClientId()) + .clientSecret(properties.getClientSecret()) + .clientAuthenticationMethod(ClientAuthenticationMethod.POST) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .redirectUriTemplate(properties.getReplyUrl()) + .scope(properties.getClientId(), "openid") + .authorizationUri(AADB2CURL.getAuthorizationUrl(properties.getTenant())) + .tokenUri(AADB2CURL.getTokenUrl(properties.getTenant(), userFlow)) + .jwkSetUri(AADB2CURL.getJwkSetUrl(properties.getTenant(), userFlow)) + .userNameAttributeName("name") + .clientName(userFlow) + .build(); + } + } +} diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CConfigurationException.java b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CConfigurationException.java new file mode 100644 index 000000000000..974e48ac964f --- /dev/null +++ b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CConfigurationException.java @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.microsoft.azure.spring.autoconfigure.btoc; + +public class AADB2CConfigurationException extends RuntimeException { + + public AADB2CConfigurationException(String message) { + super(message); + } + + public AADB2CConfigurationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CLogoutSuccessHandler.java b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CLogoutSuccessHandler.java new file mode 100644 index 000000000000..194c6ee9d39c --- /dev/null +++ b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CLogoutSuccessHandler.java @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.microsoft.azure.spring.autoconfigure.btoc; + +import org.springframework.lang.NonNull; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class AADB2CLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler { + + private final AADB2CProperties properties; + + public AADB2CLogoutSuccessHandler(@NonNull AADB2CProperties properties) { + this.properties = properties; + + super.setDefaultTargetUrl(getAADB2CEndSessionUrl()); + } + + private String getAADB2CEndSessionUrl() { + final String userFlow = properties.getUserFlows().getSignUpOrSignIn(); + final String logoutSuccessUrl = properties.getLogoutSuccessUrl(); + + return AADB2CURL.getEndSessionUrl(properties.getTenant(), logoutSuccessUrl, userFlow); + } + + @Override + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) throws IOException, ServletException { + super.onLogoutSuccess(request, response, authentication); + } +} diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2COidcLoginConfigurer.java b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2COidcLoginConfigurer.java new file mode 100644 index 000000000000..4af8bd08c272 --- /dev/null +++ b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2COidcLoginConfigurer.java @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.microsoft.azure.spring.autoconfigure.btoc; + +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; + +public class AADB2COidcLoginConfigurer extends AbstractHttpConfigurer { + + private final AADB2CProperties properties; + + private final AADB2CLogoutSuccessHandler handler; + + private final AADB2CAuthorizationRequestResolver resolver; + + public AADB2COidcLoginConfigurer(AADB2CProperties properties, + AADB2CLogoutSuccessHandler handler, AADB2CAuthorizationRequestResolver resolver) { + this.properties = properties; + this.handler = handler; + this.resolver = resolver; + } + + @Override + public void init(HttpSecurity http) throws Exception { + http.logout() + .logoutSuccessHandler(handler) + .and() + .oauth2Login() + .loginProcessingUrl(properties.getLoginProcessingUrl()) + .authorizationEndpoint().authorizationRequestResolver(resolver); + } +} diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CProperties.java b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CProperties.java new file mode 100644 index 000000000000..9423789e24ec --- /dev/null +++ b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CProperties.java @@ -0,0 +1,199 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.microsoft.azure.spring.autoconfigure.btoc; + +import org.hibernate.validator.constraints.URL; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.lang.NonNull; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationProvider; +import org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider; +import org.springframework.validation.annotation.Validated; + +import javax.validation.constraints.NotBlank; +import java.net.MalformedURLException; + +@Validated +@ConfigurationProperties(prefix = AADB2CProperties.PREFIX) +public class AADB2CProperties { + + private static final String USER_FLOWS = "user-flows"; + + /** + * We do not use ${@link String#format(String, Object...)} + * as it's not real constant, which cannot be referenced in annotation. + */ + public static final String USER_FLOW_PASSWORD_RESET = USER_FLOWS + ".password-reset"; + + public static final String USER_FLOW_PROFILE_EDIT = USER_FLOWS + ".profile-edit"; + + public static final String USER_FLOW_SIGN_UP_OR_SIGN_IN = USER_FLOWS + ".sign-up-or-sign-in"; + + public static final String DEFAULT_LOGOUT_SUCCESS_URL = "http://localhost:8080/login"; + + public static final String PREFIX = "azure.activedirectory.b2c"; + + /** + * The name of the b2c tenant. + */ + @NotBlank(message = "tenant name should not be blank") + private String tenant; + + /** + * Use OIDC ${@link OidcAuthorizationCodeAuthenticationProvider} by default. If set to false, + * will use Oauth2 ${@link OAuth2AuthorizationCodeAuthenticationProvider}. + */ + private Boolean oidcEnabled = true; + + /** + * The application ID that registered under b2c tenant. + */ + @NotBlank(message = "client ID should not be blank") + private String clientId; + + /** + * The application secret that registered under b2c tenant. + */ + @NotBlank(message = "client secret should not be blank") + private String clientSecret; + + @URL(message = "reply URL should be valid URL") + private String replyUrl; + + @URL(message = "logout success should be valid URL") + private String logoutSuccessUrl = DEFAULT_LOGOUT_SUCCESS_URL; + + /** + * The all user flows which is created under b2c tenant. + */ + private UserFlows userFlows = new UserFlows(); + + /** + * Telemetry data will be collected if true, or disable data collection. + */ + private boolean allowTelemetry = true; + + private String getReplyURLPath(@URL String replyURL) { + try { + return new java.net.URL(replyURL).getPath(); + } catch (MalformedURLException e) { + throw new AADB2CConfigurationException("Failed to get path of given URL.", e); + } + } + + @NonNull + public String getLoginProcessingUrl() { + return getReplyURLPath(replyUrl); + } + + @Validated + protected static class UserFlows { + + protected UserFlows() { + + } + + /** + * The sign-up-or-sign-in user flow which is created under b2c tenant. + */ + @NotBlank(message = "sign-up-or-in value should not be blank") + private String signUpOrSignIn; + + /** + * The profile-edit user flow which is created under b2c tenant. + */ + private String profileEdit; + + /** + * The password-reset user flow which is created under b2c tenant. + */ + private String passwordReset; + + public String getSignUpOrSignIn() { + return signUpOrSignIn; + } + + public void setSignUpOrSignIn(String signUpOrSignIn) { + this.signUpOrSignIn = signUpOrSignIn; + } + + public String getProfileEdit() { + return profileEdit; + } + + public void setProfileEdit(String profileEdit) { + this.profileEdit = profileEdit; + } + + public String getPasswordReset() { + return passwordReset; + } + + public void setPasswordReset(String passwordReset) { + this.passwordReset = passwordReset; + } + } + + public String getTenant() { + return tenant; + } + + public void setTenant(String tenant) { + this.tenant = tenant; + } + + public Boolean getOidcEnabled() { + return oidcEnabled; + } + + public void setOidcEnabled(Boolean oidcEnabled) { + this.oidcEnabled = oidcEnabled; + } + + 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; + } + + public String getReplyUrl() { + return replyUrl; + } + + public void setReplyUrl(String replyUrl) { + this.replyUrl = replyUrl; + } + + public String getLogoutSuccessUrl() { + return logoutSuccessUrl; + } + + public void setLogoutSuccessUrl(String logoutSuccessUrl) { + this.logoutSuccessUrl = logoutSuccessUrl; + } + + public UserFlows getUserFlows() { + return userFlows; + } + + public void setUserFlows(UserFlows userFlows) { + this.userFlows = userFlows; + } + + public boolean isAllowTelemetry() { + return allowTelemetry; + } + + public void setAllowTelemetry(boolean allowTelemetry) { + this.allowTelemetry = allowTelemetry; + } +} diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CURL.java b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CURL.java new file mode 100644 index 000000000000..0f1bd87824c6 --- /dev/null +++ b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CURL.java @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.microsoft.azure.spring.autoconfigure.btoc; + +import org.springframework.util.Assert; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +public final class AADB2CURL { + + private AADB2CURL() { + + } + + private static final String AUTHORIZATION_URL_PATTERN = + "https://%s.b2clogin.com/%s.onmicrosoft.com/oauth2/v2.0/authorize"; + + private static final String TOKEN_URL_PATTERN = + "https://%s.b2clogin.com/%s.onmicrosoft.com/oauth2/v2.0/token?p=%s"; + + private static final String JWKSET_URL_PATTERN = + "https://%s.b2clogin.com/%s.onmicrosoft.com/discovery/v2.0/keys?p=%s"; + + private static final String END_SESSION_URL_PATTERN = + "https://%s.b2clogin.com/%s.onmicrosoft.com/oauth2/v2.0/logout?post_logout_redirect_uri=%s&p=%s"; + + public static String getAuthorizationUrl(String tenant) { + Assert.hasText(tenant, "tenant should have text."); + + return String.format(AUTHORIZATION_URL_PATTERN, tenant, tenant); + } + + public static String getTokenUrl(String tenant, String userFlow) { + Assert.hasText(tenant, "tenant should have text."); + Assert.hasText(userFlow, "user flow should have text."); + + return String.format(TOKEN_URL_PATTERN, tenant, tenant, userFlow); + } + + public static String getJwkSetUrl(String tenant, String userFlow) { + Assert.hasText(tenant, "tenant should have text."); + Assert.hasText(userFlow, "user flow should have text."); + + return String.format(JWKSET_URL_PATTERN, tenant, tenant, userFlow); + } + + public static String getEndSessionUrl(String tenant, String logoutUrl, String userFlow) { + Assert.hasText(tenant, "tenant should have text."); + Assert.hasText(logoutUrl, "logoutUrl should have text."); + Assert.hasText(userFlow, "user flow should have text."); + + return String.format(END_SESSION_URL_PATTERN, tenant, tenant, getEncodedURL(logoutUrl), userFlow); + } + + private static String getEncodedURL(String url) { + Assert.hasText(url, "url should have text."); + + try { + return URLEncoder.encode(url, "utf-8"); + } catch (UnsupportedEncodingException e) { + throw new AADB2CConfigurationException("failed to encode url: " + url, e); + } + } +} diff --git a/sdk/spring/ci.yml b/sdk/spring/ci.yml index 450120cde9ef..17dd95635ff5 100644 --- a/sdk/spring/ci.yml +++ b/sdk/spring/ci.yml @@ -47,7 +47,9 @@ stages: - name: azure-active-directory-spring-boot-starter groupId: com.microsoft.azure safeName: azurespringbootstarteractivedirectory - + - name: azure-active-directory-b2c-spring-boot-starter + groupId: com.microsoft.azure + safeName: azurespringbootstarteractivedirectoryb2c diff --git a/sdk/spring/pom.xml b/sdk/spring/pom.xml index 84980d7d61a7..7729c97cae17 100644 --- a/sdk/spring/pom.xml +++ b/sdk/spring/pom.xml @@ -12,6 +12,7 @@ azure-spring-boot azure-spring-boot-starter azure-spring-boot-starter-active-directory + azure-spring-boot-starter-active-directory-b2c From 17af4da1753b2320ab2a781529ce63331d29fccc Mon Sep 17 00:00:00 2001 From: Yi Liu Date: Thu, 7 May 2020 13:11:25 +0800 Subject: [PATCH 02/11] update README.md and add b2c samples --- .../README.md | 139 ++++++++---------- .../btoc/AADB2COidcLoginConfigSample.java | 38 +++++ .../spring/btoc/AADB2CWebController.java | 53 +++++++ 3 files changed, 152 insertions(+), 78 deletions(-) create mode 100644 sdk/spring/azure-spring-boot/src/samples/java/com/azure/spring/btoc/AADB2COidcLoginConfigSample.java create mode 100644 sdk/spring/azure-spring-boot/src/samples/java/com/azure/spring/btoc/AADB2CWebController.java diff --git a/sdk/spring/azure-spring-boot-starter-active-directory-b2c/README.md b/sdk/spring/azure-spring-boot-starter-active-directory-b2c/README.md index 8f0873bab62c..2130ac03bf7d 100644 --- a/sdk/spring/azure-spring-boot-starter-active-directory-b2c/README.md +++ b/sdk/spring/azure-spring-boot-starter-active-directory-b2c/README.md @@ -1,4 +1,4 @@ -# Azure AAD B2C Spring Boot Starter client library for Java +# Azure AD B2C Spring Boot Starter client library for Java ## Overview Azure Active Directory (Azure AD) B2C is an identity management service that enables you to customize and control how @@ -108,87 +108,71 @@ respectively. Specify your user flow **Name** and **User attributes and claims** 9. Create a new Java file named *HelloController.java* in the *controller* folder and open it in a text editor. 10. Enter the following code, then save and close the file: + +```java +@Controller +public class AADB2CWebController { - ```java - package sample.aad.controller; - - import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; - import org.springframework.security.oauth2.core.user.OAuth2User; - import org.springframework.stereotype.Controller; - import org.springframework.ui.Model; - import org.springframework.web.bind.annotation.GetMapping; - - @Controller - public class WebController { - - private void initializeModel(Model model, OAuth2AuthenticationToken token) { - if (token != null) { - final OAuth2User user = token.getPrincipal(); - - model.addAttribute("grant_type", user.getAuthorities()); - model.addAllAttributes(user.getAttributes()); - } - } - - @GetMapping(value = "/") - public String index(Model model, OAuth2AuthenticationToken token) { - initializeModel(model, token); - - return "home"; - } - - @GetMapping(value = "/greeting") - public String greeting(Model model, OAuth2AuthenticationToken token) { - initializeModel(model, token); - - return "greeting"; - } - - @GetMapping(value = "/home") - public String home(Model model, OAuth2AuthenticationToken token) { - initializeModel(model, token); - - return "home"; + private void initializeModel(Model model, OAuth2AuthenticationToken token) { + if (token != null) { + final OAuth2User user = token.getPrincipal(); + + model.addAttribute("grant_type", user.getAuthorities()); + model.addAllAttributes(user.getAttributes()); } } - ``` + + @GetMapping(value = "/") + public String index(Model model, OAuth2AuthenticationToken token) { + initializeModel(model, token); + + return "home"; + } + + @GetMapping(value = "/greeting") + public String greeting(Model model, OAuth2AuthenticationToken token) { + initializeModel(model, token); + + return "greeting"; + } + + @GetMapping(value = "/home") + public String home(Model model, OAuth2AuthenticationToken token) { + initializeModel(model, token); + + return "home"; + } +} +``` 11. Create a folder named *security* in the Java source folder for your application. 12. Create a new Java file named *WebSecurityConfig.java* in the *security* folder and open it in a text editor. 13. Enter the following code, then save and close the file: + +```java +@EnableWebSecurity +public class AADB2COidcLoginConfigSample extends WebSecurityConfigurerAdapter { - ```java - package sample.aad.security; - - import com.microsoft.azure.spring.autoconfigure.btoc.AADB2COidcLoginConfigurer; - import org.springframework.security.config.annotation.web.builders.HttpSecurity; - import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; - import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; - - @EnableWebSecurity - public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { - - private final AADB2COidcLoginConfigurer configurer; - - public WebSecurityConfiguration(AADB2COidcLoginConfigurer configurer) { - this.configurer = configurer; - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .anyRequest() - .authenticated() - .and() - .apply(configurer) - ; - } + private final AADB2COidcLoginConfigurer configurer; + + public AADB2COidcLoginConfigSample(AADB2COidcLoginConfigurer configurer) { + this.configurer = configurer; + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests() + .anyRequest() + .authenticated() + .and() + .apply(configurer) + ; } - ``` -14. Copy the `greeting.html` and `home.html` from [Azure AD B2C Spring Boot Sample](https://github.com/Microsoft/azure-spring-boot/tree/master/azure-spring-boot-samples/azure-active-directory-b2c-oidc-spring-boot-sample/src/main/resources/templates), and replace the +} +``` +14. Copy the `greeting.html` and `home.html` from [Azure AD B2C Spring Boot Sample](../azure-spring-boot-samples/azure-spring-boot-sample-active-directory-b2c-oidc/src/main/resources/templates), and replace the `${your-profile-edit-user-flow}` and `${your-password-reset-user-flow}` with your user flow name respectively that completed earlier. @@ -210,7 +194,12 @@ you should be redirected to login page. 4. After you have logged in successfully, you should see the sample `home page` from the browser. -### Allow telemetry +## Key concepts + +## Troubleshooting + +## Next steps +#### Allow telemetry Microsoft would like to collect data about how users use this Spring boot starter. Microsoft uses this information to improve our tooling experience. Participation is voluntary. If you don't want to participate, just simply disable it by setting below configuration in `application.properties`. @@ -222,12 +211,6 @@ When telemetry is enabled, an HTTP request will be sent to URL `https://dc.servi Find more information about Azure Service Privacy Statement, please check [Microsoft Online Services Privacy Statement](https://www.microsoft.com/en-us/privacystatement/OnlineServices/Default.aspx). -## Key concepts - -## Troubleshooting - -## Next steps - ## Contributing ## Summary diff --git a/sdk/spring/azure-spring-boot/src/samples/java/com/azure/spring/btoc/AADB2COidcLoginConfigSample.java b/sdk/spring/azure-spring-boot/src/samples/java/com/azure/spring/btoc/AADB2COidcLoginConfigSample.java new file mode 100644 index 000000000000..279cea94c41d --- /dev/null +++ b/sdk/spring/azure-spring-boot/src/samples/java/com/azure/spring/btoc/AADB2COidcLoginConfigSample.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See LICENSE in the project root for + * license information. + */ +package com.azure.spring.btoc; + +import com.microsoft.azure.spring.autoconfigure.btoc.AADB2COidcLoginConfigurer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +/** + * WARNING: MODIFYING THIS FILE WILL REQUIRE CORRESPONDING UPDATES TO README.md FILE. LINE NUMBERS + * ARE USED TO EXTRACT APPROPRIATE CODE SEGMENTS FROM THIS FILE. ADD NEW CODE AT THE BOTTOM TO AVOID CHANGING + * LINE NUMBERS OF EXISTING CODE SAMPLES. + *

+ * Code samples for the AADB2COidcLoginConfigurer in README.md + */ +@EnableWebSecurity +public class AADB2COidcLoginConfigSample extends WebSecurityConfigurerAdapter { + + private final AADB2COidcLoginConfigurer configurer; + + public AADB2COidcLoginConfigSample(AADB2COidcLoginConfigurer configurer) { + this.configurer = configurer; + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests() + .anyRequest() + .authenticated() + .and() + .apply(configurer) + ; + } +} diff --git a/sdk/spring/azure-spring-boot/src/samples/java/com/azure/spring/btoc/AADB2CWebController.java b/sdk/spring/azure-spring-boot/src/samples/java/com/azure/spring/btoc/AADB2CWebController.java new file mode 100644 index 000000000000..d40ca569a735 --- /dev/null +++ b/sdk/spring/azure-spring-boot/src/samples/java/com/azure/spring/btoc/AADB2CWebController.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See LICENSE in the project root for + * license information. + */ +package com.azure.spring.btoc; + +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; + +/** + * WARNING: MODIFYING THIS FILE WILL REQUIRE CORRESPONDING UPDATES TO README.md FILE. LINE NUMBERS + * ARE USED TO EXTRACT APPROPRIATE CODE SEGMENTS FROM THIS FILE. ADD NEW CODE AT THE BOTTOM TO AVOID CHANGING + * LINE NUMBERS OF EXISTING CODE SAMPLES. + *

+ * Code samples for the AADB2COidcLoginConfigurer in README.md + */ +@Controller +public class AADB2CWebController { + + private void initializeModel(Model model, OAuth2AuthenticationToken token) { + if (token != null) { + final OAuth2User user = token.getPrincipal(); + + model.addAttribute("grant_type", user.getAuthorities()); + model.addAllAttributes(user.getAttributes()); + } + } + + @GetMapping(value = "/") + public String index(Model model, OAuth2AuthenticationToken token) { + initializeModel(model, token); + + return "home"; + } + + @GetMapping(value = "/greeting") + public String greeting(Model model, OAuth2AuthenticationToken token) { + initializeModel(model, token); + + return "greeting"; + } + + @GetMapping(value = "/home") + public String home(Model model, OAuth2AuthenticationToken token) { + initializeModel(model, token); + + return "home"; + } +} From 702cc3be652f82392776d56be5004d9649621a15 Mon Sep 17 00:00:00 2001 From: Yi Liu Date: Thu, 7 May 2020 16:30:33 +0800 Subject: [PATCH 03/11] add comments on aad b2c classes --- .../README.md | 4 ++-- .../btoc/AADB2CAuthorizationRequestResolver.java | 5 +++++ .../autoconfigure/btoc/AADB2CAutoConfiguration.java | 9 +++++++++ .../autoconfigure/btoc/AADB2CConfigurationException.java | 3 +++ .../autoconfigure/btoc/AADB2CLogoutSuccessHandler.java | 3 +++ .../autoconfigure/btoc/AADB2COidcLoginConfigurer.java | 3 +++ .../spring/autoconfigure/btoc/AADB2CProperties.java | 3 +++ .../azure/spring/autoconfigure/btoc/AADB2CURL.java | 3 +++ 8 files changed, 31 insertions(+), 2 deletions(-) diff --git a/sdk/spring/azure-spring-boot-starter-active-directory-b2c/README.md b/sdk/spring/azure-spring-boot-starter-active-directory-b2c/README.md index 2130ac03bf7d..15d78f2af702 100644 --- a/sdk/spring/azure-spring-boot-starter-active-directory-b2c/README.md +++ b/sdk/spring/azure-spring-boot-starter-active-directory-b2c/README.md @@ -105,7 +105,7 @@ respectively. Specify your user flow **Name** and **User attributes and claims** 8. Create a folder named *controller* in the Java source folder for your application. -9. Create a new Java file named *HelloController.java* in the *controller* folder and open it in a text editor. +9. Create a new Java file named *AADB2CWebController.java* in the *controller* folder and open it in a text editor. 10. Enter the following code, then save and close the file: @@ -147,7 +147,7 @@ public class AADB2CWebController { 11. Create a folder named *security* in the Java source folder for your application. -12. Create a new Java file named *WebSecurityConfig.java* in the *security* folder and open it in a text editor. +12. Create a new Java file named *AADB2COidcLoginConfigSample.java* in the *security* folder and open it in a text editor. 13. Enter the following code, then save and close the file: diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAuthorizationRequestResolver.java b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAuthorizationRequestResolver.java index 7f413832cb0e..7682354177b4 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAuthorizationRequestResolver.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAuthorizationRequestResolver.java @@ -18,6 +18,11 @@ import java.util.HashMap; import java.util.Map; +/** + * This class handles the OAuth2 authorization procession for AAD B2C authorization. + *

+ * Forgotten password redirection to password-reset page is added on the base of default OAuth2 authorization resolve. + */ public class AADB2CAuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver { private static final String REQUEST_BASE_URI = diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAutoConfiguration.java b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAutoConfiguration.java index 002d0e565eff..b538a6b7d5c6 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAutoConfiguration.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAutoConfiguration.java @@ -3,6 +3,7 @@ package com.microsoft.azure.spring.autoconfigure.btoc; import com.microsoft.azure.telemetry.TelemetrySender; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnResource; @@ -32,6 +33,14 @@ import static com.microsoft.azure.telemetry.TelemetryData.TENANT_NAME; import static com.microsoft.azure.telemetry.TelemetryData.getClassPackageSimpleName; +/** + * {@link EnableAutoConfiguration Auto-configuration} for Azure Active Authentication B2C. + *

+ * The configuration will not be activated if no {@literal azure.activedirectory.b2c.tenant-id, client-id, client-secret, reply-url and sign-up-or-sign-in} property provided. + *

+ * A client registration repository service {@link InMemoryClientRegistrationRepository} will be auto-configured by specifying + * {@literal azure.activedirectory.b2c.oidc-enabled} property as true or ignore it. + */ @Configuration @ConditionalOnWebApplication @ConditionalOnResource(resources = "classpath:aadb2c.enable.config") diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CConfigurationException.java b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CConfigurationException.java index 974e48ac964f..274d83843b25 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CConfigurationException.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CConfigurationException.java @@ -2,6 +2,9 @@ // Licensed under the MIT License. package com.microsoft.azure.spring.autoconfigure.btoc; +/** + * Throw runtime exception for configuration. + */ public class AADB2CConfigurationException extends RuntimeException { public AADB2CConfigurationException(String message) { diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CLogoutSuccessHandler.java b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CLogoutSuccessHandler.java index 194c6ee9d39c..c2ded434692a 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CLogoutSuccessHandler.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CLogoutSuccessHandler.java @@ -11,6 +11,9 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; +/** + * Get the url of successful logout and handle the navigation on logout. + */ public class AADB2CLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler { private final AADB2CProperties properties; diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2COidcLoginConfigurer.java b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2COidcLoginConfigurer.java index 4af8bd08c272..d07bebfba0a6 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2COidcLoginConfigurer.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2COidcLoginConfigurer.java @@ -5,6 +5,9 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +/** + * Configure B2C OAUTH2 login properties. + */ public class AADB2COidcLoginConfigurer extends AbstractHttpConfigurer { private final AADB2CProperties properties; diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CProperties.java b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CProperties.java index 9423789e24ec..ba51aa078570 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CProperties.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CProperties.java @@ -12,6 +12,9 @@ import javax.validation.constraints.NotBlank; import java.net.MalformedURLException; +/** + * Configuration properties for Azure Active Directory B2C. + */ @Validated @ConfigurationProperties(prefix = AADB2CProperties.PREFIX) public class AADB2CProperties { diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CURL.java b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CURL.java index 0f1bd87824c6..84b075de97d8 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CURL.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CURL.java @@ -7,6 +7,9 @@ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +/** + * To get AAD B2C URLs for configuration. + */ public final class AADB2CURL { private AADB2CURL() { From 3b31909a3d8945f4ed00be822e65dc64a7fd7cb7 Mon Sep 17 00:00:00 2001 From: Yi Liu Date: Fri, 8 May 2020 10:15:32 +0800 Subject: [PATCH 04/11] update copyright and code comment --- .../btoc/AADB2CAuthorizationRequestResolver.java | 4 ++-- .../spring/autoconfigure/btoc/AADB2CAutoConfiguration.java | 2 +- .../com/azure/spring/btoc/AADB2COidcLoginConfigSample.java | 7 ++----- .../java/com/azure/spring/btoc/AADB2CWebController.java | 7 ++----- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAuthorizationRequestResolver.java b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAuthorizationRequestResolver.java index 7682354177b4..c7e10953e45d 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAuthorizationRequestResolver.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAuthorizationRequestResolver.java @@ -19,9 +19,9 @@ import java.util.Map; /** - * This class handles the OAuth2 authorization procession for AAD B2C authorization. + * This class handles the OAuth2 request procession for AAD B2C authorization. *

- * Forgotten password redirection to password-reset page is added on the base of default OAuth2 authorization resolve. + * Userflow name is added in the request link and forgotten password redirection to password-reset page is added on the base of default OAuth2 authorization resolve. */ public class AADB2CAuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver { diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAutoConfiguration.java b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAutoConfiguration.java index b538a6b7d5c6..37ad3ec63e4b 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAutoConfiguration.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAutoConfiguration.java @@ -34,7 +34,7 @@ import static com.microsoft.azure.telemetry.TelemetryData.getClassPackageSimpleName; /** - * {@link EnableAutoConfiguration Auto-configuration} for Azure Active Authentication B2C. + * {@link EnableAutoConfiguration Auto-configuration} for AAD B2C Authentication. *

* The configuration will not be activated if no {@literal azure.activedirectory.b2c.tenant-id, client-id, client-secret, reply-url and sign-up-or-sign-in} property provided. *

diff --git a/sdk/spring/azure-spring-boot/src/samples/java/com/azure/spring/btoc/AADB2COidcLoginConfigSample.java b/sdk/spring/azure-spring-boot/src/samples/java/com/azure/spring/btoc/AADB2COidcLoginConfigSample.java index 279cea94c41d..5fdc715189ce 100644 --- a/sdk/spring/azure-spring-boot/src/samples/java/com/azure/spring/btoc/AADB2COidcLoginConfigSample.java +++ b/sdk/spring/azure-spring-boot/src/samples/java/com/azure/spring/btoc/AADB2COidcLoginConfigSample.java @@ -1,8 +1,5 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE in the project root for - * license information. - */ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. package com.azure.spring.btoc; import com.microsoft.azure.spring.autoconfigure.btoc.AADB2COidcLoginConfigurer; diff --git a/sdk/spring/azure-spring-boot/src/samples/java/com/azure/spring/btoc/AADB2CWebController.java b/sdk/spring/azure-spring-boot/src/samples/java/com/azure/spring/btoc/AADB2CWebController.java index d40ca569a735..c06ee37b0da8 100644 --- a/sdk/spring/azure-spring-boot/src/samples/java/com/azure/spring/btoc/AADB2CWebController.java +++ b/sdk/spring/azure-spring-boot/src/samples/java/com/azure/spring/btoc/AADB2CWebController.java @@ -1,8 +1,5 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE in the project root for - * license information. - */ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. package com.azure.spring.btoc; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; From ccff23baf198f6602e0646e510ae8abd7a89406c Mon Sep 17 00:00:00 2001 From: Yi Liu Date: Fri, 8 May 2020 14:00:04 +0800 Subject: [PATCH 05/11] update for checkstyle --- .../README.md | 7 +++---- .../com/azure/spring/btoc/AADB2COidcLoginConfigSample.java | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/sdk/spring/azure-spring-boot-starter-active-directory-b2c/README.md b/sdk/spring/azure-spring-boot-starter-active-directory-b2c/README.md index 15d78f2af702..a11017a652da 100644 --- a/sdk/spring/azure-spring-boot-starter-active-directory-b2c/README.md +++ b/sdk/spring/azure-spring-boot-starter-active-directory-b2c/README.md @@ -108,7 +108,7 @@ respectively. Specify your user flow **Name** and **User attributes and claims** 9. Create a new Java file named *AADB2CWebController.java* in the *controller* folder and open it in a text editor. 10. Enter the following code, then save and close the file: - + ```java @Controller public class AADB2CWebController { @@ -150,7 +150,7 @@ public class AADB2CWebController { 12. Create a new Java file named *AADB2COidcLoginConfigSample.java* in the *security* folder and open it in a text editor. 13. Enter the following code, then save and close the file: - + ```java @EnableWebSecurity public class AADB2COidcLoginConfigSample extends WebSecurityConfigurerAdapter { @@ -167,8 +167,7 @@ public class AADB2COidcLoginConfigSample extends WebSecurityConfigurerAdapter { .anyRequest() .authenticated() .and() - .apply(configurer) - ; + .apply(configurer); } } ``` diff --git a/sdk/spring/azure-spring-boot/src/samples/java/com/azure/spring/btoc/AADB2COidcLoginConfigSample.java b/sdk/spring/azure-spring-boot/src/samples/java/com/azure/spring/btoc/AADB2COidcLoginConfigSample.java index 5fdc715189ce..23ed8f070cfb 100644 --- a/sdk/spring/azure-spring-boot/src/samples/java/com/azure/spring/btoc/AADB2COidcLoginConfigSample.java +++ b/sdk/spring/azure-spring-boot/src/samples/java/com/azure/spring/btoc/AADB2COidcLoginConfigSample.java @@ -29,7 +29,6 @@ protected void configure(HttpSecurity http) throws Exception { .anyRequest() .authenticated() .and() - .apply(configurer) - ; + .apply(configurer); } } From 101afc23dca107e034fb3cdece90ec3941e01836 Mon Sep 17 00:00:00 2001 From: Yi Liu Date: Fri, 8 May 2020 14:11:32 +0800 Subject: [PATCH 06/11] add azure spring boot in jacoco --- eng/jacoco-test-coverage/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/eng/jacoco-test-coverage/pom.xml b/eng/jacoco-test-coverage/pom.xml index 352563cddc96..2e217c62453e 100644 --- a/eng/jacoco-test-coverage/pom.xml +++ b/eng/jacoco-test-coverage/pom.xml @@ -187,6 +187,11 @@ azure-sdk-template 1.0.4-beta.13 + + com.microsoft.azure + azure-spring-boot + 2.2.5-beta.1 + From 203dbe95263c44187814bc9c8264ae6d703bcfc7 Mon Sep 17 00:00:00 2001 From: Yi Liu Date: Fri, 8 May 2020 15:48:18 +0800 Subject: [PATCH 07/11] add test for aad b2c --- ...ADB2CAuthorizationRequestResolverTest.java | 66 ++++++++++++++++ .../btoc/AADB2CAutoConfigurationTest.java | 78 +++++++++++++++++++ .../autoconfigure/btoc/AADB2CConstants.java | 34 ++++++++ .../btoc/AADB2CLogoutSuccessHandlerTest.java | 49 ++++++++++++ .../autoconfigure/btoc/AADB2CURLTest.java | 72 +++++++++++++++++ 5 files changed, 299 insertions(+) create mode 100644 sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAuthorizationRequestResolverTest.java create mode 100644 sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAutoConfigurationTest.java create mode 100644 sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CConstants.java create mode 100644 sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CLogoutSuccessHandlerTest.java create mode 100644 sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CURLTest.java diff --git a/sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAuthorizationRequestResolverTest.java b/sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAuthorizationRequestResolverTest.java new file mode 100644 index 000000000000..367c69e719ca --- /dev/null +++ b/sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAuthorizationRequestResolverTest.java @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.microsoft.azure.spring.autoconfigure.btoc; + +import org.junit.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.http.HttpMethod; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.security.oauth2.core.AuthorizationGrantType; +import org.springframework.util.Assert; + +import javax.servlet.http.HttpServletRequest; + +import static com.microsoft.azure.spring.autoconfigure.btoc.AADB2CConstants.*; +import static org.assertj.core.api.Java6Assertions.assertThat; + +public class AADB2CAuthorizationRequestResolverTest { + + private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(AADB2CAutoConfiguration.class)) + .withPropertyValues( + String.format("%s=%s", TENANT, TEST_TENANT), + String.format("%s=%s", CLIENT_ID, TEST_CLIENT_ID), + String.format("%s=%s", CLIENT_SECRET, TEST_CLIENT_SECRET), + String.format("%s=%s", REPLY_URL, TEST_REPLY_URL), + String.format("%s=%s", LOGOUT_SUCCESS_URL, TEST_LOGOUT_SUCCESS_URL), + String.format("%s=%s", SIGN_UP_OR_SIGN_IN, TEST_SIGN_UP_OR_IN_NAME) + ); + + private HttpServletRequest getHttpServletRequest(String uri) { + Assert.hasText(uri, "uri must contain text."); + + final MockHttpServletRequest request = new MockHttpServletRequest(HttpMethod.GET.toString(), uri); + + request.setServletPath(uri); + + return request; + } + + @Test + public void testAutoConfigurationBean() { + this.contextRunner.run(c -> { + String requestUri = "/fake-url"; + HttpServletRequest request = getHttpServletRequest(requestUri); + final String registrationId = TEST_SIGN_UP_OR_IN_NAME; + final AADB2CAuthorizationRequestResolver resolver = c.getBean(AADB2CAuthorizationRequestResolver.class); + + assertThat(resolver).isNotNull(); + assertThat(resolver.resolve(request)).isNull(); + assertThat(resolver.resolve(request, registrationId)).isNull(); + + requestUri = "/oauth2/authorization/" + TEST_SIGN_UP_OR_IN_NAME; + request = getHttpServletRequest(requestUri); + + assertThat(resolver.resolve(request)).isNotNull(); + assertThat(resolver.resolve(request, registrationId)).isNotNull(); + + assertThat(resolver.resolve(request).getAdditionalParameters().get("p")).isEqualTo(TEST_SIGN_UP_OR_IN_NAME); + assertThat(resolver.resolve(request).getClientId()).isEqualTo(TEST_CLIENT_ID); + assertThat(resolver.resolve(request).getRedirectUri()).isEqualTo(TEST_REPLY_URL); + assertThat(resolver.resolve(request).getGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); + assertThat(resolver.resolve(request).getScopes()).contains("openid", TEST_CLIENT_ID); + }); + } +} diff --git a/sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAutoConfigurationTest.java b/sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAutoConfigurationTest.java new file mode 100644 index 000000000000..60114aad61bf --- /dev/null +++ b/sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CAutoConfigurationTest.java @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.microsoft.azure.spring.autoconfigure.btoc; + +import org.junit.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; + +import static com.microsoft.azure.spring.autoconfigure.btoc.AADB2CConstants.*; +import static org.assertj.core.api.Java6Assertions.assertThat; + +public class AADB2CAutoConfigurationTest { + + private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(AADB2CAutoConfiguration.class)) + .withPropertyValues( + String.format("%s=%s", TENANT, TEST_TENANT), + String.format("%s=%s", CLIENT_ID, TEST_CLIENT_ID), + String.format("%s=%s", CLIENT_SECRET, TEST_CLIENT_SECRET), + String.format("%s=%s", REPLY_URL, TEST_REPLY_URL), + String.format("%s=%s", LOGOUT_SUCCESS_URL, TEST_LOGOUT_SUCCESS_URL), + String.format("%s=%s", SIGN_UP_OR_SIGN_IN, TEST_SIGN_UP_OR_IN_NAME) + ); + + @Test + public void testAutoConfigurationBean() { + this.contextRunner.run(c -> { + final AADB2CAutoConfiguration config = c.getBean(AADB2CAutoConfiguration.class); + + assertThat(config).isNotNull(); + }); + } + + @Test + public void testPropertiesBean() { + this.contextRunner.run(c -> { + final AADB2CProperties properties = c.getBean(AADB2CProperties.class); + + assertThat(properties).isNotNull(); + assertThat(properties.getTenant()).isEqualTo(TEST_TENANT); + assertThat(properties.getClientId()).isEqualTo(TEST_CLIENT_ID); + assertThat(properties.getClientSecret()).isEqualTo(TEST_CLIENT_SECRET); + assertThat(properties.getReplyUrl()).isEqualTo(TEST_REPLY_URL); + + final String signUpOrSignIn = properties.getUserFlows().getSignUpOrSignIn(); + + assertThat(signUpOrSignIn).isEqualTo(TEST_SIGN_UP_OR_IN_NAME); + }); + } + + @Test + public void testAADB2CAuthorizationRequestResolverBean() { + this.contextRunner.run(c -> { + final AADB2CAuthorizationRequestResolver resolver = c.getBean(AADB2CAuthorizationRequestResolver.class); + + assertThat(resolver).isNotNull(); + }); + } + + @Test + public void testLogoutSuccessHandlerBean() { + this.contextRunner.run(c -> { + final AADB2CLogoutSuccessHandler handler = c.getBean(AADB2CLogoutSuccessHandler.class); + + assertThat(handler).isNotNull(); + }); + } + + @Test + public void testFilterBean() { + this.contextRunner.run(c -> { + final ClientRegistrationRepository repository = c.getBean(ClientRegistrationRepository.class); + + assertThat(repository).isNotNull(); + }); + } +} diff --git a/sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CConstants.java b/sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CConstants.java new file mode 100644 index 000000000000..c68f2449aaa0 --- /dev/null +++ b/sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CConstants.java @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.microsoft.azure.spring.autoconfigure.btoc; + + +import static com.microsoft.azure.spring.autoconfigure.btoc.AADB2CProperties.PREFIX; +import static com.microsoft.azure.spring.autoconfigure.btoc.AADB2CProperties.USER_FLOW_SIGN_UP_OR_SIGN_IN; + +public class AADB2CConstants { + + public static final String TEST_TENANT = "fake-tenant"; + + public static final String TEST_CLIENT_ID = "fake-client-id"; + + public static final String TEST_CLIENT_SECRET = "fake-client-secret"; + + public static final String TEST_REPLY_URL = "http://localhost:8080/index"; + + public static final String TEST_SIGN_UP_OR_IN_NAME = "fake-sign-in-or-up"; + + public static final String TEST_LOGOUT_SUCCESS_URL = "https://fake-logout-success-url"; + + public static final String TENANT = String.format("%s.%s", PREFIX, "tenant"); + + public static final String CLIENT_ID = String.format("%s.%s", PREFIX, "client-id"); + + public static final String CLIENT_SECRET = String.format("%s.%s", PREFIX, "client-secret"); + + public static final String REPLY_URL = String.format("%s.%s", PREFIX, "reply-url"); + + public static final String LOGOUT_SUCCESS_URL = String.format("%s.%s", PREFIX, "logout-success-url"); + + public static final String SIGN_UP_OR_SIGN_IN = String.format("%s.%s", PREFIX, USER_FLOW_SIGN_UP_OR_SIGN_IN); +} diff --git a/sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CLogoutSuccessHandlerTest.java b/sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CLogoutSuccessHandlerTest.java new file mode 100644 index 000000000000..0262f0ced51e --- /dev/null +++ b/sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CLogoutSuccessHandlerTest.java @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.microsoft.azure.spring.autoconfigure.btoc; + +import org.junit.Before; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class AADB2CLogoutSuccessHandlerTest { + + private static final String TEST_TENANT = "test-tenant"; + + private static final String TEST_LOGOUT_SUCCESS_URL = "http://localhost:8080/login"; + + private static final String TEST_USER_FLOW_SIGN_UP_OR_IN = "my-sign-up-or-in"; + + private AADB2CProperties properties; + + @Before + public void setUp() { + properties = new AADB2CProperties(); + + properties.setTenant(TEST_TENANT); + properties.setLogoutSuccessUrl(TEST_LOGOUT_SUCCESS_URL); + properties.getUserFlows().setSignUpOrSignIn(TEST_USER_FLOW_SIGN_UP_OR_IN); + } + + @Test + public void testDefaultTargetUrl() { + final MyLogoutSuccessHandler handler = new MyLogoutSuccessHandler(properties); + final String tenant = properties.getTenant(); + final String url = properties.getLogoutSuccessUrl(); + final String userFlow = properties.getUserFlows().getSignUpOrSignIn(); + + assertThat(handler.getTargetUrl()).isEqualTo(AADB2CURL.getEndSessionUrl(tenant, url, userFlow)); + } + + private static class MyLogoutSuccessHandler extends AADB2CLogoutSuccessHandler { + + MyLogoutSuccessHandler(AADB2CProperties properties) { + super(properties); + } + + public String getTargetUrl() { + return super.getDefaultTargetUrl(); + } + } +} diff --git a/sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CURLTest.java b/sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CURLTest.java new file mode 100644 index 000000000000..7d55ffa859c3 --- /dev/null +++ b/sdk/spring/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/btoc/AADB2CURLTest.java @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.microsoft.azure.spring.autoconfigure.btoc; + +import org.junit.Test; + +import static org.assertj.core.api.Java6Assertions.assertThat; + +public class AADB2CURLTest { + + /** + * Reference pattern see AUTHORIZATION_URL_PATTERN of ${@link AADB2CURL}. + */ + @Test + public void testGetAuthorizationUrl() { + final String expect = "https://fake-tenant.b2clogin.com/fake-tenant.onmicrosoft.com/oauth2/v2.0/authorize"; + + assertThat(AADB2CURL.getAuthorizationUrl("fake-tenant")).isEqualTo(expect); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetAuthorizationUrlException() { + AADB2CURL.getAuthorizationUrl(""); + } + + /** + * Reference pattern see TOKEN_URL_PATTERN of ${@link AADB2CURL}. + */ + @Test + public void testGetTokenUrl() { + final String expect = "https://fake-tenant.b2clogin.com/fake-tenant.onmicrosoft.com/oauth2/v2.0/token?p=fake-p"; + + assertThat(AADB2CURL.getTokenUrl("fake-tenant", "fake-p")).isEqualTo(expect); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetTokenUrlException() { + AADB2CURL.getTokenUrl("", ""); + } + + /** + * Reference pattern see JWKSET_URL_PATTERN of ${@link AADB2CURL}. + */ + @Test + public void testGetJwkSetUrl() { + final String expect = "https://new-tenant.b2clogin.com/new-tenant.onmicrosoft.com/discovery/v2.0/keys?p=new-p"; + + assertThat(AADB2CURL.getJwkSetUrl("new-tenant", "new-p")).isEqualTo(expect); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetJwkSetUrlException() { + AADB2CURL.getJwkSetUrl("", ""); + } + + /** + * Reference pattern see END_SESSION_URL_PATTERN of ${@link AADB2CURL}. + */ + @Test + public void testGetEndSessionUrl() { + final String expect = "https://my-tenant.b2clogin.com/my-tenant.onmicrosoft.com/oauth2/v2.0/logout?" + + "post_logout_redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fhome&p=my-p"; + + assertThat(AADB2CURL.getEndSessionUrl("my-tenant", "http://localhost:8080/home", + "my-p")).isEqualTo(expect); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetEndSessionUrlException() { + AADB2CURL.getJwkSetUrl("", ""); + } +} From d4a34909dd277893dda5347f9a0a8e4c036937c3 Mon Sep 17 00:00:00 2001 From: Yi Liu Date: Mon, 11 May 2020 14:57:43 +0800 Subject: [PATCH 08/11] move module decleration from version_data.txt to version_client.txt --- eng/versioning/version_client.txt | 4 ++++ eng/versioning/version_data.txt | 6 ++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/eng/versioning/version_client.txt b/eng/versioning/version_client.txt index 15fbf70c17bb..02d3ea513646 100644 --- a/eng/versioning/version_client.txt +++ b/eng/versioning/version_client.txt @@ -42,6 +42,10 @@ com.azure:azure-storage-perf;1.0.0-beta.1;1.0.0-beta.1 com.azure:azure-storage-queue;12.5.0;12.6.0-beta.1 com.azure:perf-test-core;1.0.0-beta.1;1.0.0-beta.1 com.azure:azure-test-watcher;1.0.0-beta.1;1.0.0-beta.1 +com.microsoft.azure:azure-spring-boot;2.2.4;2.2.5-beta.1 +com.microsoft.azure:azure-spring-boot-starter;2.2.4;2.2.5-beta.1 +com.microsoft.azure:azure-active-directory-spring-boot-starter;2.2.4;2.2.5-beta.1 +com.microsoft.azure:azure-active-directory-b2c-spring-boot-starter;2.2.4;2.2.5-beta.1 # Unreleased dependencies: Copy the entry from above, prepend "unreleased_" and remove the current # version. Unreleased dependencies are only valid for dependency versions. diff --git a/eng/versioning/version_data.txt b/eng/versioning/version_data.txt index 45f7715135f0..c64253ff1431 100644 --- a/eng/versioning/version_data.txt +++ b/eng/versioning/version_data.txt @@ -40,7 +40,5 @@ com.microsoft.azure.msi_auth_token_provider:azure-authentication-msi-token-provi com.microsoft.azure:azure-eventgrid;1.4.0-beta.1;1.4.0-beta.1 com.microsoft.azure:azure-loganalytics;1.0.0-beta-2;1.0.0-beta.2 com.microsoft.azure:azure-media;1.0.0-beta.1;1.0.0-beta.1 -com.microsoft.azure:azure-spring-boot;2.2.4;2.2.5-beta.1 -com.microsoft.azure:azure-spring-boot-starter;2.2.4;2.2.5-beta.1 -com.microsoft.azure:azure-active-directory-spring-boot-starter;2.2.4;2.2.5-beta.1 -com.microsoft.azure:azure-active-directory-b2c-spring-boot-starter;2.2.4;2.2.5-beta.1 + + From 27613095b14f0e218ebe5dc3ce647524a93ffde2 Mon Sep 17 00:00:00 2001 From: Yi Liu Date: Mon, 11 May 2020 16:15:47 +0800 Subject: [PATCH 09/11] update spring-web version --- .../azure-spring-boot-starter-active-directory-b2c/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/spring/azure-spring-boot-starter-active-directory-b2c/pom.xml b/sdk/spring/azure-spring-boot-starter-active-directory-b2c/pom.xml index 3f47d023fa6c..e69a06a4330f 100644 --- a/sdk/spring/azure-spring-boot-starter-active-directory-b2c/pom.xml +++ b/sdk/spring/azure-spring-boot-starter-active-directory-b2c/pom.xml @@ -41,7 +41,7 @@ org.springframework spring-web - 5.2.0.RELEASE + 5.2.5.RELEASE @@ -100,7 +100,7 @@ com.microsoft.azure:* javax.validation:validation-api:[2.0.1.Final] - org.springframework:spring-web:[5.2.0.RELEASE] + org.springframework:spring-web:[5.2.5.RELEASE] org.springframework.boot:spring-boot-starter:[2.2.0.RELEASE] org.springframework.boot:spring-boot-starter-validation:[2.2.0.RELEASE] org.springframework.security:spring-security-config:[5.2.0.RELEASE] From ea3b29437e8097d490742c167731042a8c7abadc Mon Sep 17 00:00:00 2001 From: Yi Liu Date: Thu, 14 May 2020 16:19:47 +0800 Subject: [PATCH 10/11] add CHANGELOG.md --- .../CHANGELOG.md | 3 +++ .../azure-spring-boot-starter-active-directory/CHANGELOG.md | 3 +++ sdk/spring/azure-spring-boot-starter/CHANGELOG.md | 3 +++ sdk/spring/azure-spring-boot/CHANGELOG.md | 3 +++ 4 files changed, 12 insertions(+) create mode 100644 sdk/spring/azure-spring-boot-starter-active-directory-b2c/CHANGELOG.md create mode 100644 sdk/spring/azure-spring-boot-starter-active-directory/CHANGELOG.md create mode 100644 sdk/spring/azure-spring-boot-starter/CHANGELOG.md create mode 100644 sdk/spring/azure-spring-boot/CHANGELOG.md diff --git a/sdk/spring/azure-spring-boot-starter-active-directory-b2c/CHANGELOG.md b/sdk/spring/azure-spring-boot-starter-active-directory-b2c/CHANGELOG.md new file mode 100644 index 000000000000..d51e263177a9 --- /dev/null +++ b/sdk/spring/azure-spring-boot-starter-active-directory-b2c/CHANGELOG.md @@ -0,0 +1,3 @@ +# Release History + +## 2.2.5-beta.1 (Unreleased) diff --git a/sdk/spring/azure-spring-boot-starter-active-directory/CHANGELOG.md b/sdk/spring/azure-spring-boot-starter-active-directory/CHANGELOG.md new file mode 100644 index 000000000000..d51e263177a9 --- /dev/null +++ b/sdk/spring/azure-spring-boot-starter-active-directory/CHANGELOG.md @@ -0,0 +1,3 @@ +# Release History + +## 2.2.5-beta.1 (Unreleased) diff --git a/sdk/spring/azure-spring-boot-starter/CHANGELOG.md b/sdk/spring/azure-spring-boot-starter/CHANGELOG.md new file mode 100644 index 000000000000..d51e263177a9 --- /dev/null +++ b/sdk/spring/azure-spring-boot-starter/CHANGELOG.md @@ -0,0 +1,3 @@ +# Release History + +## 2.2.5-beta.1 (Unreleased) diff --git a/sdk/spring/azure-spring-boot/CHANGELOG.md b/sdk/spring/azure-spring-boot/CHANGELOG.md new file mode 100644 index 000000000000..d51e263177a9 --- /dev/null +++ b/sdk/spring/azure-spring-boot/CHANGELOG.md @@ -0,0 +1,3 @@ +# Release History + +## 2.2.5-beta.1 (Unreleased) From 2a5c11f291b9c8aca0e536127857a6351aa9ecb9 Mon Sep 17 00:00:00 2001 From: Yi Liu Date: Thu, 21 May 2020 12:55:38 +0800 Subject: [PATCH 11/11] add entries in jacoco and plugin for javadoc --- eng/jacoco-test-coverage/pom.xml | 15 ++++ .../pom.xml | 70 +++++++++++++++++++ .../pom.xml | 70 +++++++++++++++++++ sdk/spring/azure-spring-boot-starter/pom.xml | 70 +++++++++++++++++++ 4 files changed, 225 insertions(+) diff --git a/eng/jacoco-test-coverage/pom.xml b/eng/jacoco-test-coverage/pom.xml index 2a8153c9899d..68bf51f2685c 100644 --- a/eng/jacoco-test-coverage/pom.xml +++ b/eng/jacoco-test-coverage/pom.xml @@ -192,6 +192,21 @@ azure-spring-boot 2.2.5-beta.1 + + com.microsoft.azure + azure-spring-boot-starter + 2.2.5-beta.1 + + + com.microsoft.azure + azure-active-directory-spring-boot-starter + 2.2.5-beta.1 + + + com.microsoft.azure + azure-active-directory-b2c-spring-boot-starter + 2.2.5-beta.1 + diff --git a/sdk/spring/azure-spring-boot-starter-active-directory-b2c/pom.xml b/sdk/spring/azure-spring-boot-starter-active-directory-b2c/pom.xml index e69a06a4330f..3b5520ed32d4 100644 --- a/sdk/spring/azure-spring-boot-starter-active-directory-b2c/pom.xml +++ b/sdk/spring/azure-spring-boot-starter-active-directory-b2c/pom.xml @@ -114,6 +114,76 @@ + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.1.1 + + + attach-javadocs + + jar + + + true + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.1.2 + + + empty-javadoc-jar-with-readme + package + + jar + + + javadoc + ${project.basedir}/javadocTemp + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.8 + + + copy-readme-to-javadocTemp + prepare-package + + + Deleting existing ${project.basedir}/javadocTemp + + + + Copying ${project.basedir}/README.md to + ${project.basedir}/javadocTemp/README.md + + + + + + run + + + + + diff --git a/sdk/spring/azure-spring-boot-starter-active-directory/pom.xml b/sdk/spring/azure-spring-boot-starter-active-directory/pom.xml index 615a3027b462..2903847e6022 100644 --- a/sdk/spring/azure-spring-boot-starter-active-directory/pom.xml +++ b/sdk/spring/azure-spring-boot-starter-active-directory/pom.xml @@ -97,6 +97,76 @@ + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.1.1 + + + attach-javadocs + + jar + + + true + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.1.2 + + + empty-javadoc-jar-with-readme + package + + jar + + + javadoc + ${project.basedir}/javadocTemp + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.8 + + + copy-readme-to-javadocTemp + prepare-package + + + Deleting existing ${project.basedir}/javadocTemp + + + + Copying ${project.basedir}/README.md to + ${project.basedir}/javadocTemp/README.md + + + + + + run + + + + + diff --git a/sdk/spring/azure-spring-boot-starter/pom.xml b/sdk/spring/azure-spring-boot-starter/pom.xml index 772fe5b86f80..bb2ce52779bd 100644 --- a/sdk/spring/azure-spring-boot-starter/pom.xml +++ b/sdk/spring/azure-spring-boot-starter/pom.xml @@ -55,6 +55,76 @@ + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.1.1 + + + attach-javadocs + + jar + + + true + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.1.2 + + + empty-javadoc-jar-with-readme + package + + jar + + + javadoc + ${project.basedir}/javadocTemp + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.8 + + + copy-readme-to-javadocTemp + prepare-package + + + Deleting existing ${project.basedir}/javadocTemp + + + + Copying ${project.basedir}/README.md to + ${project.basedir}/javadocTemp/README.md + + + + + + run + + + + +