7

We’re having a microservices architecture based on spring boot where we have multiple microservices talking to each other and also a Javascript UI that connects to the different microservices.

Since this is an internal application and we have the requirement to connect them to our SAML2 endpoint to provide SSO, I’m getting a bit of a headache to connect all of this together. Ideally the microservices use oAuth2 between themselves (JWT) and the UI, but User Authentication is done through SAML2

The following I want to achieve with this:

  • UI Clients talk to the microservices by using JWT
  • Microservices use JWT as well to talk to each other. When a user initiates a request to a microservice and that microservice needs more data from another one, it uses the users JWT token (this should be fairly easy to do).
  • Having one central authentication microservice which is responsible for generating new tokens and authenticate the user against the SAML endpoint.
  • Storing some SAML details (e.g. Roles) in the authentication microservice

So I have tried many different things. What I can say is the following:

I guess where I struggle with is the connection points of oauth2 Resource Server and the SAML services.

Regarding SAML I have the following that works fine:

@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Value("${security.saml2.metadata-url}")
    String metadataUrl;
    @Value("${server.ssl.key-alias}")
    String keyAlias;
    @Value("${server.ssl.key-store-password}")
    String password;
    @Value("${server.port}")
    String port;
    @Value("${server.ssl.key-store}")
    String keyStoreFilePath;

    @Autowired
    SAMLUserDetailsService samlUserDetailsService;


    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.antMatcher("/**")
                .authorizeRequests()
                .antMatchers("/oauth/**").authenticated()
                .and().exceptionHandling()

                .and()
                .authorizeRequests()
                .antMatchers("/saml*").permitAll()
                .anyRequest().authenticated()
                .and()
                .apply(saml()).userDetailsService(samlUserDetailsService)
                .serviceProvider()
                .keyStore()
                .storeFilePath("saml/keystore.jks")
                .password(this.password)
                .keyname(this.keyAlias)
                .keyPassword(this.password)
                .and()
                .protocol("https")
                .hostname(String.format("%s:%s", "localhost", this.port))
                .basePath("/")
                .and()
                .identityProvider()
                .metadataFilePath(this.metadataUrl);

    }
}

and that works fine. so when I hit a protected endpoint I will get redirected and can login through saml. I get the userdetails then in the samlUserDetailsService.

Regarding oauth I have something like this:

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore())
                .tokenEnhancer(accessTokenConverter())
                .authenticationManager(authenticationManager);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()");
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }


    @Bean
    JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("ABC"); //needs to be changed using certificates
        return converter;
    }


    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        clients.inMemory()
                .withClient("acme")
                .secret("acmesecret")
                .authorizedGrantTypes("refresh_token", "authorization_code")
                .autoApprove(true)
                .scopes("webapp")
                .accessTokenValiditySeconds(60)
                .refreshTokenValiditySeconds(3600);
    }
}

This part also works fine with other micorservices where I have @EnableResourceServer

As far as I understand the OAuth part, the ClientDetailsServiceConfigurer just configures the client applications (in my case the other microservices) and I should use client_credentials kind of grant for this (but aren't sure). But how I would wire in the SAML part is not clear to me...

As an alternative I'm thinking about splitting this up. Creating a microservice that is an OAuth Authorization Service and another one that does the SAML bit. In this scenario, the SAML Microservice would connect to SAML and provide an endpoint like /me if the user is authenticated. The OAuth Authorization Service would then use the SAML Microservice to check if a user is Authenticated there and provide a token if that is the case. I would also do the same regarding refresh tokens. As far as I understand this, I would implement this kind of logic in the public void configure(ClientDetailsServiceConfigurer clients) throws Exception {} method.

If there's a better approach, let me know!

Luk
  • 431
  • 7
  • 14

0 Answers0