Repository: SAP/cloud-security-xsuaa-integration
Pull request #736 (2021-12-01 14:50:27)

Hotfix cache miss issue
This PR fixes the issue of mutable HttpHeaders object being updated in cacheKey after the MDC header is added to the request. 
Improves test coverage and replace usage of deprecated retrieveAccessTokenViaClientCredentialsGrant method.


Patch:
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 3378be22..8de58620 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
@@ -70,10 +70,9 @@ public class XsuaaOAuth2TokenService extends AbstractOAuth2TokenService {
 		// Create URI
 		UriComponentsBuilder builder = UriComponentsBuilder.fromUri(tokenEndpointUri);
 		URI requestUri = builder.build().encode().toUri();
-		headers.withHeader(MDCHelper.CORRELATION_HEADER, MDCHelper.getOrCreateCorrelationId());
 		org.springframework.http.HttpHeaders springHeaders = new org.springframework.http.HttpHeaders();
 		headers.getHeaders().forEach(h -> springHeaders.add(h.getName(), h.getValue()));
-
+		springHeaders.add(MDCHelper.CORRELATION_HEADER, MDCHelper.getOrCreateCorrelationId());
 		// Create entity
 		HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(copyIntoForm(parameters),
 				springHeaders);


Test patch:
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 d8bb026f..43c35bc7 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
@@ -170,6 +170,7 @@ public class AbstractOAuth2TokenServiceTest {
 		OAuth2TokenResponse secondResponse = retrieveAccessTokenViaClientCredentials();
 
 		assertThat(firstResponse).isSameAs(secondResponse);
+		assertThat(cut.tokenRequestCallCount).isEqualTo(1);
 	}
 
 	@Test
@@ -200,8 +201,7 @@ public class AbstractOAuth2TokenServiceTest {
 		OAuth2TokenResponse lastResponse = retrieveAccessTokenViaClientCredentials(clientIdentity(), false);
 
 		assertThat(cut.tokenRequestCallCount).isEqualTo(2);
-		assertThat(firstResponse).isNotSameAs(secondResponse);
-		assertThat(firstResponse).isSameAs(lastResponse);
+		assertThat(firstResponse).isNotSameAs(secondResponse).isSameAs(lastResponse);
 	}
 
 	@Test
@@ -261,7 +261,7 @@ public class AbstractOAuth2TokenServiceTest {
 	private OAuth2TokenResponse retrieveAccessTokenViaJwtBearerTokenGrant(String token,
 			Map<String, String> optionalParameters) throws OAuth2ServiceException {
 		return cut.retrieveAccessTokenViaJwtBearerTokenGrant(TOKEN_ENDPOINT_URI, clientIdentity(), token, null,
-				optionalParameters);
+				optionalParameters, false);
 	}
 
 	private OAuth2TokenResponse retrieveAccessTokenViaClientCredentials() throws OAuth2ServiceException {
diff --git a/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/DefaultOAuth2TokenServiceTest.java b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/DefaultOAuth2TokenServiceTest.java
index ba50b464..62e28bc6 100644
--- a/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/DefaultOAuth2TokenServiceTest.java
+++ b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/DefaultOAuth2TokenServiceTest.java
@@ -36,7 +36,7 @@ import java.time.Instant;
 import java.util.Map;
 
 import static com.sap.cloud.security.servlet.MDCHelper.CORRELATION_ID;
-import static java.util.Collections.*;
+import static java.util.Collections.emptyMap;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.ArgumentMatchers.any;
diff --git a/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenServiceClientCredentialsTest.java b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenServiceClientCredentialsTest.java
index c7a240dd..cc5c18dc 100644
--- a/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenServiceClientCredentialsTest.java
+++ b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenServiceClientCredentialsTest.java
@@ -35,6 +35,7 @@ import java.util.Map;
 
 import static com.sap.cloud.security.servlet.MDCHelper.CORRELATION_ID;
 import static com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants.*;
+import static java.util.Collections.emptyMap;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -67,19 +68,21 @@ public class XsuaaOAuth2TokenServiceClientCredentialsTest {
 
 	@Test
 	public void retrieveToken_throwsOnNullValues() {
-		assertThatThrownBy(() -> cut.retrieveAccessTokenViaClientCredentialsGrant(null, clientIdentity, null, null))
-				.isInstanceOf(IllegalArgumentException.class).hasMessageStartingWith("tokenEndpointUri");
+		assertThatThrownBy(
+				() -> cut.retrieveAccessTokenViaClientCredentialsGrant(null, clientIdentity, null, null, null, false))
+						.isInstanceOf(IllegalArgumentException.class).hasMessageStartingWith("tokenEndpointUri");
 
-		assertThatThrownBy(() -> cut.retrieveAccessTokenViaClientCredentialsGrant(tokenEndpoint, null, null, null))
-				.isInstanceOf(IllegalArgumentException.class).hasMessageStartingWith("clientIdentity");
+		assertThatThrownBy(
+				() -> cut.retrieveAccessTokenViaClientCredentialsGrant(tokenEndpoint, null, null, null, null, false))
+						.isInstanceOf(IllegalArgumentException.class).hasMessageStartingWith("clientIdentity");
 	}
 
 	@Test(expected = OAuth2ServiceException.class)
 	public void retrieveToken_throwsIfHttpStatusUnauthorized() throws OAuth2ServiceException {
 		Mockito.when(mockRestOperations.postForEntity(any(URI.class), any(HttpEntity.class), eq(Map.class)))
 				.thenThrow(new HttpClientErrorException(HttpStatus.UNAUTHORIZED));
-		cut.retrieveAccessTokenViaClientCredentialsGrant(tokenEndpoint, clientIdentity,
-				null, null);
+		cut.retrieveAccessTokenViaClientCredentialsGrant(tokenEndpoint, clientIdentity, null,
+				null, null, false);
 	}
 
 	@Test(expected = OAuth2ServiceException.class)
@@ -87,7 +90,7 @@ public class XsuaaOAuth2TokenServiceClientCredentialsTest {
 		Mockito.when(mockRestOperations.postForEntity(any(URI.class), any(HttpEntity.class), eq(Map.class)))
 				.thenThrow(new HttpClientErrorException(HttpStatus.BAD_REQUEST));
 		cut.retrieveAccessTokenViaClientCredentialsGrant(tokenEndpoint, clientIdentity,
-				null, null);
+				null, null, null, false);
 	}
 
 	@Test
@@ -105,7 +108,7 @@ public class XsuaaOAuth2TokenServiceClientCredentialsTest {
 
 		OAuth2TokenResponse accessToken = cut.retrieveAccessTokenViaClientCredentialsGrant(tokenEndpoint,
 				clientIdentity,
-				null, null);
+				null, null, null, false);
 		assertThat(accessToken.getAccessToken(), is(responseMap.get(ACCESS_TOKEN)));
 		assertThat(accessToken.getTokenType(), is(responseMap.get(TOKEN_TYPE)));
 		assertNotNull(accessToken.getExpiredAt());
@@ -129,8 +132,8 @@ public class XsuaaOAuth2TokenServiceClientCredentialsTest {
 				.thenReturn(new ResponseEntity<>(responseMap, HttpStatus.OK));
 
 		OAuth2TokenResponse accessToken = cut.retrieveAccessTokenViaClientCredentialsGrant(tokenEndpoint,
-				clientIdentity, null,
-				additionalParameters);
+				clientIdentity, null, null,
+				additionalParameters, false);
 		assertThat(accessToken.getAccessToken(), is(responseMap.get(ACCESS_TOKEN)));
 	}
 
@@ -150,11 +153,22 @@ public class XsuaaOAuth2TokenServiceClientCredentialsTest {
 		overwrittenGrantType.put(GRANT_TYPE, "overwrite-obligatory-param");
 
 		OAuth2TokenResponse accessToken = cut.retrieveAccessTokenViaClientCredentialsGrant(tokenEndpoint,
-				clientIdentity, null,
-				overwrittenGrantType);
+				clientIdentity, null, null,
+				overwrittenGrantType, false);
 		assertThat(accessToken.getAccessToken(), is(responseMap.get(ACCESS_TOKEN)));
 	}
 
