I wanted to add integration tests for my spring cloud gateway. I found test containers and using that I spin up two services, keycloak container and gateway. Now I can already get the access token using keycloak getauthServerUrl method but I can’t seem authenticate. My security config looks like:
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.csrf(ServerHttpSecurity.CsrfSpec::disable);
http.authorizeExchange(auth -> auth.anyExchange().authenticated()).oauth2Login(withDefaults())
.oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()));
return http.build();
}
}
I think I know why I cannot authenticate in tests because my security config expects to login through a login page and in my test I add access token as a header. What can I change in my config that it works both with login page and without?
I wanted to add integration tests for my spring cloud gateway. I found test containers and using that I spin up two services, keycloak container and gateway. Now I can already get the access token using keycloak getauthServerUrl method but I can’t seem authenticate. My security config looks like:
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.csrf(ServerHttpSecurity.CsrfSpec::disable);
http.authorizeExchange(auth -> auth.anyExchange().authenticated()).oauth2Login(withDefaults())
.oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()));
return http.build();
}
}
I think I know why I cannot authenticate in tests because my security config expects to login through a login page and in my test I add access token as a header. What can I change in my config that it works both with login page and without?
Share Improve this question asked Mar 2 at 22:14 isaeedisaeed 154 bronze badges1 Answer
Reset to default 0Testing with Keycloak in a container is end-to-end testing (which is slow). If using the authorization code flow, you'll need an end-to-end test framework like Protractor to manipulate the UI as if users were actually logging in.
You can write integration tests for your Spring backend without a test container and, more importantly, without the need to manipulate a UI. For that, use:
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
- As you seem to be using the reactive version of Spring Cloud Gateway,
@AutoConfigureWebTestClient
(would be@AutoConfigureMockMvc
in a servlet) - When using
WebTestClient
to query anoauth2ResourceServer
configured with a JWT decoder and the default authentication converter, you can mock theJwtAuthenticationToken
in the test's security context withSecurityMockServerConfigurers.mockJwt()
(would beSecurityMockMvcRequestPostProcessors.jwt()
withMockMvc
). But as there are quite some limitations toWebTestClient
mutators (and theMockMvc
post-processors) I created inspring-security-tests
for OAuth2, I also created test annotations in spring-addons-oauth2-tests.
I described that in details in this Baeldung article.
Side notes
Unit-test access control
If your aim is to test access control, you should consider writing unit tests with @WebFluxTest
or WebMvcTest
.
@SpringBootTest
integration tests and end-to-end tests are slower than unit tests and should be used for testing no more than things wirering correctly together (not all possible access attempts for instance).
Configure Gateway with oauth2Login
or no security at all
On a gateway, I would not configure the security filter chain for routes to downstream resource servers with oauth2ResourceServer
. Instead:
- when the API is consumed by a SPA or mobile app using the authorization code flow, I apply the OAuth2 BFF pattern: configure such a filter chain with
oauth2Login
and the routes with theTokenRelay=
filter - when the API is consumed by an OAuth2 client, I either remove security from the gateway filter chain (and the
TokenRelay=
filter) for "routes" to downstream services, or I have OAuth2 clients query the downstream services directly (skip the gateway)
In both cases:
- resources' access control is the responsibility of downstream resource servers
- on the gateway, resources served by the gateway itself (actuator endpoints for instance) and by downstream services are not processed by the same filter chain