Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test spring-security-oauth2 resource server security?

Following the release of Spring Security 4 and it's improved support for testing I've wanted to update my current Spring security oauth2 resource server tests.

At present I have a helper class that sets up a OAuth2RestTemplate using ResourceOwnerPasswordResourceDetails with a test ClientId connecting to an actual AccessTokenUri to requests a valid token for my tests. This resttemplate is then used to make requests in my @WebIntegrationTests.

I'd like to drop the dependency on the actual AuthorizationServer, and the use of valid (if limited) user credentials in my tests, by taking advantage of the new testing support in Spring Security 4.

Up to now all my attempts at using @WithMockUser, @WithSecurityContext, SecurityMockMvcConfigurers.springSecurity() & SecurityMockMvcRequestPostProcessors.* have failed to make authenticated calls through MockMvc, and I can not find any such working examples in the Spring example projects.

Can anyone help me test my oauth2 resource server with some kind of mocked credentials, while still testing the security restrictions imposed?

** EDIT ** Sample code available here: https://github.com/timtebeek/resource-server-testing For each of the test classes I understand why it won't work as it, but I'm looking for ways that would allow me to test the security setup easily.

I'm now thinking of creating a very permissive OAuthServer under src/test/java, which might help a bit. Does anyone have any other suggestions?

like image 658
Tim Avatar asked Apr 08 '15 09:04

Tim


People also ask

How do you validate a token in a resource server?

A resource server validates such a token by making a call to the authorisation server's introspection endpoint. The token encodes the entire authorisation in itself and is cryptographically protected against tampering. JSON Web Token (JWT) has become the defacto standard for self-contained tokens.

What is resource server in OAuth2?

What Is a Resource Server? In the context of OAuth 2.0, a resource server is an application that protects resources via OAuth tokens. These tokens are issued by an authorization server, typically to a client application. The job of the resource server is to validate the token before serving a resource to the client.

What is Spring Security resource server?

Updated on 17 June, 2022 in Spring Security. Resource Server in OAuth2 is used to protect access to resources, APIs. It will validate the access token passed by the Client Application, with the Authorization Server to decide if the Client Application has access to the resources and APIs it wants.


2 Answers

I found a much easier way to do this following directions I read here: http://docs.spring.io/spring-security/site/docs/4.0.x/reference/htmlsingle/#test-method-withsecuritycontext. This solution is specific to testing @PreAuthorize with #oauth2.hasScope but I'm sure it could be adapted for other situations as well.

I create an annotation which can be applied to @Tests:

WithMockOAuth2Scope

import org.springframework.security.test.context.support.WithSecurityContext;  import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy;  @Retention(RetentionPolicy.RUNTIME) @WithSecurityContext(factory = WithMockOAuth2ScopeSecurityContextFactory.class) public @interface WithMockOAuth2Scope {      String scope() default ""; } 

WithMockOAuth2ScopeSecurityContextFactory

import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.security.test.context.support.WithSecurityContextFactory;  import java.util.HashSet; import java.util.Set;  public class WithMockOAuth2ScopeSecurityContextFactory implements WithSecurityContextFactory<WithMockOAuth2Scope> {      @Override     public SecurityContext createSecurityContext(WithMockOAuth2Scope mockOAuth2Scope) {         SecurityContext context = SecurityContextHolder.createEmptyContext();          Set<String> scope = new HashSet<>();         scope.add(mockOAuth2Scope.scope());          OAuth2Request request = new OAuth2Request(null, null, null, true, scope, null, null, null, null);          Authentication auth = new OAuth2Authentication(request, null);          context.setAuthentication(auth);          return context;     } } 

Example test using MockMvc:

@RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest public class LoadScheduleControllerTest {      private MockMvc mockMvc;      @Autowired     LoadScheduleController loadScheduleController;      @Before     public void setup() {         mockMvc = MockMvcBuilders.standaloneSetup(loadScheduleController)                     .build();     }      @Test     @WithMockOAuth2Scope(scope = "dataLicense")     public void testSchedule() throws Exception {         mockMvc.perform(post("/schedule").contentType(MediaType.APPLICATION_JSON_UTF8).content(json)).andDo(print());     } } 

And this is the controller under test:

@RequestMapping(value = "/schedule", method = RequestMethod.POST) @PreAuthorize("#oauth2.hasScope('dataLicense')") public int schedule() {     return 0; } 
like image 32
mclaassen Avatar answered Sep 23 '22 09:09

mclaassen


To test resource server security effectively, both with MockMvc and a RestTemplate it helps to configure an AuthorizationServer under src/test/java:

AuthorizationServer

