Repository: SAP/cloud-security-xsuaa-integration
Pull request #807 (2022-02-02 16:38:24)

Spring Autoconfiguration Improvements
Improve Autoconfig:
- [spring-security] should also auto-configure mtls based rest operations w/o credential-type=x509 property
- [spring-security] should also auto-configure token flows if no secret but certificate is given
- [spring-xsuaa] should also auto-configure mtls based rest operations w/o credential-type=x509 property


Patch:
diff --git a/java-api/src/main/java/com/sap/cloud/security/x509/Certificate.java b/java-api/src/main/java/com/sap/cloud/security/x509/Certificate.java
index 6ec360b4..609baca1 100644
--- a/java-api/src/main/java/com/sap/cloud/security/x509/Certificate.java
+++ b/java-api/src/main/java/com/sap/cloud/security/x509/Certificate.java
@@ -5,7 +5,6 @@
  */
 package com.sap.cloud.security.x509;
 
-
 import java.util.Map;
 
 /**
@@ -22,8 +21,8 @@ public interface Certificate {
 	 *
 	 * @return the thumbprint
 	 * @throws InvalidCertificateException
-	 *             if error occurs while encoding X509 certificate
-	 *             or when a particular cryptographic algorithm is not supported
+	 *             if error occurs while encoding X509 certificate or when a
+	 *             particular cryptographic algorithm is not supported
 	 */
 	String getThumbprint() throws InvalidCertificateException;
 
@@ -37,8 +36,9 @@ public interface Certificate {
 	/**
 	 * Gets certificate subject DN map.
 	 *
-	 * @return the subject DN map with entries such as ["CN"]["common name"], or ["OU"]["1234-5678"]
+	 * @return the subject DN map with entries such as ["CN"]["common name"], or
+	 *         ["OU"]["1234-5678"]
 	 */
-	Map<String,String> getSubjectDNMap();
+	Map<String, String> getSubjectDNMap();
 
 }
