Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KeycloakAuthenticationToken null while @Preauthorize works #12

Closed
MrMedTech opened this issue Aug 31, 2020 · 9 comments
Closed

KeycloakAuthenticationToken null while @Preauthorize works #12

MrMedTech opened this issue Aug 31, 2020 · 9 comments
Assignees

Comments

@MrMedTech
Copy link

Hi,
I used your lib in my Spring Project. Therefor I disabled all Filters in my @AutoConfigureMockMvc(addFilters = false) so Spring does not try to contact a running external keycloak instance.

@SpringBootTest
//Configure Mockmvc with the app-context
@AutoConfigureMockMvc(addFilters = false)
//Set profile test active
@ActiveProfiles("test")
//@AutoConfigureTestDatabase(replace= AutoConfigureTestDatabase.Replace.NONE)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
//Enable REstDocs
@ExtendWith(RestDocumentationExtension.class)
@AutoConfigureRestDocs
@Import({
        ServletKeycloakAuthUnitTestingSupport.UnitTestConfig.class,
        SpringSecurityConfiguration.class })

My Test is annotated with:

   @Test
    @Order(5)
    @WithMockKeycloakAuth(authorities = "ROLE_owner",
    id = @IdTokenClaims(sub = "abc"))

This is my test annotation. Thing is that the role check in my pre authorize works while my KeycloakAuthenticationToken is always null.

  @PreAuthorize("hasRole('owner')")
    @ApiOperation("Create a new product")
    @PostMapping(value = "", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    public Product createProduct(KeycloakAuthenticationToken authentication, @ApiParam(value = "Object with new product attributes") @Valid @RequestBody ProductDto newProductDto) {

Is it correct that this kind of validation only works with a running Keycloak instance?

Thx in advance and for your lib so far

@ch4mpy
Copy link
Owner

ch4mpy commented Aug 31, 2020

Welcome onboard @MrMedTech

Is it correct that this kind of validation only works with a running Keycloak instance?

No. All tests in this repo pass without any Keycloak instance running.

You should not disable filters registration. As you can see here, I write my tests with @WebMvcTest which does @AutoConfigureMockMvc with filters.

Keycloak spring-boot configuration needs properties to be set but the instance is not reached during tests execution. You might notice @ActiveProfiles("keycloak") in above test sample which loads a bunch of keycloak properties from application-keycloak.properties.

What versions of are you using (keycloak, this lib and spring-boot)?

I'm in a rush today but will further investigate tonight (UTC-10) on the effect of keycloak security filter not being registered.

@MrMedTech
Copy link
Author

MrMedTech commented Sep 1, 2020

Thank you =)
I use:

Keycloak 11,
Version 2.3.4 of your test-webmvc-addons,
Spring Boot 2.3.3

My application.properties look pretty much like yours:

keycloak.auth-server-url=http://localhost:8080/auth
keycloak.realm=demo
keycloak.resource=DemoApplication
keycloak.public-client=false
keycloak.principal-attribute=preferred_username
keycloak.bearer-only=true

When I Use MockMvc with filters, my logs show that the startup fails because the .wellknown enpoint is not reachable.
I built a work around to fix the Nullpointer problem with the auth object which is still a bit confusing to me.

I created a bean that intercepts the request and provides the accesstoken that dan be Autowired in a controller.

To make this working during my tests I have to read the token I have created with your lib and then use it as a mock for my accesstoken bean.

       Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
       SimpleKeycloakAccount account =((SimpleKeycloakAccount) authentication.getDetails());
       KeycloakSecurityContext context = account.getKeycloakSecurityContext();
       Mockito.when(accessTokenConfiguration.getAccessToken()).thenReturn(context.getToken());

I use @SpringBootTest because I test all the layers From Controller zu repository with an embedded database.

So to break it down:
How do you prevent that the spring boot autoconfiguration tries to connect to keycloak at startup?
Why is the security context / the token no more available when my mock request levaes the test method?

Thanks for your effort. The lib's helping a lot so far.

@ch4mpy
Copy link
Owner

ch4mpy commented Sep 2, 2020

@MrMedTech Did you @MockBean JwtDecoder jwtDecoder;?
Wiring actual bean, is very likely to cause:
BeanCreationException: Error creating bean with name 'jwtDecoderByIssuerUri'
and further in the stack trace:
ResourceAccessException: I/O error on GET request for "http://localhost:8080/auth/realms/master/.well-known/openid-configuration": Connection refused: connect;)

