Resource Server AuthenticationManager

Making the authentication manager for jwt() and opaqueToken()
configurable.

Fixes: gh-6832
Fixes: gh-6849
This commit is contained in:
Josh Cummings
2019-05-07 17:04:51 -06:00
parent 7200fa2dce
commit 1ed9e3a1c6
2 changed files with 181 additions and 31 deletions

View File

@@ -65,7 +65,10 @@ import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@@ -79,8 +82,6 @@ import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
import org.springframework.security.oauth2.server.resource.introspection.NimbusOAuth2TokenIntrospectionClient;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2TokenIntrospectionClient;
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtClaimNames;
@@ -90,6 +91,9 @@ import org.springframework.security.oauth2.jwt.JwtTimestampValidator;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.OAuth2IntrospectionAuthenticationToken;
import org.springframework.security.oauth2.server.resource.introspection.NimbusOAuth2TokenIntrospectionClient;
import org.springframework.security.oauth2.server.resource.introspection.OAuth2TokenIntrospectionClient;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
@@ -120,8 +124,10 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.security.oauth2.core.TestOAuth2AccessTokens.noScopes;
import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withJwkSetUri;
import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withPublicKey;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
@@ -153,6 +159,8 @@ public class OAuth2ResourceServerConfigurerTests {
private static final String INTROSPECTION_URI = "https://idp.example.com";
private static final String CLIENT_ID = "client-id";
private static final String CLIENT_SECRET = "client-secret";
private static final OAuth2IntrospectionAuthenticationToken INTROSPECTION_AUTHENTICATION_TOKEN =
new OAuth2IntrospectionAuthenticationToken(noScopes(), JWT_CLAIMS, Collections.emptyList());
@Autowired(required = false)
MockMvc mvc;
@@ -1015,6 +1023,20 @@ public class OAuth2ResourceServerConfigurerTests {
.andExpect(invalidTokenHeader("algorithm"));
}
@Test
public void getWhenCustomJwtAuthenticationManagerThenUsed() throws Exception {
this.spring.register(JwtAuthenticationManagerConfig.class, BasicController.class).autowire();
when(bean(AuthenticationProvider.class).authenticate(any(Authentication.class)))
.thenReturn(JWT_AUTHENTICATION_TOKEN);
this.mvc.perform(get("/authenticated")
.with(bearerToken("token")))
.andExpect(status().isOk())
.andExpect(content().string("mock-test-subject"));
verifyBean(AuthenticationProvider.class).authenticate(any(Authentication.class));
}
// -- opaque
@@ -1052,6 +1074,20 @@ public class OAuth2ResourceServerConfigurerTests {
.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, containsString("scope")));
}
@Test
public void getWhenCustomIntrospectionAuthenticationManagerThenUsed() throws Exception {
this.spring.register(OpaqueTokenAuthenticationManagerConfig.class, BasicController.class).autowire();
when(bean(AuthenticationProvider.class).authenticate(any(Authentication.class)))
.thenReturn(INTROSPECTION_AUTHENTICATION_TOKEN);
this.mvc.perform(get("/authenticated")
.with(bearerToken("token")))
.andExpect(status().isOk())
.andExpect(content().string("mock-test-subject"));
verifyBean(AuthenticationProvider.class).authenticate(any(Authentication.class));
}
@Test
public void configureWhenOnlyIntrospectionUrlThenException() throws Exception {
assertThatCode(() -> this.spring.register(OpaqueTokenHalfConfiguredConfig.class).autowire())
@@ -1191,6 +1227,30 @@ public class OAuth2ResourceServerConfigurerTests {
.andExpect(content().string("basic-user"));
}
// -- authentication manager
@Test
public void getAuthenticationManagerWhenConfiguredAuthenticationManagerThenTakesPrecedence() {
ApplicationContext context = mock(ApplicationContext.class);
HttpSecurityBuilder http = mock(HttpSecurityBuilder.class);
OAuth2ResourceServerConfigurer oauth2ResourceServer = new OAuth2ResourceServerConfigurer(context);
AuthenticationManager authenticationManager = mock(AuthenticationManager.class);
oauth2ResourceServer
.jwt()
.authenticationManager(authenticationManager)
.decoder(mock(JwtDecoder.class));
assertThat(oauth2ResourceServer.getAuthenticationManager(http)).isSameAs(authenticationManager);
oauth2ResourceServer = new OAuth2ResourceServerConfigurer(context);
oauth2ResourceServer
.opaqueToken()
.authenticationManager(authenticationManager)
.introspectionClient(mock(OAuth2TokenIntrospectionClient.class));
assertThat(oauth2ResourceServer.getAuthenticationManager(http)).isSameAs(authenticationManager);
verify(http, never()).authenticationProvider(any(AuthenticationProvider.class));
}
// -- Incorrect Configuration
@Test
@@ -1622,6 +1682,27 @@ public class OAuth2ResourceServerConfigurerTests {
}
}
@EnableWebSecurity
static class JwtAuthenticationManagerConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2ResourceServer()
.jwt()
.authenticationManager(authenticationProvider()::authenticate);
// @formatter:on
}
@Bean
public AuthenticationProvider authenticationProvider() {
return mock(AuthenticationProvider.class);
}
}
@EnableWebSecurity
static class CustomJwtValidatorConfig extends WebSecurityConfigurerAdapter {
@Autowired
@@ -1735,6 +1816,27 @@ public class OAuth2ResourceServerConfigurerTests {
}
}
@EnableWebSecurity
static class OpaqueTokenAuthenticationManagerConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2ResourceServer()
.opaqueToken()
.authenticationManager(authenticationProvider()::authenticate);
// @formatter:on
}
@Bean
public AuthenticationProvider authenticationProvider() {
return mock(AuthenticationProvider.class);
}
}
@EnableWebSecurity
static class OpaqueAndJwtConfig extends WebSecurityConfigurerAdapter {
@Override
@@ -1954,6 +2056,14 @@ public class OAuth2ResourceServerConfigurerTests {
.thenReturn(entity);
}
private <T> T bean(Class<T> beanClass) {
return (T) this.spring.getContext().getBean(beanClass);
}
private <T> T verifyBean(Class<T> beanClass) {
return (T) verify(this.spring.getContext().getBean(beanClass));
}
private String json(String name) throws IOException {
return resource(name + ".json");
}