+	@Test
+	public void retrieveToken_testCache() throws IOException {
+		when(mockRestOperations.postForEntity(any(), any(), any()))
+				.thenReturn(new ResponseEntity<>(responseMap, HttpStatus.OK));
+
+		cut.retrieveAccessTokenViaClientCredentialsGrant(tokenEndpoint, clientIdentity, null, null, emptyMap(), false);
+		cut.retrieveAccessTokenViaClientCredentialsGrant(tokenEndpoint, clientIdentity, null, null, emptyMap(), false);
+
+		verify(mockRestOperations, times(1)).postForEntity(any(), any(), any());
+	}
+
 	@Test
 	public void correlationIdProvisioning() throws IOException {
 		when(mockRestOperations.postForEntity(any(), any(), any()))
@@ -172,7 +186,7 @@ public class XsuaaOAuth2TokenServiceClientCredentialsTest {
 
 		MDC.put(CORRELATION_ID, "my-correlation-id");
 		cut.retrieveAccessTokenViaClientCredentialsGrant(tokenEndpoint,
-				clientIdentity, null, null, Collections.emptyMap(), false);
+				clientIdentity, "zone", null, Collections.emptyMap(), false);
 		Assertions.assertThat(listAppender.list.get(1).getLevel()).isEqualTo(Level.DEBUG);
 		Assertions.assertThat(listAppender.list.get(1).getArgumentArray()[1]).isEqualTo(("my-correlation-id"));
 		MDC.clear();
diff --git a/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenServiceJwtBearerTokenTest.java b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenServiceJwtBearerTokenTest.java
index baeb5ee5..f914c0f8 100644
--- a/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenServiceJwtBearerTokenTest.java
+++ b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenServiceJwtBearerTokenTest.java
@@ -19,27 +19,28 @@ import org.springframework.util.MultiValueMap;
 import org.springframework.web.client.HttpClientErrorException;
 import org.springframework.web.client.RestOperations;
 
+import java.io.IOException;
 import java.net.URI;
 import java.util.HashMap;
 import java.util.Map;
 
 import static com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants.*;
+import static java.util.Collections.emptyMap;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
 
 @RunWith(MockitoJUnitRunner.class)
 public class XsuaaOAuth2TokenServiceJwtBearerTokenTest {
 
 	private OAuth2TokenService cut;
 
-	private String jwtToken = "jwtToken";
-	private String subdomain = "subdomain";
-	private ClientIdentity clientIdentity = new ClientCredentials("theClientId", "test321");
-	private URI tokenEndpoint = URI.create("https://subdomain.myauth.server.com/oauth/token");
+	private final String jwtToken = "jwtToken";
+	private final String subdomain = "subdomain";
+	private final ClientIdentity clientIdentity = new ClientCredentials("theClientId", "test321");
+	private final URI tokenEndpoint = URI.create("https://subdomain.myauth.server.com/oauth/token");
 	private Map<String, String> optionalParameters;
 	private Map<String, String> response;
 
@@ -63,7 +64,7 @@ public class XsuaaOAuth2TokenServiceJwtBearerTokenTest {
 		throwExceptionOnPost(HttpStatus.UNAUTHORIZED);
 
 		cut.retrieveAccessTokenViaJwtBearerTokenGrant(tokenEndpoint, clientIdentity,
-				jwtToken, null, null);
+				jwtToken, null, null, false);
 	}
 
 	@Test(expected = OAuth2ServiceException.class)
@@ -71,23 +72,23 @@ public class XsuaaOAuth2TokenServiceJwtBearerTokenTest {
 		throwExceptionOnPost(HttpStatus.BAD_REQUEST);
 
 		cut.retrieveAccessTokenViaJwtBearerTokenGrant(tokenEndpoint, clientIdentity,
-				jwtToken, null, null);
+				jwtToken, null, null, false);
 	}
 
 	@Test
 	public void retrieveToken_requiredParametersMissing_throwsException() {
 		assertThatThrownBy(() -> cut.retrieveAccessTokenViaJwtBearerTokenGrant(null, clientIdentity,
-				jwtToken, subdomain, optionalParameters)).isInstanceOf(IllegalArgumentException.class);
+				jwtToken, subdomain, optionalParameters, false)).isInstanceOf(IllegalArgumentException.class);
 		assertThatThrownBy(() -> cut.retrieveAccessTokenViaJwtBearerTokenGrant(tokenEndpoint, null,
-				jwtToken, subdomain, optionalParameters)).isInstanceOf(IllegalArgumentException.class);
+				jwtToken, subdomain, optionalParameters, false)).isInstanceOf(IllegalArgumentException.class);
 		assertThatThrownBy(() -> cut.retrieveAccessTokenViaJwtBearerTokenGrant(tokenEndpoint, clientIdentity,
-				null, subdomain, optionalParameters)).isInstanceOf(IllegalArgumentException.class);
+				null, subdomain, optionalParameters, false)).isInstanceOf(IllegalArgumentException.class);
 	}
 
 	@Test
 	public void retrieveToken_callsTokenEndpoint() throws OAuth2ServiceException {
 		cut.retrieveAccessTokenViaJwtBearerTokenGrant(tokenEndpoint, clientIdentity,
-				jwtToken, null, null);
+				jwtToken, null, null, false);
 
 		Mockito.verify(mockRestOperations, times(1))
 				.postForEntity(eq(tokenEndpoint), any(), any());
