spring-projects / spring-projects/spring-security
WebTestClient authentication fails with form-data credentials
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 9.6k
- Forks
- 6.3k
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 52
Description
spring-boot-2.6.3
I'm migrating my MockMvc tests to WebTestClient, for having all my tests using the same underlying API.
The following example project shows that authenticating on the /login page works with MockMvc, but does not with WebTestClient.
In real world, I'm testing a ldap security configuration, but the issue is reproducible even with in-memory authentication.
This is a result result of https://github.com/spring-projects/spring-boot/issues/29825
(see the issue also for a full sample project attached)
I assume this is a bug, as authentication with MockMvc works flawless, and WebTestClient does not.
Tests:
@SpringBootTest
@AutoConfigureMockMvc
public class PersonControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private WebTestClient webTestClient;
//works
@Test
public void testMockMvc() throws Exception {
SecurityMockMvcRequestBuilders.FormLoginRequestBuilder login = formLogin()
.user("junituser")
.password("junitpw");
mockMvc.perform(login)
.andExpect(authenticated().withUsername("junituser"));
}
//works
@Test
public void testMockMvcUnauthenticated() throws Exception {
SecurityMockMvcRequestBuilders.FormLoginRequestBuilder login = formLogin()
.user("junituser")
.password("invalid");
mockMvc.perform(login)
.andExpect(unauthenticated());
}
//works
@Test
public void testRedirectToLoginPage() {
webTestClient.get().uri("/").exchange().expectStatus().is3xxRedirection();
}
//works
@Test
public void testLoginPageAnonymous() {
webTestClient.get().uri("/login").exchange().expectStatus().isOk();
}
//fails with 403 forbidden
@Test
public void testWebClient() {
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add("username", "junituser");
formData.add("password", "junitpw");
webTestClient.post()
.uri("/login") //the test would fail the same executed against '/example' @RestController
.body(BodyInserters.fromFormData(formData))
.exchange()
.expectStatus()
.isOk();
}
//throws NPE
@Test
public void testWebClientCsrf() {
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add("username", "junituser");
formData.add("password", "junitpw");
//there is no FormLoginRequestBuilder for WebTestClient?
webTestClient.mutateWith(csrf())
.post()
.uri("/login")
.body(BodyInserters.fromFormData(formData))
.exchange()
.expectStatus()
.isOk();
}
}
Source:
@RestController
public class PersonController {
@GetMapping("/example")
public String example() {
return "Authorized user";
}
@PostMapping("/example")
public String examplePost() {
return "Authorized user";
}
}
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("junituser")
.password("{noop}junitpw")
.roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
}
@SpringBootApplication
public class MainApp {
public static void main(String[] args) {
SpringApplication.run(MainApp.class, args);
}
}
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the provided WebTestClient tests and compare their form-data login and CSRF behavior with the working MockMvc tests. Investigate the WebTestClient authentication path described in the issue; done means valid credentials authenticate successfully without 403, while invalid credentials remain unauthenticated and the CSRF test no longer throws an NPE.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, spring, spring-boot
- Domain
- backend, security, testing
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100