21

I am trying to get a basic in-memory OAuth2 server running using the Spring Libraries. I have been following the sparklr example.

I currently have configured the Server and almost everything is working, however I cannot access my restricted resource from the resource server.

My test workflow:

  1. Access the oauth authorized URI to start the OAuth2 flow: http://localhost:8080/server/oauth/authorize?response_type=code&client_id=client

  2. Redirect to the login page: http://localhost:8080/server/login

  3. Handle the approval and redirect to my configured redirect page w/ a code parameter: http://localhost:8080/client?code=HMJO4K

  4. Construct a GET request using Basic Auth using the client id and secret along with the grant type and code: http://localhost:8080/server/oauth/token?grant_type=authorization_code&code=HMJO4K

  5. Receive an access_token and refresh token object in return

    { access_token: "f853bcc5-7801-42d3-9cb8-303fc67b0453" token_type: "bearer" refresh_token: "57100377-dea9-4df0-adab-62e33f2a1b49" expires_in: 299 scope: "read write" }

  6. Attempt to access a restricted resource using the access_token: http://localhost:8080/server/me?access_token=f853bcc5-7801-42d3-9cb8-303fc67b0453

  7. Receive an invalid token reply

    { error: "invalid_token" error_description: "Invalid access token: f853bcc5-7801-42d3-9cb8-303fc67b0453" }

  8. POST to the token uri again to refresh token: http://localhost:8080/server/oauth/token?grant_type=refresh_token&refresh_token=57100377-dea9-4df0-adab-62e33f2a1b49

  9. Receive a new token

    { access_token: "ed104994-899c-4cd9-8860-43d5689a9420" token_type: "bearer" refresh_token: "57100377-dea9-4df0-adab-62e33f2a1b49" expires_in: 300 scope: "read write" }

I am really not sure what I am doing wrong, but it appears that everything other than accessing the restricted uri is working. Here is my configuration:

@Configuration
public class Oauth2ServerConfiguration {

    private static final String SERVER_RESOURCE_ID = "oauth2-server";

    @Configuration
    @EnableResourceServer
    protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources.resourceId(SERVER_RESOURCE_ID);
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
                .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .and().requestMatchers()
                    .antMatchers("/me")
                .and().authorizeRequests()
                    .antMatchers("/me").access("#oauth2.clientHasRole('ROLE_CLIENT')")
            ;
        }
    }

    @Configuration
    @EnableAuthorizationServer
    protected static class AuthotizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

        @Autowired
        private ClientDetailsService clientDetailsService;

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

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
                .withClient("client")
                    .resourceIds(SERVER_RESOURCE_ID)
                    .secret("secret")
                    .authorizedGrantTypes("authorization_code", "refresh_token")
                    .authorities("ROLE_CLIENT")
                    .scopes("read","write")
                    .redirectUris("http://localhost:8080/client")
                    .accessTokenValiditySeconds(300)
                    .autoApprove(true)
            ;
        }

        @Bean
        public TokenStore tokenStore() {
            return new InMemoryTokenStore();
        }

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints
                .tokenStore(tokenStore())
                .userApprovalHandler(userApprovalHandler())
                .authenticationManager(authenticationManager)
            ;
        }

        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
            oauthServer.realm("oauth");
        }

        @Bean
        public ApprovalStore approvalStore() throws Exception {
            TokenApprovalStore store = new TokenApprovalStore();
            store.setTokenStore(tokenStore());
            return store;
        }

        @Bean
        public UserApprovalHandler userApprovalHandler() throws Exception {
            TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
            handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
            handler.setClientDetailsService(clientDetailsService);
            handler.setTokenStore(tokenStore());

            return handler;
        }
    }
}

Is there something I am missing or am I approaching this incorrectly? Any help would be greatly appreciated.

1
  • After some more testing, I still cannot get it to work. Somehow, it seems that the ResourceServer is not loading the correct token store or something. I have a token store bean and autowired it into a controller which will print the tokens for my client, which works fine. I autowire the same token store bean (using unique qualifier) into a custom authentication manager, and it cannot find any tokes in the store. I am really not sure how this is possible, unless something is implicitly session scoped?? Commented Apr 14, 2015 at 3:10

3 Answers 3

8

Your step #6 is wrong - the access token should not be sent in the URL as it is vulnerable this way. rathen than GET, use POST.

Besides, I don't understand your step #1 - why do you call /oauth/authorize? it should be done implicitly when you try to get a protected resource. I mean, your flow should start with:

