diff --git a/aad/spring-cloud-azure-starter-active-directory/aad-resource-server-obo/src/main/java/com/azure/spring/sample/aad/configuration/AADSampleConfiguration.java b/aad/spring-cloud-azure-starter-active-directory/aad-resource-server-obo/src/main/java/com/azure/spring/sample/aad/configuration/AADSampleConfiguration.java
index 583541071..ec2b1411e 100644
--- a/aad/spring-cloud-azure-starter-active-directory/aad-resource-server-obo/src/main/java/com/azure/spring/sample/aad/configuration/AADSampleConfiguration.java
+++ b/aad/spring-cloud-azure-starter-active-directory/aad-resource-server-obo/src/main/java/com/azure/spring/sample/aad/configuration/AADSampleConfiguration.java
@@ -10,7 +10,7 @@
public class AADSampleConfiguration {
@Bean
- public static WebClient webClient(OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) {
+ public WebClient webClient(OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction function =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(oAuth2AuthorizedClientManager);
return WebClient.builder()
diff --git a/aad/spring-cloud-azure-starter-active-directory/aad-web-application-and-resource-server/src/main/java/com/azure/spring/sample/aad/config/WebClientConfig.java b/aad/spring-cloud-azure-starter-active-directory/aad-web-application-and-resource-server/src/main/java/com/azure/spring/sample/aad/config/WebClientConfig.java
index 846c28b5c..e815a5935 100644
--- a/aad/spring-cloud-azure-starter-active-directory/aad-web-application-and-resource-server/src/main/java/com/azure/spring/sample/aad/config/WebClientConfig.java
+++ b/aad/spring-cloud-azure-starter-active-directory/aad-web-application-and-resource-server/src/main/java/com/azure/spring/sample/aad/config/WebClientConfig.java
@@ -15,7 +15,7 @@
public class WebClientConfig {
@Bean
- public static WebClient webClient(ClientRegistrationRepository clientRegistrationRepository,
+ public WebClient webClient(ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
ServletOAuth2AuthorizedClientExchangeFilterFunction function =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository,
diff --git a/aad/spring-cloud-azure-starter-active-directory/aad-web-application/src/main/java/com/azure/spring/sample/aad/config/WebClientConfig.java b/aad/spring-cloud-azure-starter-active-directory/aad-web-application/src/main/java/com/azure/spring/sample/aad/config/WebClientConfig.java
index 846c28b5c..e815a5935 100644
--- a/aad/spring-cloud-azure-starter-active-directory/aad-web-application/src/main/java/com/azure/spring/sample/aad/config/WebClientConfig.java
+++ b/aad/spring-cloud-azure-starter-active-directory/aad-web-application/src/main/java/com/azure/spring/sample/aad/config/WebClientConfig.java
@@ -15,7 +15,7 @@
public class WebClientConfig {
@Bean
- public static WebClient webClient(ClientRegistrationRepository clientRegistrationRepository,
+ public WebClient webClient(ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
ServletOAuth2AuthorizedClientExchangeFilterFunction function =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository,
diff --git a/aad/spring-security/docs/README.md b/aad/spring-security/docs/README.md
index e0cadb091..17e7a296d 100644
--- a/aad/spring-security/docs/README.md
+++ b/aad/spring-security/docs/README.md
@@ -1,4 +1,3 @@
-
- [1. About](#1-about)
- [2. Ask for help](#2-ask-for-help)
- [3. Homeworks](#3-homeworks)
@@ -7,6 +6,8 @@
+ [4.1.1. OAuth 2](#411-oauth-2)
+ [4.1.2. SAML2](#412-saml2)
* [4.2. Reactive](#42-reactive)
+ + [4.2.1. Web flux](#421-web-flux)
+ - [4.2.1.1. OAuth2](#4211-oauth2)
@@ -16,13 +17,13 @@
# 1. About
-This repo demonstrates how to use [Azure Active Directory] in [Spring Boot] application by [spring-security].
+This repo demonstrates how to use [Azure Active Directory](https://azure.microsoft.com/services/active-directory/) in [Spring Boot](https://spring.io/projects/spring-boot) application by [spring-security](https://github.com/spring-projects/spring-security).
# 2. Ask for help
-If you have any question about these samples, please ask by [creating a new github issue].
+If you have any question about these samples, please ask by [creating a new github issue](https://github.com/Azure-Samples/azure-spring-boot-samples/issues/new).
# 3. Homeworks
-Each sample has homework, you can get the answer in [homework answers].
+Each sample has homework, you can get the answer in [homework answers](homework-answers.md).
# 4. Sample applications
@@ -30,28 +31,21 @@ Each sample has homework, you can get the answer in [homework answers].
### 4.1.1. OAuth 2
-1. [login]
-2. [client-access-resource-server]
-3. [resource-server-check-permissions-by-claims-in-access-token]
-4. [client-access-multiple-resource-server]
-5. [resource-server-support-on-behalf-of-flow]
+1. [login](./servlet/oauth2/login.md)
+2. [client-access-resource-server](./servlet/oauth2/client-access-resource-server.md)
+3. [resource-server-check-permissions-by-claims-in-access-token](./servlet/oauth2/resource-server-check-permissions-by-claims-in-access-token.md)
+4. [client-access-multiple-resource-server](./servlet/oauth2/client-access-multiple-resource-server.md)
+5. [resource-server-support-on-behalf-of-flow](./servlet/oauth2/resource-server-support-on-behalf-of-flow.md)
### 4.1.2. SAML2
To be done.
## 4.2. Reactive
-To be done.
+### 4.2.1. Web flux
+
+#### 4.2.1.1. OAuth2
-[Azure Active Directory]: https://azure.microsoft.com/services/active-directory/
-[Spring Boot]: https://spring.io/projects/spring-boot
-[spring-security]: https://github.com/spring-projects/spring-security
-[creating a new github issue]: https://github.com/Azure-Samples/azure-spring-boot-samples/issues/new
-[homework answers]: homework-answers.md
-[login]: servlet/oauth2/login.md
-[client-access-resource-server]: servlet/oauth2/client-access-resource-server.md
-[resource-server-check-permissions-by-claims-in-access-token]: servlet/oauth2/resource-server-check-permissions-by-claims-in-access-token.md
-[client-access-multiple-resource-server]: servlet/oauth2/client-access-multiple-resource-server.md
-[resource-server-support-on-behalf-of-flow]: servlet/oauth2/resource-server-support-on-behalf-of-flow.md
+1. [spring-cloud-gateway](./reactive/webflux/oauth2/spring-cloud-gateway.md)
diff --git a/aad/spring-security/docs/reactive/webflux/oauth2/spring-cloud-gateway.md b/aad/spring-security/docs/reactive/webflux/oauth2/spring-cloud-gateway.md
new file mode 100644
index 000000000..73e743af0
--- /dev/null
+++ b/aad/spring-security/docs/reactive/webflux/oauth2/spring-cloud-gateway.md
@@ -0,0 +1,81 @@
+- [1. About](#1-about)
+- [2. Get sample applications](#2-get-sample-applications)
+- [3. Create resources in Azure](#3-create-resources-in-azure)
+ * [3.1. Create a tenant](#31-create-a-tenant)
+ * [3.2. Add a new user](#32-add-a-new-user)
+ * [3.3. Register client-1](#33-register-client-1)
+ * [3.4. Add a client secret for client-1](#34-add-a-client-secret-for-client-1)
+ * [3.5. Add a redirect URI for client-1](#35-add-a-redirect-uri-for-client-1)
+ * [3.6. Register resource-server-1](#36-register-resource-server-1)
+ * [3.7. Expose apis for resource-server-1](#37-expose-apis-for-resource-server-1)
+ * [3.8. Set accessTokenAcceptedVersion to 2 for resource-server-1](#38-set-accesstokenacceptedversion-to-2-for-resource-server-1)
+ * [3.9. Register resource-server-2](#39-register-resource-server-2)
+ * [3.10. Expose apis for resource-server-2](#310-expose-apis-for-resource-server-2)
+ * [3.11. Set accessTokenAcceptedVersion to 2 for resource-server-2](#311-set-accesstokenacceptedversion-to-2-for-resource-server-2)
+- [4. Run sample applications](#4-run-sample-applications)
+- [5. Homework](#5-homework)
+
+
+# 1. About
+
+This section will demonstrate this scenario:
+
+
+
+1. Client get [access token](https://docs.microsoft.com/azure/active-directory/develop/access-tokens) from [Azure Active Directory](https://azure.microsoft.com/services/active-directory/)
+2. Client use the access token to access Gateway.
+3. Gateway validate the access token. If the access token is valid, use the access token to access the ResourceServer. There are 2 ResourceServers, which ResourceServer to access depends on the request URL, it's configured in Gateway's application.yml. Gateway is implemented by [spring-cloud-gateway](https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/)
+4. Gateway get the response from ResourceServer, then return to Client.
+
+# 2. Get sample applications
+Get samples applications from in GitHub: [spring-cloud-gateway](../../../../reactive/webflux/oauth2/spring-cloud-gateway).
+
+# 3. Create resources in Azure
+
+## 3.1. Create a tenant
+Read [document about creating an Azure AD tenant](https://docs.microsoft.com/azure/active-directory/develop/quickstart-create-new-tenant#create-a-new-azure-ad-tenant), create a new tenant. Get the tenant-id: **${TENANT-ID}**.
+
+## 3.2. Add a new user
+Read [document about adding users](https://docs.microsoft.com/azure/active-directory/fundamentals/add-users-azure-active-directory), add a new user: **user-1@${tenant-name}.com**. Get the user's password.
+
+## 3.3. Register client-1
+Read [document about registering an application](https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app), register an application named **client-1**. Get the client-id: **${CLIENT-1-CLIENT-ID}**.
+
+## 3.4. Add a client secret for client-1
+Read [document about adding a client secret](https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app#add-a-client-secret), add a client secret. Get the client-secret value: **${CLIENT-1-CLIENT-SECRET}**.
+
+## 3.5. Add a redirect URI for client-1
+Read [document about adding a redirect URI](https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app#add-a-redirect-uri), add 2 redirect URIs: **http://localhost:8080/login/oauth2/code/client-1-resource-server-1**, **http://localhost:8080/login/oauth2/code/client-1-resource-server-2**.
+
+## 3.6. Register resource-server-1
+Read [document about registering an application](https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app), register an application named **resource-server-1**. Get the client-id: **${RESOURCE-SERVER-1-CLIENT-ID}**.
+
+## 3.7. Expose apis for resource-server-1
+Read [document about exposing an api](https://docs.microsoft.com/azure/active-directory/develop/quickstart-configure-app-expose-web-apis), expose 2 scopes for resource-server-1: **resource-server-1.scope-1** and **resource-server-1.scope-2**, choose **Admins and users** for **Who can consent** option.
+
+## 3.8. Set accessTokenAcceptedVersion to 2 for resource-server-1
+Read [document about Application manifest](https://docs.microsoft.com/azure/active-directory/develop/reference-app-manifest#accesstokenacceptedversion-attribute), set `accessTokenAcceptedVersion` to `2`.
+
+## 3.9. Register resource-server-2
+Read [document about registering an application](https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app), register an application named **resource-server-2**. Get the client-id: **${RESOURCE-SERVER-2-CLIENT-ID}**.
+
+## 3.10. Expose apis for resource-server-2
+Read [document about exposing an api](https://docs.microsoft.com/azure/active-directory/develop/quickstart-configure-app-expose-web-apis), expose 2 scopes for resource-server-2: **resource-server-2.scope-1** and **resource-server-2.scope-2**, choose **Admins and users** for **Who can consent** option.
+
+## 3.11. Set accessTokenAcceptedVersion to 2 for resource-server-2
+Read [document about Application manifest](https://docs.microsoft.com/azure/active-directory/develop/reference-app-manifest#accesstokenacceptedversion-attribute), set `accessTokenAcceptedVersion` to `2`.
+
+# 4. Run sample applications
+1. Open sample application: [client](../../../../reactive/webflux/oauth2/spring-cloud-gateway/client), fill the placeholders in **application.yml**, then run the application.
+2. Open sample application: [gateway](../../../../reactive/webflux/oauth2/spring-cloud-gateway/gateway), fill the placeholders in **application.yml**, then run the application.
+3. Open sample application: [resource-server-1](../../../../reactive/webflux/oauth2/spring-cloud-gateway/resource-server-1), fill the placeholders in **application.yml**, then run the application.
+4. Open sample application: [resource-server-2](../../../../reactive/webflux/oauth2/spring-cloud-gateway/resource-server-2), fill the placeholders in **application.yml**, then run the application.
+5. Open browser(for example: [Edge](https://www.microsoft.com/edge?r=1)), close all [InPrivate window](https://support.microsoft.com/microsoft-edge/browse-inprivate-in-microsoft-edge-cd2c9a48-0bc4-b98e-5e46-ac40c84e27e2), and open a new InPrivate window.
+6. Access **http://localhost:8080/**, it will return **Hello, this is client-1.**.
+7. Access **http://localhost:8080/resource-server-1**, it will redirect to Microsoft consent page.
+9. Input username and password (update password if it requests you to), it will return permission request page: let user permit **client-1** to access **resource-server-1**.
+11. Click **Accept**, then it will return **Hello, resource-server-1.**. This means Client can access ResourceServer1 through Gateway successfully.
+12. Access **http://localhost:8080/resource-server-2**, consent as before, it will return **Hello, this is resource-server-2.**.
+
+# 5. Homework
+1. Read [rfc6749](https://datatracker.ietf.org/doc/html/rfc6749).
\ No newline at end of file
diff --git a/aad/spring-security/pom.xml b/aad/spring-security/pom.xml
index 26428afb8..70e853f4b 100644
--- a/aad/spring-security/pom.xml
+++ b/aad/spring-security/pom.xml
@@ -4,7 +4,14 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
- com.azure.spring
+
+
+ com.azure.spring
+ azure-spring-boot-samples
+ 1.0.0
+ ../../pom.xml
+
+
spring-boot-application-with-azure-active-directory
1.0.0
pom
@@ -16,6 +23,10 @@
+ reactive/webflux/oauth2/spring-cloud-gateway/client
+ reactive/webflux/oauth2/spring-cloud-gateway/gateway
+ reactive/webflux/oauth2/spring-cloud-gateway/resource-server-1
+ reactive/webflux/oauth2/spring-cloud-gateway/resource-server-2
servlet/oauth2/login
servlet/oauth2/client-access-resource-server/client
servlet/oauth2/client-access-resource-server/resource-server
diff --git a/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/pom.xml b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/pom.xml
new file mode 100644
index 000000000..b2347df4e
--- /dev/null
+++ b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/pom.xml
@@ -0,0 +1,30 @@
+
+
+ 4.0.0
+
+
+ com.azure.spring
+ azure-spring-boot-samples
+ 1.0.0
+ ../../../../../../../pom.xml
+
+
+ spring-security-sample-reactive-webflux-oauth2-spring-cloud-gateway-client-application
+ 1.0.0
+ jar
+ Spring Security Sample: Reactive: Webflux: OAuth2: Spring Cloud Gateway: Client Application
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-client
+
+
+
+
diff --git a/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/client/ClientApplication.java b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/client/ClientApplication.java
new file mode 100644
index 000000000..87f6bfb74
--- /dev/null
+++ b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/client/ClientApplication.java
@@ -0,0 +1,12 @@
+package com.azure.spring.sample.activedirectory.reactive.webflux.oauth2.gateway.client;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class ClientApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(ClientApplication.class, args);
+ }
+}
diff --git a/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/client/configuration/ApplicationConfiguration.java b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/client/configuration/ApplicationConfiguration.java
new file mode 100644
index 000000000..ebbee282c
--- /dev/null
+++ b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/client/configuration/ApplicationConfiguration.java
@@ -0,0 +1,23 @@
+package com.azure.spring.sample.activedirectory.reactive.webflux.oauth2.gateway.client.configuration;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
+import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction;
+import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
+import org.springframework.web.reactive.function.client.WebClient;
+
+@Configuration
+public class ApplicationConfiguration {
+
+ @Bean
+ public WebClient webClient(ReactiveClientRegistrationRepository clientRegistrationRepository,
+ ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
+ ServerOAuth2AuthorizedClientExchangeFilterFunction function =
+ new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository,
+ authorizedClientRepository);
+ return WebClient.builder()
+ .filter(function)
+ .build();
+ }
+}
diff --git a/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/client/configuration/SecurityConfiguration.java b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/client/configuration/SecurityConfiguration.java
new file mode 100644
index 000000000..812e09c8d
--- /dev/null
+++ b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/client/configuration/SecurityConfiguration.java
@@ -0,0 +1,17 @@
+package com.azure.spring.sample.activedirectory.reactive.webflux.oauth2.gateway.client.configuration;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
+import org.springframework.security.config.web.server.ServerHttpSecurity;
+import org.springframework.security.web.server.SecurityWebFilterChain;
+
+@EnableWebFluxSecurity
+public class SecurityConfiguration {
+
+ @Bean
+ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
+ http.oauth2Client();
+ return http.build();
+ }
+
+}
diff --git a/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/client/controller/HomeController.java b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/client/controller/HomeController.java
new file mode 100644
index 000000000..77bdcfd63
--- /dev/null
+++ b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/client/controller/HomeController.java
@@ -0,0 +1,13 @@
+package com.azure.spring.sample.activedirectory.reactive.webflux.oauth2.gateway.client.controller;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class HomeController {
+
+ @GetMapping("/")
+ public String home() {
+ return "Hello, this is client-1.";
+ }
+}
diff --git a/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/client/controller/ResourceServer1Controller.java b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/client/controller/ResourceServer1Controller.java
new file mode 100644
index 000000000..bef1bf09f
--- /dev/null
+++ b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/client/controller/ResourceServer1Controller.java
@@ -0,0 +1,31 @@
+package com.azure.spring.sample.activedirectory.reactive.webflux.oauth2.gateway.client.controller;
+
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
+import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Mono;
+
+import static org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient;
+
+@RestController
+public class ResourceServer1Controller {
+
+ private final WebClient webClient;
+
+ public ResourceServer1Controller(WebClient webClient) {
+ this.webClient = webClient;
+ }
+
+ @GetMapping("/resource-server-1")
+ public Mono hello(
+ @RegisteredOAuth2AuthorizedClient("client-1-resource-server-1") OAuth2AuthorizedClient client1ResourceServer1) {
+ return webClient
+ .get()
+ .uri("http://localhost:8083/resource-server-1")
+ .attributes(oauth2AuthorizedClient(client1ResourceServer1))
+ .retrieve()
+ .bodyToMono(String.class);
+ }
+}
diff --git a/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/client/controller/ResourceServer2Controller.java b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/client/controller/ResourceServer2Controller.java
new file mode 100644
index 000000000..f6b0a70d8
--- /dev/null
+++ b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/client/controller/ResourceServer2Controller.java
@@ -0,0 +1,31 @@
+package com.azure.spring.sample.activedirectory.reactive.webflux.oauth2.gateway.client.controller;
+
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
+import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Mono;
+
+import static org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient;
+
+@RestController
+public class ResourceServer2Controller {
+
+ private final WebClient webClient;
+
+ public ResourceServer2Controller(WebClient webClient) {
+ this.webClient = webClient;
+ }
+
+ @GetMapping("/resource-server-2")
+ public Mono hello(
+ @RegisteredOAuth2AuthorizedClient("client-1-resource-server-2") OAuth2AuthorizedClient client1ResourceServer1) {
+ return webClient
+ .get()
+ .uri("http://localhost:8083/resource-server-2")
+ .attributes(oauth2AuthorizedClient(client1ResourceServer1))
+ .retrieve()
+ .bodyToMono(String.class);
+ }
+}
diff --git a/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/resources/application.yml b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/resources/application.yml
new file mode 100644
index 000000000..3406730fb
--- /dev/null
+++ b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/client/src/main/resources/application.yml
@@ -0,0 +1,35 @@
+# Please fill these placeholders before running this application:
+# 1. ${TENANT-ID}
+# 2. ${CLIENT-1-CLIENT-ID}
+# 3. ${CLIENT-1-CLIENT-SECRET}
+# 4. ${RESOURCE-SERVER-1-CLIENT-ID}
+# 5. ${RESOURCE-SERVER-2-CLIENT-ID}
+
+logging:
+ level:
+ root: DEBUG
+server:
+ port: 8080
+spring:
+ security:
+ oauth2:
+ client:
+ provider: # Refs: https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2login-common-oauth2-provider
+ azure-active-directory:
+ issuer-uri: https://login.microsoftonline.com/${TENANT-ID}/v2.0 # Refs: https://docs.spring.io/spring-security/site/docs/current/reference/html5/#webflux-oauth2-login-openid-provider-configuration
+ user-name-attribute: name
+ registration:
+ client-1-resource-server-1:
+ provider: azure-active-directory
+ client-name: client-1-resource-server-1
+ client-id: ${CLIENT-1-CLIENT-ID}
+ client-secret: ${CLIENT-1-CLIENT-SECRET}
+ scope: api://${RESOURCE-SERVER-1-CLIENT-ID}/resource-server-1.scope-1
+ client-1-resource-server-2:
+ provider: azure-active-directory
+ client-name: client-1-resource-server-2
+ client-id: ${CLIENT-1-CLIENT-ID}
+ client-secret: ${CLIENT-1-CLIENT-SECRET}
+ scope: api://${RESOURCE-SERVER-2-CLIENT-ID}/resource-server-2.scope-1
+ profiles:
+ active: develop
diff --git a/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/gateway/pom.xml b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/gateway/pom.xml
new file mode 100644
index 000000000..5f5d16cd5
--- /dev/null
+++ b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/gateway/pom.xml
@@ -0,0 +1,30 @@
+
+
+ 4.0.0
+
+
+ com.azure.spring
+ azure-spring-boot-samples
+ 1.0.0
+ ../../../../../../../pom.xml
+
+
+ spring-security-sample-reactive-webflux-oauth2-spring-cloud-gateway-gateway-application
+ 1.0.0
+ jar
+ Spring Security Sample: Reactive: Webflux: OAuth2: Spring Cloud Gateway: Gateway Application
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-gateway
+
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-resource-server
+
+
+
+
diff --git a/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/gateway/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/gateway/GatewayApplication.java b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/gateway/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/gateway/GatewayApplication.java
new file mode 100644
index 000000000..6df609e15
--- /dev/null
+++ b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/gateway/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/gateway/GatewayApplication.java
@@ -0,0 +1,12 @@
+package com.azure.spring.sample.activedirectory.reactive.webflux.oauth2.gateway.gateway;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class GatewayApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(GatewayApplication.class, args);
+ }
+}
diff --git a/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/gateway/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/gateway/configuration/ApplicationConfiguration.java b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/gateway/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/gateway/configuration/ApplicationConfiguration.java
new file mode 100644
index 000000000..3afb83332
--- /dev/null
+++ b/aad/spring-security/reactive/webflux/oauth2/spring-cloud-gateway/gateway/src/main/java/com/azure/spring/sample/activedirectory/reactive/webflux/oauth2/gateway/gateway/configuration/ApplicationConfiguration.java
@@ -0,0 +1,69 @@
+package com.azure.spring.sample.activedirectory.reactive.webflux.oauth2.gateway.gateway.configuration;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
+import org.springframework.security.oauth2.core.OAuth2TokenValidator;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.jwt.JwtClaimNames;
+import org.springframework.security.oauth2.jwt.JwtClaimValidator;
+import org.springframework.security.oauth2.jwt.JwtIssuerValidator;
+import org.springframework.security.oauth2.jwt.JwtTimestampValidator;
+import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
+import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
+import org.springframework.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Predicate;
+
+@Configuration
+public class ApplicationConfiguration {
+
+ @Value("${spring.security.oauth2.resourceserver.jwt.audiences}")
+ private List audiences;
+
+ private final OAuth2ResourceServerProperties.Jwt properties;
+
+ public ApplicationConfiguration(OAuth2ResourceServerProperties properties) {
+ this.properties = properties.getJwt();
+ }
+
+ @Bean
+ ReactiveJwtDecoder jwtDecoder() {
+ NimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder.withJwkSetUri(properties.getJwkSetUri()).build();
+ decoder.setJwtValidator(jwtValidator());
+ return decoder;
+ }
+
+ private OAuth2TokenValidator jwtValidator() {
+ List> validators = new ArrayList<>();
+ String issuerUri = properties.getIssuerUri();
+ if (StringUtils.hasText(issuerUri)) {
+ validators.add(new JwtIssuerValidator(issuerUri));
+ }
+ if (audiences != null && ! audiences.isEmpty()) {
+ validators.add(new JwtClaimValidator<>(JwtClaimNames.AUD, audiencePredicate(audiences)));
+ }
+ validators.add(new JwtTimestampValidator());
+ return new DelegatingOAuth2TokenValidator<>(validators);
+ }
+
+ Predicate