Search code examples
springoauthspring-securityoauth-2.0spring-security-oauth2

Spring Security OAuth2: Purge TokenStore


Is there any way to configure Spring Security OAuth2 so it automatically purge TokenStore?

I want to remove the expired tokens from time to time. I've seen the InMemoryTokenStore code and it performs a flush every now and again.

But JdbcTokenStore does not perform any purge, so Who is in charge of removing the expried tokens from the storage?

I've implemented a TokenStore that uses MongoDB as storage, but I have the same problem. Nobody is removing the expired tokens from the storage.


Solution

  • Unfortunately, JdbcTokenStore does not purge expired tokens automatically. It's up to you to purge old tokens. Here's an idea how I would add such a mechanism.

    Expiration date is part of OAuth2AccessToken which gets persisted as a serialized java object in the database. In order to detect whether an OAuth2AccessToken is eligible for deletion you would need to read it from the database an deserialize it. This may lead to performance penalties where you need to purge a high amount of OAuth2AccessTokens.

    My suggestion is to expand oauth_access_token table by a column expiration of type TIMESTAMP (H2 dialect) for saving expiration date.

    create table oauth_access_token (
      token_id VARCHAR(256),
      token LONGVARBINARY,
      authentication_id VARCHAR(256),
      user_name VARCHAR(256),
      client_id VARCHAR(256),
      authentication LONGVARBINARY,
      refresh_token VARCHAR(256),
      expiration TIMESTAMP
    );
    

    Extend JdbcTokenStore and override storeAccessToken method. Don't forget to alter insertAccessTokenSql in order to honor new column expiration in the insert statement.

    public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
        String refreshToken = null;
        if (token.getRefreshToken() != null) {
            refreshToken = token.getRefreshToken().getValue();
        }
    
        if (readAccessToken(token.getValue())!=null) {
            removeAccessToken(token.getValue());
        }
    
        jdbcTemplate.update(insertAccessTokenSql, new Object[] { extractTokenKey(token.getValue()),
                new SqlLobValue(serializeAccessToken(token)), authenticationKeyGenerator.extractKey(authentication),
                authentication.isClientOnly() ? null : authentication.getName(),
                authentication.getOAuth2Request().getClientId(),
                new SqlLobValue(serializeAuthentication(authentication)), extractTokenKey(refreshToken), token.getExpiration() }, new int[] {
                Types.VARCHAR, Types.BLOB, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.BLOB, Types.VARCHAR, Types.TIMESTAMP });
    }
    

    Enable Spring's Task Execution and Scheduling and add a scheduled method which purges old tokens.

    @Scheduled(fixedRate = 10000)
    public void purgeOldTokens() {
        java.util.Date now = new Date();
        jdbcTemplate.update("delete from oauth_access_token where expiration <?", now);
    }
    

    Caution. This code just demonstrates my idea. I can't guarantee that there are no errors. Your code may and should vary from my example code.