|
65 | 65 | import org.springframework.mock.web.MockHttpServletRequest;
|
66 | 66 | import org.springframework.security.access.prepost.PreAuthorize;
|
67 | 67 | import org.springframework.security.authentication.AbstractAuthenticationToken;
|
| 68 | +import org.springframework.security.authentication.AuthenticationManager; |
| 69 | +import org.springframework.security.authentication.AuthenticationProvider; |
68 | 70 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
| 71 | +import org.springframework.security.config.annotation.web.HttpSecurityBuilder; |
69 | 72 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
70 | 73 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
71 | 74 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
|
79 | 82 | import org.springframework.security.oauth2.core.OAuth2Error;
|
80 | 83 | import org.springframework.security.oauth2.core.OAuth2TokenValidator;
|
81 | 84 | import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
|
82 |
| -import org.springframework.security.oauth2.server.resource.introspection.NimbusOAuth2TokenIntrospectionClient; |
83 |
| -import org.springframework.security.oauth2.server.resource.introspection.OAuth2TokenIntrospectionClient; |
84 | 85 | import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
|
85 | 86 | import org.springframework.security.oauth2.jwt.Jwt;
|
86 | 87 | import org.springframework.security.oauth2.jwt.JwtClaimNames;
|
|
90 | 91 | import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
91 | 92 | import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
|
92 | 93 | import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
| 94 | +import org.springframework.security.oauth2.server.resource.authentication.OAuth2IntrospectionAuthenticationToken; |
| 95 | +import org.springframework.security.oauth2.server.resource.introspection.NimbusOAuth2TokenIntrospectionClient; |
| 96 | +import org.springframework.security.oauth2.server.resource.introspection.OAuth2TokenIntrospectionClient; |
93 | 97 | import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
|
94 | 98 | import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
95 | 99 | import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
|
|
120 | 124 | import static org.mockito.ArgumentMatchers.anyString;
|
121 | 125 | import static org.mockito.ArgumentMatchers.eq;
|
122 | 126 | import static org.mockito.Mockito.mock;
|
| 127 | +import static org.mockito.Mockito.never; |
123 | 128 | import static org.mockito.Mockito.verify;
|
124 | 129 | import static org.mockito.Mockito.when;
|
| 130 | +import static org.springframework.security.oauth2.core.TestOAuth2AccessTokens.noScopes; |
125 | 131 | import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withJwkSetUri;
|
126 | 132 | import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withPublicKey;
|
127 | 133 | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
@@ -153,6 +159,8 @@ public class OAuth2ResourceServerConfigurerTests {
|
153 | 159 | private static final String INTROSPECTION_URI = "https://idp.example.com";
|
154 | 160 | private static final String CLIENT_ID = "client-id";
|
155 | 161 | private static final String CLIENT_SECRET = "client-secret";
|
| 162 | + private static final OAuth2IntrospectionAuthenticationToken INTROSPECTION_AUTHENTICATION_TOKEN = |
| 163 | + new OAuth2IntrospectionAuthenticationToken(noScopes(), JWT_CLAIMS, Collections.emptyList()); |
156 | 164 |
|
157 | 165 | @Autowired(required = false)
|
158 | 166 | MockMvc mvc;
|
@@ -1015,6 +1023,20 @@ public void requestWhenUsingPublicKeyAlgorithmDoesNotMatchThenReturnsInvalidToke
|
1015 | 1023 | .andExpect(invalidTokenHeader("algorithm"));
|
1016 | 1024 | }
|
1017 | 1025 |
|
| 1026 | + @Test |
| 1027 | + public void getWhenCustomJwtAuthenticationManagerThenUsed() throws Exception { |
| 1028 | + this.spring.register(JwtAuthenticationManagerConfig.class, BasicController.class).autowire(); |
| 1029 | + |
| 1030 | + when(bean(AuthenticationProvider.class).authenticate(any(Authentication.class))) |
| 1031 | + .thenReturn(JWT_AUTHENTICATION_TOKEN); |
| 1032 | + this.mvc.perform(get("/authenticated") |
| 1033 | + .with(bearerToken("token"))) |
| 1034 | + .andExpect(status().isOk()) |
| 1035 | + .andExpect(content().string("mock-test-subject")); |
| 1036 | + |
| 1037 | + verifyBean(AuthenticationProvider.class).authenticate(any(Authentication.class)); |
| 1038 | + } |
| 1039 | + |
1018 | 1040 | // -- opaque
|
1019 | 1041 |
|
1020 | 1042 |
|
@@ -1052,6 +1074,20 @@ public void getWhenIntrospectionLacksScopeThenForbidden() throws Exception {
|
1052 | 1074 | .andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, containsString("scope")));
|
1053 | 1075 | }
|
1054 | 1076 |
|
| 1077 | + @Test |
| 1078 | + public void getWhenCustomIntrospectionAuthenticationManagerThenUsed() throws Exception { |
| 1079 | + this.spring.register(OpaqueTokenAuthenticationManagerConfig.class, BasicController.class).autowire(); |
| 1080 | + |
| 1081 | + when(bean(AuthenticationProvider.class).authenticate(any(Authentication.class))) |
| 1082 | + .thenReturn(INTROSPECTION_AUTHENTICATION_TOKEN); |
| 1083 | + this.mvc.perform(get("/authenticated") |
| 1084 | + .with(bearerToken("token"))) |
| 1085 | + .andExpect(status().isOk()) |
| 1086 | + .andExpect(content().string("mock-test-subject")); |
| 1087 | + |
| 1088 | + verifyBean(AuthenticationProvider.class).authenticate(any(Authentication.class)); |
| 1089 | + } |
| 1090 | + |
1055 | 1091 | @Test
|
1056 | 1092 | public void configureWhenOnlyIntrospectionUrlThenException() throws Exception {
|
1057 | 1093 | assertThatCode(() -> this.spring.register(OpaqueTokenHalfConfiguredConfig.class).autowire())
|
@@ -1191,6 +1227,30 @@ public void getWhenAlsoUsingHttpBasicThenCorrectProviderEngages()
|
1191 | 1227 | .andExpect(content().string("basic-user"));
|
1192 | 1228 | }
|
1193 | 1229 |
|
| 1230 | + // -- authentication manager |
| 1231 | + |
| 1232 | + @Test |
| 1233 | + public void getAuthenticationManagerWhenConfiguredAuthenticationManagerThenTakesPrecedence() { |
| 1234 | + ApplicationContext context = mock(ApplicationContext.class); |
| 1235 | + HttpSecurityBuilder http = mock(HttpSecurityBuilder.class); |
| 1236 | + |
| 1237 | + OAuth2ResourceServerConfigurer oauth2ResourceServer = new OAuth2ResourceServerConfigurer(context); |
| 1238 | + AuthenticationManager authenticationManager = mock(AuthenticationManager.class); |
| 1239 | + oauth2ResourceServer |
| 1240 | + .jwt() |
| 1241 | + .authenticationManager(authenticationManager) |
| 1242 | + .decoder(mock(JwtDecoder.class)); |
| 1243 | + assertThat(oauth2ResourceServer.getAuthenticationManager(http)).isSameAs(authenticationManager); |
| 1244 | + |
| 1245 | + oauth2ResourceServer = new OAuth2ResourceServerConfigurer(context); |
| 1246 | + oauth2ResourceServer |
| 1247 | + .opaqueToken() |
| 1248 | + .authenticationManager(authenticationManager) |
| 1249 | + .introspectionClient(mock(OAuth2TokenIntrospectionClient.class)); |
| 1250 | + assertThat(oauth2ResourceServer.getAuthenticationManager(http)).isSameAs(authenticationManager); |
| 1251 | + verify(http, never()).authenticationProvider(any(AuthenticationProvider.class)); |
| 1252 | + } |
| 1253 | + |
1194 | 1254 | // -- Incorrect Configuration
|
1195 | 1255 |
|
1196 | 1256 | @Test
|
@@ -1622,6 +1682,27 @@ public JwtDecoder decoder() {
|
1622 | 1682 | }
|
1623 | 1683 | }
|
1624 | 1684 |
|
| 1685 | + @EnableWebSecurity |
| 1686 | + static class JwtAuthenticationManagerConfig extends WebSecurityConfigurerAdapter { |
| 1687 | + @Override |
| 1688 | + protected void configure(HttpSecurity http) throws Exception { |
| 1689 | + // @formatter:off |
| 1690 | + http |
| 1691 | + .authorizeRequests() |
| 1692 | + .anyRequest().authenticated() |
| 1693 | + .and() |
| 1694 | + .oauth2ResourceServer() |
| 1695 | + .jwt() |
| 1696 | + .authenticationManager(authenticationProvider()::authenticate); |
| 1697 | + // @formatter:on |
| 1698 | + } |
| 1699 | + |
| 1700 | + @Bean |
| 1701 | + public AuthenticationProvider authenticationProvider() { |
| 1702 | + return mock(AuthenticationProvider.class); |
| 1703 | + } |
| 1704 | + } |
| 1705 | + |
1625 | 1706 | @EnableWebSecurity
|
1626 | 1707 | static class CustomJwtValidatorConfig extends WebSecurityConfigurerAdapter {
|
1627 | 1708 | @Autowired
|
@@ -1735,6 +1816,27 @@ protected void configure(HttpSecurity http) throws Exception {
|
1735 | 1816 | }
|
1736 | 1817 | }
|
1737 | 1818 |
|
| 1819 | + @EnableWebSecurity |
| 1820 | + static class OpaqueTokenAuthenticationManagerConfig extends WebSecurityConfigurerAdapter { |
| 1821 | + @Override |
| 1822 | + protected void configure(HttpSecurity http) throws Exception { |
| 1823 | + // @formatter:off |
| 1824 | + http |
| 1825 | + .authorizeRequests() |
| 1826 | + .anyRequest().authenticated() |
| 1827 | + .and() |
| 1828 | + .oauth2ResourceServer() |
| 1829 | + .opaqueToken() |
| 1830 | + .authenticationManager(authenticationProvider()::authenticate); |
| 1831 | + // @formatter:on |
| 1832 | + } |
| 1833 | + |
| 1834 | + @Bean |
| 1835 | + public AuthenticationProvider authenticationProvider() { |
| 1836 | + return mock(AuthenticationProvider.class); |
| 1837 | + } |
| 1838 | + } |
| 1839 | + |
1738 | 1840 | @EnableWebSecurity
|
1739 | 1841 | static class OpaqueAndJwtConfig extends WebSecurityConfigurerAdapter {
|
1740 | 1842 | @Override
|
@@ -1954,6 +2056,14 @@ private void mockRestOperations(String response) {
|
1954 | 2056 | .thenReturn(entity);
|
1955 | 2057 | }
|
1956 | 2058 |
|
| 2059 | + private <T> T bean(Class<T> beanClass) { |
| 2060 | + return (T) this.spring.getContext().getBean(beanClass); |
| 2061 | + } |
| 2062 | + |
| 2063 | + private <T> T verifyBean(Class<T> beanClass) { |
| 2064 | + return (T) verify(this.spring.getContext().getBean(beanClass)); |
| 2065 | + } |
| 2066 | + |
1957 | 2067 | private String json(String name) throws IOException {
|
1958 | 2068 | return resource(name + ".json");
|
1959 | 2069 | }
|
|
0 commit comments