6

I want to use OAuth2 for my REST spring boot project. Using some examples I have created configuration for OAuth2:

@Configuration
public class OAuth2Configuration {

    private static final String RESOURCE_ID = "restservice";

    @Configuration
    @EnableResourceServer
    protected static class ResourceServerConfiguration extends
          ResourceServerConfigurerAdapter {

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            // @formatter:off
            resources
                    .resourceId(RESOURCE_ID);
            // @formatter:on
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            // @formatter:off
            http
                    .anonymous().disable()
                    .authorizeRequests().anyRequest().authenticated();
            // @formatter:on
        }

    }

    @Configuration
    @EnableAuthorizationServer
    protected static class AuthorizationServerConfiguration extends
             AuthorizationServerConfigurerAdapter {

        private TokenStore tokenStore = new InMemoryTokenStore();

        @Autowired
        @Qualifier("authenticationManagerBean")
        private AuthenticationManager authenticationManager;

        @Autowired
        private UserDetailsServiceImpl userDetailsService;

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints)
            throws Exception {
          // @formatter:off
          endpoints
                  .tokenStore(this.tokenStore)
                  .authenticationManager(this.authenticationManager)
                  .userDetailsService(userDetailsService);
          // @formatter:on
        }

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            // @formatter:off
            clients
                  .inMemory()
                  .withClient("clientapp")
                  .authorizedGrantTypes("password", "refresh_token", "trust")
                  .authorities("USER")
                  .scopes("read", "write")
                  .resourceIds(RESOURCE_ID)
                  .secret("clientsecret")
                  .accessTokenValiditySeconds(1200)
                  .refreshTokenValiditySeconds(3600);
            // @formatter:on
        }

        @Bean
        @Primary
        public DefaultTokenServices tokenServices() {
            DefaultTokenServices tokenServices = new DefaultTokenServices();
            tokenServices.setSupportRefreshToken(true);
            tokenServices.setTokenStore(this.tokenStore);
            return tokenServices;
        }
    }
}

This is my SecurityConfiguration class:

@Configuration
@EnableWebSecurity
@Order(1)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http
                .authorizeRequests().antMatchers("/api/register").permitAll()
                .and()
                .authorizeRequests().antMatchers("/api/free").permitAll()
                .and()
                .authorizeRequests().antMatchers("/oauth/token").permitAll()
                .and()
                .authorizeRequests().antMatchers("/api/secured").hasRole("USER")
                .and()
                .authorizeRequests().anyRequest().authenticated();
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

I tried to check my application with 2 simple requests:

@RequestMapping(value = "/api/secured", method = RequestMethod.GET)
public String checkSecured(){
    return "Authorization is ok";
}

@RequestMapping(value = "/api/free", method = RequestMethod.GET)
public String checkFree(){
    return "Free from authorization";
}

Firstly I checked two requests:

/api/free returned code 200 and the string "Free from authorization"

/api/secured returned {"timestamp":1487451065106,"status":403,"error":"Forbidden","message":"Access Denied","path":"/api/secured"}

And it seems that they work fine.

Then I got access_token (using credentials from my users database)

/oauth/token?grant_type=password&username=emaila&password=emailo

Response:

{"access_token":"3344669f-c66c-4161-9516-d7e2f31a32e8","token_type":"bearer","refresh_token":"c71c17e4-45ba-458c-9d98-574de33d1859","expires_in":1199,"scope":"read write"}

Then I tried to send a request (with the token I got) for resource which requires authentication:

/api/secured?access_token=3344669f-c66c-4161-9516-d7e2f31a32e8

Here is response:

{"timestamp":1487451630224,"status":403,"error":"Forbidden","message":"Access Denied","path":"/api/secured"}

I cannot understand why access is denied. I am not sure in configurations and it seems that they are incorrect. Also I still do not clearly understand relationships of methods configure(HttpSecurity http) in class which extends WebSecurityConfigurerAdapter and in another which extends ResourceServerConfigurerAdapter. Thank you for any help!

Alex
  • 65
  • 1
  • 4
  • What happens when you send the Token as Header `Authorization: Bearer [TOKEN]`? – dav1d Feb 19 '17 at 11:06
  • @dav1d I tried but access is still denied – Alex Feb 19 '17 at 13:41
  • Your question was helpful, however I got it to work only after removing the "@Order(1)" from your security class. It would have been very beneficial if you provided the full code or a Github link. – SamwellTarly Apr 04 '18 at 15:01
  • 1
    @SamwellTarly Sorry for late reply, here is the link: https://github.com/ahea/SocNetworkSpringApp – Alex Apr 23 '18 at 09:55

1 Answers1

24

If you are using spring boot 1.5.1 or recently updated to it, note that they changed the filter order for spring security oauth2 (Spring Boot 1.5 Release Notes).

According to the release notes, try to add the following property to application.properties/yml, after doing that the resource server filters will be used after your other filters as a fallback - this should cause the authorization to be accepted before falling to the resource server:

security.oauth2.resource.filter-order = 3

You can find a good answer for your other questions here: https://stackoverflow.com/questions/28537181

Community
  • 1
  • 1
Tom
  • 3,711
  • 2
  • 25
  • 31
  • Thank you very much for your answer. I added this property and removed @Order annotation. Now I can get access token and then, using it, finally get /api/secured resource. But I cannot get /api/free without token now. I think it can be fixed by changing configure methods. – Alex Feb 19 '17 at 17:14
  • I'm having a similar problem, while I'm requesting a secured resource with the access_token, it seems like this totally ignored and I'm redirected to make login... Do you know how can I debug it? – kmualem Aug 29 '18 at 15:07
  • You might be trying to do an `authorization_code` flow instead of `client_credentials` / `password` flows, in auth code flow you should be redirected to a login page so it makes sense (https://oauth.net/2/grant-types/authorization-code/) – Tom Aug 30 '18 at 14:59
  • @Tom can you pls look into this issue https://stackoverflow.com/questions/53537133/unable-to-access-resources-with-access-token-spring-boot-oauth2 – vjnan369 Nov 29 '18 at 14:10