7

I have followed the following links to try and test OAuth2 @PreAuthorise(hasAnyRole('ADMIN', 'TEST') for example but I can't any of the tests to pass or even authenticate.

When I try to access the end point with admin (or any role) it will never authenticate properly. Am I missing something obvious, it seems I have everything just as it is in the examples. I have also tried another alternative to the WithSecurityContext Factory with OAuth Specific Authentication and still no luck. Any help would be appreciated.

https://stackoverflow.com/a/31679649/2594130 and http://docs.spring.io/spring-security/site/docs/4.0.x/reference/htmlsingle/#test

My Controller I'm testing

@RestController
@RequestMapping("/bookmark/")
public class GroupBookmarkController {

    @Autowired
    BookmarkService bookmarkService;

    /**
    * Get list of all bookmarks
    */
    @RequestMapping(value = "{groupId}", method = RequestMethod.GET)
    @PreAuthorize("hasAnyRole(['ADMIN', 'USER'])")
    public ResponseEntity<List<Bookmark>> listAllGroupBookmarks(@PathVariable("groupId") String groupId) throws BookmarkNotFoundException {
        List<Bookmark> bookmarks = bookmarkService.findAllBookmarksByGroupId(groupId);
        return new ResponseEntity<>(bookmarks, HttpStatus.OK);
    }
    ...
}

My Test class

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = BookmarkServiceApplication.class)
@WebAppConfiguration
public class BookmarkServiceApplicationTests {

    private MockMvc mockMvc;

    @Autowired
    private WebApplicationContext webApplicationContext;

    @Before
    public void loadData() {
        this.mockMvc = MockMvcBuilders
                .webAppContextSetup(webApplicationContext)
                .apply(springSecurity())
                .alwaysDo(print())
                .build();
    }

    @Test
    @WithMockCustomUser(username = "test")
    public void getBookmarkAuthorised() throws Exception {
        mockMvc.perform(get("/bookmark/nvjdbngkjlsdfngkjlfdsnlkgsd"))
                .andExpect(status().is(HttpStatus.SC_OK));
        // always 401 here
    }
}

My BookmarkServiceApplication

@SpringBootApplication
@EnableResourceServer
public class BookmarkServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(BookmarkServiceApplication.class, args);
    }
}

My WithSecurityContextFactory

public class WithMockCustomUserSecurityContextFactory implements WithSecurityContextFactory<WithMockCustomUser> {
    @Override
    public SecurityContext createSecurityContext(WithMockCustomUser customUser) {
        SecurityContext context = SecurityContextHolder.createEmptyContext();

        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));

        UserDetails principal = new User(customUser.username(), "password", true, true, true, true, grantedAuthorities);


        Authentication authentication = new UsernamePasswordAuthenticationToken(
                principal, principal.getPassword(), principal.getAuthorities());
        context.setAuthentication(authentication);

        return context;
    }
}

My WithSecurityContext Annotation

@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithMockCustomUserSecurityContextFactory.class)
public @interface WithMockCustomUser {

    String username() default "user";

    String name() default "Test User";
}

As per @RobWinch 's reply

Hi @RobWinch I've tried you suggestion with the stateless flag, this helped with part of the answer. However in your reply to this question [Spring OAuth and Boot Integration Test] (https://stackoverflow.com/a/31679649/2594130) you mention

You no longer need to worry about running in stateless mode or not

Why is it that I need to still add the stateless false, is this a bug or are we using it slightly differently?

The other thing I needed to do to get this to work was adding OAuth2Request and OAuth2Authentication to the WithSecurityContextFactory as you can see in the following

public class WithMockCustomUserSecurityContextFactory implements WithSecurityContextFactory<WithMockOAuthUser> {

    @Override
    public SecurityContext createSecurityContext(WithMockOAuthUser withClient) {
        // Get the username
        String username = withClient.username();
        if (username == null) {
            throw new IllegalArgumentException("Username cannot be null");
        }

        // Get the user roles
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (String role : withClient.roles()) {
            if (role.startsWith("ROLE_")) {
                throw new IllegalArgumentException("roles cannot start with ROLE_ Got " + role);
            }
            authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
        }

        // Get the client id
        String clientId = withClient.clientId();
        // get the oauth scopes
        String[] scopes = withClient.scope();
        Set<String> scopeCollection = Sets.newSet(scopes);

        // Create the UsernamePasswordAuthenticationToken
        User principal = new User(username, withClient.password(), true, true, true, true, authorities);
        Authentication authentication = new UsernamePasswordAuthenticationToken(principal, principal.getPassword(),
                principal.getAuthorities());


        // Create the authorization request and OAuth2Authentication object
        OAuth2Request authRequest = new OAuth2Request(null, clientId, null, true, scopeCollection, null, null, null,
                null);
        OAuth2Authentication oAuth = new OAuth2Authentication(authRequest, authentication);

        // Add the OAuth2Authentication object to the security context
        SecurityContext context = SecurityContextHolder.createEmptyContext();
        context.setAuthentication(oAuth);
        return context;
    }

}
Community
  • 1
  • 1
revilo
  • 169
  • 1
  • 8
  • Where is the code for WithMockOAuthUser and it's related WithSecurityContextFactory? – Rob Winch Jun 02 '16 at 13:48
  • @RobWinch Sorry that's a typo, it is actually WithMockCustomUser that was a typo with me messing around, same problem exists. I had another interface which created an OAuth authentication instead of the UsernamePasswordAuthentication – revilo Jun 03 '16 at 15:24
  • What does your Security Configuration look like? Specifically what does the web and the method security config look like? – Rob Winch Jun 03 '16 at 20:02
  • @RobWinch I don't actually have a config running with default, this is a micro service and communications behind a gateway to the auth service which carries the config. These tests are being run in isolation of the AuthService should I still have a config? – revilo Jun 07 '16 at 11:58
  • @revilo If I may ask, is it possible for you to share a repository with your test environment ? I am trying to test spring-security-oauth2 and I cannot get anything else than 401. Your post is the one that helped me the most to understand. A project test with spring-security and spring-security-oauth2 does not exist yet. It would be super nice. Thanks in advance – Dimitri Kopriwa Nov 18 '17 at 16:55

2 Answers2

3

The problem is that OAuth2AuthenticationProcessingFilter will clear the SecurityContext if it is marked as stateless. To workaround this configure it to allow the state to be populated externally (i.e. stateless = false).

Rob Winch
  • 21,440
  • 2
  • 59
  • 76
  • Hi @RobWinch I've tried you suggestion with the stateless flag, this helped with part of the answer. However in your reply to this question [Spring OAuth and Boot Integration Test] (http://stackoverflow.com/a/31679649/2594130) you mention _"You no longer need to worry about running in stateless mode or not"_ Why is it that it is required I need this, is this a bug or are we using it slightly differently. I've updated the answer as i've run out of characters here – revilo Jun 21 '16 at 14:38
  • Spring Security OAuth handles stateless differently than Spring Security and so that statement is not applicable for OAuth (unfortunately). – Rob Winch Jul 01 '16 at 13:45
  • Exactly how do you set that = false in the above example? – PaulNUK Nov 02 '16 at 13:30
  • @RobWinch Dear rob, we've been trying for days to test spring-security-oauth2 and we also hit 401 errors. Spring-security-oauth2 doesn't have any example, and we need one. Is there a way to have an example or extra information? – Dimitri Kopriwa Nov 18 '17 at 16:57
1

to add some more infos how to set stateless to false:

in your ResourceServerConfigurerAdapter do the following:

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.stateless(false);
    }

which worked for me.

David Steiman
  • 3,055
  • 2
  • 16
  • 22