Repository: SAP/cloud-security-xsuaa-integration
Pull request #296 (2020-05-29 14:18:07)

Token key cache configurable from XsuaaTokenAuthenticator
This PR adds a new interface `CacheConfiguration` to the `java-api` package that both `TokenKeyCache` and `TokenCache` implement.
It is now possible to configure the `XsuaaTokenAuthenticator` with a cache configuration for the token key cache.

I also added `isCacheDisabled()` to the CacheConfiguration interface that implementations should use to check if the cache is disabled. I made the `DEFAULT` and `CACHE_DISABLED` constants private and accesible with `TokenCacheConfiguration.defaultConfiguration()` and `TokenCacheConfiguration.cacheDisabled()`. 

It is still not possible to disable the token key cache.


nenaraab: Hi @hassler-d 
somehow it has conflicts with master branch.
The rest is ready for merge!!!
Thanks a lot!

Patch:
diff --git a/etc/suppression.xml b/etc/suppression.xml
index ca4f383b..55c538ac 100644
--- a/etc/suppression.xml
+++ b/etc/suppression.xml
@@ -1,11 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd">
-    <suppress until="2020-06-01Z">
+    <suppress>
         <notes><![CDATA[
         This suppresses CVE-2018-1258 which is necessary because of a bug in the dependency-check plugin itself
         and needs to be active until it is resolved: https://github.com/jeremylong/DependencyCheck/issues/1827
-        It will expire on March 1. 2020 to force reevaluation and might be expanded further.
         ]]></notes>
         <cve>CVE-2018-1258</cve>
     </suppress>
