Spring boot oauth2 integration with keycloak using Spring webflux along with multi-tenancy

1.6k Views Asked by At

I need to implement Authentication & Authorization using spring boot oauth2 with keycloak as a provider. I also need to support muti-tenancy. I tried example with authentication using spring-boot-starter-auth2-client to authenticate, but not able to add multi-tenancy.

When I used spring-boot-starter-auth2-client, I need to configure hardcode keycloak urls(specific to one tenant) in properties and not able to support multi-tenancy.

I also analyze spring-boot-starter-auth2-resouce-server, but not clear. I understand that resouce server use for validation of token and expiry.

Note: I don't want to use keycloak adapter library which is provided by keycloak.

Could you please help me -

  1. Where need to use spring-boot-starter-oauth2-client and spring-boot-starter-oauth2-resouce-server?
  2. Is spring-boot-starter-oauth2-resouce-server also use to authentication?
  3. How to authenticat user using spring-boot-starter-oauth2-client and pass to spring-boot-starter-oauth2-resouce-server for authorization.
  4. How to implement multi-tenacy e.g. take tenant id from url and redirect user to tenant specific keycloak login page.
  5. I tried some example but won't succeed, working example will be helpful with - Spring Webflux + spring-boot-starter-oauth2-client+ spring-boot-starter-oauth2-resouce-server + multi-tenancy + keycloak as a provider.

Thanks & Regards, Pravin Nawale

tried some example found on internet, but didn't work.

1

There are 1 best solutions below

2
On

This question should not be answered because:

  • it is actually a container for many questions
  • quite a few are way too wide or lack precision.

But as it seems to be a first question... (break it down next time, give more details and edit your question when you get comments asking precisions)

0. Usefull Resource

I maintain up to date samples and tutorials covering most OAuth2 use-cases with Spring for both reactive applications and servlets. Start with tutorials main README if you are new to OAuth2 with Spring.

1. Where need to use spring-boot-starter-oauth2-client and spring-boot-starter-oauth2-resouce-server?

This one is important to start with as I suspect you lack OAuth2 background, specifically regarding involved parties and how it is implemented with spring-security:

  • spring-boot-starter-oauth2-client is to be used with OAuth2 clients:
    • apps serving UI with oauth2Login (@Controllers with methods returning template names)
    • apps consuming REST APIs with auto-configured Spring client: WebClient, @FeignClient, RestTemplate
  • spring-boot-starter-oauth2-resouce-server is to be used with resource-servers: apps serving REST APIs (@RestController or @Controller with @ResponseBody)