@@ -96,7 +97,7 @@ public class XsuaaOAuth2TokenServiceJwtBearerTokenTest {
 	@Test
 	public void retrieveToken_setsCorrectGrantType() throws OAuth2ServiceException {
 		cut.retrieveAccessTokenViaJwtBearerTokenGrant(tokenEndpoint, clientIdentity,
-				jwtToken, null, null);
+				jwtToken, null, null, false);
 
 		ArgumentCaptor<HttpEntity<MultiValueMap<String, String>>> requestEntityCaptor = captureRequestEntity();
 
@@ -107,7 +108,7 @@ public class XsuaaOAuth2TokenServiceJwtBearerTokenTest {
 	@Test
 	public void retrieveToken_setsToken() throws OAuth2ServiceException {
 		cut.retrieveAccessTokenViaJwtBearerTokenGrant(tokenEndpoint, clientIdentity,
-				jwtToken, null, null);
+				jwtToken, null, null, false);
 
 		ArgumentCaptor<HttpEntity<MultiValueMap<String, String>>> requestEntityCaptor = captureRequestEntity();
 
@@ -117,7 +118,7 @@ public class XsuaaOAuth2TokenServiceJwtBearerTokenTest {
 	@Test
 	public void retrieveToken_setsClientCredentials() throws OAuth2ServiceException {
 		cut.retrieveAccessTokenViaJwtBearerTokenGrant(tokenEndpoint, clientIdentity,
-				jwtToken, null, null);
+				jwtToken, null, null, false);
 
 		ArgumentCaptor<HttpEntity<MultiValueMap<String, String>>> requestEntityCaptor = captureRequestEntity();
 
@@ -136,7 +137,7 @@ public class XsuaaOAuth2TokenServiceJwtBearerTokenTest {
 		optionalParameters.put(responseTypeParameterName, loginHint);
 
 		cut.retrieveAccessTokenViaJwtBearerTokenGrant(tokenEndpoint, clientIdentity,
-				jwtToken, null, optionalParameters);
+				jwtToken, null, optionalParameters, false);
 
 		ArgumentCaptor<HttpEntity<MultiValueMap<String, String>>> requestEntityCaptor = captureRequestEntity();
 		assertThat(valueOfParameter(tokenFormatParameterName, requestEntityCaptor)).isEqualTo(tokenFormat);
@@ -146,7 +147,7 @@ public class XsuaaOAuth2TokenServiceJwtBearerTokenTest {
 	@Test
 	public void retrieveToken_setsCorrectHeaders() throws OAuth2ServiceException {
 		cut.retrieveAccessTokenViaJwtBearerTokenGrant(tokenEndpoint, clientIdentity,
-				jwtToken, null, optionalParameters);
+				jwtToken, null, optionalParameters, false);
 
 		ArgumentCaptor<HttpEntity<MultiValueMap<String, String>>> requestEntityCaptor = captureRequestEntity();
 		HttpHeaders headers = requestEntityCaptor.getValue().getHeaders();
@@ -159,13 +160,21 @@ public class XsuaaOAuth2TokenServiceJwtBearerTokenTest {
 	public void retrieveToken() throws OAuth2ServiceException {
 		OAuth2TokenResponse actualResponse = cut.retrieveAccessTokenViaJwtBearerTokenGrant(tokenEndpoint,
 				clientIdentity,
-				jwtToken, null, null);
+				jwtToken, null, null, false);
 
 		assertThat(actualResponse.getAccessToken()).isEqualTo(response.get(ACCESS_TOKEN));
 		assertThat(actualResponse.getTokenType()).isEqualTo(response.get(TOKEN_TYPE));
 		assertThat(actualResponse.getExpiredAt()).isNotNull();
 	}
 
+	@Test
+	public void retrieveToken_testCache() throws IOException {
+		cut.retrieveAccessTokenViaJwtBearerTokenGrant(tokenEndpoint, clientIdentity, jwtToken, null, emptyMap(), false);
+		cut.retrieveAccessTokenViaJwtBearerTokenGrant(tokenEndpoint, clientIdentity, jwtToken, null, emptyMap(), false);
+
+		verify(mockRestOperations, times(1)).postForEntity(any(), any(), any());
+	}
+
 	private ArgumentCaptor<HttpEntity<MultiValueMap<String, String>>> captureRequestEntity() {
 		ArgumentCaptor<HttpEntity<MultiValueMap<String, String>>> requestEntityCaptor = ArgumentCaptor
 				.forClass(HttpEntity.class);
diff --git a/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenServicePasswordTest.java b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenServicePasswordTest.java
index b4301290..956003ab 100644
--- a/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenServicePasswordTest.java
+++ b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaOAuth2TokenServicePasswordTest.java
@@ -19,30 +19,31 @@ import org.springframework.util.MultiValueMap;
 import org.springframework.web.client.HttpClientErrorException;
 import org.springframework.web.client.RestOperations;
 
+import java.io.IOException;
 import java.net.URI;
 import java.util.HashMap;
 import java.util.Map;
 
 import static com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants.*;
+import static java.util.Collections.emptyMap;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
 
 @RunWith(MockitoJUnitRunner.class)
 public class XsuaaOAuth2TokenServicePasswordTest {
 
 	private OAuth2TokenService cut;
 
-	private String clientSecret = "test321";
-	private String clientId = "theClientId";
-	private String password = "test123";
-	private String username = "bob";
-	private String subdomain = "subdomain";
-	private ClientIdentity clientIdentity = new ClientCredentials(clientId, clientSecret);
-	private URI tokenEndpoint = URI.create("https://subdomain.myauth.server.com/oauth/token");
+	private final String clientSecret = "test321";
+	private final String clientId = "theClientId";
+	private final String password = "test123";
+	private final String username = "bob";
+	private final String subdomain = "subdomain";
+	private final ClientIdentity clientIdentity = new ClientCredentials(clientId, clientSecret);
+	private final URI tokenEndpoint = URI.create("https://subdomain.myauth.server.com/oauth/token");
 	private Map<String, String> optionalParameters;
 	private Map<String, String> response;
 
@@ -66,7 +67,7 @@ public class XsuaaOAuth2TokenServicePasswordTest {
 		throwExceptionOnPost(HttpStatus.UNAUTHORIZED);
 
 		cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientIdentity,
-				username, password, null, null);
+				username, password, null, null, false);
 	}
 
 	@Test(expected = OAuth2ServiceException.class)
@@ -74,25 +75,25 @@ public class XsuaaOAuth2TokenServicePasswordTest {
 		throwExceptionOnPost(HttpStatus.BAD_REQUEST);
 
 		cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientIdentity,
-				username, password, null, null);
+				username, password, null, null, false);
 	}
 
 	@Test
 	public void retrieveToken_requiredParametersMissing_throwsException() {
 		assertThatThrownBy(() -> cut.retrieveAccessTokenViaPasswordGrant(null, clientIdentity,
-				username, password, subdomain, optionalParameters)).isInstanceOf(IllegalArgumentException.class);
+				username, password, subdomain, optionalParameters, false)).isInstanceOf(IllegalArgumentException.class);
 		assertThatThrownBy(() -> cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, null,
-				username, password, subdomain, optionalParameters)).isInstanceOf(IllegalArgumentException.class);
+				username, password, subdomain, optionalParameters, false)).isInstanceOf(IllegalArgumentException.class);
 		assertThatThrownBy(() -> cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientIdentity,
-				null, password, subdomain, optionalParameters)).isInstanceOf(IllegalArgumentException.class);
+				null, password, subdomain, optionalParameters, false)).isInstanceOf(IllegalArgumentException.class);
 		assertThatThrownBy(() -> cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientIdentity,
-				username, null, subdomain, optionalParameters)).isInstanceOf(IllegalArgumentException.class);
+				username, null, subdomain, optionalParameters, false)).isInstanceOf(IllegalArgumentException.class);
 	}
 
 	@Test
 	public void retrieveToken_callsTokenEndpoint() throws OAuth2ServiceException {
 		cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientIdentity,
-				username, password, null, null);
+				username, password, null, null, false);
 
 		Mockito.verify(mockRestOperations, times(1))
 				.postForEntity(eq(tokenEndpoint), any(), any());