-</suppressions>
\ No newline at end of file
+</suppressions>
diff --git a/java-api/src/main/java/com/sap/cloud/security/config/CacheConfiguration.java b/java-api/src/main/java/com/sap/cloud/security/config/CacheConfiguration.java
new file mode 100644
index 00000000..d4217c1f
--- /dev/null
+++ b/java-api/src/main/java/com/sap/cloud/security/config/CacheConfiguration.java
@@ -0,0 +1,33 @@
+package com.sap.cloud.security.config;
+
+import java.time.Duration;
+
+/**
+ * Interface used for the configuration of caches.
+ */
+public interface CacheConfiguration {
+
+	/**
+	 * Returns the duration of the expire after write property of the cache. Cached
+	 * elements are automatically invalidated after this fixed duration has elapsed.
+	 *
+	 * @return duration of expire after write.
+	 */
+	Duration getCacheDuration();
+
+	/**
+	 * Returns the number of elements the cache can hold.
+	 *
+	 * @return the size of the cache.
+	 */
+	int getCacheSize();
+
+	/**
+	 * Caching is disabled when this returns {@code true}.
+	 *
+	 * @return {@code true} if cache is disabled
+	 */
+	default boolean isCacheDisabled() {
+		return false;
+	}
+}
diff --git a/java-api/src/main/java/com/sap/cloud/security/json/JsonObject.java b/java-api/src/main/java/com/sap/cloud/security/json/JsonObject.java
index 6452452a..f0bd5d4a 100644
--- a/java-api/src/main/java/com/sap/cloud/security/json/JsonObject.java
+++ b/java-api/src/main/java/com/sap/cloud/security/json/JsonObject.java
@@ -71,6 +71,19 @@ public interface JsonObject {
 	@Nullable
 	Instant getAsInstant(String name);
 
+	/**
+	 * Returns a {@link Long} identified by the given property {@code name}. If the
+	 * property with the given name is not found, null is returned.
+	 *
+	 * @param name
+	 * @return the {@link Long} object.
+	 * @throws JsonParsingException
+	 *             if the json object identified by the given property does not
+	 *             represent a long value
+	 */
+	@Nullable
+	Long getAsLong(String name);
+
 	/**
 	 * Returns a nested JSON object as @{link JsonObject} instance.
 	 * 
diff --git a/java-security/pom.xml b/java-security/pom.xml
index 4d5de207..265ac933 100644
--- a/java-security/pom.xml
+++ b/java-security/pom.xml
@@ -33,10 +33,6 @@
 			<artifactId>token-client</artifactId>
 			<version>${project.version}</version>
 		</dependency>
-		<dependency>
-			<groupId>com.github.ben-manes.caffeine</groupId>
-			<artifactId>caffeine</artifactId>
-		</dependency>
 		<dependency>
 			<groupId>org.apache.httpcomponents</groupId>
 			<artifactId>httpclient</artifactId>
diff --git a/java-security/src/main/java/com/sap/cloud/security/config/TokenKeyCacheConfiguration.java b/java-security/src/main/java/com/sap/cloud/security/config/TokenKeyCacheConfiguration.java
new file mode 100644
index 00000000..16b2e907
--- /dev/null
+++ b/java-security/src/main/java/com/sap/cloud/security/config/TokenKeyCacheConfiguration.java
@@ -0,0 +1,61 @@
+package com.sap.cloud.security.config;
+
+import com.sap.cloud.security.xsuaa.Assertions;
+
+import javax.annotation.Nonnull;
+import java.time.Duration;
+
+/**
+ *
+ */
+public class TokenKeyCacheConfiguration implements CacheConfiguration {
+
+	private static final TokenKeyCacheConfiguration DEFAULT = TokenKeyCacheConfiguration
+			.getInstance(Duration.ofMinutes(10), 1000);
+
+	private Duration cacheDuration;
+	private int cacheSize;
+
+	/**
+	 * Creates a new {@link TokenKeyCacheConfiguration} instance with the given
+	 * properties. See {@link CacheConfiguration#getCacheDuration()} and
+	 * {@link CacheConfiguration#getCacheSize()} for an explanation of the
+	 * respective properties.
+	 *
+	 * @param cacheDuration
+	 *            the cache duration property.
+	 * @param cacheSize
+	 *            the cache size property.
+	 * @return a new {@link TokenKeyCacheConfiguration} instance.
+	 */
+	public static TokenKeyCacheConfiguration getInstance(Duration cacheDuration, int cacheSize) {
+		Assertions.assertNotNull(cacheDuration, "The cache duration write must not be null!");
+		return new TokenKeyCacheConfiguration(cacheDuration, cacheSize);
+	}
+
+	/**
+	 * The default configuration for the token key cache. The default cache size is
+	 * 1000. The default cache duration is 10 minutes.
+	 * 
+	 * @return the default configuration
+	 */
+	public static TokenKeyCacheConfiguration defaultConfiguration() {
+		return DEFAULT;
+	}
+
+	private TokenKeyCacheConfiguration(Duration cacheDuration, int cacheSize) {
+		this.cacheDuration = cacheDuration;
+		this.cacheSize = cacheSize;
+	}
+
+	@Override
+	@Nonnull
+	public Duration getCacheDuration() {
+		return cacheDuration;
+	}
+
+	@Override
+	public int getCacheSize() {
+		return cacheSize;
+	}
+}
diff --git a/java-security/src/main/java/com/sap/cloud/security/json/DefaultJsonObject.java b/java-security/src/main/java/com/sap/cloud/security/json/DefaultJsonObject.java
index e3d792f2..1c206505 100644
--- a/java-security/src/main/java/com/sap/cloud/security/json/DefaultJsonObject.java
+++ b/java-security/src/main/java/com/sap/cloud/security/json/DefaultJsonObject.java
@@ -71,6 +71,15 @@ public class DefaultJsonObject implements JsonObject {
 		return null;
 	}
 
+	@Nullable
+	@Override
+	public Long getAsLong(String name) {
+		if (contains(name)) {
+			return getLong(name).orElse(null);
+		}
+		return null;
+	}
+
 	@Override
 	@Nullable
 	public JsonObject getJsonObject(String name) {
diff --git a/java-security/src/main/java/com/sap/cloud/security/servlet/AbstractTokenAuthenticator.java b/java-security/src/main/java/com/sap/cloud/security/servlet/AbstractTokenAuthenticator.java
index f91845cd..873dd4d2 100644
--- a/java-security/src/main/java/com/sap/cloud/security/servlet/AbstractTokenAuthenticator.java
+++ b/java-security/src/main/java/com/sap/cloud/security/servlet/AbstractTokenAuthenticator.java
@@ -1,5 +1,6 @@
 package com.sap.cloud.security.servlet;
 
+import com.sap.cloud.security.config.CacheConfiguration;
 import com.sap.cloud.security.config.OAuth2ServiceConfiguration;
 import com.sap.cloud.security.token.SecurityContext;
 import com.sap.cloud.security.token.Token;
@@ -20,6 +21,7 @@ import javax.servlet.http.HttpServletResponse;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Optional;
 
 public abstract class AbstractTokenAuthenticator implements TokenAuthenticator {
 
@@ -28,6 +30,7 @@ public abstract class AbstractTokenAuthenticator implements TokenAuthenticator {
 	private Validator<Token> tokenValidator;
 	protected CloseableHttpClient httpClient;
 	protected OAuth2ServiceConfiguration serviceConfiguration;
+	private CacheConfiguration tokenKeyCacheConfiguration;
 
 	@Override
 	public TokenAuthenticationResult validateRequest(ServletRequest request, ServletResponse response) {
@@ -54,11 +57,37 @@ public abstract class AbstractTokenAuthenticator implements TokenAuthenticator {
 		return TokenAuthenticatorResult.createUnauthenticated("Could not process request " + request);
 	}
 
+	/**
+	 * Use to configure the token key cache.
+	 * 
+	 * @param cacheConfiguration
+	 *            the cache configuration
+	 * @return this authenticator
+	 */
+	public AbstractTokenAuthenticator withCacheConfiguration(CacheConfiguration cacheConfiguration) {
+		this.tokenKeyCacheConfiguration = cacheConfiguration;
+		return this;
+	}
+
+	/**
+	 * Use to configure the HttpClient that is used to retrieve token keys.
+	 * 
+	 * @param httpClient
+	 *            the HttpClient
+	 * @return this authenticator
+	 */
 	public AbstractTokenAuthenticator withHttpClient(CloseableHttpClient httpClient) {
 		this.httpClient = httpClient;
 		return this;
 	}
 
+	/**
+	 * Use to override the service configuration used.
+	 * 
+	 * @param serviceConfiguration
+	 *            the service configuration to use
+	 * @return this authenticator
+	 */
 	public AbstractTokenAuthenticator withServiceConfiguration(OAuth2ServiceConfiguration serviceConfiguration) {
 		this.serviceConfiguration = serviceConfiguration;
 		return this;
@@ -109,6 +138,7 @@ public abstract class AbstractTokenAuthenticator implements TokenAuthenticator {
 			JwtValidatorBuilder jwtValidatorBuilder = JwtValidatorBuilder.getInstance(getServiceConfiguration())
 					.withHttpClient(httpClient);
 			jwtValidatorBuilder.configureAnotherServiceInstance(getOtherServiceConfiguration());
+			Optional.ofNullable(tokenKeyCacheConfiguration).ifPresent(jwtValidatorBuilder::withCacheConfiguration);
 			validationListeners.forEach(jwtValidatorBuilder::withValidatorListener);
 			tokenValidator = jwtValidatorBuilder.build();
 		}
diff --git a/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/JwtValidatorBuilder.java b/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/JwtValidatorBuilder.java
index 9a03744f..57235eb1 100644
--- a/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/JwtValidatorBuilder.java
+++ b/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/JwtValidatorBuilder.java
@@ -1,5 +1,6 @@
 package com.sap.cloud.security.token.validation.validators;
 
+import com.sap.cloud.security.config.CacheConfiguration;
 import com.sap.cloud.security.config.OAuth2ServiceConfiguration;
 import com.sap.cloud.security.config.cf.CFConstants;
 import com.sap.cloud.security.token.Token;
@@ -31,6 +32,7 @@ public class JwtValidatorBuilder {
 	private OidcConfigurationService oidcConfigurationService = null;
 	private OAuth2TokenKeyService tokenKeyService = null;
 	private Validator<Token> customAudienceValidator;
+	private CacheConfiguration tokenKeyCacheConfiguration;
 
 	private JwtValidatorBuilder() {
 		// use getInstance factory method
@@ -66,6 +68,18 @@ public class JwtValidatorBuilder {
 		return this;
 	}
 
+	/**
+	 * Use to configure the token key cache.
+	 * 
+	 * @param tokenKeyCacheConfiguration
+	 *            the cache configuration
+	 * @return this builder
+	 */
+	public JwtValidatorBuilder withCacheConfiguration(CacheConfiguration tokenKeyCacheConfiguration) {
+		this.tokenKeyCacheConfiguration = tokenKeyCacheConfiguration;
+		return this;
+	}
+
 	/**
 	 * Sets / overwrites the default audience validator.
 	 *
@@ -178,9 +192,11 @@ public class JwtValidatorBuilder {
 		} else if (configuration.getService() == IAS) {
 			defaultValidators.add(new JwtIssuerValidator(configuration.getUrl()));
 		}
+		OAuth2TokenKeyServiceWithCache tokenKeyServiceWithCache = getTokenKeyServiceWithCache();
+		Optional.ofNullable(tokenKeyCacheConfiguration).ifPresent(tokenKeyServiceWithCache::withCacheConfiguration);
 		JwtSignatureValidator signatureValidator = new JwtSignatureValidator(
 				configuration,
-				getTokenKeyServiceWithCache(),
+				tokenKeyServiceWithCache,
 				getOidcConfigurationServiceWithCache());
 		defaultValidators.add(signatureValidator);
 
diff --git a/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/OAuth2TokenKeyServiceWithCache.java b/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/OAuth2TokenKeyServiceWithCache.java
index 904aab2a..cda11424 100644
--- a/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/OAuth2TokenKeyServiceWithCache.java
+++ b/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/OAuth2TokenKeyServiceWithCache.java
@@ -3,30 +3,38 @@ package com.sap.cloud.security.token.validation.validators;
 import static com.sap.cloud.security.xsuaa.Assertions.assertHasText;
 import static com.sap.cloud.security.xsuaa.Assertions.assertNotNull;
 
+import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
 import java.net.URI;
 import java.security.NoSuchAlgorithmException;
 import java.security.PublicKey;
 import java.security.spec.InvalidKeySpecException;
+import java.time.Duration;
 import java.util.Set;
-import java.util.concurrent.TimeUnit;
 
 import com.github.benmanes.caffeine.cache.Cache;
 import com.github.benmanes.caffeine.cache.Caffeine;
+import com.sap.cloud.security.config.CacheConfiguration;
+import com.sap.cloud.security.config.TokenKeyCacheConfiguration;
+import com.sap.cloud.security.xsuaa.Assertions;
 import com.sap.cloud.security.xsuaa.client.DefaultOAuth2TokenKeyService;
 import com.sap.cloud.security.xsuaa.client.OAuth2ServiceException;
 import com.sap.cloud.security.xsuaa.client.OAuth2TokenKeyService;
+import com.sap.cloud.security.xsuaa.tokenflows.Cacheable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Decorates {@link OAuth2TokenKeyService} with a cache, which gets looked up
  * before the identity service is requested via http.
  */
-public class OAuth2TokenKeyServiceWithCache {
+public class OAuth2TokenKeyServiceWithCache implements Cacheable {
+	private static final Logger LOGGER = LoggerFactory.getLogger(OAuth2TokenKeyServiceWithCache.class);
+
 	private OAuth2TokenKeyService tokenKeyService; // access via getter
 	private Cache<String, PublicKey> cache; // access via getter
-	private long cacheValidityInSeconds = 600; // old keys should expire after 15 minutes
-	private long cacheSize = 1000;
+	private CacheConfiguration cacheConfiguration = TokenKeyCacheConfiguration.defaultConfiguration();
 
 	private OAuth2TokenKeyServiceWithCache() {
 		// use getInstance factory method
@@ -43,32 +51,51 @@ public class OAuth2TokenKeyServiceWithCache {
 	}
 
 	/**
-	 * Caches the Json web keys. Overwrite the cache time (default: 900 seconds).
+	 * Caches the Json web keys. Overwrite the cache time (default: 600 seconds).
 	 *
+	 * @deprecated in favor of {@link #withCacheConfiguration(CacheConfiguration)}
 	 * @param timeInSeconds
 	 *            time to cache the signing keys
 	 * @return this
 	 */
+	@Deprecated
 	public OAuth2TokenKeyServiceWithCache withCacheTime(int timeInSeconds) {
-		if (timeInSeconds <= 600) {
-			throw new IllegalArgumentException("The cache validity must be minimum 600 seconds");
-		}
-		this.cacheValidityInSeconds = timeInSeconds;
+		withCacheConfiguration(TokenKeyCacheConfiguration
+				.getInstance(Duration.ofSeconds(timeInSeconds), this.cacheConfiguration.getCacheSize()));
 		return this;
 	}
 
 	/**
-	 * Caches the Json web keys. Overwrite the size of the cache (default: 100).
+	 * Caches the Json web keys. Overwrite the size of the cache (default: 1000).
 	 *
+	 * @deprecated in favor of {@link #withCacheConfiguration(CacheConfiguration)}
 	 * @param size
 	 *            number of cached json web keys.
 	 * @return this
 	 */
+	@Deprecated
 	public OAuth2TokenKeyServiceWithCache withCacheSize(int size) {
-		if (size <= 1000) {
-			throw new IllegalArgumentException("The cache size must be 1000 or more");
-		}
-		this.cacheSize = size;
+		withCacheConfiguration(TokenKeyCacheConfiguration.getInstance(cacheConfiguration.getCacheDuration(), size));
+		return this;
+	}
+
+	/**
+	 * Configures the token key cache. Use
+	 * {@link TokenKeyCacheConfiguration#getInstance(Duration, int)} to pass a
+	 * custom configuration.
+	 *
+	 * Note that the cache size must be 1000 or more and the cache duration must be
+	 * at least 600 seconds!
+	 * 
+	 * @param cacheConfiguration
+	 *            the cache configuration
+	 * @return this tokenKeyServiceWithCache
+	 */
+	public OAuth2TokenKeyServiceWithCache withCacheConfiguration(CacheConfiguration cacheConfiguration) {
+		checkCacheConfiguration(cacheConfiguration);
+		this.cacheConfiguration = cacheConfiguration;
+		LOGGER.debug("Configured token key cache with cacheDuration={} seconds and cacheSize={}",
+				cacheConfiguration.getCacheDuration().getSeconds(), cacheConfiguration.getCacheSize());
 		return this;
 	}
 
@@ -121,6 +148,18 @@ public class OAuth2TokenKeyServiceWithCache {
 		return getCache().getIfPresent(cacheKey);
 	}
 
+	private void checkCacheConfiguration(CacheConfiguration cacheConfiguration) {
+		Assertions.assertNotNull(cacheConfiguration, "CacheConfiguration must not be null!");
+		int size = cacheConfiguration.getCacheSize();
+		long timeInSeconds = cacheConfiguration.getCacheDuration().getSeconds();
+		if (size < 1000) {
+			throw new IllegalArgumentException("The cache size must be 1000 or more");
+		}
+		if (timeInSeconds < 600) {
+			throw new IllegalArgumentException("The cache validity must be minimum 600 seconds");
+		}
+	}
+
 	private void retrieveTokenKeysAndFillCache(URI jwksUri)
 			throws OAuth2ServiceException, InvalidKeySpecException, NoSuchAlgorithmException {
 		JsonWebKeySet keySet = JsonWebKeySetFactory.createFromJson(getTokenKeyService().retrieveTokenKeys(jwksUri));
@@ -135,8 +174,9 @@ public class OAuth2TokenKeyServiceWithCache {
 
 	private Cache<String, PublicKey> getCache() {
 		if (cache == null) {
-			cache = Caffeine.newBuilder().expireAfterWrite(cacheValidityInSeconds, TimeUnit.SECONDS)
-					.maximumSize(cacheSize)
+			cache = Caffeine.newBuilder()
+					.expireAfterWrite(cacheConfiguration.getCacheDuration())
+					.maximumSize(cacheConfiguration.getCacheSize())
 					.build();
 		}
 		return cache;
@@ -149,6 +189,13 @@ public class OAuth2TokenKeyServiceWithCache {
 		return tokenKeyService;
 	}
 
+	@Nonnull
+	@Override
+	public CacheConfiguration getCacheConfiguration() {
+		return cacheConfiguration;
+	}
+
+	@Override
 	public void clearCache() {
 		if (cache != null) {
 			cache.invalidateAll();
diff --git a/token-client/pom.xml b/token-client/pom.xml
index fcc2b425..b2fe9f1e 100644
--- a/token-client/pom.xml
+++ b/token-client/pom.xml
@@ -23,6 +23,10 @@
 			<groupId>${project.groupId}</groupId>
 			<artifactId>api</artifactId>
 		</dependency>
+		<dependency>
+			<groupId>com.sap.cloud.security</groupId>
+			<artifactId>java-api</artifactId>
+		</dependency>
         <dependency>
 			<groupId>org.springframework</groupId>
 			<artifactId>spring-web</artifactId>
diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/AbstractOAuth2TokenService.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/AbstractOAuth2TokenService.java
index 264a7df3..3667cdf4 100644
--- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/AbstractOAuth2TokenService.java
+++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/AbstractOAuth2TokenService.java
@@ -6,7 +6,7 @@ import com.github.benmanes.caffeine.cache.Ticker;
 import com.sap.cloud.security.xsuaa.Assertions;
 import com.sap.cloud.security.xsuaa.http.HttpHeaders;
 import com.sap.cloud.security.xsuaa.http.HttpHeadersFactory;
-import com.sap.cloud.security.xsuaa.tokenflows.CacheConfiguration;
+import com.sap.cloud.security.xsuaa.tokenflows.TokenCacheConfiguration;
 import com.sap.cloud.security.xsuaa.tokenflows.Cacheable;
 import com.sap.cloud.security.xsuaa.util.TokenLogger;
 import com.sap.cloud.security.xsuaa.util.UriUtil;
@@ -30,27 +30,28 @@ public abstract class AbstractOAuth2TokenService implements OAuth2TokenService,
 
 	private static final Logger LOGGER = LoggerFactory.getLogger(AbstractOAuth2TokenService.class);
 	private final Cache<CacheKey, OAuth2TokenResponse> responseCache;
-	private final CacheConfiguration cacheConfiguration;
+	private final TokenCacheConfiguration tokenCacheConfiguration;
 	private final TokenLogger tokenLogger;
 
 	public AbstractOAuth2TokenService() {
-		this(CacheConfiguration.DEFAULT, Ticker.systemTicker(), false);
+		this(TokenCacheConfiguration.defaultConfiguration(), Ticker.systemTicker(), false);
 	}
 
 	/**
 	 * Constructor used to overwrite the default cache configuration.
 	 *
-	 * @param cacheConfiguration
+	 * @param tokenCacheConfiguration
 	 *            the cache configuration used to configure the cache.
 	 */
-	public AbstractOAuth2TokenService(CacheConfiguration cacheConfiguration) {
-		this(cacheConfiguration, Ticker.systemTicker(), false);
+	public AbstractOAuth2TokenService(TokenCacheConfiguration tokenCacheConfiguration) {
+		this(tokenCacheConfiguration, Ticker.systemTicker(), false);
+
 	}
 
 	/**
 	 * This constructor is used for testing purposes only.
 	 *
-	 * @param cacheConfiguration
+	 * @param tokenCacheConfiguration
 	 *            sets the cache configuration used to configure or disable the
 	 *            cache.
 	 * @param cacheTicker
@@ -59,15 +60,16 @@ public abstract class AbstractOAuth2TokenService implements OAuth2TokenService,
 	 *            set to true disables maintenance jobs of the cache. This makes the
 	 *            cache slower but more predictable for testing.
 	 */
-	AbstractOAuth2TokenService(CacheConfiguration cacheConfiguration, Ticker cacheTicker, boolean sameThreadCache) {
-		Assertions.assertNotNull(cacheConfiguration, "cacheConfiguration is required");
-		this.cacheConfiguration = cacheConfiguration;
+	AbstractOAuth2TokenService(TokenCacheConfiguration tokenCacheConfiguration, Ticker cacheTicker,
+			boolean sameThreadCache) {
+		Assertions.assertNotNull(tokenCacheConfiguration, "cacheConfiguration is required");
+		this.tokenCacheConfiguration = tokenCacheConfiguration;
 		this.responseCache = createResponseCache(cacheTicker, sameThreadCache);
 		this.tokenLogger = TokenLogger.getInstance(LOGGER);
 		if (isCacheDisabled()) {
 			LOGGER.debug("Configured token service with cache disabled");
 		} else {
-			LOGGER.debug("Configured token service with {}", cacheConfiguration);
+			LOGGER.debug("Configured token service with {}", tokenCacheConfiguration);
 		}
 	}
 
@@ -78,8 +80,8 @@ public abstract class AbstractOAuth2TokenService implements OAuth2TokenService,
 
 	@Override
 	@Nonnull
-	public CacheConfiguration getCacheConfiguration() {
-		return cacheConfiguration;
+	public TokenCacheConfiguration getCacheConfiguration() {
+		return tokenCacheConfiguration;
 	}
 
 	@Override
@@ -259,20 +261,19 @@ public abstract class AbstractOAuth2TokenService implements OAuth2TokenService,
 	}
 
 	private void getAndCacheToken(CacheKey cacheKey) throws OAuth2ServiceException {
-		LOGGER.debug("Getting new token from url={} using cacheKey={}", cacheKey.tokenEndpointUri, cacheKey);
 		responseCache.put(cacheKey,
 				requestAccessToken(cacheKey.tokenEndpointUri, cacheKey.headers, cacheKey.parameters));
 	}
 
 	private boolean isCacheDisabled() {
-		return CacheConfiguration.CACHE_DISABLED.equals(getCacheConfiguration());
+		return getCacheConfiguration().isCacheDisabled();
 	}
 
 	private Cache<CacheKey, OAuth2TokenResponse> createResponseCache(Ticker cacheTicker, boolean sameThreadCache) {
 		Caffeine<Object, Object> cacheBuilder = Caffeine.newBuilder()
 				.maximumSize(getCacheConfiguration().getCacheSize())
 				.ticker(cacheTicker)
-				.expireAfterWrite(getCacheConfiguration().getExpireAfterWrite());
+				.expireAfterWrite(getCacheConfiguration().getCacheDuration());
 		if (sameThreadCache) {
 			cacheBuilder.executor(Runnable::run);
 		}
diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/DefaultOAuth2TokenService.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/DefaultOAuth2TokenService.java
index 7e108d6a..da45f341 100644
--- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/DefaultOAuth2TokenService.java
+++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/DefaultOAuth2TokenService.java
@@ -2,7 +2,7 @@ package com.sap.cloud.security.xsuaa.client;
 
 import com.sap.cloud.security.xsuaa.Assertions;
 import com.sap.cloud.security.xsuaa.http.HttpHeaders;
-import com.sap.cloud.security.xsuaa.tokenflows.CacheConfiguration;
+import com.sap.cloud.security.xsuaa.tokenflows.TokenCacheConfiguration;
 import com.sap.cloud.security.xsuaa.util.HttpClientUtil;
 import org.apache.http.HttpResponse;
 import org.apache.http.HttpStatus;
@@ -33,20 +33,20 @@ public class DefaultOAuth2TokenService extends AbstractOAuth2TokenService {
 	private final CloseableHttpClient httpClient;
 
 	public DefaultOAuth2TokenService() {
-		this(HttpClients.createDefault(), CacheConfiguration.DEFAULT);
+		this(HttpClients.createDefault(), TokenCacheConfiguration.defaultConfiguration());
 	}
 
 	public DefaultOAuth2TokenService(@Nonnull CloseableHttpClient httpClient) {
-		this(httpClient, CacheConfiguration.DEFAULT);
+		this(httpClient, TokenCacheConfiguration.defaultConfiguration());
 	}
 
-	public DefaultOAuth2TokenService(@Nonnull CacheConfiguration cacheConfiguration) {
-		this(HttpClients.createDefault(), cacheConfiguration);
+	public DefaultOAuth2TokenService(@Nonnull TokenCacheConfiguration tokenCacheConfiguration) {
+		this(HttpClients.createDefault(), tokenCacheConfiguration);
 	}
 
 	public DefaultOAuth2TokenService(@Nonnull CloseableHttpClient httpClient,
-			@Nonnull CacheConfiguration cacheConfiguration) {
-		super(cacheConfiguration);
+			@Nonnull TokenCacheConfiguration tokenCacheConfiguration) {
+		super(tokenCacheConfiguration);
 		Assertions.assertNotNull(httpClient, "http client is required");
 		this.httpClient = httpClient;
 	}
diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenService.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenService.java
index 6f82cfa4..d282593b 100644
--- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenService.java
+++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenService.java
@@ -1,8 +1,7 @@
 package com.sap.cloud.security.xsuaa.client;
 
 import com.sap.cloud.security.xsuaa.http.HttpHeaders;
-import com.sap.cloud.security.xsuaa.tokenflows.CacheConfiguration;
-import com.sap.cloud.security.xsuaa.util.TokenLogger;
+import com.sap.cloud.security.xsuaa.tokenflows.TokenCacheConfiguration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.http.HttpEntity;
@@ -31,20 +30,20 @@ public class XsuaaOAuth2TokenService extends AbstractOAuth2TokenService {
 	private final RestOperations restOperations;
 
 	public XsuaaOAuth2TokenService() {
-		this(new RestTemplate(), CacheConfiguration.DEFAULT);
+		this(new RestTemplate(), TokenCacheConfiguration.defaultConfiguration());
 	}
 
-	public XsuaaOAuth2TokenService(@Nonnull CacheConfiguration cacheConfiguration) {
-		this(new RestTemplate(), cacheConfiguration);
+	public XsuaaOAuth2TokenService(@Nonnull TokenCacheConfiguration tokenCacheConfiguration) {
+		this(new RestTemplate(), tokenCacheConfiguration);
 	}
 
 	public XsuaaOAuth2TokenService(@Nonnull RestOperations restOperations) {
-		this(restOperations, CacheConfiguration.DEFAULT);
+		this(restOperations, TokenCacheConfiguration.defaultConfiguration());
 	}
 
 	public XsuaaOAuth2TokenService(@Nonnull RestOperations restOperations,
-			@Nonnull CacheConfiguration cacheConfiguration) {
-		super(cacheConfiguration);
+			@Nonnull TokenCacheConfiguration tokenCacheConfiguration) {
+		super(tokenCacheConfiguration);
 		assertNotNull(restOperations, "restOperations is required");
 		this.restOperations = restOperations;
 	}
diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/tokenflows/CacheConfiguration.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/tokenflows/CacheConfiguration.java
deleted file mode 100644
index 6cc64cc7..00000000
--- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/tokenflows/CacheConfiguration.java
+++ /dev/null
@@ -1,116 +0,0 @@
-package com.sap.cloud.security.xsuaa.tokenflows;
-
-import com.sap.cloud.security.xsuaa.Assertions;
-
-import javax.annotation.Nonnull;
-import java.time.Duration;
-import java.util.Objects;
-
-/**
- * Data class to capture configuration options of token caches.
- */
-public class CacheConfiguration {
-
-	/**
-	 * Creates the default cache configuration.
-	 */
-	public static final CacheConfiguration DEFAULT = new CacheConfiguration(Duration.ofMinutes(10), 1000,
-			Duration.ofSeconds(30));
-
-	/**
-	 * Special cache configuration used to disable caching.
-	 */
-	public static final CacheConfiguration CACHE_DISABLED = new CacheConfiguration(Duration.ZERO, 0, Duration.ZERO);
-
-	private final Duration expireAfterWrite;
-	private final int cacheSize;
-	private final Duration tokenExpirationDelta;
-
-	/**
-	 * Creates a new {@link CacheConfiguration} instance with the given properties.
-	 * See {@link CacheConfiguration#getExpireAfterWrite()},
-	 * {@link CacheConfiguration#getCacheSize()} and
-	 * {@link CacheConfiguration#getTokenExpirationDelta()} for a explanation of the
-	 * respective properties.
-	 *
-	 * @param expireAfterWrite
-	 *            the expire after write cache property.
-	 * @param cacheSize
-	 *            the cache size property.
-	 * @param tokenExpirationDelta
-	 *            the token expiration delta.
-	 * @return a new {@link CacheConfiguration} instance.
-	 */
-	public static CacheConfiguration getInstance(@Nonnull Duration expireAfterWrite, int cacheSize,
-			@Nonnull Duration tokenExpirationDelta) {
-		Assertions.assertNotNull(expireAfterWrite, "Expire after write must not be null!");
-		return new CacheConfiguration(expireAfterWrite, cacheSize, tokenExpirationDelta);
-	}
-
-	private CacheConfiguration(Duration expireAfterWrite, int cacheSize, Duration tokenExpirationDelta) {
-		this.expireAfterWrite = expireAfterWrite;
-		this.cacheSize = cacheSize;
-		this.tokenExpirationDelta = tokenExpirationDelta;
-	}
-
-	/**
-	 * Returns the expire after write property of the cache. Cached elements are
-	 * automatically invalidated after this fixed duration has elapsed.
-	 *
-	 * @return duration of expire after write.
-	 */
-	@Nonnull
-	public Duration getExpireAfterWrite() {
-		return expireAfterWrite;
-	}
-
-	/**
-	 * Returns the number of elements the cache can hold.
-	 *
-	 * @return the size of the cache.
-	 */
-	public int getCacheSize() {
-		return cacheSize;
-	}
-
-	/**
-	 * If a cached token expires because its expiration time (exp) has been reached,
-	 * it should not be retrieved from the cache. A new token should be requested in
-	 * this case. For this to work cached tokens are being checked if their
-	 * expiration time (exp) has already been reached or if it is soon going to be
-	 * reached. The expiration delta controls what <em>soon</em> means. For example
-	 * if the expiration delta is set to 10 seconds and a cached token will expire
-	 * in 5 seconds, it will not be retrieved from cache anymore because the
-	 * duration until the token is expired is smaller than delta.
-	 *
-	 * @return the token expiration delta.
-	 */
-	public Duration getTokenExpirationDelta() {
-		return tokenExpirationDelta;
-	}
-
-	@Override
-	public boolean equals(Object o) {
-		if (this == o)
-			return true;
-		if (o == null || getClass() != o.getClass())
-			return false;
-		CacheConfiguration that = (CacheConfiguration) o;
-		return cacheSize == that.cacheSize &&
-				Objects.equals(expireAfterWrite, that.expireAfterWrite);
-	}
-
-	@Override
-	public int hashCode() {
-		return Objects.hash(expireAfterWrite, cacheSize);
-	}
-
-	@Override
-	public String toString() {
-		return "CacheConfiguration{" +
-				"expireAfterWrite=" + expireAfterWrite +
-				", cacheSize=" + cacheSize +
-				", tokenExpirationDelta=" + tokenExpirationDelta +
-				'}';
-	}
-}
diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/tokenflows/Cacheable.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/tokenflows/Cacheable.java
index 7075c92f..b19a5da8 100644
--- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/tokenflows/Cacheable.java
+++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/tokenflows/Cacheable.java
@@ -1,5 +1,7 @@
 package com.sap.cloud.security.xsuaa.tokenflows;
 
+import com.sap.cloud.security.config.CacheConfiguration;
+
 import javax.annotation.Nonnull;
 
 /**
@@ -9,8 +11,7 @@ import javax.annotation.Nonnull;
 public interface Cacheable {
 
 	/**
-	 * Provides the cache configuration of the component. Must not be null. Use
-	 * {@link CacheConfiguration#CACHE_DISABLED} to disable caching.
+	 * Provides the cache configuration of the component. Must not be null.
 	 *
 	 * @return the cache configuration
 	 */
diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/tokenflows/TokenCacheConfiguration.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/tokenflows/TokenCacheConfiguration.java
new file mode 100644
index 00000000..53c19a38
--- /dev/null
+++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/tokenflows/TokenCacheConfiguration.java
@@ -0,0 +1,128 @@
+package com.sap.cloud.security.xsuaa.tokenflows;
+
+import com.sap.cloud.security.config.CacheConfiguration;
+import com.sap.cloud.security.xsuaa.Assertions;
+
+import javax.annotation.Nonnull;
+import java.time.Duration;
+import java.util.Objects;
+
+/**
+ * Data class to capture configuration options of token caches.
+ */
+public class TokenCacheConfiguration implements CacheConfiguration {
+
+	private static final TokenCacheConfiguration DEFAULT = new TokenCacheConfiguration(Duration.ofMinutes(10), 1000,
+			Duration.ofSeconds(30));
+
+	private static final TokenCacheConfiguration CACHE_DISABLED = new DisabledCache();
+
+	private final Duration cacheDuration;
+	private final int cacheSize;
+	private final Duration tokenExpirationDelta;
+
+	/**
+	 * Creates a new {@link TokenCacheConfiguration} instance with the given
+	 * properties. See {@link CacheConfiguration#getCacheDuration()},
+	 * {@link CacheConfiguration#getCacheSize()} and
+	 * {@link TokenCacheConfiguration#getTokenExpirationDelta()} for an explanation
+	 * of the respective properties.
+	 *
+	 * @param cacheDuration
+	 *            the cache duration property.
+	 * @param cacheSize
+	 *            the cache size property.
+	 * @param tokenExpirationDelta
+	 *            the token expiration delta.
+	 * @return a new {@link TokenCacheConfiguration} instance.
+	 */
+	public static TokenCacheConfiguration getInstance(@Nonnull Duration cacheDuration, int cacheSize,
+			@Nonnull Duration tokenExpirationDelta) {
+		Assertions.assertNotNull(cacheDuration, "The cache duration write must not be null!");
+		return new TokenCacheConfiguration(cacheDuration, cacheSize, tokenExpirationDelta);
+	}
+
+	/**
+	 * The default cache configuration.
+	 */
+	public static TokenCacheConfiguration cacheDisabled() {
+		return CACHE_DISABLED;
+	}
+
+	/**
+	 * A special cache configuration used to disable caching.
+	 */
+	public static TokenCacheConfiguration defaultConfiguration() {
+		return DEFAULT;
+	}
+
+	private TokenCacheConfiguration(Duration cacheDuration, int cacheSize, Duration tokenExpirationDelta) {
+		this.cacheDuration = cacheDuration;
+		this.cacheSize = cacheSize;
+		this.tokenExpirationDelta = tokenExpirationDelta;
+	}
+
+	@Nonnull
+	@Override
+	public Duration getCacheDuration() {
+		return cacheDuration;
+	}
+
+	@Override
+	public int getCacheSize() {
+		return cacheSize;
+	}
+
+	/**
+	 * If a cached token expires because its expiration time (exp) has been reached,
+	 * it should not be retrieved from the cache. A new token should be requested in
+	 * this case. For this to work cached tokens are being checked if their
+	 * expiration time (exp) has already been reached or if it is soon going to be
+	 * reached. The expiration delta controls what <em>soon</em> means. For example
+	 * if the expiration delta is set to 10 seconds and a cached token will expire
+	 * in 5 seconds, it will not be retrieved from cache anymore because the
+	 * duration until the token is expired is smaller than delta.
+	 *
+	 * @return the token expiration delta.
+	 */
+	public Duration getTokenExpirationDelta() {
+		return tokenExpirationDelta;
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if (this == o)
+			return true;
+		if (o == null || getClass() != o.getClass())
+			return false;
+		TokenCacheConfiguration that = (TokenCacheConfiguration) o;
+		return cacheSize == that.cacheSize &&
+				Objects.equals(cacheDuration, that.cacheDuration);
+	}
+
+	@Override
+	public int hashCode() {
+		return Objects.hash(cacheDuration, cacheSize);
+	}
+
+	@Override
+	public String toString() {
+		return "TokenCacheConfiguration{" +
+				"cacheDuration=" + cacheDuration +
+				", cacheSize=" + cacheSize +
+				", tokenExpirationDelta=" + tokenExpirationDelta +
+				'}';
+	}
+
+	private static class DisabledCache extends TokenCacheConfiguration {
+
+		private DisabledCache() {
+			super(Duration.ZERO, 0, Duration.ZERO);
+		}
+
+		@Override
+		public boolean isCacheDisabled() {
+			return true;
+		}
+	}
+}


Test patch:
diff --git a/java-security/src/test/java/com/sap/cloud/security/json/DefaultJsonObjectTest.java b/java-security/src/test/java/com/sap/cloud/security/json/DefaultJsonObjectTest.java
index 0d43b5f9..fab9e563 100644
--- a/java-security/src/test/java/com/sap/cloud/security/json/DefaultJsonObjectTest.java
+++ b/java-security/src/test/java/com/sap/cloud/security/json/DefaultJsonObjectTest.java
@@ -121,7 +121,22 @@ public class DefaultJsonObjectTest {
 	}
 
 	@Test
-	public void getJsonObject_propertExists_returnsJsonObject() {
+	public void getAsLong_propertyExists_returnsValue() {
+		cut = createJsonParser(KEY_1, 42);
+
+		assertThat(cut.getAsLong(KEY_1)).isEqualTo(42);
+	}
+
+	@Test
+	public void getAsLong_propertyExistsButIsNotANumber_throwsException() {
+		cut = createJsonParser(KEY_1, "not a number");
+
+		assertThatThrownBy(() -> cut.getAsLong(KEY_1))
+				.isInstanceOf(JsonParsingException.class);
+	}
+
+	@Test
+	public void getJsonObject_propertyExists_returnsJsonObject() {
 		cut = createJsonParser(KEY_1, createJsonObjectString(KEY_1, STRING_VALUE));
 
 		JsonObject jsonObject = cut.getJsonObject(KEY_1);
@@ -131,7 +146,7 @@ public class DefaultJsonObjectTest {
 	}
 
 	@Test
-	public void getJsonObjects_propertExists_returnsJsonObjects() {
+	public void getJsonObjects_propertyExists_returnsJsonObjects() {
 		cut = createJsonParser(KEY_1, "[" + createJsonObjectString(KEY_1, STRING_VALUE) + "]");
 
 		List<JsonObject> jsonObjects = cut.getJsonObjects(KEY_1);
diff --git a/java-security/src/test/java/com/sap/cloud/security/token/validation/validators/OAuth2TokenKeyServiceWithCacheTest.java b/java-security/src/test/java/com/sap/cloud/security/token/validation/validators/OAuth2TokenKeyServiceWithCacheTest.java
index cc237332..c58a7b02 100644
--- a/java-security/src/test/java/com/sap/cloud/security/token/validation/validators/OAuth2TokenKeyServiceWithCacheTest.java
+++ b/java-security/src/test/java/com/sap/cloud/security/token/validation/validators/OAuth2TokenKeyServiceWithCacheTest.java
@@ -41,14 +41,14 @@ public class OAuth2TokenKeyServiceWithCacheTest {
 
 	@Test
 	public void changeCacheConfiguration() {
-		cut = cut.withCacheSize(1001).withCacheTime(601);
+		cut = cut.withCacheSize(1000).withCacheTime(600);
 
 		assertThatThrownBy(() -> {
-			cut = cut.withCacheSize(1000).withCacheTime(601);
+			cut = cut.withCacheSize(999).withCacheTime(600);
 		}).isInstanceOf(IllegalArgumentException.class).hasMessageContainingAll("size");
 
 		assertThatThrownBy(() -> {
-			cut = cut.withCacheSize(1001).withCacheTime(600);
+			cut = cut.withCacheSize(1000).withCacheTime(599);
 		}).isInstanceOf(IllegalArgumentException.class).hasMessageContainingAll("validity");
 	}
 
diff --git a/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/AbstractOAuth2TokenServiceTest.java b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/AbstractOAuth2TokenServiceTest.java
index 74ce2933..81c5744f 100644
--- a/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/AbstractOAuth2TokenServiceTest.java
+++ b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/AbstractOAuth2TokenServiceTest.java
@@ -2,7 +2,7 @@ package com.sap.cloud.security.xsuaa.client;
 
 import com.github.benmanes.caffeine.cache.Ticker;
 import com.sap.cloud.security.xsuaa.http.HttpHeaders;
-import com.sap.cloud.security.xsuaa.tokenflows.CacheConfiguration;
+import com.sap.cloud.security.xsuaa.tokenflows.TokenCacheConfiguration;
 import org.assertj.core.util.Maps;
 import org.junit.Before;
 import org.junit.Test;
@@ -21,7 +21,8 @@ public class AbstractOAuth2TokenServiceTest {
 	public static final URI TOKEN_ENDPOINT_URI = URI.create("http://test.token.endpoint/oauth/token");
 	public static final String SUBDOMAIN = "subdomain";
 	private static final Instant NOW = LocalDateTime.of(2020, 1, 1, 0, 0, 0, 0).toInstant(UTC);
-	public static final CacheConfiguration TEST_CACHE_CONFIGURATION = CacheConfiguration.DEFAULT;
+	public static final TokenCacheConfiguration TEST_CACHE_CONFIGURATION = TokenCacheConfiguration
+			.defaultConfiguration();
 
 	private TestOAuth2TokenService cut;
 
@@ -38,7 +39,7 @@ public class AbstractOAuth2TokenServiceTest {
 
 	@Test
 	public void retrieveAccessTokenViaClientCredentials_noCache_responseNotNull() throws OAuth2ServiceException {
-		cut = new TestOAuth2TokenService(CacheConfiguration.CACHE_DISABLED);
+		cut = new TestOAuth2TokenService(TokenCacheConfiguration.cacheDisabled());
 		OAuth2TokenResponse oAuth2TokenResponse = retrieveAccessTokenViaClientCredentials();
 		assertThat(oAuth2TokenResponse).isNotNull();
 	}
@@ -122,7 +123,7 @@ public class AbstractOAuth2TokenServiceTest {
 
 	@Test
 	public void requestAccessToken_cacheGloballyDisabled_requestsFreshTokens() throws OAuth2ServiceException {
-		cut = new TestOAuth2TokenService(CacheConfiguration.CACHE_DISABLED);
+		cut = new TestOAuth2TokenService(TokenCacheConfiguration.cacheDisabled());
 
 		OAuth2TokenResponse firstResponse = retrieveAccessTokenViaClientCredentials();
 		OAuth2TokenResponse secondResponse = retrieveAccessTokenViaClientCredentials();
@@ -152,7 +153,7 @@ public class AbstractOAuth2TokenServiceTest {
 	@Test
 	public void requestAccessToken_tokensAreInvalidatedAfterTime_requestsFreshToken() throws OAuth2ServiceException {
 		OAuth2TokenResponse firstResponse = retrieveAccessTokenViaClientCredentials();
-		cut.advanceTime(TEST_CACHE_CONFIGURATION.getExpireAfterWrite());
+		cut.advanceTime(TEST_CACHE_CONFIGURATION.getCacheDuration());
 		OAuth2TokenResponse secondResponse = retrieveAccessTokenViaClientCredentials();
 
 		assertThat(cut.tokenRequestCallCount).isEqualTo(2);
@@ -261,13 +262,13 @@ public class AbstractOAuth2TokenServiceTest {
 		return new ClientCredentials("clientId", "clientSecret");
 	}
 
-	private CacheConfiguration cacheConfigurationWithDelta(Duration delta) {
-		return CacheConfiguration.getInstance(TEST_CACHE_CONFIGURATION.getExpireAfterWrite(),
+	private TokenCacheConfiguration cacheConfigurationWithDelta(Duration delta) {
+		return TokenCacheConfiguration.getInstance(TEST_CACHE_CONFIGURATION.getCacheDuration(),
 				TEST_CACHE_CONFIGURATION.getCacheSize(), delta);
 	}
 
-	private CacheConfiguration cacheConfigurationWithSize(int size) {
-		return CacheConfiguration.getInstance(TEST_CACHE_CONFIGURATION.getExpireAfterWrite(), size,
+	private TokenCacheConfiguration cacheConfigurationWithSize(int size) {
+		return TokenCacheConfiguration.getInstance(TEST_CACHE_CONFIGURATION.getCacheDuration(), size,
 				TEST_CACHE_CONFIGURATION.getTokenExpirationDelta());
 	}
 
@@ -278,8 +279,8 @@ public class AbstractOAuth2TokenServiceTest {
 		private Instant expiredAt = NOW.plus(Duration.ofDays(1));
 		private Clock clock = Clock.fixed(NOW, UTC);
 
-		public TestOAuth2TokenService(CacheConfiguration cacheConfiguration) {
-			super(cacheConfiguration, testCacheTicker, true);
+		public TestOAuth2TokenService(TokenCacheConfiguration tokenCacheConfiguration) {
+			super(tokenCacheConfiguration, testCacheTicker, true);
 			testCacheTicker.reset();
 		}
 
diff --git a/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenServiceTest.java b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenServiceTest.java
index 15c81b38..ff43b05c 100644
--- a/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenServiceTest.java
+++ b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenServiceTest.java
@@ -1,6 +1,6 @@
 package com.sap.cloud.security.xsuaa.client;
 
-import com.sap.cloud.security.xsuaa.tokenflows.CacheConfiguration;
+import com.sap.cloud.security.xsuaa.tokenflows.TokenCacheConfiguration;
 import org.junit.Test;
 import org.mockito.Mockito;
 import org.springframework.web.client.RestOperations;
@@ -9,7 +9,7 @@ public class XsuaaOAuth2TokenServiceTest {
 
 	@Test(expected = IllegalArgumentException.class)
 	public void initialize_throwsIfRestOperationsIsNull() {
-		new XsuaaOAuth2TokenService(null, CacheConfiguration.CACHE_DISABLED);
+		new XsuaaOAuth2TokenService(null, TokenCacheConfiguration.cacheDisabled());
 	}
 
 	@Test(expected = IllegalArgumentException.class)