I confirm @AutoConfigureMockMvc(addFilters = false) is bad idea: no filter => no security filter => nothing to attach a security context to the request (reason why you had to write your hack)

So please:

  1. @AutoConfigureMockMvc(addFilters = true)
  2. @MockBean JwtDecoder jwtDecoder; in your test (like it's done in mine)

If above is not enough, would you share your SpringSecurityConfiguration? I need to figure out why your conf requires the server to be up.

P.S., two alternatives to

@SpringBootTest
@AutoConfigureMockMvc()
  • @WebMvcTest pointing to the controller under test, mock repos and unit test it separately if needed. You'll save a lot of time setting-up the database before each test. I personally don't write integration tests any more. Just Unit (every dependency mocked) and e2e (everything real, including client, authorization server tokens, the all spring-security layer, etc.)
  • @Import(YourServiceTest.TestConfig.class) with very minimal conf when testing a service (and as so without the context of a MockMvc HTTP request) like I do here

@MrMedTech
Copy link
Author

Hi,
I tried your suggestion with the filter enableing and the Mock of the jwt decoder. Did I mention that I use junit 5 ( not sure if this is important )?

@SpringBootTest
@AutoConfigureMockMvc()
@ActiveProfiles("test")
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@ExtendWith(RestDocumentationExtension.class)
@AutoConfigureRestDocs
@Import({
        ServletKeycloakAuthUnitTestingSupport.UnitTestConfig.class,
        SpringSecurityConfiguration.class })`
class ProductControllerIntegrationTest {

    // Create the
    String URL = "/products";
    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @InjectMocks
    private ProductServiceImpl productService;

    @MockBean
    AccessTokenConfiguration accessTokenConfiguration;

    @MockBean
    JwtDecoder jwtDecoder;

The error I get is:

2020-09-03 07:57:06.577  WARN 12292 --- [           main] o.keycloak.adapters.KeycloakDeployment   : Failed to load URLs from http://localhost:8080/auth/realms/demo/.well-known/openid-configuration

java.net.ConnectException: Connection refused: connect

I was a bit confused by the different testing strategies. But you are right. I will change my stragtegy to test every method as a unit und mock the rest of the Methods. I was also planning to use e2e tests with postman to test the "user stories".
But is it that bad to neglect the filters in that scenario? I want to test my spring security configuration and not the decoding an security checks as I wont't even provide a real signed token. With my current approach I can test the role base authorization and I can use the access token like in production mode. What's the benefit of including the whoile chain? Of course if the test runs with the whole chain enabled I would prefer this absolutely.

Ah here my security config:

`@KeycloakConfiguration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter {

    @Bean
    public KeycloakConfigResolver KeyCloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }

    @Autowired
    public void configureGlobal(
            AuthenticationManagerBuilder auth) {

        KeycloakAuthenticationProvider keycloakAuthenticationProvider
                = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(
                new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }


    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new NullAuthenticatedSessionStrategy();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);

        http.csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .sessionAuthenticationStrategy(sessionAuthenticationStrategy())
                .and()
                .authorizeRequests()
                .anyRequest().authenticated();
    }


}`

@MrMedTech
Copy link
Author

I just tried this MVC version but I'm running still in the same problem that it tries to reach a real keycloak instance on localhost:

@WebMvcTest(controllers = ProductController.class)
@ActiveProfiles("test")
@ExtendWith(RestDocumentationExtension.class)
@AutoConfigureRestDocs
@Import({
        ServletKeycloakAuthUnitTestingSupport.UnitTestConfig.class,
        SpringSecurityConfiguration.class })
public class ProductControllerMvcTest {

    // Create the
    String URL = "/products";

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @MockBean
    private ProductServiceImpl productService;

    @InjectMocks
    AccessTokenConfiguration accessTokenConfiguration;

    @MockBean
    JwtDecoder jwtDecoder;

    private static ProductDto testProductDto = new ProductDto();
    private static Product  testProduct = new Product();


    @BeforeAll
    private static void setUp(){
        testProductDto.setName("Pasta");
        testProductDto.setPrice(1.5);

        testProduct.setId(1L);
        testProduct.setName(testProductDto.getName());
        testProduct.setPrice(testProductDto.getPrice());
        testProduct.setCreated(1L);

    }

    @Test
    @WithMockKeycloakAuth(authorities = "ROLE_owner",
            id = @IdTokenClaims(sub = "abc"))
    void createProduct_RealmRoleOwner_HttpStatusCreated() throws Exception {


        ConstraintFieldsExtractor fields = new ConstraintFieldsExtractor(ProductDto.class);
        Mockito.when(productService.createProduct(testProductDto)).thenReturn(testProduct);
        mockMvc.perform(post(URL).contentType(MediaType.APPLICATION_JSON).header("Authorization", "Bearer accessToken")
                .content(objectMapper.writeValueAsString(testProductDto))
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.created").isNumber())
                .andExpect(jsonPath("$.name").value(testProductDto.getName()))
                .andExpect(jsonPath("$.price").value(testProductDto.getPrice()))
                .andExpect(jsonPath("$.id").value(1L))
                .andDo(document("{methodName}",
                        preprocessResponse(prettyPrint()),
                        requestFields(fields.withPath("name")
                                        .description("Name of the product")
                                        .type(JsonFieldType.STRING),
                                fields.withPath("price")
                                        .description("Price of the product")
                                        .type(JsonFieldType.NUMBER)
                                        .optional()),
                        responseFields(fieldWithPath("id").description("Id of the new product"),
                                fieldWithPath("name").description("Name of the product"),
                                fieldWithPath("price").description("Price of the new product"),
                                fieldWithPath("created").description("Unix timestamp when the product was created")
                        ),

                        requestHeaders(
                                headerWithName("Authorization").description(
                                        "Bearer auth credentials"))

                        )//document
                )//andDo
                .andReturn();
    }

@ch4mpy
Copy link
Owner

ch4mpy commented Sep 4, 2020

It's curious, my tests pass with your conf (except for those expecting routes to be be allowed to AUTHORIZED_PERSONNEL only off course).

How do you run your tests?
Are you sure the exception you report is thrown by ProductControllerMvcTest::createProduct_RealmRoleOwner_HttpStatusCreated just above? (are you running this test alone, not all tests including one with non mocked JwtDecoder?)
Would you report the all stack trace?
Is your code base accessible or in private repo?

P.S.
you're not using ServletKeycloakAuthUnitTestingSupport.UnitTestConfig.class. I think you can remove it. I use it in my test to @Autowired MockMvcSupport api; as a replacement to MockMvc. This allows me to replace

        mockMvc.perform(post(URL).contentType(MediaType.APPLICATION_JSON).header("Authorization", "Bearer accessToken")
                .content(objectMapper.writeValueAsString(testProductDto))
                .accept(MediaType.APPLICATION_JSON))

with

        api.post(testProductDto, URL)

Default accept and content-type being application/json with UTF-8 charset if not overridden in test application.properties (see MockMvcSupport javadoc). It also uses the message converters registered in your conf to perform payloads (de)serialization, meaning tests will fail if your conf is broken for expected content-types.

@MrMedTech
Copy link
Author

I run the test with right klick on the class -> Run .
this is the whole trace of my webmvc example. Thanks for your effort.

2020-09-04 07:44:06.585  INFO 19504 --- [           main] o.a.http.impl.client.DefaultHttpClient   : I/O exception (java.net.SocketException) caught when processing request to {}->http://localhost:8080: Software caused connection abort: recv failed
2020-09-04 07:44:06.586  INFO 19504 --- [           main] o.a.http.impl.client.DefaultHttpClient   : Retrying request to {}->http://localhost:8080
2020-09-04 07:44:06.587  INFO 19504 --- [           main] o.a.http.impl.client.DefaultHttpClient   : I/O exception (java.net.SocketException) caught when processing request to {}->http://localhost:8080: Connection reset
2020-09-04 07:44:06.587  INFO 19504 --- [           main] o.a.http.impl.client.DefaultHttpClient   : Retrying request to {}->http://localhost:8080
2020-09-04 07:44:06.588  INFO 19504 --- [           main] o.a.http.impl.client.DefaultHttpClient   : I/O exception (java.net.SocketException) caught when processing request to {}->http://localhost:8080: Connection reset
2020-09-04 07:44:06.589  INFO 19504 --- [           main] o.a.http.impl.client.DefaultHttpClient   : Retrying request to {}->http://localhost:8080
2020-09-04 07:44:06.601  WARN 19504 --- [           main] o.keycloak.adapters.KeycloakDeployment   : Failed to load URLs from http://localhost:8080/auth/realms/demo/.well-known/openid-configuration

java.net.SocketException: Connection reset
	at java.base/java.net.SocketInputStream.read(SocketInputStream.java:186) ~[na:na]
	at java.base/java.net.SocketInputStream.read(SocketInputStream.java:140) ~[na:na]
	at org.apache.http.impl.io.AbstractSessionInputBuffer.fillBuffer(AbstractSessionInputBuffer.java:161) ~[httpcore-4.4.13.jar:4.4.13]
	at org.apache.http.impl.io.SocketInputBuffer.fillBuffer(SocketInputBuffer.java:82) ~[httpcore-4.4.13.jar:4.4.13]
	at org.apache.http.impl.io.AbstractSessionInputBuffer.readLine(AbstractSessionInputBuffer.java:276) ~[httpcore-4.4.13.jar:4.4.13]
	at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:138) ~[httpclient-4.5.12.jar:4.5.12]
	at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:56) ~[httpclient-4.5.12.jar:4.5.12]
	at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:259) ~[httpcore-4.4.13.jar:4.4.13]
	at org.apache.http.impl.AbstractHttpClientConnection.receiveResponseHeader(AbstractHttpClientConnection.java:294) ~[httpcore-4.4.13.jar:4.4.13]
	at org.apache.http.impl.conn.DefaultClientConnection.receiveResponseHeader(DefaultClientConnection.java:257) ~[httpclient-4.5.12.jar:4.5.12]
	at org.apache.http.impl.conn.AbstractClientConnAdapter.receiveResponseHeader(AbstractClientConnAdapter.java:230) ~[httpclient-4.5.12.jar:4.5.12]
	at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:273) ~[httpcore-4.4.13.jar:4.4.13]
	at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125) ~[httpcore-4.4.13.jar:4.4.13]
	at org.apache.http.impl.client.DefaultRequestDirector.tryExecute(DefaultRequestDirector.java:679) ~[httpclient-4.5.12.jar:4.5.12]
	at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:481) ~[httpclient-4.5.12.jar:4.5.12]
	at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:835) ~[httpclient-4.5.12.jar:4.5.12]
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83) ~[httpclient-4.5.12.jar:4.5.12]
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108) ~[httpclient-4.5.12.jar:4.5.12]
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56) ~[httpclient-4.5.12.jar:4.5.12]
	at org.keycloak.adapters.KeycloakDeployment.getOidcConfiguration(KeycloakDeployment.java:219) ~[keycloak-adapter-core-11.0.0.jar:11.0.0]
	at org.keycloak.adapters.KeycloakDeployment.resolveUrls(KeycloakDeployment.java:178) ~[keycloak-adapter-core-11.0.0.jar:11.0.0]
	at org.keycloak.adapters.KeycloakDeployment.getRealmInfoUrl(KeycloakDeployment.java:232) ~[keycloak-adapter-core-11.0.0.jar:11.0.0]
	at org.keycloak.adapters.rotation.AdapterTokenVerifier.createVerifier(AdapterTokenVerifier.java:107) ~[keycloak-adapter-core-11.0.0.jar:11.0.0]
	at org.keycloak.adapters.rotation.AdapterTokenVerifier.verifyToken(AdapterTokenVerifier.java:47) ~[keycloak-adapter-core-11.0.0.jar:11.0.0]
	at org.keycloak.adapters.BearerTokenRequestAuthenticator.authenticateToken(BearerTokenRequestAuthenticator.java:103) ~[keycloak-adapter-core-11.0.0.jar:11.0.0]
	at org.keycloak.adapters.BearerTokenRequestAuthenticator.authenticate(BearerTokenRequestAuthenticator.java:88) ~[keycloak-adapter-core-11.0.0.jar:11.0.0]
	at org.keycloak.adapters.RequestAuthenticator.authenticate(RequestAuthenticator.java:67) ~[keycloak-adapter-core-11.0.0.jar:11.0.0]
	at org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter.attemptAuthentication(KeycloakAuthenticationProcessingFilter.java:154) ~[keycloak-spring-security-adapter-11.0.0.jar:11.0.0]
	at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) ~[spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
	at org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter.doFilter(KeycloakPreAuthActionsFilter.java:96) ~[keycloak-spring-security-adapter-11.0.0.jar:11.0.0]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
	at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92) ~[spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
	at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77) ~[spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
	at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) ~[spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) ~[spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) ~[spring-security-web-5.3.4.RELEASE.jar:5.3.4.RELEASE]
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
	at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134) ~[spring-test-5.2.8.RELEASE.jar:5.2.8.RELEASE]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
	at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134) ~[spring-test-5.2.8.RELEASE.jar:5.2.8.RELEASE]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
	at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134) ~[spring-test-5.2.8.RELEASE.jar:5.2.8.RELEASE]
	at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:183) ~[spring-test-5.2.8.RELEASE.jar:5.2.8.RELEASE]
	at com.example.template.integration.ProductControllerMvcTest.createProduct_RealmRoleOwner_HttpStatusCreated(ProductControllerMvcTest.java:103) ~[test-classes/:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:686) ~[junit-platform-commons-1.6.2.jar:1.6.2]
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) ~[junit-jupiter-engine-5.6.2.jar:5.6.2]
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) ~[junit-jupiter-engine-5.6.2.jar:5.6.2]
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149) ~[junit-jupiter-engine-5.6.2.jar:5.6.2]
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140) ~[junit-jupiter-engine-5.6.2.jar:5.6.2]
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84) ~[junit-jupiter-engine-5.6.2.jar:5.6.2]
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115) ~[junit-jupiter-engine-5.6.2.jar:5.6.2]
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105) ~[junit-jupiter-engine-5.6.2.jar:5.6.2]
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) ~[junit-jupiter-engine-5.6.2.jar:5.6.2]
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) ~[junit-jupiter-engine-5.6.2.jar:5.6.2]
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) ~[junit-jupiter-engine-5.6.2.jar:5.6.2]
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) ~[junit-jupiter-engine-5.6.2.jar:5.6.2]
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104) ~[junit-jupiter-engine-5.6.2.jar:5.6.2]
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98) ~[junit-jupiter-engine-5.6.2.jar:5.6.2]
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:212) ~[junit-jupiter-engine-5.6.2.jar:5.6.2]
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:208) ~[junit-jupiter-engine-5.6.2.jar:5.6.2]
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:137) ~[junit-jupiter-engine-5.6.2.jar:5.6.2]
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:71) ~[junit-jupiter-engine-5.6.2.jar:5.6.2]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1540) ~[na:na]
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1540) ~[na:na]
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51) ~[junit-platform-engine-1.6.2.jar:1.6.2]
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248) ~[junit-platform-launcher-1.6.2.jar:1.6.2]
	at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$5(DefaultLauncher.java:211) ~[junit-platform-launcher-1.6.2.jar:1.6.2]
	at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226) ~[junit-platform-launcher-1.6.2.jar:1.6.2]
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199) ~[junit-platform-launcher-1.6.2.jar:1.6.2]
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132) ~[junit-platform-launcher-1.6.2.jar:1.6.2]
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71) ~[junit5-rt.jar:na]
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33) ~[junit-rt.jar:na]
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220) ~[junit-rt.jar:na]
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53) ~[junit-rt.jar:na]


MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /products
       Parameters = {}
          Headers = [Content-Type:"application/json", Authorization:"Bearer accessToken", Accept:"application/json", Content-Length:"28"]
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = null

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 401
    Error message = Unable to authenticate using the Authorization header
          Headers = [WWW-Authenticate:"Bearer realm="demo", error="invalid_token", error_description="Failed to parse JWT"", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []


java.lang.AssertionError: Status expected:<200> but was:<401>
Expected :200
Actual   :401
<Click to see difference>


	at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:59)
	at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:122)
	at org.springframework.test.web.servlet.result.StatusResultMatchers.lambda$matcher$9(StatusResultMatchers.java:627)
	at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:196)
	at com.example.template.integration.ProductControllerMvcTest.createProduct_RealmRoleOwner_HttpStatusCreated(ProductControllerMvcTest.java:106)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:686)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:212)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:208)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:137)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:71)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248)
	at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$5(DefaultLauncher.java:211)
	at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)

The project is private.

@ch4mpy
Copy link
Owner

ch4mpy commented Sep 4, 2020

WWW-Authenticate:"Bearer realm="demo", error="invalid_token", error_description="Failed to parse JWT"

Have you tried removing .header("Authorization", "Bearer accessToken")?
Keeping @MockBean JwtDecoder jwtDecoder; and @WebMvcTest(controllers = ProductController.class)) like in your last snippet.

My guess is because you provide a bearer token, spring-security tries to parse and validate it against the authorization server.

@MrMedTech
Copy link
Author

You are right. The now I have another problem with Mockito but that should not be part of the discussione here.
The problem I have now is that if I leave the header field in the request I cannot document it with spring rest docs.
org.springframework.restdocs.snippet.SnippetException: Headers with the following names were not found in the request: [Authorization]

But as you said if I use the header, spring security tries to connect to keycloak.

@ch4mpy ch4mpy closed this as completed Sep 7, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants