Repository: SAP/cloud-security-xsuaa-integration
Pull request #961 (2022-08-09 17:17:16)

Workaround for malformed verificationkey in xsuaa configuration
- As a workaround for cases when xsuaa service configuration `verificationkey` attribute is malformed and throws `IllegalArgumentException` when decoding RSA public key, `XsuaaJwtDecoder` will catch `IllegalArgumentException` to avoid 500 error to be thrown by it. 
>RC of such malformed `verificationkey` remains unclear as the affected user could not provide service configuration details, therefore this is a workaround not an actual fix

- introduce stricter rule for fallback verificationkey validation


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cd029e69..a7ae3880 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,16 @@
 # Change Log
 All notable changes to this project will be documented in this file.
 
+## 2.13.1
+- [token-client]
+  - `DefaultHttpClientFactory` creates `CloseableHttpClient` with disabled redirects to avoid security vulnerabilities. :bangbang: For your custom `CloseableHttpClient` implementation make sure to disable redirects as well.
+  - all `TokenServices` and `TokenKeyServices` have been enhanced to add to all outgoing requests a `user-agent` header that contains value `token-client/x.x.x` where x.x.x is token-client version being used
+
+#### Dependency upgrades
+* Bump `spring.boot.version` from 2.7.1 to 2.7.2.
+* Bump https://github.com/SAP/btp-environment-variable-access from 0.3.1 to 0.4.0.
+* Bump reactor-core from 3.4.21 to 3.4.22
+
 ## 2.13.0
 - [env] 
   - uses https://github.com/SAP/btp-environment-variable-access (version 0.3.1), which supports access to service credentials in K8s/Kyma environment provisioned by [SAP BTP Service Operator](https://github.com/SAP/sap-btp-service-operator). Usage of service-catalog is no longer supported. With that there is no service-manager longer required to distinguish the service instance plan, when multiple xsuaa instances are bound (#855).
@@ -11,8 +21,8 @@ All notable changes to this project will be documented in this file.
 
 #### Dependency upgrades
 * Bump log4j2.version from 2.17.2 to 2.18.0
-* Bumps `spring.core.version` from 5.3.21 to 5.3.22.
-* Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.4.19 to 3.4.21.
+* Bump `spring.core.version` from 5.3.21 to 5.3.22.
+* Bump [reactor-core](https://github.com/reactor/reactor-core) from 3.4.19 to 3.4.21.
 
 
 **Full Changelog**: https://github.com/SAP/cloud-security-xsuaa-integration/compare/2.12.3...2.13.0
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoder.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoder.java
index 16e451e2..132ba7d5 100644
--- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoder.java
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoder.java
@@ -40,8 +40,8 @@ public class XsuaaJwtDecoder implements JwtDecoder {
 	private final XsuaaServiceConfiguration xsuaaServiceConfiguration;
 
 	Cache<String, JwtDecoder> cache;
-	private OAuth2TokenValidator<Jwt> tokenValidators;
-	private Collection<PostValidationAction> postValidationActions;
+	private final OAuth2TokenValidator<Jwt> tokenValidators;
+	private final Collection<PostValidationAction> postValidationActions;
 	private TokenInfoExtractor tokenInfoExtractor;
 	private RestOperations restOperations;
 
@@ -109,8 +109,12 @@ public class XsuaaJwtDecoder implements JwtDecoder {
 			String kid = tokenInfoExtractor.getKid(jwt);
 			String uaaDomain = tokenInfoExtractor.getUaaDomain(jwt);
 			return verifyToken(jwt.getParsedString(), jku, kid, uaaDomain);
-		} catch (JwtException e) {
-			return tryToVerifyWithVerificationKey(jwt.getParsedString(), e);
+		} catch (BadJwtException e) {
+			if (e.getMessage().contains("Couldn't retrieve remote JWK set") || e.getMessage().contains("Cannot verify with online token key, uaadomain is")) {
+				return tryToVerifyWithVerificationKey(jwt.getParsedString(), e);
+			} else {
+				throw e;
+			}
 		}
 	}
 
@@ -188,12 +192,13 @@ public class XsuaaJwtDecoder implements JwtDecoder {
 
 	private Jwt verifyWithVerificationKey(String token, String verificationKey) {
 		try {
-			RSAPublicKey verficationKey = createPublicKey(verificationKey);
-			NimbusJwtDecoder decoder = NimbusJwtDecoder.withPublicKey(verficationKey).build();
+			RSAPublicKey rsaPublicKey = createPublicKey(verificationKey);
+			NimbusJwtDecoder decoder = NimbusJwtDecoder.withPublicKey(rsaPublicKey).build();
 			decoder.setJwtValidator(tokenValidators);
 			return decoder.decode(token);
-		} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
-			throw new BadJwtException(e.getMessage());
+		} catch (NoSuchAlgorithmException | IllegalArgumentException | InvalidKeySpecException e) {
+			logger.debug("Jwt signature validation with fallback verificationkey failed: {}", e.getMessage());
+			throw new BadJwtException("Jwt validation with fallback verificationkey failed");
 		}
 	}
 
diff --git a/token-client/pom.xml b/token-client/pom.xml
index 9a85a4e2..6d529724 100644
--- a/token-client/pom.xml
+++ b/token-client/pom.xml
@@ -117,6 +117,22 @@
     </dependencies>
 
 	<build>
+		<resources>
+			<resource>
+				<directory>src/main/resources</directory>
+				<filtering>true</filtering>
+				<includes>
+					<include>**/token-client.properties</include>
+				</includes>
+			</resource>
+			<resource>
+				<directory>src/main/resources</directory>
+				<filtering>false</filtering>
+				<excludes>
+					<exclude>**/token-client.properties</exclude>
+				</excludes>
+			</resource>
+		</resources>
 		<plugins>
 			<plugin>
 				<groupId>org.owasp</groupId>
diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/DefaultOAuth2TokenKeyService.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/DefaultOAuth2TokenKeyService.java
index 4bde6d8f..a7d2eb4d 100644
--- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/DefaultOAuth2TokenKeyService.java
+++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/DefaultOAuth2TokenKeyService.java
@@ -8,6 +8,7 @@ package com.sap.cloud.security.xsuaa.client;
 import com.sap.cloud.security.client.HttpClientFactory;
 import com.sap.cloud.security.xsuaa.Assertions;
 import com.sap.cloud.security.xsuaa.util.HttpClientUtil;
+import org.apache.http.HttpHeaders;
 import org.apache.http.HttpStatus;
 import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpGet;
@@ -45,6 +46,9 @@ public class DefaultOAuth2TokenKeyService implements OAuth2TokenKeyService {
 		if (zoneId != null) {
 			request.addHeader(X_ZONE_UUID, zoneId);
 		}
+		request.addHeader(HttpHeaders.USER_AGENT, HttpClientUtil.getUserAgent());
+
+		LOGGER.debug("Executing token key retrieval GET request to {} with headers: {} ", tokenKeysEndpointUri, request.getAllHeaders());
 		try (CloseableHttpResponse response = httpClient.execute(request)) {
 			String bodyAsString = HttpClientUtil.extractResponseBodyAsString(response);
 			int statusCode = response.getStatusLine().getStatusCode();
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 85ad58a9..301bacec 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
@@ -31,6 +31,8 @@ import java.util.Map;
 import java.util.stream.Collectors;
 
 import static com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants.*;
+import static org.apache.http.HttpHeaders.USER_AGENT;
+
 
 public class DefaultOAuth2TokenService extends AbstractOAuth2TokenService {
 
@@ -84,6 +86,7 @@ public class DefaultOAuth2TokenService extends AbstractOAuth2TokenService {
 	}
 
 	private OAuth2TokenResponse executeRequest(HttpPost httpPost) throws OAuth2ServiceException {
+		httpPost.addHeader(USER_AGENT, HttpClientUtil.getUserAgent());
 		LOGGER.debug("Requesting access token from url {} with headers {}", httpPost.getURI(),
 				httpPost.getAllHeaders());
 		try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/DefaultOidcConfigurationService.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/DefaultOidcConfigurationService.java
index 4fcf09b4..704600f2 100644
--- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/DefaultOidcConfigurationService.java
+++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/DefaultOidcConfigurationService.java
@@ -20,6 +20,8 @@ import javax.annotation.Nonnull;
 import java.io.IOException;
 import java.net.URI;
 
+import org.apache.http.HttpHeaders;
+
 /**
  * https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest
  */
@@ -48,6 +50,8 @@ public class DefaultOidcConfigurationService implements OidcConfigurationService
 		Assertions.assertNotNull(discoveryEndpointUri, "discoveryEndpointUri must not be null!");
 
 		HttpUriRequest request = new HttpGet(discoveryEndpointUri);
+		request.addHeader(HttpHeaders.USER_AGENT, HttpClientUtil.getUserAgent());
+
 		try (CloseableHttpResponse response = httpClient.execute(request)) {
 			String bodyAsString = HttpClientUtil.extractResponseBodyAsString(response);
 			int statusCode = response.getStatusLine().getStatusCode();
diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/SpringOAuth2TokenKeyService.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/SpringOAuth2TokenKeyService.java
index 5ec507bf..6238694d 100644
--- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/SpringOAuth2TokenKeyService.java
+++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/SpringOAuth2TokenKeyService.java
@@ -6,6 +6,7 @@
 package com.sap.cloud.security.xsuaa.client;
 
 import com.sap.cloud.security.xsuaa.Assertions;
+import com.sap.cloud.security.xsuaa.util.HttpClientUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.http.*;
@@ -39,6 +40,7 @@ public class SpringOAuth2TokenKeyService implements OAuth2TokenKeyService {
 			// create headers
 			HttpHeaders headers = new HttpHeaders();
 			headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
+			headers.set(HttpHeaders.USER_AGENT, HttpClientUtil.getUserAgent());
 			if (zoneId != null) {
 				headers.set(X_ZONE_UUID, zoneId);
 			}
diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/SpringOidcConfigurationService.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/SpringOidcConfigurationService.java
index afc8a0ad..96971d94 100644
--- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/SpringOidcConfigurationService.java
+++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/SpringOidcConfigurationService.java
@@ -9,8 +9,8 @@ import javax.annotation.Nonnull;
 
 import java.net.URI;
 
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
+import com.sap.cloud.security.xsuaa.util.HttpClientUtil;
+import org.springframework.http.*;
 import org.springframework.web.client.HttpClientErrorException;
 import org.springframework.web.client.RestOperations;
 
@@ -29,7 +29,10 @@ public class SpringOidcConfigurationService implements OidcConfigurationService
 			throws OAuth2ServiceException {
 		Assertions.assertNotNull(discoveryEndpointUri, "discoveryEndpointUri must not be null!");
 		try {
-			ResponseEntity<String> response = restOperations.getForEntity(discoveryEndpointUri, String.class);
+			HttpHeaders headers = new HttpHeaders();
+			headers.set(HttpHeaders.USER_AGENT, HttpClientUtil.getUserAgent());
+
+			ResponseEntity<String> response = restOperations.exchange(discoveryEndpointUri, HttpMethod.GET, new HttpEntity(headers), String.class);
 			if (HttpStatus.OK.value() == response.getStatusCode().value()) {
 				return new DefaultOidcConfigurationService.OidcEndpointsProvider(response.getBody());
 			} else {
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 8de58620..79d2cd64 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
@@ -8,6 +8,7 @@ package com.sap.cloud.security.xsuaa.client;
 import com.sap.cloud.security.servlet.MDCHelper;
 import com.sap.cloud.security.xsuaa.http.HttpHeaders;
 import com.sap.cloud.security.xsuaa.tokenflows.TokenCacheConfiguration;
+import com.sap.cloud.security.xsuaa.util.HttpClientUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.http.HttpEntity;
@@ -73,6 +74,7 @@ public class XsuaaOAuth2TokenService extends AbstractOAuth2TokenService {
 		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());
+		springHeaders.add(org.springframework.http.HttpHeaders.USER_AGENT, HttpClientUtil.getUserAgent());
 		// Create entity
 		HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(copyIntoForm(parameters),
 				springHeaders);
diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/util/HttpClientUtil.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/util/HttpClientUtil.java
index f4bc7e93..744420bc 100644
--- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/util/HttpClientUtil.java
+++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/util/HttpClientUtil.java
@@ -10,7 +10,9 @@ import org.apache.http.HttpResponse;
 
 import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.util.Properties;
 import java.util.stream.Collectors;
 
 public class HttpClientUtil {
@@ -25,4 +27,15 @@ public class HttpClientUtil {
 				.lines().collect(Collectors.joining(System.lineSeparator()));
 	}
 
+	public static String getUserAgent() {
+		Properties props = new Properties();
+		InputStream stream = HttpClientUtil.class.getResourceAsStream("/token-client.properties");
+		try {
+			props.load(stream);
+			return props.getProperty("artifactId") + "/" + props.getProperty("version");
+		} catch (IOException | NullPointerException | IllegalArgumentException e) {
+			return "token-client/0.0.0";
+		}
+	}
+
 }
diff --git a/token-client/src/main/resources/token-client.properties b/token-client/src/main/resources/token-client.properties
new file mode 100644
index 00000000..7303e137
--- /dev/null
+++ b/token-client/src/main/resources/token-client.properties
@@ -0,0 +1,2 @@
+artifactId=${project.artifactId}
+version=${project.version}
\ No newline at end of file


Test patch:
diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoderTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoderTest.java
index 82cf6892..e103b667 100644
--- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoderTest.java
+++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoderTest.java
@@ -14,7 +14,9 @@ import com.sap.cloud.security.xsuaa.token.TokenClaims;
 import org.apache.commons.io.IOUtils;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mockito;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.oauth2.jwt.BadJwtException;
 import org.springframework.security.oauth2.jwt.Jwt;
 import org.springframework.security.oauth2.jwt.JwtDecoder;
 import org.springframework.security.oauth2.jwt.JwtException;
@@ -39,7 +41,7 @@ public class XsuaaJwtDecoderTest {
 	XsuaaServiceConfiguration configuration = new XsuaaServiceConfigurationCustom(new XsuaaCredentials());
 
 	@Test
-	public void decode_withVerficationKey() throws IOException {
+	public void decode_withVerificationKey() throws IOException {
 		String token = IOUtils.resourceToString("/accessTokenRSA256WithVerificationKey.txt", StandardCharsets.UTF_8);
 		final JwtDecoder cut = new XsuaaJwtDecoderBuilder(configurationWithVerificationKey).build();
 
@@ -49,6 +51,32 @@ public class XsuaaJwtDecoderTest {
 		assertThat(jwt.getClaimAsString(TokenClaims.CLAIM_CLIENT_ID)).isEqualTo("sb-clientId!t0815");
 	}
 
+	@Test
+	public void decode_withInvalidFallbackVerificationKey_withoutUaaDomain() throws IOException {
+		XsuaaServiceConfiguration config = Mockito.mock(XsuaaServiceConfiguration.class);
+		Mockito.when(config.getVerificationKey()).thenReturn("xsuaa.verificationkey=-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm1QaZzMjtEfHdimrHP3/2Yr+1z685eiOUlwybRVG9i8wsgOUh+PUGuQL8hgulLZWXU5MbwBLTECAEMQbcRTNVTolkq4i67EP6JesHJIFADbK1Ni0KuMcPuiyOLvDKiDEMnYG1XP3X3WCNfsCVT9YoU+lWIrZr/ZsIvQri8jczr4RkynbTBsPaAOygPUlipqDrpadMO1momNCbea/o6GPn38LxEw609ItfgDGhL6f/yVid5pFzZQWb+9l6mCuJww0hnhO6gt6Rv98OWDty9G0frWAPyEfuIW9B+mR/2vGhyU9IbbWpvFXiy9RVbbsM538TCjd5JF2dJvxy24addC4oQIDAQAB-----END PUBLIC KEY-----");
+
+		String token = IOUtils.resourceToString("/accessTokenRSA256WithVerificationKey.txt", StandardCharsets.UTF_8);
+		final JwtDecoder cut = new XsuaaJwtDecoderBuilder(config).build();
+
+		assertThatThrownBy(() -> cut.decode(token)).isInstanceOf(BadJwtException.class)
+				.hasMessageContaining("Jwt validation with fallback verificationkey failed");
+	}
+
+	@Test
+	public void decode_withFallbackVerificationKey_remoteKeyFetchFailed() throws IOException {
+		XsuaaServiceConfiguration config = Mockito.mock(XsuaaServiceConfiguration.class);
+		Mockito.when(config.getVerificationKey()).thenReturn("-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm1QaZzMjtEfHdimrHP3/2Yr+1z685eiOUlwybRVG9i8wsgOUh+PUGuQL8hgulLZWXU5MbwBLTECAEMQbcRTNVTolkq4i67EP6JesHJIFADbK1Ni0KuMcPuiyOLvDKiDEMnYG1XP3X3WCNfsCVT9YoU+lWIrZr/ZsIvQri8jczr4RkynbTBsPaAOygPUlipqDrpadMO1momNCbea/o6GPn38LxEw609ItfgDGhL6f/yVid5pFzZQWb+9l6mCuJww0hnhO6gt6Rv98OWDty9G0frWAPyEfuIW9B+mR/2vGhyU9IbbWpvFXiy9RVbbsM538TCjd5JF2dJvxy24addC4oQIDAQAB-----END PUBLIC KEY-----");
+		Mockito.when(config.getUaaDomain()).thenReturn("localhost");
+		Mockito.when(config.getClientId()).thenReturn("sb-clientId!t0815");
+
+		String token = IOUtils.resourceToString("/accessTokenRSA256WithVerificationKey.txt", StandardCharsets.UTF_8);
+		final JwtDecoder cut = new XsuaaJwtDecoderBuilder(config).build();
+		final Jwt jwt = cut.decode(token);
+
+		assertThat(jwt.getAudience().get(0)).isEqualTo("sb-clientId!t0815");
+	}
+
 	@Test
 	public void decode_withNonMatchingVerificationKey_throwsException() throws IOException {
 		String token = IOUtils.resourceToString("/token_cc.txt", StandardCharsets.UTF_8);
diff --git a/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/DefaultOidcConfigurationServiceTest.java b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/DefaultOidcConfigurationServiceTest.java
index 988e9d38..2d015a86 100644
--- a/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/DefaultOidcConfigurationServiceTest.java
+++ b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/DefaultOidcConfigurationServiceTest.java
@@ -62,7 +62,7 @@ public class DefaultOidcConfigurationServiceTest {
 
 	@Test
 	public void retrieveEndpoints_badRequest_throwsException() throws IOException {
-		String errorDescription = "Something wen't wrong";
+		String errorDescription = "Something went wrong";
 		CloseableHttpResponse response = HttpClientTestFactory
 				.createHttpResponse(errorDescription, HttpStatus.SC_BAD_REQUEST);
 		when(httpClientMock.execute(any())).thenReturn(response);
diff --git a/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/SpringOidcConfigurationServiceTest.java b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/SpringOidcConfigurationServiceTest.java
index 85b3aa07..e3c1deb8 100644
--- a/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/SpringOidcConfigurationServiceTest.java
+++ b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/SpringOidcConfigurationServiceTest.java
@@ -5,27 +5,26 @@
  */
 package com.sap.cloud.security.xsuaa.client;
 
-import static com.sap.cloud.security.xsuaa.client.OidcConfigurationService.DISCOVERY_ENDPOINT_DEFAULT;
-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.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.when;
-
-import java.io.IOException;
-import java.net.URI;
-import java.nio.charset.StandardCharsets;
-
 import org.apache.commons.io.IOUtils;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
+import org.springframework.http.HttpMethod;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.client.RestOperations;
 
+import java.io.IOException;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+
+import static com.sap.cloud.security.xsuaa.client.OidcConfigurationService.DISCOVERY_ENDPOINT_DEFAULT;
+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.*;
+
 public class SpringOidcConfigurationServiceTest {
 	public static final URI CONFIG_ENDPOINT_URI = URI.create("https://sub.myauth.com" + DISCOVERY_ENDPOINT_DEFAULT);
 
@@ -51,16 +50,16 @@ public class SpringOidcConfigurationServiceTest {
 	}
 
 	@Test
-	public void retrieveEndpoints_parameterIsNull_throwsException() throws OAuth2ServiceException {
+	public void retrieveEndpoints_parameterIsNull_throwsException() {
 		assertThatThrownBy(() -> retrieveEndpoints(null))
 				.isInstanceOf(IllegalArgumentException.class);
 	}
 
 	@Test
 	public void retrieveEndpoints_badRequest_throwsException() {
-		String errorDescription = "Something wen't wrong";
+		String errorDescription = "Something went wrong";
 		mockResponse(errorDescription, HttpStatus.BAD_REQUEST);
-		assertThatThrownBy(() -> retrieveEndpoints())
+		assertThatThrownBy(this::retrieveEndpoints)
 				.isInstanceOf(OAuth2ServiceException.class)
 				.hasMessageContaining(errorDescription);
 	}
@@ -72,7 +71,7 @@ public class SpringOidcConfigurationServiceTest {
 		retrieveEndpoints();
 
 		Mockito.verify(restOperationsMock, times(1))
-				.getForEntity(CONFIG_ENDPOINT_URI, String.class);
+				.exchange(eq(CONFIG_ENDPOINT_URI), eq(HttpMethod.GET), any(), eq(String.class));
 	}
 
 	@Test
@@ -81,9 +80,9 @@ public class SpringOidcConfigurationServiceTest {
 
 		OAuth2ServiceEndpointsProvider result = retrieveEndpoints();
 
-		assertThat(result.getTokenEndpoint().toString()).isEqualTo("http://localhost/oauth/token");
-		assertThat(result.getJwksUri().toString()).isEqualTo("http://localhost/token_keys");
-		assertThat(result.getAuthorizeEndpoint().toString()).isEqualTo("http://localhost/oauth/authorize");
+		assertThat(result.getTokenEndpoint()).hasToString("http://localhost/oauth/token");
+		assertThat(result.getJwksUri()).hasToString("http://localhost/token_keys");
+		assertThat(result.getAuthorizeEndpoint()).hasToString("http://localhost/oauth/authorize");
 	}
 
 	private void mockResponse() {
@@ -92,7 +91,7 @@ public class SpringOidcConfigurationServiceTest {
 
 	private void mockResponse(String responseAsString, HttpStatus httpStatus) {
 		ResponseEntity<String> stringResponseEntity = new ResponseEntity<>(responseAsString, httpStatus);
-		when(restOperationsMock.getForEntity(any(URI.class), eq(String.class)))
+		when(restOperationsMock.exchange(any(URI.class), eq(HttpMethod.GET), any(), eq(String.class)))
 				.thenReturn(stringResponseEntity);
 	}