Repository: SAP/cloud-security-xsuaa-integration
Pull request #193 (2019-11-20 13:56:34)

Make it possible to disable the XsuaaAudienceValidator
Plus a little bit refactoring.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 10925553..77cc70db 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
 ## 2.3.0
 - Issue: Spring tests fail with version `2.2.0`, when auto-configuration is disabled and no `RestOperations` bean is specified.
 - [token-client] Supports basically JWT Bearer Token Grant as documented [here](https://docs.cloudfoundry.org/api/uaa/version/74.4.0/index.html#jwt-bearer-token-grant).
-- [token-client] **Bug fix** for state issue in HttpHeaderFactor ([#200](https://github.com/SAP-samples/cloud-security-xsuaa-integration/issues/200)) that causes interference between different types of token flows.
+- [token-client] Supports the setup of mTLS with X509 client certificates. Use [`SSLContextFactory`](/token-client/src/main/java/com/sap/cloud/security/xsuaa/mtls/SSLContextFactory.java) to configure a `SSLContext` for Apache Rest Client to identify your client with the XSUAA identity service as documented [here](/token-client/README.md).
 
 ## 2.2.0
 - [spring-xsuaa] `PropertySourceFactory` supports custom property sources and default can optionally be disabled with  `spring.xsuaa.disable-default-property-source=true`
diff --git a/samples/java-tokenclient-usage/README.md b/samples/java-tokenclient-usage/README.md
index 051c3aeb..562fc323 100644
--- a/samples/java-tokenclient-usage/README.md
+++ b/samples/java-tokenclient-usage/README.md
@@ -1,7 +1,7 @@
 # Description
 This sample is a Java back-end application running on the Cloud Foundry. On incoming requests it reads 
 credentials from the `VCAP_SERVICES` environment variable and requests a new access token via client credentials token
-flow provided by the [Token Client](/token-client/) library.
+flow provided by the [Token Client](../../token-client/) library.
 
 # Deployment on Cloud Foundry
 To deploy the application, the following steps are required:
diff --git a/samples/spring-security-xsuaa-usage/README.md b/samples/spring-security-xsuaa-usage/README.md
index 51fd35ac..8e292cc5 100644
--- a/samples/spring-security-xsuaa-usage/README.md
+++ b/samples/spring-security-xsuaa-usage/README.md
@@ -3,9 +3,8 @@ This sample uses the SAP application router as OAuth client and forwards as reve
 In a typcal UI5 application, the application router serves HTML files and REST data would be provided by a back-end application. To focus on the security part, UI5 has been omitted.
 
 # Coding
-This sample is using the [`spring-xsuaa` descriptions](/spring-xsuaa/) library which bases on [spring-security](https://github.com/spring-projects/spring-security) project. As of version 5 of spring-security, this includes the OAuth resource-server functionality. The security configuration needs to configure JWT for authentication.
-
-Furthermore it demonstrates how to leverage the token flows provided by the [Token Client](/token-client/) library to request / exchange access tokens.
+This sample is using the spring-security project. As of version 5 of spring-security, this includes the OAuth resource-server functionality. The security configuration needs to configure JWT for authentication.
+Please see the [`spring-xsuaa` descriptions](/spring-xsuaa/README.md) for details.
 
 # Deployment To Cloud Foundry
 To deploy the application, the following steps are required:
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServiceConfiguration.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServiceConfiguration.java
index 21cb4d1f..34a355c9 100644
--- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServiceConfiguration.java
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServiceConfiguration.java
@@ -44,14 +44,14 @@ public interface XsuaaServiceConfiguration {
 	 *
 	 * @return certificates
 	 */
-	//@Nullable
-	//String getCertificates();
+	@Nullable
+	String getCertificates();
 
 	/**
 	 * Private key the certificate is signed with.
 	 *
 	 * @return private key
 	 */
-	//@Nullable
-	//String getPrivateKey();
+	@Nullable
+	String getPrivateKey();
 }
\ No newline at end of file
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 d1ccbbcb..8178cd1d 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
@@ -63,7 +63,7 @@ public class XsuaaServiceConfigurationDefault implements XsuaaServiceConfigurati
 		return uaadomain;
 	}
 
-	/*@Override
+	@Override
 	public String getCertificates() {
 		return certificates;
 	}
@@ -71,5 +71,5 @@ public class XsuaaServiceConfigurationDefault implements XsuaaServiceConfigurati
 	@Override
 	public String getPrivateKey() {
 		return privateKey;
-	}*/
+	}
 }
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServicesParser.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServicesParser.java
index 720b557a..5e8cdf05 100644
--- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServicesParser.java
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/XsuaaServicesParser.java
@@ -140,14 +140,12 @@ public class XsuaaServicesParser {
 			JSONArray tags = (JSONArray) binding.get(TAGS);
 
 			for (int j = 0; j < tags.size(); j++) {
-				boolean isApiAccessPlan	= binding.getAsString("plan").equals("apiaccess");
-
-				if (tags.get(j).equals(tag) && !isApiAccessPlan) {
+				if (tags.get(j).equals(tag)) {
 					if (xsuaaBinding == null) {
 						xsuaaBinding = binding;
 					} else {
 						throw new IllegalStateException(
-								"Found more than one xsuaa bindings. Please consider unified broker plan.");
+								"Found more than one xsuaa binding. Please consider unified broker plan.");
 					}
 				}
 			}
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java
index 0b41c83b..2a7d0248 100644
--- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java
@@ -10,10 +10,6 @@ import java.util.Date;
 
 public interface Token extends UserDetails {
 
-	/**
-	 * Use {@link com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants#GRANT_TYPE_CLIENT_CREDENTIALS} instead
-	 */
-	@Deprecated
 	static final String GRANTTYPE_CLIENTCREDENTIAL = "client_credentials";
 
 	/**
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java
index d09882f5..057e7020 100644
--- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java
@@ -14,8 +14,6 @@ import java.util.*;
 
 import static com.sap.cloud.security.xsuaa.token.TokenClaims.*;
 
-import com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants;
-
 /**
  * Custom XSUAA token implementation.
  * <p>
@@ -64,8 +62,7 @@ public class XsuaaToken extends Jwt implements Token {
 
 	@Override
 	public String getUsername() {
-		if (OAuth2TokenServiceConstants.GRANT_TYPE_CLIENT_CREDENTIALS.equals(getGrantType()) ||
-				OAuth2TokenServiceConstants.GRANT_TYPE_CLIENT_X509.equalsIgnoreCase(getGrantType())) {
+		if (GRANTTYPE_CLIENTCREDENTIAL.equals(getGrantType())) {
 			return String.format(UNIQUE_CLIENT_NAME_FORMAT, getClientId());
 		} else {
 			return getUniquePrincipalName(getOrigin(), getLogonName());
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 ddd8df6b..a016f87a 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
@@ -1,31 +1,29 @@
 package com.sap.cloud.security.xsuaa.token.authentication;
 
-import static com.sap.cloud.security.xsuaa.token.TokenClaims.CLAIM_JKU;
-import static com.sap.cloud.security.xsuaa.token.TokenClaims.CLAIM_KID;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.text.ParseException;
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.security.oauth2.core.OAuth2TokenValidator;
-import org.springframework.security.oauth2.jwt.Jwt;
-import org.springframework.security.oauth2.jwt.JwtDecoder;
-import org.springframework.security.oauth2.jwt.JwtException;
-import org.springframework.security.oauth2.jwt.JwtValidationException;
-import org.springframework.security.oauth2.jwt.NimbusJwtDecoderJwkSupport;
-import org.springframework.util.Assert;
-
 import com.github.benmanes.caffeine.cache.Cache;
 import com.github.benmanes.caffeine.cache.Caffeine;
 import com.nimbusds.jwt.JWT;
 import com.nimbusds.jwt.JWTParser;
 import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.oauth2.core.OAuth2TokenValidator;
+import org.springframework.security.oauth2.jwt.*;
+import org.springframework.util.Assert;
 import org.springframework.web.client.RestOperations;
 
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import static com.sap.cloud.security.xsuaa.token.TokenClaims.CLAIM_JKU;
+import static com.sap.cloud.security.xsuaa.token.TokenClaims.CLAIM_KID;
+
 public class XsuaaJwtDecoder implements JwtDecoder {
 	private final Logger logger = LoggerFactory.getLogger(getClass());
 
@@ -132,8 +130,11 @@ public class XsuaaJwtDecoder implements JwtDecoder {
 
 	// TODO extract into separate class / bean
 	private JwtDecoder getDecoder(String jku) {
-		NimbusJwtDecoderJwkSupport decoder = new NimbusJwtDecoderJwkSupport(jku);
-		Optional.ofNullable(restOperations).ifPresent(decoder::setRestOperations);
+		NimbusJwtDecoder.JwkSetUriJwtDecoderBuilder jwkSetUriJwtDecoderBuilder = NimbusJwtDecoder.withJwkSetUri(jku);
+		if (restOperations != null) {
+			jwkSetUriJwtDecoderBuilder.restOperations(restOperations);
+		}
+		NimbusJwtDecoder decoder = jwkSetUriJwtDecoderBuilder.build();
 		decoder.setJwtValidator(tokenValidators);
 		return decoder;
 	}
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoderBuilder.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoderBuilder.java
index 20bf0ae7..b77ceac8 100644
--- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoderBuilder.java
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoderBuilder.java
@@ -1,28 +1,25 @@
 package com.sap.cloud.security.xsuaa.token.authentication;
 
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Optional;
-
+import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration;
 import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
 import org.springframework.security.oauth2.core.OAuth2TokenValidator;
 import org.springframework.security.oauth2.jwt.Jwt;
 import org.springframework.security.oauth2.jwt.JwtDecoder;
 import org.springframework.security.oauth2.jwt.JwtValidators;
 import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
-
-import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration;
 import org.springframework.web.client.RestOperations;
 
+import java.util.*;
+import java.util.stream.Collectors;
+
 public class XsuaaJwtDecoderBuilder {
 
-	private XsuaaServiceConfiguration configuration;
 	int decoderCacheValidity; // in seconds
 	int decoderCacheSize;
-	OAuth2TokenValidator<Jwt> xsuaaTokenValidators;
-	OAuth2TokenValidator<Jwt> defaultTokenValidators;
 	Collection<PostValidationAction> postValidationActions;
+	private XsuaaServiceConfiguration configuration;
 	private RestOperations restOperations;
+	private List<OAuth2TokenValidator<Jwt>> xsuaaTokenValidators;
 
 	/**
 	 * Utility for building a JWT decoder configuration
@@ -32,8 +29,8 @@ public class XsuaaJwtDecoderBuilder {
 	 */
 	public XsuaaJwtDecoderBuilder(XsuaaServiceConfiguration configuration) {
 		this.configuration = configuration;
-		withDefaultValidators(JwtValidators.createDefault());
-		withTokenValidators(new XsuaaAudienceValidator(configuration));
+		xsuaaTokenValidators = new ArrayList<>();
+		xsuaaTokenValidators.add(new XsuaaAudienceValidator(configuration));
 		withDecoderCacheSize(100);
 		withDecoderCacheTime(900);
 	}
@@ -44,11 +41,8 @@ public class XsuaaJwtDecoderBuilder {
 	 * @return JwtDecoder
 	 */
 	public JwtDecoder build() {
-		DelegatingOAuth2TokenValidator<Jwt> combinedTokenValidators = new DelegatingOAuth2TokenValidator<>(
-				defaultTokenValidators,
-				xsuaaTokenValidators);
 		XsuaaJwtDecoder jwtDecoder = new XsuaaJwtDecoder(configuration, decoderCacheValidity, decoderCacheSize,
-				combinedTokenValidators, postValidationActions);
+				getValidators(), postValidationActions);
 		Optional.ofNullable(restOperations).ifPresent(jwtDecoder::setRestOperations);
 		return jwtDecoder;
 	}
@@ -59,11 +53,13 @@ public class XsuaaJwtDecoderBuilder {
 	 * @return ReactiveJwtDecoder
 	 */
 	public ReactiveJwtDecoder buildAsReactive() {
-		DelegatingOAuth2TokenValidator<Jwt> combinedTokenValidators = new DelegatingOAuth2TokenValidator<>(
-				defaultTokenValidators,
-				xsuaaTokenValidators);
-		return new ReactiveXsuaaJwtDecoder(configuration, decoderCacheValidity, decoderCacheSize,
-				combinedTokenValidators, postValidationActions);
+		return new ReactiveXsuaaJwtDecoder(configuration, decoderCacheValidity, decoderCacheSize, getValidators(),
+				postValidationActions);
+	}
+
+	private DelegatingOAuth2TokenValidator<Jwt> getValidators() {
+		return new DelegatingOAuth2TokenValidator<>(new DelegatingOAuth2TokenValidator<>(xsuaaTokenValidators),
+				JwtValidators.createDefault());
 	}
 
 	/**
@@ -113,11 +109,8 @@ public class XsuaaJwtDecoderBuilder {
 	 *            the token validators
 	 * @return this
 	 */
-	// var arg it is only being assigned to a OAuth2TokenValidator<Jwt>[], therefore
-	// its type safe.
-	@SuppressWarnings("unchecked")
 	public XsuaaJwtDecoderBuilder withTokenValidators(OAuth2TokenValidator<Jwt>... tokenValidators) {
-		this.xsuaaTokenValidators = new DelegatingOAuth2TokenValidator<>(tokenValidators);
+		xsuaaTokenValidators = Arrays.asList(tokenValidators);
 		return this;
 	}
 
@@ -135,8 +128,15 @@ public class XsuaaJwtDecoderBuilder {
 		return this;
 	}
 
-	XsuaaJwtDecoderBuilder withDefaultValidators(OAuth2TokenValidator<Jwt>... defaultTokenValidators) {
-		this.defaultTokenValidators = new DelegatingOAuth2TokenValidator<>(defaultTokenValidators);
+	/**
+	 * Disables the JWT {@link XsuaaAudienceValidator} which is enabled by default.
+	 *
+	 * @return the builder itself.
+	 */
+	public XsuaaJwtDecoderBuilder withoutXsuaaAudienceValidator() {
+		xsuaaTokenValidators = xsuaaTokenValidators.stream()
+				.filter(validator -> !(validator instanceof XsuaaAudienceValidator))
+				.collect(Collectors.toList());
 		return this;
 	}
 
diff --git a/token-client/README.md b/token-client/README.md
index dd580ddc..5cb8691c 100644
--- a/token-client/README.md
+++ b/token-client/README.md
@@ -45,6 +45,18 @@ The `DefaultOAuth2TokenService` can also be instantiated with a custom `Closeabl
 
 > The `<uaa_base_url>`, `<client_id>` and `<client_secret>` are placeholders for the information you get from the XSUAA service binding. 
 
+#### Setup Apache Rest client for mutual TLS 
+```java
+SSLContext sslContext = SSLContextFactory.getInstance().create(<certificate>, <key>);
+
+SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);
+
+CloseableHttpClient httpClient = HttpClients.custom()
+        .setSSLContext(sslContext)
+        .setSSLSocketFactory(socketFactory)
+        .build();
+```
+> The `<certificate>` represents the the PEM encoded certificate chain and `<key>` is the private key the certificate is signed with. Both are placeholders for the information you get from the XSUAA service binding.
 
 ## Configuration for Java/Spring Applications
 
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 09d2c34b..2660212d 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
@@ -14,8 +14,10 @@ import static com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants.*;
 
 public abstract class AbstractOAuth2TokenService implements OAuth2TokenService {
 
+	protected final HttpHeadersFactory httpHeadersFactory;
 
 	public AbstractOAuth2TokenService() {
+		this.httpHeadersFactory = new HttpHeadersFactory();
 	}
 
 	@Override
@@ -32,7 +34,7 @@ public abstract class AbstractOAuth2TokenService implements OAuth2TokenService {
 				.withOptionalParameters(optionalParameters)
 				.buildAsMap();
 
-		HttpHeaders headers = HttpHeadersFactory.createWithoutAuthorizationHeader();
+		HttpHeaders headers = httpHeadersFactory.createWithoutAuthorizationHeader();
 
 		return requestAccessToken(UriUtil.replaceSubdomain(tokenEndpointUri, subdomain), headers, parameters);
 	}
@@ -52,7 +54,7 @@ public abstract class AbstractOAuth2TokenService implements OAuth2TokenService {
 				.withOptionalParameters(optionalParameters)
 				.buildAsMap();
 
-		HttpHeaders headers = HttpHeadersFactory.createWithAuthorizationBearerHeader(token);
+		HttpHeaders headers = httpHeadersFactory.createWithAuthorizationBearerHeader(token);
 
 		return requestAccessToken(UriUtil.replaceSubdomain(tokenEndpointUri, subdomain), headers, parameters);
 	}
@@ -71,7 +73,7 @@ public abstract class AbstractOAuth2TokenService implements OAuth2TokenService {
 				.withClientCredentials(clientCredentials)
 				.buildAsMap();
 
-		HttpHeaders headers = HttpHeadersFactory.createWithoutAuthorizationHeader();
+		HttpHeaders headers = httpHeadersFactory.createWithoutAuthorizationHeader();
 
 		return requestAccessToken(UriUtil.replaceSubdomain(tokenEndpointUri, subdomain), headers, parameters);
 	}
@@ -94,7 +96,7 @@ public abstract class AbstractOAuth2TokenService implements OAuth2TokenService {
 				.withOptionalParameters(optionalParameters)
 				.buildAsMap();
 
-		HttpHeaders headers = HttpHeadersFactory.createWithoutAuthorizationHeader();
+		HttpHeaders headers = httpHeadersFactory.createWithoutAuthorizationHeader();
 
 		return requestAccessToken(UriUtil.replaceSubdomain(tokenEndpoint, subdomain), headers, parameters);
 	}
@@ -114,7 +116,7 @@ public abstract class AbstractOAuth2TokenService implements OAuth2TokenService {
 				.withOptionalParameters(optionalParameters)
 				.buildAsMap();
 
-		HttpHeaders headers = HttpHeadersFactory.createWithoutAuthorizationHeader();
+		HttpHeaders headers = httpHeadersFactory.createWithoutAuthorizationHeader();
 
 		return requestAccessToken(UriUtil.replaceSubdomain(tokenEndpoint, subdomain), headers, parameters);
 	}
diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenServiceConstants.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenServiceConstants.java
index 6d3d06db..7ffb6e80 100644
--- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenServiceConstants.java
+++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/client/OAuth2TokenServiceConstants.java
@@ -21,7 +21,6 @@ public class OAuth2TokenServiceConstants {
 	public static final String GRANT_TYPE_CLIENT_CREDENTIALS = "client_credentials";
 	public static final String GRANT_TYPE_PASSWORD = "password";
 	public static final String GRANT_TYPE_JWT_BEARER = "urn:ietf:params:oauth:grant-type:jwt-bearer";
-	public static final String GRANT_TYPE_CLIENT_X509 = "client_x509";
 
 	public static final String TOKEN_TYPE_OPAQUE = "opaque";
 
diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/http/HttpHeader.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/http/HttpHeader.java
index 4fa9bff0..1c939043 100644
--- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/http/HttpHeader.java
+++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/http/HttpHeader.java
@@ -1,7 +1,5 @@
 package com.sap.cloud.security.xsuaa.http;
 
-import java.util.Objects;
-
 public class HttpHeader {
 
 	private final String name;
@@ -19,20 +17,4 @@ public class HttpHeader {
 	public String getName() {
 		return name;
 	}
-
-	@Override
-	public boolean equals(Object o) {
-		if (this == o)
-			return true;
-		if (o == null || getClass() != o.getClass())
-			return false;
-		final HttpHeader that = (HttpHeader) o;
-		return Objects.equals(getName(), that.getName()) &&
-				Objects.equals(getValue(), that.getValue());
-	}
-
-	@Override
-	public int hashCode() {
-		return Objects.hash(getName(), getValue());
-	}
 }
diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/http/HttpHeadersFactory.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/http/HttpHeadersFactory.java
index 6c587944..1f081b08 100644
--- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/http/HttpHeadersFactory.java
+++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/http/HttpHeadersFactory.java
@@ -7,7 +7,12 @@ import java.util.stream.Collectors;
 
 public class HttpHeadersFactory {
 
-	private HttpHeadersFactory() {
+	private final Map<String, String> headers;
+
+	public HttpHeadersFactory() {
+		headers = new HashMap<>();
+		headers.put(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON.value());
+		headers.put(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED.value());
 	}
 
 	/**
@@ -17,11 +22,10 @@ public class HttpHeadersFactory {
 	 *            - the token which should be part of the header.
 	 * @return the builder instance.
 	 */
-	public static HttpHeaders createWithAuthorizationBearerHeader(String token) {
-		Map<String, String> headers = createDefaultHeaders();
+	public HttpHeaders createWithAuthorizationBearerHeader(String token) {
 		final String AUTHORIZATION_BEARER_TOKEN_FORMAT = "Bearer %s";
 		headers.put(HttpHeaders.AUTHORIZATION, String.format(AUTHORIZATION_BEARER_TOKEN_FORMAT, token));
-		return createFromHeaders(headers);
+		return createFromHeaders();
 	}
 
 	/**
@@ -30,24 +34,16 @@ public class HttpHeadersFactory {
 	 *
 	 * @return the HTTP headers.
 	 */
-	public static HttpHeaders  createWithoutAuthorizationHeader() {
-		return createFromHeaders(createDefaultHeaders());
+	public HttpHeaders createWithoutAuthorizationHeader() {
+		return createFromHeaders();
 	}
 
-	private static HttpHeaders createFromHeaders(Map<String, String> headers) {
-		List<HttpHeader> httpHeaders = headers.entrySet()
+	private HttpHeaders createFromHeaders() {
+		List<HttpHeader> httpHeaders = this.headers.entrySet()
 				.stream()
 				.map(header -> new HttpHeader(header.getKey(), header.getValue()))
 				.collect(Collectors.toList());
 		return new HttpHeaders(httpHeaders);
 	}
 
-	private static Map<String, String> createDefaultHeaders() {
-		Map<String, String> headers = new HashMap<>();
-		headers.put(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON.value());
-		headers.put(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED.value());
-		return headers;
-	}
-
-
 }
diff --git a/token-client/src/main/java/com/sap/cloud/security/xsuaa/mtls/SSLContextFactory.java b/token-client/src/main/java/com/sap/cloud/security/xsuaa/mtls/SSLContextFactory.java
index ac7232e3..f6538143 100644
--- a/token-client/src/main/java/com/sap/cloud/security/xsuaa/mtls/SSLContextFactory.java
+++ b/token-client/src/main/java/com/sap/cloud/security/xsuaa/mtls/SSLContextFactory.java
@@ -28,10 +28,7 @@ import org.slf4j.LoggerFactory;
 
 /**
  * Creates a SSLContext (without Bouncy Castle crypto lib).
- *
- * @deprecated This class might be removed again in the future
  */
-@Deprecated
 public class SSLContextFactory {
 	private static final char[] noPassword = "".toCharArray();
 	private static final SSLContextFactory instance = new SSLContextFactory();
@@ -45,16 +42,6 @@ public class SSLContextFactory {
 		return instance;
 	}
 
-	/**
-	 * Creates a SSLContext which can be used to parameterize your Rest client,
-	 *  in order to support mutual TLS.
-	 *
-	 * @param x509Certificates, you can get from your Service Configuration
-	 * @param rsaPrivateKey, you can get from your Service Configuration
-	 * @return a new SSLContext instance
-	 * @throws GeneralSecurityException
-	 * @throws IOException
-	 */
 	public SSLContext create(String x509Certificates, String rsaPrivateKey)
 			throws GeneralSecurityException, IOException {
 		assertHasText(x509Certificates, "x509Certificate is required");


Test patch:
diff --git a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/AuthenticationInformationExtractorTest.java b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/AuthenticationInformationExtractorTest.java
index 0d3eb75e..64657cb6 100644
--- a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/AuthenticationInformationExtractorTest.java
+++ b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/AuthenticationInformationExtractorTest.java
@@ -24,14 +24,14 @@ public class AuthenticationInformationExtractorTest {
 	 * @throws java.lang.Exception
 	 */
 	@BeforeClass
-	public static void setUpBeforeClass() {
+	public static void setUpBeforeClass() throws Exception {
 	}
 
 	/**
 	 * @throws java.lang.Exception
 	 */
 	@AfterClass
-	public static void tearDownAfterClass() {
+	public static void tearDownAfterClass() throws Exception {
 	}
 
 	/**
@@ -47,7 +47,7 @@ public class AuthenticationInformationExtractorTest {
 	 * @throws java.lang.Exception
 	 */
 	@After
-	public void tearDown() {
+	public void tearDown() throws Exception {
 	}
 
 	public AuthenticationInformationExtractor authenticationConfiguration() {
diff --git a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/BasicCredentialExtractorTest.java b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/BasicCredentialExtractorTest.java
index 89ff2388..b1564f2c 100644
--- a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/BasicCredentialExtractorTest.java
+++ b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/BasicCredentialExtractorTest.java
@@ -43,7 +43,7 @@ public class BasicCredentialExtractorTest {
 	 * @throws java.lang.Exception
 	 */
 	@Before
-	public void setUp() {
+	public void setUp() throws Exception {
 		request = new MockHttpServletRequest();
 	}
 
diff --git a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/XsuaaServiceConfigurationDummy.java b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/XsuaaServiceConfigurationDummy.java
index 1e77551d..d50c6f28 100644
--- a/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/XsuaaServiceConfigurationDummy.java
+++ b/spring-xsuaa-it/src/test/java/com/sap/cloud/security/xsuaa/extractor/XsuaaServiceConfigurationDummy.java
@@ -1,5 +1,7 @@
 package com.sap.cloud.security.xsuaa.extractor;
 
+import javax.annotation.Nullable;
+
 import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration;
 
 public class XsuaaServiceConfigurationDummy implements XsuaaServiceConfiguration {
@@ -35,14 +37,16 @@ public class XsuaaServiceConfigurationDummy implements XsuaaServiceConfiguration
 		return uaaDomain;
 	}
 
-	/*
-	 * @Nullable
-	 * 
-	 * @Override public String getCertificates() { return null; }
-	 * 
-	 * @Nullable
-	 * 
-	 * @Override public String getPrivateKey() { return null; }
-	 */
+	@Nullable
+	@Override
+	public String getCertificates() {
+		return null;
+	}
+
+	@Nullable
+	@Override
+	public String getPrivateKey() {
+		return null;
+	}
 
 }
diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/CustomPropertySourceFactoryTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/CustomPropertySourceFactoryTest.java
index 93218fc7..0ffb5703 100644
--- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/CustomPropertySourceFactoryTest.java
+++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/CustomPropertySourceFactoryTest.java
@@ -74,7 +74,7 @@ class CustomConfiguration {
 }
 
 class CustomPropertySourceFactory implements PropertySourceFactory {
-	private String vcapJsonString = "{\"xsuaa\":[{\"credentials\":{\"xsappname\":\"customAppId!t2344\"},\"plan\": \"application\",\"tags\":[\"xsuaa\"]}]}";
+	private String vcapJsonString = "{\"xsuaa\":[{\"credentials\":{\"xsappname\":\"customAppId!t2344\"},\"tags\":[\"xsuaa\"]}]}";
 
 	@Override
 	public org.springframework.core.env.PropertySource<?> createPropertySource(String s,
diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/DummyXsuaaServiceConfiguration.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/DummyXsuaaServiceConfiguration.java
index 8898ff5c..de18dd77 100644
--- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/DummyXsuaaServiceConfiguration.java
+++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/DummyXsuaaServiceConfiguration.java
@@ -41,7 +41,7 @@ public class DummyXsuaaServiceConfiguration implements XsuaaServiceConfiguration
 		return uaaDomain;
 	}
 
-	/*@Nullable
+	@Nullable
 	@Override
 	public String getCertificates() {
 		return null;
@@ -51,5 +51,5 @@ public class DummyXsuaaServiceConfiguration implements XsuaaServiceConfiguration
 	@Override
 	public String getPrivateKey() {
 		return null;
-	}*/
+	}
 }
diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/XsuaaServicesParserTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/XsuaaServicesParserTest.java
deleted file mode 100644
index dc18de75..00000000
--- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/XsuaaServicesParserTest.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.sap.cloud.security.xsuaa;
-
-import org.apache.commons.io.IOUtils;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.nio.charset.Charset;
-import java.util.Properties;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class XsuaaServicesParserTest {
-
-    @Test
-    public void ignoreApiAccessPlan() throws IOException {
-        String vcapMultipleBindings = IOUtils.resourceToString("/vcap_multipleBindings.json", Charset.forName("UTF-8"));
-        XsuaaServicesParser cut = new XsuaaServicesParser(vcapMultipleBindings);
-        Properties properties = cut.parseCredentials();
-        assertThat(properties.getProperty("clientid")).isEqualTo("client-id");
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void doNotAllowBrokerAndApplicationPlan() throws IOException {
-        String vcapMultipleBindings = IOUtils.resourceToString("/vcap_multipleBindings.json", Charset.forName("UTF-8"));
-        vcapMultipleBindings = vcapMultipleBindings.replace("apiaccess", "broker");
-        XsuaaServicesParser cut = new XsuaaServicesParser(vcapMultipleBindings);
-        cut.parseCredentials();
-    }
-}
diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaResourceServerJwkAutoConfigurationTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaResourceServerJwkAutoConfigurationTest.java
index be7f2906..663980e4 100644
--- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaResourceServerJwkAutoConfigurationTest.java
+++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaResourceServerJwkAutoConfigurationTest.java
@@ -1,11 +1,6 @@
 package com.sap.cloud.security.xsuaa.autoconfiguration;
 
-import static org.hamcrest.Matchers.instanceOf;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.hamcrest.Matchers.nullValue;
-import static org.junit.Assert.assertThat;
-
+import com.sap.cloud.security.xsuaa.token.authentication.XsuaaJwtDecoder;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -20,10 +15,11 @@ import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.security.oauth2.jwt.Jwt;
 import org.springframework.security.oauth2.jwt.JwtDecoder;
-import org.springframework.security.oauth2.jwt.NimbusJwtDecoderJwkSupport;
+import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
 import org.springframework.test.context.junit4.SpringRunner;
 
-import com.sap.cloud.security.xsuaa.token.authentication.XsuaaJwtDecoder;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.assertThat;
 
 @RunWith(SpringRunner.class)
 @SpringBootTest(classes = { XsuaaResourceServerJwkAutoConfiguration.class, XsuaaAutoConfiguration.class })
@@ -86,8 +82,7 @@ public class XsuaaResourceServerJwkAutoConfigurationTest {
 				.run((context) -> {
 					assertThat(context.containsBean("xsuaaJwtDecoder"), is(false));
 					assertThat(context.containsBean("customJwtDecoder"), is(true));
-					assertThat(context.getBean("customJwtDecoder"),
-							instanceOf(NimbusJwtDecoderJwkSupport.class));
+					assertThat(context.getBean("customJwtDecoder"), instanceOf(NimbusJwtDecoder.class));
 				});
 	}
 
@@ -108,7 +103,7 @@ public class XsuaaResourceServerJwkAutoConfigurationTest {
 
 		@Bean
 		public JwtDecoder customJwtDecoder() {
-			return new NimbusJwtDecoderJwkSupport("http://localhost:8080/uaa/oauth/token_keys");
+			return NimbusJwtDecoder.withJwkSetUri("http://localhost:8080/uaa/oauth/token_keys").build();
 		}
 	}
 }
diff --git a/spring-xsuaa/src/test/resources/vcap_multipleBindings.json b/spring-xsuaa/src/test/resources/vcap_multipleBindings.json
deleted file mode 100644
index fe1c6501..00000000
--- a/spring-xsuaa/src/test/resources/vcap_multipleBindings.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
-  "xsuaa": [
-    {
-      "credentials": {
-        "clientid": "client-id-api",
-        "clientsecret": "client-secret-api"
-      },
-      "instance_name": "xsuaa-api",
-      "label": "xsuaa",
-      "name": "xsuaa-api",
-      "plan": "apiaccess",
-      "tags": [
-        "xsuaa"
-      ]
-    },
-    {
-      "credentials": {
-        "clientid": "client-id",
-        "clientsecret": "client-secret"
-      },
-      "instance_name": "xsuaa-main",
-      "label": "xsuaa",
-      "name": "xsuaa-main",
-      "plan": "application",
-      "tags": [
-        "xsuaa"
-      ]
-    }
-  ]
-}
-}
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 a122779c..dea64be8 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
@@ -97,7 +97,7 @@ public class DefaultOAuth2TokenServiceTest {
 
 	private OAuth2TokenResponse requestAccessToken() throws OAuth2ServiceException {
 		URI tokenEndpointUri = URI.create("https://subdomain.myauth.server.com/oauth/token");
-		HttpHeaders withoutAuthorizationHeader = HttpHeadersFactory.createWithoutAuthorizationHeader();
+		HttpHeaders withoutAuthorizationHeader = new HttpHeadersFactory().createWithoutAuthorizationHeader();
 		Map<String, String> parameters = Collections.emptyMap();
 		return cut.requestAccessToken(tokenEndpointUri, withoutAuthorizationHeader, parameters);
 	}
diff --git a/token-client/src/test/java/com/sap/cloud/security/xsuaa/http/HttpHeadersFactoryTest.java b/token-client/src/test/java/com/sap/cloud/security/xsuaa/http/HttpHeadersFactoryTest.java
deleted file mode 100644
index df22b2c4..00000000
--- a/token-client/src/test/java/com/sap/cloud/security/xsuaa/http/HttpHeadersFactoryTest.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.sap.cloud.security.xsuaa.http;
-
-import org.junit.Test;
-
-import static com.sap.cloud.security.xsuaa.http.HttpHeaders.*;
-import static com.sap.cloud.security.xsuaa.http.MediaType.*;
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class HttpHeadersFactoryTest {
-
-	private static final HttpHeader ACCEPT_JSON_HEADER = new HttpHeader(ACCEPT, APPLICATION_JSON.value());
-	private static final HttpHeader CONTENT_TYPE_URL_ENCODED = new HttpHeader(CONTENT_TYPE, APPLICATION_FORM_URLENCODED.value());
-	private static final String TOKEN = "TOKEN CONTENT";
-
-	@Test
-	public void createWithoutAuthorizationHeader_containsDefaultHeaders() {
-		HttpHeaders headers = HttpHeadersFactory.createWithoutAuthorizationHeader();
-		assertThat(headers.getHeaders()).hasSize(2);
-		assertThat(headers.getHeaders()).containsExactlyInAnyOrder(ACCEPT_JSON_HEADER, CONTENT_TYPE_URL_ENCODED);
-	}
-
-	@Test
-	public void createWithAuthorizationBearerHeader_containsDefaultHeadersAndToken() {
-		HttpHeaders headers = HttpHeadersFactory.createWithAuthorizationBearerHeader(TOKEN);
-		assertThat(headers.getHeaders()).hasSize(3);
-		assertThat(headers.getHeaders()).contains(ACCEPT_JSON_HEADER, CONTENT_TYPE_URL_ENCODED);
-		HttpHeader tokenHeader = new HttpHeader(AUTHORIZATION, "Bearer " + TOKEN);
-		assertThat(headers.getHeaders()).contains(tokenHeader);
-	}
-
-}
\ No newline at end of file