7

I am new to Spring Security and Oauth2. In my Spring Boot application, I have implemented authentication with OAuth2 for one tenant. Now I am trying to multi-tenancy in my Spring Boot application. From the answer to the previous post: OAUTH2 user service with Custom Authentication Providers, I have implemented two security configurations in order to support two tenants: Tenant1 and Tenant2 as follows:

Custom OAuth2 user service is as follows:

  @Component
  public class CustomOAuth2UserService extends DefaultOAuth2UserService {

     private UserRepository userRepository;

     @Autowired
     public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
     }

    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        ...
    }
 }

Tenant 1 security configuration is as follows:

@Configuration
public class Tenant1SecurityConfiguration extends WebSecurityConfigurerAdapter {
  private final CustomOAuth2UserService customOAuth2UserService;    

  public SecurityConfiguration(CustomOAuth2UserService customOAuth2UserService) {
    this.customOAuth2UserService = customOAuth2UserService;
  }

  public void configure(HttpSecurity http) throws Exception {
    http
        .csrf().disable()
        .authorizeRequests()
            .antMatchers("/login**").permitAll()
            .antMatchers("/manage/**").permitAll()
            .antMatchers("/api/auth-info").permitAll()
            .antMatchers("/api/**").authenticated()
            .antMatchers("/management/health").permitAll()
            .antMatchers("/management/info").permitAll()
            .antMatchers("/management/prometheus").permitAll()
            .antMatchers("/management/**").hasAuthority("ADMIN")
            .antMatchers("/tenant1/**").authenticated()
            .and()
        .oauth2Login()
            .userInfoEndpoint().userService(oauth2UserService());
    http
        .cors().disable();
  }

  private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
    return customOAuth2UserService;
  }
}

Tenant 2 security configuration is as follows:

@Order(90)
@Configuration
public class Tenant2SecurityConfiguration extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
        .requestMatcher(new AntPathRequestMatcher("/tenant2/**"))
        .csrf().disable()
        .authorizeRequests()
            .antMatchers("/tenant2/**").hasAuthority("USER")
            .and()
        .httpBasic();
    http
        .cors().disable();
  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth
        .inMemoryAuthentication()
            .withUser("user")
            .password("password")
            .roles("USER");
  }
}

application properties are as given below:

clientApp.name=myapp
spring.security.oauth2.client.registration.keycloak.client-id=abcd
spring.security.oauth2.client.registration.keycloak.client-name=Auth Server
spring.security.oauth2.client.registration.keycloak.scope=api
spring.security.oauth2.client.registration.keycloak.provider=keycloak
spring.security.oauth2.client.registration.keycloak.client-authentication-method=basic
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
myapp.oauth2.path=https://my.app.com/oauth2/
spring.security.oauth2.client.provider.keycloak.token-uri=${myapp.oauth2.path}token
spring.security.oauth2.client.provider.keycloak.authorization-uri=${myapp.oauth2.path}authorize
spring.security.oauth2.client.provider.keycloak.user-info-uri=${myapp.oauth2.path}userinfo
spring.security.oauth2.client.provider.keycloak.user-name-attribute=name

Basically, the intent of my application is B2B. So if I want to onboard a new business entity B as a tenant of my application, plugin its authentication provider, all its existing users should get authenticated seamlessly.

So, in view of the above, I have thought of the approach (though I am not sure if it's the best approach) as follows:

  1. There can be a single endpoint for all the tenants i.e. there can be a common login page for all the users regardless of the tenant. On this login page, there can be the provision for the users to enter only email IDs.
  2. The tenant ID can be determined from the email ID entered by the user.
  3. Based on tenant ID, authentication provider of associated tenant ID gets invoked in order to authenticate the user of associated tenant.
  4. On successful authentication, redirect to the home page for the associated tenant as: https://my.app.com/<tenant-id>/

In addition to the above, I would like to build a setup, where my application has quite a few, say, 40 tenants, out of which say 20 tenants use OAuth2, 10 uses basic auth and 10 uses form login.

Here in order to implement the above type of functionality, from Multi tenancy for spring security, it seems I have to support one authentication method, add tenant ID to authentication token and then create an adapter to other authentication methods, as needed.

But, in this regard, I did not find any concrete idea in any post so far on what changes should I do in the existing code base in order to achieve this.

Could anyone please help here?

Joy
  • 4,197
  • 14
  • 61
  • 131
  • Very long post for a short question: How to add new OAuth2 logins dynamically without changing code? Right? – dur Dec 12 '21 at 09:39
  • @dur not only OAuth2 but any other authentication method as well. – Joy Dec 13 '21 at 02:04
  • @dur, actually being new to this area, I don’t have much idea. It would be super helpful if you could give a sample working code, so that I can get some idea. Thanks. – Joy Dec 14 '21 at 00:29
  • Could you please clarify your question, what is it you actually want to know? How to add users to your system without new code, or how to properly implement a multi tenant system? Or do you just want to have multiple authentication methods work alongside each other? – 3Fish Dec 14 '21 at 10:28
  • @Joy I don't know how to do it. You have to find a way to add `WebSecurityConfigurerAdapter`s dynamically. I never saw an example for that. – dur Dec 14 '21 at 12:36
  • Also, note you will need to store per Customer their auth provider configurations and priority if multiple are defined in a Database or remote key-value storage. In Runtime, you will have to have some sort of Factory that providers the needed config to authorize and authenticate based on client identified using domain or another identifier. – Edwin M. Cruz Dec 18 '21 at 06:14

0 Answers0