@@ -101,7 +102,7 @@ public class XsuaaOAuth2TokenServicePasswordTest {
 	@Test
 	public void retrieveToken_setsCorrectGrantType() throws OAuth2ServiceException {
 		cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientIdentity,
-				username, password, null, null);
+				username, password, null, null, false);
 
 		ArgumentCaptor<HttpEntity<MultiValueMap<String, String>>> requestEntityCaptor = captureRequestEntity();
 
@@ -112,7 +113,7 @@ public class XsuaaOAuth2TokenServicePasswordTest {
 	@Test
 	public void retrieveToken_setsUsername() throws OAuth2ServiceException {
 		cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientIdentity,
-				username, password, null, null);
+				username, password, null, null, false);
 
 		ArgumentCaptor<HttpEntity<MultiValueMap<String, String>>> requestEntityCaptor = captureRequestEntity();
 
@@ -122,7 +123,7 @@ public class XsuaaOAuth2TokenServicePasswordTest {
 	@Test
 	public void retrieveToken_setsPassword() throws OAuth2ServiceException {
 		cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientIdentity,
-				username, password, null, null);
+				username, password, null, null, false);
 
 		ArgumentCaptor<HttpEntity<MultiValueMap<String, String>>> requestEntityCaptor = captureRequestEntity();
 
