-1

I have two Spring Boot services, S1 and S2, both with their own API endpoints. S1 is protected with OAuth2 client credentials, so I have configured S2 as an OAuth2 client and am using WebClient to make API requests on S2. To achieve this, I added two dependencies to my S2 application

  1. spring-boot-starter-oauth2-client and
  2. spring-boot-starter-webflux.

The configuration of WebClient and my application.properties file is as follows:

spring:
  security:
    oauth2:
      client:
        registration:
          liferayclient:
            client-id: ${CLIENT_ID:bcvc7yt7}
            client-secret: ${CLIENT_SECRET:vhsvchc76}
            authorization-grant-type: client_credentials
        provider:
          liferayclient:
            token-uri: localhost:8080/o/oauth2/token

The WebClient configuration is:

@Configuration
public class WebClientConfig {

    @Bean
    WebClient webClient(AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
        ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
                .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(16 * 1024 * 1024))
                .build();

        ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
                new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
        oauth2Client.setDefaultClientRegistrationId("liferayclient");
        HttpClient httpClient = HttpClient.create()
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10 * 1000)
                .doOnConnected(connection -> {
                    connection.addHandlerLast(new ReadTimeoutHandler(2 * 60 * 1000L, MILLISECONDS));
                    connection.addHandlerLast(new WriteTimeoutHandler(2 * 60 * 1000L, MILLISECONDS));
                });

        return WebClient.builder()
                .filter(oauth2Client)
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .exchangeStrategies(exchangeStrategies)
                .build();
    }

    @Bean
    ReactiveClientRegistrationRepository clientRegistrations(
            @Value("${spring.security.oauth2.client.provider.liferayclient.token-uri}") String token_uri,
            @Value("${spring.security.oauth2.client.registration.liferayclient.client-id}") String client_id,
            @Value("${spring.security.oauth2.client.registration.liferayclient.client-secret}") String client_secret,
            @Value("${spring.security.oauth2.client.registration.liferayclient.authorization-grant-type}") String authorizationGrantType

    ) {
        ClientRegistration registration = ClientRegistration
                .withRegistrationId("liferayclient")
                .tokenUri(token_uri)
                .clientId(client_id)
                .clientSecret(client_secret)
                .authorizationGrantType(new AuthorizationGrantType(authorizationGrantType))
                .build();
        return new InMemoryReactiveClientRegistrationRepository(registration);
    }

    @Bean
    public AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager(
            ReactiveClientRegistrationRepository clientRegistrationRepository) {

        InMemoryReactiveOAuth2AuthorizedClientService clientService =
                new InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository);

        ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
                ReactiveOAuth2AuthorizedClientProviderBuilder.builder().clientCredentials().build();

        AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =
                new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
                        clientRegistrationRepository, clientService);

        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

        return authorizedClientManager;
    }
}

Now coming to the issue, I am able to successfully make an API call on S1 from S2 using a web client. However, I am encountering an issue where I am unable to call any API on the service S2. Whenever I attempt to make an API call on S2, I receive a "login with OAuth 2.0" prompt on the web browser. The HTML response I am getting is as folows.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Please sign in</title>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
    <link href="https://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet"
        crossorigin="anonymous" />
</head>

<body>
    <div class="container">
        <h2 class="form-signin-heading">Login with OAuth 2.0</h2>
        <table class="table table-striped">
        </table>
    </div>
</body>

</html>

The web view

Before adding the spring-boot-starter-oauth2-client dependency, I was able to make these API calls using Postman without any issues. It appears that since the dependency was added, Spring now expects that the current microservice is also protected by OAuth2. As a result, any REST API created in the current microservice will automatically be protected by OAuth2.

I am seeking advice on how to address this issue and enable outgoing calls to require OAuth2 authorization, while allowing incoming calls to bypass this requirement. I am utilizing Spring Boot version 3.0.0 and one of the solution that I have tried is as follows.

package com.report.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig{
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
        return http
            .csrf().disable()
            .authorizeHttpRequests()
                .requestMatchers("*").permitAll()
                .and()
            .build();
    }
}

I tried to create a custom SecurityFilterChain bean that permits all HTTP requests, but it did not help me. Although I am able to make API calls from S2 to S1 with the WebClient like previously, I am still unable to make any API calls on S2 via Postman. I am now getting a Forbidden error after adding this.

dur
  • 15,689
  • 25
  • 79
  • 125
Jithin M V
  • 175
  • 1
  • 1
  • 10

1 Answers1

0

After some investigation, I found the solution to my own question. When using Spring Security to allow access to all requests without authentication or authorization, I initially used the following configuration:

http
    .csrf().disable()
    .authorizeRequests()
        .requestMatchers("*").permitAll()
        .and()
    .build();

However, this did not work as expected and I was still getting a 401 Unauthorized error when making requests to my Spring Boot application.

I then tried using the following configuration instead:

http
    .csrf().disable()
    .authorizeRequests()
        .anyRequest().permitAll()
        .and()
    .build();

And this worked as expected, allowing access to all requests without authentication or authorization.

I'm not sure why using requestMatchers("*") did not work in my case, but using anyRequest() seems to have fixed the issue.

I hope this helps someone else facing a similar issue.

dur
  • 15,689
  • 25
  • 79
  • 125
Jithin M V
  • 175
  • 1
  • 1
  • 10