| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 | 
							- = OAuth 2.0 Resource Server Multi-tenancy
 
- [[webflux-oauth2resourceserver-multitenancy]]
 
- == Multi-tenancy
 
- A resource server is considered multi-tenant when there are multiple strategies for verifying a bearer token, keyed by some tenant identifier.
 
- For example, your resource server can accept bearer tokens from two different authorization servers.
 
- Alternately, your authorization server can represent a multiplicity of issuers.
 
- In each case, two things need to be done and trade-offs are associated with how you choose to do them:
 
- . Resolve the tenant.
 
- . Propagate the tenant.
 
- === Resolving the Tenant By Claim
 
- One way to differentiate tenants is by the issuer claim. Since the issuer claim accompanies signed JWTs, you can do so with the `JwtIssuerReactiveAuthenticationManagerResolver`:
 
- [tabs]
 
- ======
 
- Java::
 
- +
 
- [source,java,role="primary"]
 
- ----
 
- JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver
 
-     .fromTrustedIssuers("https://idp.example.org/issuerOne", "https://idp.example.org/issuerTwo");
 
- http
 
-     .authorizeExchange(exchanges -> exchanges
 
-         .anyExchange().authenticated()
 
-     )
 
-     .oauth2ResourceServer(oauth2 -> oauth2
 
-         .authenticationManagerResolver(authenticationManagerResolver)
 
-     );
 
- ----
 
- Kotlin::
 
- +
 
- [source,kotlin,role="secondary"]
 
- ----
 
- val customAuthenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver
 
-     .fromTrustedIssuers("https://idp.example.org/issuerOne", "https://idp.example.org/issuerTwo")
 
- return http {
 
-     authorizeExchange {
 
-         authorize(anyExchange, authenticated)
 
-     }
 
-     oauth2ResourceServer {
 
-         authenticationManagerResolver = customAuthenticationManagerResolver
 
-     }
 
- }
 
- ----
 
- ======
 
- This is nice because the issuer endpoints are loaded lazily.
 
- In fact, the corresponding `JwtReactiveAuthenticationManager` is instantiated only when the first request with the corresponding issuer is sent.
 
- This allows for an application startup that is independent from those authorization servers being up and available.
 
- ==== Dynamic Tenants
 
- You may not want to restart the application each time a new tenant is added.
 
- In this case, you can configure the `JwtIssuerReactiveAuthenticationManagerResolver` with a repository of `ReactiveAuthenticationManager` instances, which you can edit at runtime:
 
- [tabs]
 
- ======
 
- Java::
 
- +
 
- [source,java,role="primary"]
 
- ----
 
- private Mono<ReactiveAuthenticationManager> addManager(
 
- 		Map<String, ReactiveAuthenticationManager> authenticationManagers, String issuer) {
 
- 	return Mono.fromCallable(() -> ReactiveJwtDecoders.fromIssuerLocation(issuer))
 
-             .subscribeOn(Schedulers.boundedElastic())
 
-             .map(JwtReactiveAuthenticationManager::new)
 
-             .doOnNext(authenticationManager -> authenticationManagers.put(issuer, authenticationManager));
 
- }
 
- // ...
 
- JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver =
 
-         new JwtIssuerReactiveAuthenticationManagerResolver(authenticationManagers::get);
 
- http
 
-     .authorizeExchange(exchanges -> exchanges
 
-         .anyExchange().authenticated()
 
-     )
 
-     .oauth2ResourceServer(oauth2 -> oauth2
 
-         .authenticationManagerResolver(authenticationManagerResolver)
 
-     );
 
- ----
 
- Kotlin::
 
- +
 
- [source,kotlin,role="secondary"]
 
- ----
 
- private fun addManager(
 
-         authenticationManagers: MutableMap<String, ReactiveAuthenticationManager>, issuer: String): Mono<JwtReactiveAuthenticationManager> {
 
-     return Mono.fromCallable { ReactiveJwtDecoders.fromIssuerLocation(issuer) }
 
-             .subscribeOn(Schedulers.boundedElastic())
 
-             .map { jwtDecoder: ReactiveJwtDecoder -> JwtReactiveAuthenticationManager(jwtDecoder) }
 
-             .doOnNext { authenticationManager: JwtReactiveAuthenticationManager -> authenticationManagers[issuer] = authenticationManager }
 
- }
 
- // ...
 
- var customAuthenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver(authenticationManagers::get)
 
- return http {
 
-     authorizeExchange {
 
-         authorize(anyExchange, authenticated)
 
-     }
 
-     oauth2ResourceServer {
 
-         authenticationManagerResolver = customAuthenticationManagerResolver
 
-     }
 
- }
 
- ----
 
- ======
 
- In this case, you construct `JwtIssuerReactiveAuthenticationManagerResolver` with a strategy for obtaining the `ReactiveAuthenticationManager` given to the issuer.
 
- This approach lets us add and remove elements from the repository (shown as a `Map` in the preceding snippet) at runtime.
 
- [NOTE]
 
- ====
 
- It would be unsafe to simply take any issuer and construct an `ReactiveAuthenticationManager` from it.
 
- The issuer should be one that the code can verify from a trusted source, such as an allowed list of issuers.
 
- ====
 
 
  |