@@ -132,7 +133,7 @@ public class XsuaaOAuth2TokenServicePasswordTest {
 	@Test
 	public void retrieveToken_setsClientCredentials() throws OAuth2ServiceException {
 		cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientIdentity,
-				username, password, null, null);
+				username, password, null, null, false);
 
 		ArgumentCaptor<HttpEntity<MultiValueMap<String, String>>> requestEntityCaptor = captureRequestEntity();
 
@@ -151,7 +152,7 @@ public class XsuaaOAuth2TokenServicePasswordTest {
 		optionalParameters.put(loginHintParameterKey, loginHint);
 
 		cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientIdentity,
-				username, password, null, optionalParameters);
+				username, password, null, optionalParameters, false);
 
 		ArgumentCaptor<HttpEntity<MultiValueMap<String, String>>> requestEntityCaptor = captureRequestEntity();
 		assertThat(valueOfParameter(tokenFormatParameterKey, requestEntityCaptor)).isEqualTo(tokenFormat);
@@ -161,7 +162,7 @@ public class XsuaaOAuth2TokenServicePasswordTest {
 	@Test
 	public void retrieveToken_setsCorrectHeaders() throws OAuth2ServiceException {
 		cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientIdentity,
-				username, password, null, optionalParameters);
+				username, password, null, optionalParameters, false);
 
 		ArgumentCaptor<HttpEntity<MultiValueMap<String, String>>> requestEntityCaptor = captureRequestEntity();
 		HttpHeaders headers = requestEntityCaptor.getValue().getHeaders();
@@ -173,11 +174,21 @@ public class XsuaaOAuth2TokenServicePasswordTest {
 	@Test
 	public void retrieveToken() throws OAuth2ServiceException {
 		OAuth2TokenResponse actualResponse = cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientIdentity,
-				username, password, null, null);
+				username, password, null, null, false);
 
 		assertThat(actualResponse.getAccessToken()).isEqualTo(response.get(ACCESS_TOKEN));
 		assertThat(actualResponse.getTokenType()).isEqualTo(response.get(TOKEN_TYPE));
-		assertThat(actualResponse.getExpiredAtDate()).isNotNull();
+		assertThat(actualResponse.getExpiredAt()).isNotNull();
+	}
+
+	@Test
+	public void retrieveToken_testCache() throws IOException {
+		cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientIdentity, username, password, null, emptyMap(),
+				false);
+		cut.retrieveAccessTokenViaPasswordGrant(tokenEndpoint, clientIdentity, username, password, null, emptyMap(),
+				false);
+
+		verify(mockRestOperations, times(1)).postForEntity(any(), any(), any());
 	}
 
 	private ArgumentCaptor<HttpEntity<MultiValueMap<String, String>>> captureRequestEntity() {