I have a reactive(Spring WebFlux) web-application where I am having few REST APIs which are protected resources.(Oauth2) . To access them manually, I need to get an authorization token with client credentials grant type and use that token in the request.
Now, I need to write tests where I can invoke the APIs by making a call through Spring's WebTestClient. I am getting 403 forbidden on trying to access the API. Where am I doing wrong when writing the test case.
Below is my security configuration:
@EnableWebFluxSecurity
public class WebSecurityConfiguration {
@Bean
SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeExchange()
.pathMatchers(ACTUATOR_ENDPOINT_PATTERN)
.permitAll()
.pathMatchers("/my/api/*")
.hasAuthority("SCOPE_myApi")
.anyExchange().authenticated()
.and()
.oauth2ResourceServer()
.jwt();
http.addFilterAfter(new SomeFilter(), SecurityWebFiltersOrder.AUTHORIZATION);
return http.build();
}
@Bean
public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
ReactiveClientRegistrationRepository clientRegistrationRepository,
ReactiveOAuth2AuthorizedClientService authorizedClientService) {
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
@Bean
public WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
return WebClient.builder().filter(oauth).build();
}
}
Note:- I need this webclient bean because inside that filter (which I added to the SecurityWebFilterChain) I am calling another protected resource/API and the response of that API is being set in the reactive context
My application yaml:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: ${oidc-issuer-uri}
client:
provider:
myProvider:
issuer-uri: ${oidc-issuer-uri}
registration:
myProvider:
client-id: another-service-client
client-secret: ${another-service-clientSecret}
scope: anotherServiceScope
authorization-grant-type: client_credentials
My Controller:
@RestController
public class MyController {
@GetMapping(value = "/my/api/greet")
public Mono<String> greet() {
return Mono.subscriberContext()
.flatMap(context -> {
String someVal = context.get("MY_CONTEXT"); //This context is being set inside the filter 'SomeFilter'
//Use this someVal
return Mono.just("Hello World");
});
}
}
My Test Case:
@RunWith(SpringRunner.class)
@WebFluxTest(controllers = {MyController.class})
@Import({WebSecurityConfiguration.class})
@WithMockUser
public class MyControllerTest {
@Autowired
private WebTestClient webTestClient;
@Test
public void test_greet() throws Exception {
webTestClient.mutateWith(csrf()).get()
.uri("/my/api/greet")
.exchange()
.expectStatus().isOk();
}
}
Note:- I cannot bypass by not using my WebSecurityConfiguration class. Because the reactive context is being set in the filter which is added in the websecurityconfiguration.
2 things are required here:
@Test
public void test_greet() {
webTestClient
.mutateWith(mockOidcLogin().authorities(new SimpleGrantedAuthority("SCOPE_myApi")))
.get()
.uri("/my/api/greet")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("mockSasToken");
}
For this one option is to use spring boot @AutoConfigureWireMock(port = 0) to automatically boot up a wiremock server and shutdown for us at a random port.
Next we stub the response for the "another service" and the Oauth2 token endpoint in the test method.
Lastly, we need a "test" spring profile and a corresponding application-test.yaml where we tell spring to use the wiremock endpoints to fetch token:
spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: http://localhost:${wiremock.server.port}/.well-known/jwks_uri
client:
provider:
myProvider:
token-uri: http://localhost:${wiremock.server.port}/.well-known/token
registration:
myProvider:
client-id: mockClient
client-secret: mockSecret
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With