3

I try to create spring rest service, whis is autenticated by my own oauth2 resources server. I created resource server:

@Configuration
@EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {

    @Autowired
    private TokenStore tokenStore;

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

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/api/shop /**").authenticated().and()
                .authorizeRequests().antMatchers("/auth/**").anonymous();
    }

}

and authorization server:

@Configuration
@EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager auth;

    @Autowired
    private DataSource dataSource;

    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    @Bean
    public JdbcTokenStore tokenStore() {
        return new JdbcTokenStore(dataSource);
    }

    @Bean
    protected AuthorizationCodeServices authorizationCodeServices() {
        return new JdbcAuthorizationCodeServices(dataSource);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.passwordEncoder(passwordEncoder);
    }

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

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.jdbc(dataSource)
                .passwordEncoder(passwordEncoder);
                .withClient("mobile")
                .authorizedGrantTypes("password", "refresh_token")
                .authorities("ROLE_CLIENT")
                .scopes("read", "write", "trust")
                .autoApprove(true)
               .resourceIds("mobileapp")
                .secret("123456");
    }

When I try to receive an access token from server, using curl:

curl -X POST -vu mobile:123456 http://localhost:8080/oauth/token -H "Accept: application/json" -d "password=test123&username=admin@gmail.com&grant_type=password&scope=read&client_secret=123456&client_id=mobile"

I get this error as a response message:

{"error":"server_error","error_description":"java.io.NotSerializableException: org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"}

In tomcat logs there is also

o.s.s.o.p.token.store.JdbcTokenStore - Failed to find access token for token

EDIT: Bean definition of password encoder:

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

This bean is created in class, in which OAuth2Config and ResourceServer are declared.

I checked code and found out which table spring uses and the table is empty. My question is: should it be auto generated or there is a problem with my code?

Thanks in advance for help.

Marcin
  • 508
  • 2
  • 10
  • 28
  • Could you update the question with your `@Bean` definition of the `BCryptPasswordEncoder` bean? – sthzg Mar 31 '16 at 12:53
  • I added BCryptPasswordEncoder bean definition. – Marcin Mar 31 '16 at 13:23
  • Thanks, the `@Bean` looks fine, in my opinion (although you could use the `PasswordEncoder` interface for its type). When I test it locally I have to set `withClient(..).secret(...)` with the encrypted PW like `withClient(...).secret(passwordEncoder.encode("123456"))`, but that doesn't explain the serialization error (it would rather explain a `BadCredentials` response, which I get when I don't encode it. Did you check that everything works when you don't set the `security.passwordEncoder(...)`? – sthzg Mar 31 '16 at 14:12
  • Sorry, I take that last sentence back, I haven't seen that you've also configured the `passwordEncoder` in the `clients` config, so no need for `passwordEncoder.encode()` there. – sthzg Mar 31 '16 at 14:47

3 Answers3

10

Override JdbcTokenStore class and replace this function with.

public OAuth2AccessToken readAccessToken(String tokenValue) {
    OAuth2AccessToken accessToken = null;

    try {
        accessToken = new DefaultOAuth2AccessToken(tokenValue);
    }
    catch (EmptyResultDataAccessException e) {
        if (LOG.isInfoEnabled()) {
            LOG.info("Failed to find access token for token "+tokenValue);
        }
    }
    catch (IllegalArgumentException e) {
        LOG.warn("Failed to deserialize access token for " +tokenValue,e);
        removeAccessToken(tokenValue);
    }

    return accessToken;
}

Your problem of failed to find access token is resolved.Use this class in OAuth2Config.

Mamta Soni
  • 101
  • 1
  • 3
  • This is actually the correct answer. I don't know why it hasn't been ticked. Been looking for this for a week now only to find it hidden here! – Akin_Glen Feb 05 '19 at 04:28
1

Your model must have BCryptPasswordEncoder which is not serialized. Make it transient in your user bmodel.

private transient BCryptPasswordEncoder passwordEncoder;
help-info.de
  • 6,695
  • 16
  • 39
  • 41
Mehul Aneja
  • 84
  • 1
  • 3
0

The solution, as far as Postgres is concerned, use BYTEA for ALL token and authentication columns.

The columns are defined as LONGVARBINARY in this schema reference: https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql

In other words, replace LONGVARBINARY with BYTEA if you are using Postgres.

Cheers

Seun Matt
  • 1,660
  • 1
  • 9
  • 14