|  | @@ -66,6 +66,8 @@ import org.springframework.security.oauth2.jwt.JwtClaimNames;
 | 
	
		
			
				|  |  |  import org.springframework.security.oauth2.jwt.JwtDecoder;
 | 
	
		
			
				|  |  |  import org.springframework.security.oauth2.jwt.NimbusJwtDecoderJwkSupport;
 | 
	
		
			
				|  |  |  import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
 | 
	
		
			
				|  |  | +import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
 | 
	
		
			
				|  |  | +import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
 | 
	
		
			
				|  |  |  import org.springframework.security.provisioning.InMemoryUserDetailsManager;
 | 
	
		
			
				|  |  |  import org.springframework.test.web.servlet.MockMvc;
 | 
	
		
			
				|  |  |  import org.springframework.test.web.servlet.MvcResult;
 | 
	
	
		
			
				|  | @@ -82,9 +84,11 @@ import org.springframework.web.context.support.GenericWebApplicationContext;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import static org.assertj.core.api.Assertions.assertThat;
 | 
	
		
			
				|  |  |  import static org.assertj.core.api.Assertions.assertThatCode;
 | 
	
		
			
				|  |  | +import static org.hamcrest.CoreMatchers.containsString;
 | 
	
		
			
				|  |  |  import static org.mockito.ArgumentMatchers.anyString;
 | 
	
		
			
				|  |  |  import static org.mockito.Mockito.mock;
 | 
	
		
			
				|  |  |  import static org.mockito.Mockito.when;
 | 
	
		
			
				|  |  | +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
 | 
	
		
			
				|  |  |  import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
 | 
	
		
			
				|  |  |  import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
 | 
	
		
			
				|  |  |  import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
 | 
	
	
		
			
				|  | @@ -526,6 +530,134 @@ public class OAuth2ResourceServerConfigurerTests {
 | 
	
		
			
				|  |  |  		assertThat(result.getRequest().getSession(false)).isNotNull();
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	// -- custom bearer token resolver
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	@Test
 | 
	
		
			
				|  |  | +	public void requestWhenBearerTokenResolverAllowsRequestBodyThenEitherHeaderOrRequestBodyIsAccepted()
 | 
	
		
			
				|  |  | +			throws Exception {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		this.spring.register(AllowBearerTokenInRequestBodyConfig.class, JwtDecoderConfig.class,
 | 
	
		
			
				|  |  | +				BasicController.class).autowire();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);
 | 
	
		
			
				|  |  | +		when(decoder.decode(anyString())).thenReturn(JWT);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		this.mvc.perform(get("/authenticated")
 | 
	
		
			
				|  |  | +				.with(bearerToken(JWT_TOKEN)))
 | 
	
		
			
				|  |  | +				.andExpect(status().isOk())
 | 
	
		
			
				|  |  | +				.andExpect(content().string(JWT_SUBJECT));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		this.mvc.perform(post("/authenticated")
 | 
	
		
			
				|  |  | +				.param("access_token", JWT_TOKEN))
 | 
	
		
			
				|  |  | +				.andExpect(status().isOk())
 | 
	
		
			
				|  |  | +				.andExpect(content().string(JWT_SUBJECT));
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	@Test
 | 
	
		
			
				|  |  | +	public void requestWhenBearerTokenResolverAllowsQueryParameterThenEitherHeaderOrQueryParameterIsAccepted()
 | 
	
		
			
				|  |  | +			throws Exception {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		this.spring.register(AllowBearerTokenAsQueryParameterConfig.class, JwtDecoderConfig.class,
 | 
	
		
			
				|  |  | +				BasicController.class).autowire();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);
 | 
	
		
			
				|  |  | +		when(decoder.decode(anyString())).thenReturn(JWT);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		this.mvc.perform(get("/authenticated")
 | 
	
		
			
				|  |  | +				.with(bearerToken(JWT_TOKEN)))
 | 
	
		
			
				|  |  | +				.andExpect(status().isOk())
 | 
	
		
			
				|  |  | +				.andExpect(content().string(JWT_SUBJECT));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		this.mvc.perform(get("/authenticated")
 | 
	
		
			
				|  |  | +				.param("access_token", JWT_TOKEN))
 | 
	
		
			
				|  |  | +				.andExpect(status().isOk())
 | 
	
		
			
				|  |  | +				.andExpect(content().string(JWT_SUBJECT));
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	@Test
 | 
	
		
			
				|  |  | +	public void requestWhenBearerTokenResolverAllowsRequestBodyAndRequestContainsTwoTokensThenInvalidRequest()
 | 
	
		
			
				|  |  | +			throws Exception {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		this.spring.register(AllowBearerTokenInRequestBodyConfig.class, JwtDecoderConfig.class,
 | 
	
		
			
				|  |  | +				BasicController.class).autowire();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);
 | 
	
		
			
				|  |  | +		when(decoder.decode(anyString())).thenReturn(JWT);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		this.mvc.perform(post("/authenticated")
 | 
	
		
			
				|  |  | +				.param("access_token", JWT_TOKEN)
 | 
	
		
			
				|  |  | +				.with(bearerToken(JWT_TOKEN))
 | 
	
		
			
				|  |  | +				.with(csrf()))
 | 
	
		
			
				|  |  | +				.andExpect(status().isBadRequest())
 | 
	
		
			
				|  |  | +				.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, containsString("invalid_request")));
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	@Test
 | 
	
		
			
				|  |  | +	public void requestWhenBearerTokenResolverAllowsQueryParameterAndRequestContainsTwoTokensThenInvalidRequest()
 | 
	
		
			
				|  |  | +			throws Exception {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		this.spring.register(AllowBearerTokenAsQueryParameterConfig.class, JwtDecoderConfig.class,
 | 
	
		
			
				|  |  | +				BasicController.class).autowire();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);
 | 
	
		
			
				|  |  | +		when(decoder.decode(anyString())).thenReturn(JWT);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		this.mvc.perform(get("/authenticated")
 | 
	
		
			
				|  |  | +				.with(bearerToken(JWT_TOKEN))
 | 
	
		
			
				|  |  | +				.param("access_token", JWT_TOKEN))
 | 
	
		
			
				|  |  | +				.andExpect(status().isBadRequest())
 | 
	
		
			
				|  |  | +				.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, containsString("invalid_request")));
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	@Test
 | 
	
		
			
				|  |  | +	public void getBearerTokenResolverWhenDuplicateResolverBeansAndAnotherOnTheDslThenTheDslOneIsUsed() {
 | 
	
		
			
				|  |  | +		BearerTokenResolver resolverBean = mock(BearerTokenResolver.class);
 | 
	
		
			
				|  |  | +		BearerTokenResolver resolver = mock(BearerTokenResolver.class);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		GenericWebApplicationContext context = new GenericWebApplicationContext();
 | 
	
		
			
				|  |  | +		context.registerBean("resolverOne", BearerTokenResolver.class, () -> resolverBean);
 | 
	
		
			
				|  |  | +		context.registerBean("resolverTwo", BearerTokenResolver.class, () -> resolverBean);
 | 
	
		
			
				|  |  | +		this.spring.context(context).autowire();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		oauth2.bearerTokenResolver(resolver);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		assertThat(oauth2.getBearerTokenResolver()).isEqualTo(resolver);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	@Test
 | 
	
		
			
				|  |  | +	public void getBearerTokenResolverWhenDuplicateResolverBeansThenWiringException() {
 | 
	
		
			
				|  |  | +		assertThatCode(() -> this.spring.register(MultipleBearerTokenResolverBeansConfig.class).autowire())
 | 
	
		
			
				|  |  | +				.isInstanceOf(BeanCreationException.class)
 | 
	
		
			
				|  |  | +				.hasRootCauseInstanceOf(NoUniqueBeanDefinitionException.class);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	@Test
 | 
	
		
			
				|  |  | +	public void getBearerTokenResolverWhenResolverBeanAndAnotherOnTheDslThenTheDslOneIsUsed() {
 | 
	
		
			
				|  |  | +		BearerTokenResolver resolver = mock(BearerTokenResolver.class);
 | 
	
		
			
				|  |  | +		BearerTokenResolver resolverBean = mock(BearerTokenResolver.class);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		GenericWebApplicationContext context = new GenericWebApplicationContext();
 | 
	
		
			
				|  |  | +		context.registerBean(BearerTokenResolver.class, () -> resolverBean);
 | 
	
		
			
				|  |  | +		this.spring.context(context).autowire();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
 | 
	
		
			
				|  |  | +		oauth2.bearerTokenResolver(resolver);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		assertThat(oauth2.getBearerTokenResolver()).isEqualTo(resolver);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	@Test
 | 
	
		
			
				|  |  | +	public void getBearerTokenResolverWhenNoResolverSpecifiedThenTheDefaultIsUsed() {
 | 
	
		
			
				|  |  | +		ApplicationContext context =
 | 
	
		
			
				|  |  | +				this.spring.context(new GenericWebApplicationContext()).getContext();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		assertThat(oauth2.getBearerTokenResolver()).isInstanceOf(DefaultBearerTokenResolver.class);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	// -- custom jwt decoder
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	@Test
 | 
	
	
		
			
				|  | @@ -563,8 +695,10 @@ public class OAuth2ResourceServerConfigurerTests {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	@Test
 | 
	
		
			
				|  |  |  	public void getJwtDecoderWhenConfiguredWithDecoderAndJwkSetUriThenLastOneWins() {
 | 
	
		
			
				|  |  | +		ApplicationContext context = mock(ApplicationContext.class);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  		OAuth2ResourceServerConfigurer.JwtConfigurer jwtConfigurer =
 | 
	
		
			
				|  |  | -				new OAuth2ResourceServerConfigurer().new JwtConfigurer(null);
 | 
	
		
			
				|  |  | +				new OAuth2ResourceServerConfigurer(context).jwt();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		JwtDecoder decoder = mock(JwtDecoder.class);
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -574,7 +708,7 @@ public class OAuth2ResourceServerConfigurerTests {
 | 
	
		
			
				|  |  |  		assertThat(jwtConfigurer.getJwtDecoder()).isEqualTo(decoder);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		jwtConfigurer =
 | 
	
		
			
				|  |  | -				new OAuth2ResourceServerConfigurer().new JwtConfigurer(null);
 | 
	
		
			
				|  |  | +				new OAuth2ResourceServerConfigurer(context).jwt();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		jwtConfigurer.decoder(decoder);
 | 
	
		
			
				|  |  |  		jwtConfigurer.jwkSetUri(JWK_SET_URI);
 | 
	
	
		
			
				|  | @@ -593,7 +727,7 @@ public class OAuth2ResourceServerConfigurerTests {
 | 
	
		
			
				|  |  |  		when(context.getBean(JwtDecoder.class)).thenReturn(decoderBean);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		OAuth2ResourceServerConfigurer.JwtConfigurer jwtConfigurer =
 | 
	
		
			
				|  |  | -				new OAuth2ResourceServerConfigurer().new JwtConfigurer(context);
 | 
	
		
			
				|  |  | +				new OAuth2ResourceServerConfigurer(context).jwt();
 | 
	
		
			
				|  |  |  		jwtConfigurer.decoder(decoder);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		assertThat(jwtConfigurer.getJwtDecoder()).isEqualTo(decoder);
 | 
	
	
		
			
				|  | @@ -607,7 +741,7 @@ public class OAuth2ResourceServerConfigurerTests {
 | 
	
		
			
				|  |  |  		when(context.getBean(JwtDecoder.class)).thenReturn(decoder);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		OAuth2ResourceServerConfigurer.JwtConfigurer jwtConfigurer =
 | 
	
		
			
				|  |  | -				new OAuth2ResourceServerConfigurer().new JwtConfigurer(context);
 | 
	
		
			
				|  |  | +				new OAuth2ResourceServerConfigurer(context).jwt();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		jwtConfigurer.jwkSetUri(JWK_SET_URI);
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -627,7 +761,7 @@ public class OAuth2ResourceServerConfigurerTests {
 | 
	
		
			
				|  |  |  		this.spring.context(context).autowire();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		OAuth2ResourceServerConfigurer.JwtConfigurer jwtConfigurer =
 | 
	
		
			
				|  |  | -				new OAuth2ResourceServerConfigurer().new JwtConfigurer(context);
 | 
	
		
			
				|  |  | +				new OAuth2ResourceServerConfigurer(context).jwt();
 | 
	
		
			
				|  |  |  		jwtConfigurer.decoder(decoder);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		assertThat(jwtConfigurer.getJwtDecoder()).isEqualTo(decoder);
 | 
	
	
		
			
				|  | @@ -644,7 +778,7 @@ public class OAuth2ResourceServerConfigurerTests {
 | 
	
		
			
				|  |  |  		this.spring.context(context).autowire();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		OAuth2ResourceServerConfigurer.JwtConfigurer jwtConfigurer =
 | 
	
		
			
				|  |  | -				new OAuth2ResourceServerConfigurer().new JwtConfigurer(context);
 | 
	
		
			
				|  |  | +				new OAuth2ResourceServerConfigurer(context).jwt();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		assertThatCode(() -> jwtConfigurer.getJwtDecoder())
 | 
	
		
			
				|  |  |  				.isInstanceOf(NoUniqueBeanDefinitionException.class);
 | 
	
	
		
			
				|  | @@ -833,6 +967,82 @@ public class OAuth2ResourceServerConfigurerTests {
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	@EnableWebSecurity
 | 
	
		
			
				|  |  | +	static class AllowBearerTokenInRequestBodyConfig extends WebSecurityConfigurerAdapter {
 | 
	
		
			
				|  |  | +		@Override
 | 
	
		
			
				|  |  | +		protected void configure(HttpSecurity http) throws Exception {
 | 
	
		
			
				|  |  | +			// @formatter:off
 | 
	
		
			
				|  |  | +			http
 | 
	
		
			
				|  |  | +				.authorizeRequests()
 | 
	
		
			
				|  |  | +					.anyRequest().authenticated()
 | 
	
		
			
				|  |  | +					.and()
 | 
	
		
			
				|  |  | +				.oauth2()
 | 
	
		
			
				|  |  | +					.resourceServer()
 | 
	
		
			
				|  |  | +						.bearerTokenResolver(allowRequestBody())
 | 
	
		
			
				|  |  | +						.jwt();
 | 
	
		
			
				|  |  | +			// @formatter:on
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		private BearerTokenResolver allowRequestBody() {
 | 
	
		
			
				|  |  | +			DefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();
 | 
	
		
			
				|  |  | +			resolver.setAllowFormEncodedBodyParameter(true);
 | 
	
		
			
				|  |  | +			return resolver;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	@EnableWebSecurity
 | 
	
		
			
				|  |  | +	static class AllowBearerTokenAsQueryParameterConfig extends WebSecurityConfigurerAdapter {
 | 
	
		
			
				|  |  | +		@Override
 | 
	
		
			
				|  |  | +		protected void configure(HttpSecurity http) throws Exception {
 | 
	
		
			
				|  |  | +			// @formatter:off
 | 
	
		
			
				|  |  | +			http
 | 
	
		
			
				|  |  | +				.authorizeRequests()
 | 
	
		
			
				|  |  | +					.anyRequest().authenticated()
 | 
	
		
			
				|  |  | +					.and()
 | 
	
		
			
				|  |  | +				.oauth2()
 | 
	
		
			
				|  |  | +					.resourceServer()
 | 
	
		
			
				|  |  | +						.jwt();
 | 
	
		
			
				|  |  | +			// @formatter:on
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		@Bean
 | 
	
		
			
				|  |  | +		BearerTokenResolver allowQueryParameter() {
 | 
	
		
			
				|  |  | +			DefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();
 | 
	
		
			
				|  |  | +			resolver.setAllowUriQueryParameter(true);
 | 
	
		
			
				|  |  | +			return resolver;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	@EnableWebSecurity
 | 
	
		
			
				|  |  | +	static class MultipleBearerTokenResolverBeansConfig extends WebSecurityConfigurerAdapter {
 | 
	
		
			
				|  |  | +		@Override
 | 
	
		
			
				|  |  | +		protected void configure(HttpSecurity http) throws Exception {
 | 
	
		
			
				|  |  | +			// @formatter:off
 | 
	
		
			
				|  |  | +			http
 | 
	
		
			
				|  |  | +				.authorizeRequests()
 | 
	
		
			
				|  |  | +					.anyRequest().authenticated()
 | 
	
		
			
				|  |  | +					.and()
 | 
	
		
			
				|  |  | +				.oauth2()
 | 
	
		
			
				|  |  | +					.resourceServer()
 | 
	
		
			
				|  |  | +						.jwt();
 | 
	
		
			
				|  |  | +			// @formatter:on
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		@Bean
 | 
	
		
			
				|  |  | +		BearerTokenResolver resolverOne() {
 | 
	
		
			
				|  |  | +			DefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();
 | 
	
		
			
				|  |  | +			resolver.setAllowUriQueryParameter(true);
 | 
	
		
			
				|  |  | +			return resolver;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		@Bean
 | 
	
		
			
				|  |  | +		BearerTokenResolver resolverTwo() {
 | 
	
		
			
				|  |  | +			DefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();
 | 
	
		
			
				|  |  | +			resolver.setAllowFormEncodedBodyParameter(true);
 | 
	
		
			
				|  |  | +			return resolver;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	@EnableWebSecurity
 | 
	
		
			
				|  |  |  	static class CustomJwtDecoderOnDsl extends WebSecurityConfigurerAdapter {
 | 
	
		
			
				|  |  |  		JwtDecoder decoder = mock(JwtDecoder.class);
 | 
	
	
		
			
				|  | @@ -877,6 +1087,14 @@ public class OAuth2ResourceServerConfigurerTests {
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	@Configuration
 | 
	
		
			
				|  |  | +	static class JwtDecoderConfig {
 | 
	
		
			
				|  |  | +		@Bean
 | 
	
		
			
				|  |  | +		public JwtDecoder jwtDecoder() {
 | 
	
		
			
				|  |  | +			return mock(JwtDecoder.class);
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	@RestController
 | 
	
		
			
				|  |  |  	static class BasicController {
 | 
	
		
			
				|  |  |  		@GetMapping("/")
 |