Attempt to access a restricted resource using the access_token: http://localhost:8080/server/me

Then the negotiation will start "behind the scenes": a redirect to "/oauth/authorize" etc.

In addition, in step #8, note that you are not asking for "another access token", but instead it is a request for "refresh token". As if your access-token was expired.

Note: The identity provider and the resource server should share the tokenStore! Read here: Spring Security OAuth2 pure resource server

HTH

Sign up to request clarification or add additional context in comments.

7 Comments

Thanks for the reply. I have tried GET & POST when trying to access the resource server. I have tried sending as query param, form data, and as the header Authorization: Bearer <token> and in every scenario, I continue to get the 'invalid token' response. I have setup some debug endpoints to dump the current tokens by client and by user and my token is in both lists.
the resource server must know how to parse the token... did you write the protected resource?
The resource is simply a spring-mvc endpoint setup to return a json string via @RequestMapping("/me") @ResponseBody public String me() { return "hello";}. The org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter is rejecting the token. Since posting, I've tried to add to the configuration of ResourceServerSecurityConfirer a reference to both the TokenStore and the TokenServices beans, neither of which have worked.
try to stop (breakpoint) in ResourceServerTokenServices.loadAuthentication() - do you get there?
indeed. the identity provider and the resource server should share the tokenStore.
|
7

The problem ended up being that the resource server and the authorization server were not getting the same token store reference. Not sure how the wiring was not working correctly, but using a fixed object in the configuration class worked like a charm. Ultimately, I'll move to a persistence backed token store, which probably would not have had any issues.

Thanks goes to @OhadR for the answer and the help!

Ultimately, I simplified the configuration, went thru the same workflow, and it worked out

@Configuration
public class Oauth2ServerConfiguration {

    private static final String SERVER_RESOURCE_ID = "oauth2-server";

    private static InMemoryTokenStore tokenStore = new InMemoryTokenStore();


    @Configuration
    @EnableResourceServer
    protected static class ResourceServer extends ResourceServerConfigurerAdapter {

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources.tokenStore(tokenStore).resourceId(SERVER_RESOURCE_ID);
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.requestMatchers().antMatchers("/me").and().authorizeRequests().antMatchers("/me").access("#oauth2.hasScope('read')");
        }
    }

    @Configuration
    @EnableAuthorizationServer
    protected static class AuthConfig extends AuthorizationServerConfigurerAdapter {

        @Autowired
        private AuthenticationManager authenticationManager;


        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore).approvalStoreDisabled();
        }

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
                .withClient("client")
                    .authorizedGrantTypes("authorization_code","refresh_token")
                    .authorities("ROLE_CLIENT")
                    .scopes("read")
                    .resourceIds(SERVER_RESOURCE_ID)
                    .secret("secret")
            ;
        }
    }
}

Anyone that stumbles upon this post, I recommend looking more at the unit tests for example rather than the full blown sparklr/tonr example, as it has a lot of extra configuration that are not necessarily needed to get started.

3 Comments

thanks for posting this question and answer. It helped with my own research into this. You should mark OhadR's response as a correct answer though because he led you to the right conclusion.
I ntice that your answer involves an AuthorizationServerConfigurerAdapter. I am getting an org.springframework.security.oauth2.provider.NoSuchClientException error in a Spring OAuth2 app that seems to result from my AuthorizationServerConfigurerAdapter. Are you willing to take a look? Here is the link: stackoverflow.com/questions/36899386/…
I am online if you are willing to give me a hint on the other question. Not sure where to start with it.
0

This works for me:

@Configuration
public class Oauth2ServerConfiguration {

    private static final String SERVER_RESOURCE_ID = "oauth2-server";

    @Autowired
    private TokenStore tokenStore;

    @Bean
    public TokenStore tokenStore() {
        return new InMemoryTokenStore();
    }

    @Configuration
    @EnableResourceServer
    protected static class ResourceServer extends ResourceServerConfigurerAdapter {

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources.tokenStore(tokenStore).resourceId(SERVER_RESOURCE_ID);
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            // ... Not important at this stage
        }
    }

    @Configuration
    @EnableAuthorizationServer
    protected static class AuthConfig extends AuthorizationServerConfigurerAdapter {

        @Autowired
        private AuthenticationManager authenticationManager;


        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore).approvalStoreDisabled();
        }

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            //... Not important at this stage
        }
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.