Now, if your app has controllers for both the resources and the UI to manipulate it (with Thymeleaf or any other server-side rendering engine), then define two different security filter-chains: one for each, ordered, and with securityMatcher in the first in order to limit the routes it applies to (the second being used as fallback for unmatched routes). Sample in this answer (the sample is for servlet, but it's the exact same principles): Use Keycloak Spring Adapter with Spring Boot 3

2. Is spring-boot-starter-oauth2-resouce-server also use to authentication?

OAuth2 requests should be authorized with an Authorization header containing a Bearer access-token.

The client is responsible for acquiring such an access-token from the authorization-server before sending requests to resource-server.

Your question is not quite clear but here are a few statements which could answer:

  • resource-server should return 401 (unauthorized) and not 302 (redirect to login) when authorization is missing or invalid => do not configure oauth2Login in resource-server filter-chain. Again, this is client business
  • resource-server is responsible for resources access-control: check that access-token is valid, that the user has required authorities, etc.

3. How to authenticat user using spring-boot-starter-oauth2-client and pass to spring-boot-starter-oauth2-resouce-server for authorization.

This question is not focused enough to get a single answer: what kind of client? what kind of request? context?

I see three main cases here:

  • the UI is rendered on Spring server with Thymeleaf, JSF, and alike => use spring's oauth2Login and refer to its documentation to overrides defaults and implement your authorization-server selection logic
  • the UI is rendered in the browser (Angular, React, Vue, ...) and you are ok to make it an OAuth2 client => find a certified client lib for your framework and implement the logic in the client (angular-auth-oidc-client, for instance, supports multi-tenancy)
  • the UI is rendered in the browser, but you prefer to implement the Backend For Frontend pattern to hide tokens from browser, then choose a BFF (like spring-cloud-gateway with tokenRelay filter) and refer to its doc for implementing your logic in it

If that can be of any help, I have:

  • here a tutorial for configuring an app with a Thymeleaf UI client and REST API
  • there a sample repo with an Angular workspace (app configured as OIDC client + API client lib generated from OpenAPI spec) and spring-boot resource-server (using servlet, but this makes no difference to the client).

4. How to implement multi-tenacy e.g. take tenant id from url and redirect user to tenant specific keycloak login page

Note

One of key principles of OAuth2 is that identities (tokens) are emitted (issued) by trusted 3rd parties (authorization-servers) => you must configure the list of issuers your resource-servers can trust (and clients can fetch tokens from). This list is static (loaded with conf at startup). The only reasonable trick for "dynamic" multi-tenancy is configuring an authentication manager resolver for a given host and dynamically creating authentication manager for new realms on this host. There is a tutorial covering that case among those linked at point 0.

Accept identities from various issuers on the resource-server

This is done by overriding the default ReactiveAuthenticationManagerResolver<ServerWebExchange> in your SecurityWebFilterChain configuration: http.oauth2ResourceServer().authenticationManagerResolver(authenticationManagerResolver)

I provide with thin wrappers around spring-boot-starter-oauth2-resource-server which support "static" multi-tenancy just by defining properties. Complete sample there:

Instead of spring-boot-starter-oauth2-resource-server (which is a transient dependency):

<dependency>
    <groupId>com.c4-soft.springaddons</groupId>
    <artifactId>spring-addons-webflux-jwt-resource-server</artifactId>
</dependency>

Instead of all your resource-server Java conf (unless you want access control from configuration and not with method-security, in which case, you'd have to define an AuthorizeExchangeSpecPostProcessor bean here). Of course, you'll have to add here a client filter-chain with a restrictive securityMatcher if you also serve UI client with oauth2Login:

@EnableReactiveMethodSecurity
@Configuration
public class SecurityConfig {
}

Instead of spring.security.oauth2.resourceserver properties:

com.c4-soft.springaddons.security.issuers[0].location=https://localhost:8443/realms/realm-1
com.c4-soft.springaddons.security.issuers[0].authorities.claims=realm_access.roles,resource_access.client-1.roles,resource_access.client-2.roles

com.c4-soft.springaddons.security.issuers[1].location=https://localhost:8443/realms/realm-2
com.c4-soft.springaddons.security.issuers[1].authorities.claims=realm_access.roles,resource_access.client-1.roles,resource_access.client-2.roles

# Comma separated list of routes accessible to anonymous
com.c4-soft.springaddons.security.permit-all=/api/v1/public/**,/actuator/health/readiness,/actuator/health/liveness

# Fine-grained CORS configuration can be set per path as follow:
com.c4-soft.springaddons.security.cors[0].path=/api/**
com.c4-soft.springaddons.security.cors[0].allowed-origins=https://localhost,https://localhost:8100,https://localhost:4200
# this are defaults and can be omitted
com.c4-soft.springaddons.security.cors[0].allowedOrigins=*
com.c4-soft.springaddons.security.cors[0].allowedMethods=*
com.c4-soft.springaddons.security.cors[0].allowedHeaders=*
com.c4-soft.springaddons.security.cors[0].exposedHeaders=*

If you don't want to use "my" wrappers, just copy from the source, it is open.

Redirect the user to the right authorization-server from client UI

As explained at point 3., this depends on the kind of client, used framework and if BFF pattern is applied or not

5. I tried some example but won't succeed, working example will be helpful with - Spring Webflux + spring-boot-starter-oauth2-client + spring-boot-starter-oauth2-resouce-server + multi-tenancy + keycloak as a provider

With all the elements above and linked resources, you should have enough to find your own path