@Configuration @EnableAuthorizationServer @SuppressWarnings("static-method") class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {     @Bean     public JwtAccessTokenConverter accessTokenConverter() throws Exception {         JwtAccessTokenConverter jwt = new JwtAccessTokenConverter();         jwt.setSigningKey(SecurityConfig.key("rsa"));         jwt.setVerifierKey(SecurityConfig.key("rsa.pub"));         jwt.afterPropertiesSet();         return jwt;     }      @Autowired     private AuthenticationManager   authenticationManager;      @Override     public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {         endpoints         .authenticationManager(authenticationManager)         .accessTokenConverter(accessTokenConverter());     }      @Override     public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {         clients.inMemory()         .withClient("myclientwith")         .authorizedGrantTypes("password")         .authorities("myauthorities")         .resourceIds("myresource")         .scopes("myscope")          .and()         .withClient("myclientwithout")         .authorizedGrantTypes("password")         .authorities("myauthorities")         .resourceIds("myresource")         .scopes(UUID.randomUUID().toString());     } } 

Integration test
For integration tests one can then simply use built in OAuth2 test support rule and annotions:

@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = MyApp.class) @WebIntegrationTest(randomPort = true) @OAuth2ContextConfiguration(MyDetails.class) public class MyControllerIT implements RestTemplateHolder {     @Value("http://localhost:${local.server.port}")     @Getter     String                      host;      @Getter     @Setter     RestOperations              restTemplate    = new TestRestTemplate();      @Rule     public OAuth2ContextSetup   context         = OAuth2ContextSetup.standard(this);      @Test     public void testHelloOAuth2WithRole() {         ResponseEntity<String> entity = getRestTemplate().getForEntity(host + "/hello", String.class);         assertTrue(entity.getStatusCode().is2xxSuccessful());     } }  class MyDetails extends ResourceOwnerPasswordResourceDetails {     public MyDetails(final Object obj) {         MyControllerIT it = (MyControllerIT) obj;         setAccessTokenUri(it.getHost() + "/oauth/token");         setClientId("myclientwith");         setUsername("user");         setPassword("password");     } } 

MockMvc test
Testing with MockMvc is also possible, but needs a little helper class to get a RequestPostProcessor that sets the Authorization: Bearer <token> header on requests:

@Component public class OAuthHelper {     // For use with MockMvc     public RequestPostProcessor bearerToken(final String clientid) {         return mockRequest -> {             OAuth2AccessToken token = createAccessToken(clientid);             mockRequest.addHeader("Authorization", "Bearer " + token.getValue());             return mockRequest;         };     }      @Autowired     ClientDetailsService                clientDetailsService;     @Autowired     AuthorizationServerTokenServices    tokenservice;      OAuth2AccessToken createAccessToken(final String clientId) {         // Look up authorities, resourceIds and scopes based on clientId         ClientDetails client = clientDetailsService.loadClientByClientId(clientId);         Collection<GrantedAuthority> authorities = client.getAuthorities();         Set<String> resourceIds = client.getResourceIds();         Set<String> scopes = client.getScope();          // Default values for other parameters         Map<String, String> requestParameters = Collections.emptyMap();         boolean approved = true;         String redirectUrl = null;         Set<String> responseTypes = Collections.emptySet();         Map<String, Serializable> extensionProperties = Collections.emptyMap();          // Create request         OAuth2Request oAuth2Request = new OAuth2Request(requestParameters, clientId, authorities, approved, scopes,                 resourceIds, redirectUrl, responseTypes, extensionProperties);          // Create OAuth2AccessToken         User userPrincipal = new User("user", "", true, true, true, true, authorities);         UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userPrincipal, null, authorities);         OAuth2Authentication auth = new OAuth2Authentication(oAuth2Request, authenticationToken);         return tokenservice.createAccessToken(auth);     } } 

Your MockMvc tests must then get a RequestPostProcessor from the OauthHelper class and pass it when making requests:

@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = MyApp.class) @WebAppConfiguration public class MyControllerTest {     @Autowired     private WebApplicationContext   webapp;      private MockMvc                 mvc;      @Before     public void before() {         mvc = MockMvcBuilders.webAppContextSetup(webapp)                 .apply(springSecurity())                 .alwaysDo(print())                 .build();     }      @Autowired     private OAuthHelper helper;      @Test     public void testHelloWithRole() throws Exception {         RequestPostProcessor bearerToken = helper.bearerToken("myclientwith");         mvc.perform(get("/hello").with(bearerToken)).andExpect(status().isOk());     }      @Test     public void testHelloWithoutRole() throws Exception {         RequestPostProcessor bearerToken = helper.bearerToken("myclientwithout");         mvc.perform(get("/hello").with(bearerToken)).andExpect(status().isForbidden());     } } 

A full sample project is available on GitHub:
https://github.com/timtebeek/resource-server-testing

like image 140
Tim Avatar answered Sep 19 '22 09:09

Tim