Philipp Jahoda
Philipp Jahoda

Reputation: 51411

Spring Security OAuth2 InvalidGrantException

I have successfully create a web-service using the spring-boot framework. Now I want to secure my web-service with OAuth2 (using spring) and have a few questions regarding that:

According to my research, spring provides some kind of default-url to request an access token (baseURL/oauth/token). I have tested the URL using postman, and a valid access token was returned (using client_credentials grant type), but no refresh token. However this method does not work with grant_type=password and results in the following error response:

{"error":"invalid_grant","error_description":"Bad credentials"}

My spring application logs InvalidGrantException.

The curl I used to test grant_type=password is the following:

curl -v -X POST -H "Content-Type: application/json" -H "Authorization: Basic base64encodedclientidandsecret" 'http://localhost:8888/oauth/token?grant_type=password&username=user&password=1234'

I did not test using postman because it does not support grant_type=password.

How can I get spring to return both accessToken and refreshToken using grant_type=password?

Is there anything wrong with my configuration?

My spring application (configuration) looks as follows:

@Configuration
@ComponentScan
@EnableAutoConfiguration(exclude = { MongoAutoConfiguration.class, MongoDataAutoConfiguration.class })
@SpringBootApplication
public class CsWebServerApplication {

    public static final String RESOURCE_ID = "myresource";

    public static final String CLIENT_ID = "myapplication";
    public static final String CLIENT_SECRET = "application_secret";

    public static void main(String[] args) {

        SpringApplication.run(MyWebServerApplication.class, args);
    }

    @Configuration
    @EnableAuthorizationServer
    protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {

        @Inject
        private AuthenticationManager authenticationManager;

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

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

            clients.inMemory().withClient(CLIENT_ID)
            .authorizedGrantTypes("client_credentials", "password", "refresh_token")
            .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
            .scopes("read", "write", "trust")
            .secret(CLIENT_SECRET);
        }

        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
            super.configure(oauthServer);
        }
    }

    @Configuration
    @EnableResourceServer
    protected static class ResourceConfig extends ResourceServerConfigurerAdapter {

        @Override 
        public void configure(HttpSecurity http) throws Exception {

            http.requestMatchers().antMatchers("/*", "/admin/beans").and().authorizeRequests().anyRequest()
                .access("#oauth2.hasScope('read')"); 
        }

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

    @Configuration
    @EnableWebSecurity
    protected static class WebConfigurer extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            super.configure(http);
        }

        @Override
        public void configure(WebSecurity webSecurity) throws Exception {
            webSecurity.ignoring()
                    // All of Spring Security will ignore the requests
                    .antMatchers("/accessibleservices/**")
        }
    }

}

Upvotes: 4

Views: 7456

Answers (4)

Yang Ou
Yang Ou

Reputation: 1

pom.xml add

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

add config

@Configuration
public class RedisTokenStoreConfig {

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Bean
    public TokenStore redisTokenStore (){
        return new RedisTokenStore(redisConnectionFactory);
    }
}

in your OAuth2Config

@Autowired
@Qualifier("redisTokenStore")
private TokenStore tokenStore;

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
    endpoints.authenticationManager(authenticationManager)
            .userDetailsService(userService)
            .tokenStore(tokenStore);//set tokenStore
}

Upvotes: 0

Yang Ou
Yang Ou

Reputation: 1

DefaultTokenServices.class

not set TokenStore

OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(refreshTokenValue);
    if (refreshToken == null) {
        throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
    }

Upvotes: 0

eg04lt3r
eg04lt3r

Reputation: 2610

About supporting of refresh tokens. By default spring oauth using DefaultTokenServices class and support of refresh tokens disabled by default. You should override its initialization in your OAuth2Config.class.

Example:

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

Upvotes: 0

Takahiko Kawasaki
Takahiko Kawasaki

Reputation: 19001

"4.3.2. Access Token Request" in RFC 6749 (The OAuth 2.0 Authorization Framework) says as follows.

The client makes a request to the token endpoint by adding the following parameters using the "application/x-www-form-urlencoded" format per Appendix B with a character encoding of UTF-8 in the HTTP request entity-body:

So, -H "Content-Type: application/json" is wrong. In addition, your curl command line is wrong. Use -d option to specify a form parameter for POST.

Upvotes: 3

Related Questions