Replace In-Memory Token Store With a Persistent one in Spring Boot OAuth2

Eranga Dulshan
3 min readJul 7, 2019
just an image

Recently I was working on a mobile application with a spring boot backend along with a MySql database. I used a token from an in memory token store to authenticate each user. But the problem is, whenever the server (backend) restarts, user has to log in again because the existing token in the mobile app is not in the memory of the server even though the existing token has not been expired yet.

Following is the Authorization server configuration code that I had when I was using the in-memory token store.

@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends
AuthorizationServerConfigurerAdapter {

private TokenStore tokenStore = new InMemoryTokenStore();

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

@Autowired
private CustomUserDetailsService userDetailsService;

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

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("app")
.authorizedGrantTypes("password", "refresh_token")
.authorities("USER")
.scopes("read", "write")
.resourceIds("rest_service")
.secret("secret")
.accessTokenValiditySeconds(24 * 365 * 60 * 60);
}

@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
PasswordEncoder passwordEncoder = new PasswordEncoder() {
@Override
public String encode(CharSequence charSequence) {
return charSequence != null ? charSequence.toString() : null;
}

@Override
public boolean matches(CharSequence charSequence, String s) {
return charSequence != null && s != null && charSequence.toString().equals(s) ? true : false;
}
};
oauthServer.passwordEncoder(passwordEncoder);
}

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

}

As you can see, I was using an in memory token store.

private TokenStore tokenStore = new InMemoryTokenStore();

In order to persist the token store, you just have to do is just changing in-memory token store to a JdbcTokenStore (Because I am using a MySql database).

First you have to add two tables to store the tokens. Following are the two statements to create those tables.

CREATE TABLE oauth_access_token (
authentication_id varchar(255) NOT NULL PRIMARY KEY,
token_id varchar(255) NOT NULL,
token blob NOT NULL,
user_name varchar(255) NOT NULL,
client_id varchar(255) NOT NULL,
authentication blob NOT NULL,
refresh_token varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE oauth_refresh_token (
token_id varchar(255) NOT NULL,
token blob NOT NULL,
authentication blob NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Now you have to configure the JdbcTokenStore and use that token store. Following is the configuration of the JdbcTokenStore.

@Configuration
public class JDBCTokenConfig {

@Value("${spring.datasource.url}")
private String datasourceUrl;

@Value("${spring.datasource.driver-class-name}")
private String dbDriverClassName;

@Value("${spring.datasource.username}")
private String dbUsername;

@Value("${spring.datasource.password}")
private String dbPassword;

@Bean
public DataSource dataSource() {
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(dbDriverClassName);
dataSource.setUrl(datasourceUrl);
dataSource.setUsername(dbUsername);
dataSource.setPassword(dbPassword);
return dataSource;
}

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

You have to provide the database url, jdbc drive class name, username and password of the database. I am using a property file just for the clarity. This is how my property file looks.

spring.datasource.url= jdbc:mysql://localhost:3306/db_name?useUnicode=true&autoReconnect=true&useSSL=false
spring.datasource.username
=username
spring.datasource.password
=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

Now you can just auto-wire the above JdbcTokenStore bean in the Authorization server configuration like following.

@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends
AuthorizationServerConfigurerAdapter {

@Autowired
private TokenStore tokenStore;

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

@Autowired
private CustomUserDetailsService userDetailsService;

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

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("app")
.authorizedGrantTypes("password", "refresh_token")
.authorities("USER")
.scopes("read", "write")
.resourceIds("rest_service")
.secret("secret")
.accessTokenValiditySeconds(24 * 365 * 60 * 60);
}

@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
PasswordEncoder passwordEncoder = new PasswordEncoder() {
@Override
public String encode(CharSequence charSequence) {
return charSequence != null ? charSequence.toString() : null;
}

@Override
public boolean matches(CharSequence charSequence, String s) {
return charSequence != null && s != null && charSequence.toString().equals(s) ? true : false;
}
};
oauthServer.passwordEncoder(passwordEncoder);
}

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

}

As you can see I just changed the token store to JdbcTokenStore.

@Autowired 
private TokenStore tokenStore;

And that’s all you have to do. Now the spring security will store the tokens in the database. If you query the tables, you can find the tokens issued. Now even the server restarts, if the token has not been expired, users will not have to login again.

Happy Coding 😃.

--

--