diff --git a/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/JwtSignatureValidator.java b/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/JwtSignatureValidator.java
index 321feb70..b77ff7ac 100644
--- a/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/JwtSignatureValidator.java
+++ b/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/JwtSignatureValidator.java
@@ -63,7 +63,8 @@ class JwtSignatureValidator implements Validator<Token> {
 		String jwksUri;
 		String keyId;
 
-		if (Service.IAS == configuration.getService() && !token.getIssuer().equals("" + configuration.getUrl()) && token.getZoneId() == null) { // lgtm[java/dereferenced-value-may-be-null]
+		if (Service.IAS == configuration.getService() && !token.getIssuer().equals("" + configuration.getUrl())
+				&& token.getZoneId() == null) { // lgtm[java/dereferenced-value-may-be-null]
 			return createInvalid("Error occurred during signature validation: OIDC token must provide zone_uuid.");
 		}
 		try {
diff --git a/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/JwtX5tValidator.java b/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/JwtX5tValidator.java
index fe19d0f0..e59ebf10 100644
--- a/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/JwtX5tValidator.java
+++ b/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/JwtX5tValidator.java
@@ -31,7 +31,6 @@ public class JwtX5tValidator implements Validator<Token> {
 	private static final Logger LOGGER = LoggerFactory.getLogger(JwtX5tValidator.class);
 	private final OAuth2ServiceConfiguration config;
 
-
 	public JwtX5tValidator(OAuth2ServiceConfiguration config) {
 		this.config = config;
 	}
@@ -64,9 +63,12 @@ public class JwtX5tValidator implements Validator<Token> {
 				if (token.getAudiences().size() == 1 && token.getAudiences().contains(config.getClientId())) {
 					return ValidationResults.createValid();
 				}
-				LOGGER.error("Audience validation failed -> \"aud\": {} != \"clientid\": \"{}\"", token.getAudiences(), config.getClientId());
+				LOGGER.error("Audience validation failed -> \"aud\": {} != \"clientid\": \"{}\"", token.getAudiences(),
+						config.getClientId());
 			} else {
-				LOGGER.error("Thumbprint validation failed -> x5t from token: \"{}\" != thumbprint from client certificate: \"{}\"", tokenX5t,
+				LOGGER.error(
+						"Thumbprint validation failed -> x5t from token: \"{}\" != thumbprint from client certificate: \"{}\"",
+						tokenX5t,
 						clientCertificateX5t);
 			}
 		}
diff --git a/spring-security/src/main/java/com/sap/cloud/security/spring/autoconfig/XsuaaTokenFlowAutoConfiguration.java b/spring-security/src/main/java/com/sap/cloud/security/spring/autoconfig/XsuaaTokenFlowAutoConfiguration.java
index 8f7318e9..aac753fd 100644
--- a/spring-security/src/main/java/com/sap/cloud/security/spring/autoconfig/XsuaaTokenFlowAutoConfiguration.java
+++ b/spring-security/src/main/java/com/sap/cloud/security/spring/autoconfig/XsuaaTokenFlowAutoConfiguration.java
@@ -59,7 +59,7 @@ public class XsuaaTokenFlowAutoConfiguration {
 	}
 
 	@Bean
-	@Conditional({ PropertyConditions.class })
+	@Conditional(PropertyConditions.class)
 	public XsuaaTokenFlows xsuaaTokenFlows(RestOperations xsuaaRestOperations) {
 		logger.debug("auto-configures XsuaaTokenFlows using {} based restOperations",
 				xsuaaConfig.getClientIdentity().isCertificateBased() ? "certificate" : "client secret");
@@ -76,7 +76,7 @@ public class XsuaaTokenFlowAutoConfiguration {
 	 * @return the {@link RestOperations} instance.
 	 */
 	@Bean
-	@Conditional({ OnNotX509CredentialTypeCondition.class })
+	@Conditional({ OnSecretCredentialTypeCondition.class, NoClientCertificateCondition.class })
 	@ConditionalOnMissingBean
 	public RestOperations restOperations() {
 		logger.warn("In productive environment provide a well configured client secret based RestOperations bean");
@@ -90,7 +90,6 @@ public class XsuaaTokenFlowAutoConfiguration {
 	 * @return the {@link RestOperations} instance.
 	 */
 	@Bean
-	@ConditionalOnProperty(prefix = "sap.security.services.xsuaa", name = "credential-type", havingValue = "x509")
 	@ConditionalOnClass(name = "org.apache.http.impl.client.CloseableHttpClient")
 	@ConditionalOnMissingBean
 	public RestOperations mtlsRestOperations(XsuaaServiceConfiguration xsuaaConfig) {
@@ -98,7 +97,7 @@ public class XsuaaTokenFlowAutoConfiguration {
 		return SpringHttpClient.getInstance().create(xsuaaConfig.getClientIdentity());
 	}
 
-	private static class OnNotX509CredentialTypeCondition implements Condition {
+	private static class OnSecretCredentialTypeCondition implements Condition {
 		@Override
 		public boolean matches(ConditionContext context, @Nonnull AnnotatedTypeMetadata metadata) {
 			CredentialType credentialType = CredentialType
@@ -108,17 +107,25 @@ public class XsuaaTokenFlowAutoConfiguration {
 		}
 	}
 
+	private static class NoClientCertificateCondition implements Condition {
+		@Override
+		public boolean matches(ConditionContext context, @Nonnull AnnotatedTypeMetadata metadata) {
+			return context.getEnvironment().getProperty("sap.security.services.xsuaa.certificate") == null &&
+					context.getEnvironment().getProperty("sap.security.services.xsuaa[0].certificate") == null;
+		}
+	}
+
 	private static class PropertyConditions extends AnyNestedCondition {
 
 		public PropertyConditions() {
 			super(ConfigurationPhase.REGISTER_BEAN);
 		}
 
-		@ConditionalOnProperty(prefix = "sap.security.services", name = "xsuaa[0].clientsecret")
+		@ConditionalOnProperty(prefix = "sap.security.services", name = "xsuaa[0].clientid")
 		static class MultipleBindingsCondition {
 		}
 
-		@ConditionalOnProperty(prefix = "sap.security.services", name = "xsuaa.clientsecret")
+		@ConditionalOnProperty(prefix = "sap.security.services", name = "xsuaa.clientid")
 		static class SingleBindingCondition {
 		}
 	}
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServiceConfigurationDefault.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServiceConfigurationDefault.java
index 0354b44e..111eadfa 100644
--- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServiceConfigurationDefault.java
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServiceConfigurationDefault.java
@@ -41,7 +41,7 @@ public class XsuaaServiceConfigurationDefault implements XsuaaServiceConfigurati
 	@Value("${xsuaa.verificationkey:}")
 	private String verificationKey;
 
-	@Value("${xsuaa.credentialtype:#{null}}")
+	@Value("${xsuaa.credential-type:#{null}}")
 	private String credentialType;
 
 	@Value("${xsuaa.certurl:#{null}}")
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaAutoConfiguration.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaAutoConfiguration.java
index 0eced4a5..b4fd7e80 100644
--- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaAutoConfiguration.java
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaAutoConfiguration.java
@@ -81,7 +81,7 @@ public class XsuaaAutoConfiguration {
 	 * @return the {@link RestOperations} instance.
 	 */
 	@Bean
-	@Conditional({ OnNotX509CredentialTypeCondition.class })
+	@Conditional({ OnSecretCredentialTypeCondition.class, NoClientCertificateCondition.class })
 	@ConditionalOnMissingBean
 	@ConditionalOnBean(XsuaaServiceConfiguration.class)
 	public RestOperations xsuaaRestOperations() {
@@ -96,7 +96,6 @@ public class XsuaaAutoConfiguration {
 	 * @return the {@link RestOperations} instance.
 	 */
 	@Bean
-	@ConditionalOnProperty(prefix = "xsuaa", name = "credential-type", havingValue = "x509")
 	@ConditionalOnMissingBean
 	@ConditionalOnClass(name = "org.apache.http.impl.client.CloseableHttpClient")
 	@ConditionalOnBean(XsuaaServiceConfiguration.class)
@@ -105,7 +104,7 @@ public class XsuaaAutoConfiguration {
 		return SpringHttpClient.getInstance().create(xsuaaServiceConfiguration.getClientIdentity());
 	}
 
-	private static class OnNotX509CredentialTypeCondition implements Condition {
+	private static class OnSecretCredentialTypeCondition implements Condition {
 		@Override
 		public boolean matches(ConditionContext context, @Nonnull AnnotatedTypeMetadata metadata) {
 			CredentialType credentialType = CredentialType
@@ -115,4 +114,12 @@ public class XsuaaAutoConfiguration {
 		}
 	}
 
+	private static class NoClientCertificateCondition implements Condition {
+		@Override
+		public boolean matches(ConditionContext context, @Nonnull AnnotatedTypeMetadata metadata) {
+			return context.getEnvironment().getProperty("xsuaa.certificate") == null &&
+					context.getEnvironment().getProperty("xsuaa.key") == null;
+		}
+	}
+
 }
\ No newline at end of file
diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenKeyService.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenKeyService.java
index c6a1ee18..13bd738b 100644
--- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenKeyService.java
+++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenKeyService.java
@@ -10,7 +10,8 @@ import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
 /**
- * Service that targets Identity service (xsuaa and identity) to request Json Web Keys.
+ * Service that targets Identity service (xsuaa and identity) to request Json
+ * Web Keys.
  */
 public interface OAuth2TokenKeyService {
 
diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/XsuaaDefaultEndpoints.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/XsuaaDefaultEndpoints.java
index f01fed85..7a478b65 100644
--- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/XsuaaDefaultEndpoints.java
+++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/XsuaaDefaultEndpoints.java
@@ -104,6 +104,7 @@ public class XsuaaDefaultEndpoints implements OAuth2ServiceEndpointsProvider {
 
 	@Override
 	public URI getJwksUri() {
+		assertNotNull(baseUri, "XsuaaDefaultEndpoints.getJwksUri() requires baseUri not to be null.");
 		return expandPath(baseUri, KEYSET_ENDPOINT);
 	}
 
diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/util/UriUtil.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/util/UriUtil.java
index f751872e..85943a2b 100644
--- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/util/UriUtil.java
+++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/util/UriUtil.java
@@ -69,7 +69,8 @@ public class UriUtil {
 	 */
 	// TODO rename to getUriWithPathAppended
 	@Nonnull
-	public static URI expandPath(URI baseUri, String pathToAppend) {
+	public static URI expandPath(@Nonnull URI baseUri, String pathToAppend) {
+		Assertions.assertNotNull(baseUri, "the baseUri parameter must not be null");
 		try {
 			String newPath = baseUri.getPath() + pathToAppend;
 			return new URI(baseUri.getScheme(), baseUri.getUserInfo(), baseUri.getHost(), baseUri.getPort(),


Test patch:
diff --git a/env/src/test/java/com/sap/cloud/security/config/OAuth2ServiceConfigurationBuilderTest.java b/env/src/test/java/com/sap/cloud/security/config/OAuth2ServiceConfigurationBuilderTest.java
index ec5a4d50..881adb07 100644
--- a/env/src/test/java/com/sap/cloud/security/config/OAuth2ServiceConfigurationBuilderTest.java
+++ b/env/src/test/java/com/sap/cloud/security/config/OAuth2ServiceConfigurationBuilderTest.java
@@ -5,6 +5,7 @@
  */
 package com.sap.cloud.security.config;
 
+import com.sap.cloud.security.xsuaa.client.XsuaaDefaultEndpoints;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -197,7 +198,31 @@ public class OAuth2ServiceConfigurationBuilderTest {
 		assertThat(configuration.getClientId()).isEqualTo("base-client-id");
 		assertThat(configuration.getClientSecret()).isEqualTo("base-client-secret");
 		assertThat(configuration.getUrl()).isEqualTo(URI.create("http://url.base"));
+		assertThat(configuration.getCertUrl()).isNull();
 		assertThat(configuration.getProperty("testing-key")).isEqualTo("base-value");
 		assertThat(configuration.getDomains()).isEqualTo(Collections.EMPTY_LIST);
 	}
+
+	@Test
+	public void x509IntegrationTest() {
+		String url = "http://the.cert.Url.org";
+
+		OAuth2ServiceConfiguration configuration = OAuth2ServiceConfigurationBuilder.forService(Service.XSUAA)
+				.withClientId("client-id")
+				.withCertUrl(url)
+				.withCertificate("cert")
+				.withPrivateKey("key")
+				.withCredentialType(CredentialType.X509)
+				.build();
+
+		assertThat(configuration.getClientId()).isEqualTo("client-id");
+		assertThat(configuration.getClientIdentity().getCertificate()).isEqualTo("cert");
+		assertThat(configuration.getClientIdentity().getKey()).isEqualTo("key");
+		assertThat(configuration.getUrl()).isNull();
+		assertThat(configuration.getCertUrl()).isEqualTo(URI.create(url));
+		assertThat(configuration.getCredentialType()).isEqualTo(CredentialType.X509);
+
+		XsuaaDefaultEndpoints endpoints = new XsuaaDefaultEndpoints(configuration);
+		assertThat(endpoints.getAuthorizeEndpoint().toString()).isEqualTo("http://the.cert.Url.org/oauth/authorize");
+	}
 }
\ No newline at end of file
diff --git a/java-security/src/test/java/com/sap/cloud/security/token/validation/validators/JwtX5tValidatorTest.java b/java-security/src/test/java/com/sap/cloud/security/token/validation/validators/JwtX5tValidatorTest.java
index f0f92e09..ce67c67b 100644
--- a/java-security/src/test/java/com/sap/cloud/security/token/validation/validators/JwtX5tValidatorTest.java
+++ b/java-security/src/test/java/com/sap/cloud/security/token/validation/validators/JwtX5tValidatorTest.java
@@ -50,7 +50,8 @@ class JwtX5tValidatorTest {
 				.withClientId("myClientId")
 				.build();
 		CUT = new JwtX5tValidator(configuration);
-		Mockito.when(JSON_MOCK.getAsString(TokenClaims.CNF_X5T)).thenReturn("fU-XoQlhMTpQsz9ArXl6zHIpMGuRO4ExLKdLRTc5VjM");
+		Mockito.when(JSON_MOCK.getAsString(TokenClaims.CNF_X5T))
+				.thenReturn("fU-XoQlhMTpQsz9ArXl6zHIpMGuRO4ExLKdLRTc5VjM");
 	}
 
 	@Test
diff --git a/java-security/src/test/java/com/sap/cloud/security/x509/X509CertificateTest.java b/java-security/src/test/java/com/sap/cloud/security/x509/X509CertificateTest.java
index a3638381..965035de 100644
--- a/java-security/src/test/java/com/sap/cloud/security/x509/X509CertificateTest.java
+++ b/java-security/src/test/java/com/sap/cloud/security/x509/X509CertificateTest.java
@@ -41,13 +41,16 @@ class X509CertificateTest {
 
 	@Test
 	void getSubjectDN() {
-		assertThat(cut.getSubjectDN()).isEqualTo("CN=bdcd300c-b202-4a7a-bb95-2a7e6d15fe47/2b585405-d391-4986-b76d-b4f24685f3c8, L=aoxk2addh.accounts400.ondemand.com, OU=8e1affb2-62a1-43cc-a687-2ba75e4b3d84, OU=Canary, OU=SAP Cloud Platform Clients, O=SAP SE, C=DE");
+		assertThat(cut.getSubjectDN()).isEqualTo(
+				"CN=bdcd300c-b202-4a7a-bb95-2a7e6d15fe47/2b585405-d391-4986-b76d-b4f24685f3c8, L=aoxk2addh.accounts400.ondemand.com, OU=8e1affb2-62a1-43cc-a687-2ba75e4b3d84, OU=Canary, OU=SAP Cloud Platform Clients, O=SAP SE, C=DE");
 	}
 
 	@Test
 	void getSubjectDNMap() {
 		assertThat(cut.getSubjectDNMap().size()).isEqualTo(5);
-		assertThat(cut.getSubjectDNMap().get("CN")).isEqualTo("bdcd300c-b202-4a7a-bb95-2a7e6d15fe47/2b585405-d391-4986-b76d-b4f24685f3c8");
-		assertThat(cut.getSubjectDNMap().get("OU")).isEqualTo("8e1affb2-62a1-43cc-a687-2ba75e4b3d84,Canary,SAP Cloud Platform Clients");
+		assertThat(cut.getSubjectDNMap().get("CN"))
+				.isEqualTo("bdcd300c-b202-4a7a-bb95-2a7e6d15fe47/2b585405-d391-4986-b76d-b4f24685f3c8");
+		assertThat(cut.getSubjectDNMap().get("OU"))
+				.isEqualTo("8e1affb2-62a1-43cc-a687-2ba75e4b3d84,Canary,SAP Cloud Platform Clients");
 	}
 }
\ No newline at end of file
diff --git a/spring-security/src/test/java/com/sap/cloud/security/spring/autoconfig/XsuaaTokenFlowAutoConfigurationTest.java b/spring-security/src/test/java/com/sap/cloud/security/spring/autoconfig/XsuaaTokenFlowAutoConfigurationTest.java
index f9eed0dd..785c3c80 100644
--- a/spring-security/src/test/java/com/sap/cloud/security/spring/autoconfig/XsuaaTokenFlowAutoConfigurationTest.java
+++ b/spring-security/src/test/java/com/sap/cloud/security/spring/autoconfig/XsuaaTokenFlowAutoConfigurationTest.java
@@ -72,9 +72,11 @@ class XsuaaTokenFlowAutoConfigurationTest {
 
 	@Test
 	void configures_xsuaaMtlsRestTemplate() {
-		runner
-				.withPropertyValues("sap.spring.security.xsuaa.flows.auto:true")
-				.withPropertyValues("sap.security.services.xsuaa.credential-type:x509")
+		runner = new ApplicationContextRunner()
+				.withConfiguration(AutoConfigurations.of(HybridIdentityServicesAutoConfiguration.class,
+						XsuaaTokenFlowAutoConfiguration.class))
+				.withPropertyValues("sap.security.services.xsuaa.certurl:https://domain.cert.auth.com")
+				.withPropertyValues("sap.security.services.xsuaa.clientid:cid")
 				.withPropertyValues("sap.security.services.xsuaa.certificate:" + cert)
 				.withPropertyValues("sap.security.services.xsuaa.key:" + key)
 				.run((context) -> {
@@ -90,7 +92,7 @@ class XsuaaTokenFlowAutoConfigurationTest {
 	}
 
 	@Test
-	void autoConfigurationDisabledWhenNoClientSecretIsGiven() {
+	void autoConfigurationDisabledWhenNoClientIdIsGiven() {
 		WebApplicationContextRunner mt_runner;
 
 		mt_runner = new WebApplicationContextRunner()
@@ -105,7 +107,7 @@ class XsuaaTokenFlowAutoConfigurationTest {
 		WebApplicationContextRunner mt_runner;
 
 		List<String> mt_properties = new ArrayList<>(properties);
-		mt_properties.add("sap.security.services.xsuaa[0].url:http://localhost");
+		mt_properties.add("sap.security.services.xsuaa[0].certurl:https://domain.cert.auth.com");
 		mt_properties.add("sap.security.services.xsuaa[0].clientid:cid");
 		mt_properties.add("sap.security.services.xsuaa[0].clientsecret:pwd");
 		mt_properties.add("sap.security.services.xsuaa[1].clientid:cid");
@@ -118,6 +120,26 @@ class XsuaaTokenFlowAutoConfigurationTest {
 		mt_runner.run(context -> assertTrue(context.containsBean("xsuaaTokenFlows")));
 	}
 
+	@Test
+	void configures_xsuaaMtlsRestTemplateForMultipleXsuaaServicesAsPrimary() {
+		WebApplicationContextRunner mt_runner;
+
+		List<String> mt_properties = new ArrayList<>(properties);
+		mt_properties.add("sap.security.services.xsuaa[0].url:http://localhost");
+		mt_properties.add("sap.security.services.xsuaa[0].clientid:cid");
+		mt_properties.add("sap.security.services.xsuaa[0].certificate:cert");
+		mt_properties.add("sap.security.services.xsuaa[0].key:key");
+		mt_properties.add("sap.security.services.xsuaa[1].clientid:cid");
+
+		mt_runner = new WebApplicationContextRunner()
+				.withPropertyValues(mt_properties.toArray(new String[0]))
+				.withConfiguration(AutoConfigurations.of(HybridIdentityServicesAutoConfiguration.class,
+						XsuaaTokenFlowAutoConfiguration.class));
+
+		mt_runner.run(context -> assertTrue(context.containsBean("xsuaaTokenFlows")));
+		mt_runner.run(context -> assertTrue(context.containsBean("mtlsRestOperations")));
+	}
+
 	@Test
 	void userConfigurationCanOverrideDefaultBeans() {
 		runner.withUserConfiguration(UserConfiguration.class)
diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/XsuaaServiceConfigurationDefaultTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/XsuaaServiceConfigurationDefaultTest.java
index 21d194c0..164d4e74 100644
--- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/XsuaaServiceConfigurationDefaultTest.java
+++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/XsuaaServiceConfigurationDefaultTest.java
@@ -15,7 +15,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 @RunWith(SpringJUnit4ClassRunner.class)
 @TestPropertySource(properties = { "xsuaa.clientid=client", "xsuaa.certificate=cert", "xsuaa.key=key",
-		"xsuaa.certurl=https://my.cert.authentication.sap.com", "xsuaa.credentialtype=x509" })
+		"xsuaa.certurl=https://my.cert.authentication.sap.com", "xsuaa.credential-type=x509" })
 @ContextConfiguration(classes = { XsuaaServiceConfigurationDefault.class })
 public class XsuaaServiceConfigurationDefaultTest {
 
diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/XsuaaServicePropertySourceFactoryTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/XsuaaServicePropertySourceFactoryTest.java
index 9221c39a..69ff7aca 100644
--- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/XsuaaServicePropertySourceFactoryTest.java
+++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/XsuaaServicePropertySourceFactoryTest.java
@@ -82,7 +82,7 @@ class TestConfiguration {
 	@Value("${xsuaa.key:}")
 	public String key;
 
-	@Value("${xsuaa.credentialtype:}")
+	@Value("${xsuaa.credential-type:}")
 	public String credentialType;
 
 	@Value("${xsuaa.certurl:}")
diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaAutoConfigurationTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaAutoConfigurationTest.java
index b128af29..5a36537e 100644
--- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaAutoConfigurationTest.java
+++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaAutoConfigurationTest.java
@@ -70,7 +70,7 @@ public class XsuaaAutoConfigurationTest {
 	}
 
 	@Test
-	public void configures_xsuaaMtlsRestTemplate() {
+	public void configures_xsuaaMtlsRestTemplateWithCredType() {
 		contextRunner
 				.withPropertyValues("spring.xsuaa.flows.auto:true")
 				.withPropertyValues("xsuaa.credential-type:x509")
@@ -84,6 +84,19 @@ public class XsuaaAutoConfigurationTest {
 				});
 	}
 
+	@Test
+	public void configures_xsuaaMtlsRestTemplate() {
+		contextRunner
+				.withPropertyValues("xsuaa.clientid:client")
+				.withPropertyValues("xsuaa.certificate:" + cert)
+				.withPropertyValues("xsuaa.key:" + key)
+				.withPropertyValues("xsuaa.certurl:https://domain.cert.authentication.sap.com")
+				.run((context) -> {
+					assertThat(context).hasSingleBean(RestOperations.class);
+					assertThat(context).hasBean("xsuaaMtlsRestOperations");
+				});
+	}
+
 	@Test
 	public void configures_xsuaaServiceConfiguration_withProperties() {
 		contextRunner.withClassLoader(new FilteredClassLoader(CloseableHttpClient.class))
diff --git a/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaDefaultEndpointsTest.java b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaDefaultEndpointsTest.java
index e14572f7..7eab99d3 100644
--- a/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaDefaultEndpointsTest.java
+++ b/token-client/src/test/java/com/sap/cloud/security/xsuaa/client/XsuaaDefaultEndpointsTest.java
@@ -65,6 +65,17 @@ public class XsuaaDefaultEndpointsTest {
 		new XsuaaDefaultEndpoints(null, CERT_URL);
 	}
 
+	@Test(expected = IllegalArgumentException.class)
+	public void getJwksUri_throwsException_whenBaseUriIsNull() {
+		Mockito.when(oAuth2ServiceConfiguration.getCertUrl()).thenReturn(URI.create(CERT_URL));
+		Mockito.when(oAuth2ServiceConfiguration.getCredentialType()).thenReturn(X509);
+
+		cut = new XsuaaDefaultEndpoints(oAuth2ServiceConfiguration);
+
+		assertThat(cut.getTokenEndpoint().toString(), is(CERT_URL + "/oauth/token")); // ok
+		cut.getJwksUri(); // raise exception
+	}
+
 	@Test
 	@Deprecated
 	public void getEndpoint_forBaseUrl() {