spring-security: WebMvcTest / MockMvc bug with mocked Authentication ?

Summary

Injected Authentication as @Controller method parameter is null in a @WebMvcTest when using a (Mockito) mocked authentication.

I tried with both an annotation and a request post processor.

Note that I have not the same problem in @WebFluxTest, using the same annotation or a WebTestClient Configurer with implementation very similar to above mentioned post-processor.

Actual / Expected Behavior

The mocked authentication from the TestSecurityContext is not injected when it should

Version

Versions pulled by boot 2.2.6.Release (same issue at least with 2.2.4 and 2.2.5)

Sample

You can clone https://github.com/ch4mpy/spring-addons which contains

What I’d like to use:

	@Test
	@WithMockAuthentication(
			authType = OidcIdAuthenticationToken.class,
			name = "Ch4mpy",
			authorities = "ROLE_AUTHORIZED_PERSONNEL")
	public void greetCh4mpyWithAnnotation() throws Exception {
		api.get("/greet")
				.andExpect(content().string("Hello Ch4mpy! You are granted with [ROLE_AUTHORIZED_PERSONNEL]."));
	}

	@Test
	public void greetCh4mpyWithRequestPostProcessor() throws Exception {
		api.with(
				mockAuthentication(OidcIdAuthenticationToken.class).name("Ch4mpy")
						.authorities("ROLE_AUTHORIZED_PERSONNEL"))
				.get("/greet")
				.andExpect(content().string("Hello Ch4mpy! You are granted with [ROLE_AUTHORIZED_PERSONNEL]."));
	}

Whith a controller like:

@RestController
public class GreetingController {
	private final MessageService<OidcIdAuthenticationToken> messageService;

	@Autowired
	public GreetingController(MessageService<OidcIdAuthenticationToken> messageService) {
		this.messageService = messageService;
	}

	@GetMapping("/greet")
	public String greet(OidcIdAuthenticationToken auth) {
		return messageService.greet(auth);
	}
}

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 20 (20 by maintainers)

Most upvoted comments

Original reason why only some tests need to do advanced configuration on the mock.

In “real world”, I’d wrap authentication extraction:

public static KeycloakAuthenticationToken authentication() {
    return (KeycloakAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
}

and client code would be like

@PreAuthorize("isAuthenticated()")
public String greetSimple() {
    // simple, no need to further configure authentication mock in tests
    return String.format("Hello, %s!", authentication().getName());
}

@PreAuthorize("isAuthenticated()")
public String greetComplex() {
    // more complex, further configure authentication mock is required in tests
    var oidcKeycloakAccount = authentication().getAccount();
    return String.format("Hello, %s!, you are granted with %s.", oidcKeycloakAccount.getPrincipal().getName(), oidcKeycloakAccount.getRoles());
}

P.S. Please note I’m not requesting mocked authentication in spring-framework. I have it in a lib of mine I publish on maven-central and as so can use in any project I want since you explained me how to work around a Spring framework “expectation” about getPrincipal() having to be non nul for Authentication to be resolved as controller method argument.

I find it useful enough to maintain it, I’d understand you don’t.