Repository: SAP/cloud-security-xsuaa-integration
Pull request #209 (2020-01-17 12:35:18)

With validator listener support
With the PR it is possible to pass a ValidationListener to the `JwtValidatorBuilder` and to the `AbstractTokenAuthenticator`.

I made a refactoring on the way: Created the `TokenTestValidator` that creates validators that can be used in testing.
(This _might_ be something to put into the test library. The problem with that is, that the java-security library cannot access the test library because the test library already depends on the java-security library. So I guess it will stay here.)


Patch:
diff --git a/java-security/README.md b/java-security/README.md
index 81e8e691..203cb636 100644
--- a/java-security/README.md
+++ b/java-security/README.md
@@ -86,9 +86,16 @@ Now configure the `JwtValidatorBuilder` once with the service configuration from
 CombiningValidator<Token> validators = JwtValidatorBuilder.getInstance(serviceConfig).build();
 ```
 
+
 > Note: By default `JwtValidatorBuilder` builds a `CombiningValidator`. 
 > For the Signature validation it needs to fetch the Json Web Token Keys (jwks) from the OAuth server using `DefaultOAuth2TokenKeyService`. In case the token does not provide a `jku` header parameter it also requests the Open-ID Provider Configuration from the OAuth Server to determine the `jwks_uri` using `DefaultOidcConfigurationService`. Both default services uses Apache Rest client and can be customized via the `JwtValidatorBuilder` builder.
-          
+
+#### [Optional] Step 2.1: Add validation listeners
+Optionally, you can add a validation listener to the validator to be able to get callbacks whenever a token is validated.
+```java
+JwtValidatorBuilder.getInstance(serviceConfig).withValidatorListener(validationListener);
+```
+The validation listener needs to implement the [ValidationListener](src/main/java/com/sap/cloud/security/token/validation/ValidationListener.java) interface to be able to receive callbacks on validation success or failure.
 
 ### Create a Token Object 
 This decodes an encoded access token (Jwt token) and parses its json header and payload. The `Token` interface provides a simple access to its JWT header parameters and its claims.
diff --git a/java-security/src/main/java/com/sap/cloud/security/servlet/AbstractTokenAuthenticator.java b/java-security/src/main/java/com/sap/cloud/security/servlet/AbstractTokenAuthenticator.java
index 366fd135..a674bc34 100644
--- a/java-security/src/main/java/com/sap/cloud/security/servlet/AbstractTokenAuthenticator.java
+++ b/java-security/src/main/java/com/sap/cloud/security/servlet/AbstractTokenAuthenticator.java
@@ -3,6 +3,7 @@ package com.sap.cloud.security.servlet;
 import com.sap.cloud.security.config.OAuth2ServiceConfiguration;
 import com.sap.cloud.security.token.SecurityContext;
 import com.sap.cloud.security.token.Token;
+import com.sap.cloud.security.token.validation.ValidationListener;
 import com.sap.cloud.security.token.validation.ValidationResult;
 import com.sap.cloud.security.token.validation.Validator;
 import com.sap.cloud.security.token.validation.validators.JwtValidatorBuilder;
@@ -17,11 +18,14 @@ import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 
 public abstract class AbstractTokenAuthenticator implements TokenAuthenticator {
 
 	private static final Logger logger = LoggerFactory.getLogger(AbstractTokenAuthenticator.class);
+	private final List<ValidationListener> validationListeners = new ArrayList<>();
 	private Validator<Token> tokenValidator;
 	protected OidcConfigurationServiceWithCache oidcConfigurationService;
 	protected OAuth2TokenKeyServiceWithCache tokenKeyService;
@@ -70,6 +74,19 @@ public abstract class AbstractTokenAuthenticator implements TokenAuthenticator {
 		return this;
 	}
 
+	/**
+	 * Adds the validation listener to the jwt validator that is being used by the
+	 * authenticator to validate the tokens.
+	 * 
+	 * @param validationListener
+	 *            the listener to be added.
+	 * @return the authenticator instance
+	 */
+	public AbstractTokenAuthenticator withValidationListener(ValidationListener validationListener) {
+		this.validationListeners.add(validationListener);
+		return this;
+	}
+
 	/**
 	 * Return configured service configuration or Environments.getCurrent() if not
 	 * configured.
@@ -82,17 +99,19 @@ public abstract class AbstractTokenAuthenticator implements TokenAuthenticator {
 	/**
 	 * Extracts the {@link Token} from the authorization header.
 	 *
-	 * @param authorizationHeader the value of the 'Authorization' request header
+	 * @param authorizationHeader
+	 *            the value of the 'Authorization' request header
 	 * @return the {@link Token} instance.
 	 */
 	protected abstract Token extractFromHeader(String authorizationHeader);
 
 	private Validator<Token> getOrCreateTokenValidator() {
 		if (tokenValidator == null) {
-			tokenValidator = JwtValidatorBuilder.getInstance(getServiceConfiguration())
+			JwtValidatorBuilder jwtValidatorBuilder = JwtValidatorBuilder.getInstance(getServiceConfiguration())
 					.withOAuth2TokenKeyService(tokenKeyService)
-					.withOidcConfigurationService(oidcConfigurationService)
-					.build();
+					.withOidcConfigurationService(oidcConfigurationService);
+			validationListeners.forEach(jwtValidatorBuilder::withValidatorListener);
+			tokenValidator = jwtValidatorBuilder.build();
 		}
 		return tokenValidator;
 	}
diff --git a/java-security/src/main/java/com/sap/cloud/security/token/validation/ValidationListener.java b/java-security/src/main/java/com/sap/cloud/security/token/validation/ValidationListener.java
index 4c270da6..483dde9b 100644
--- a/java-security/src/main/java/com/sap/cloud/security/token/validation/ValidationListener.java
+++ b/java-security/src/main/java/com/sap/cloud/security/token/validation/ValidationListener.java
@@ -5,7 +5,7 @@ package com.sap.cloud.security.token.validation;
  * {@link CombiningValidator}. Those classes will be called whenever a token is
  * being validated. <br>
  *
- * This might be relevant for writing your Audit logs.
+ * This might be relevant for writing Audit logs.
  */
 public interface ValidationListener {
 
diff --git a/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/JwtValidatorBuilder.java b/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/JwtValidatorBuilder.java
index 6c617dfa..44a5b62b 100644
--- a/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/JwtValidatorBuilder.java
+++ b/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/JwtValidatorBuilder.java
@@ -3,6 +3,7 @@ package com.sap.cloud.security.token.validation.validators;
 import com.sap.cloud.security.config.OAuth2ServiceConfiguration;
 import com.sap.cloud.security.token.Token;
 import com.sap.cloud.security.token.validation.CombiningValidator;
+import com.sap.cloud.security.token.validation.ValidationListener;
 import com.sap.cloud.security.token.validation.Validator;
 import com.sap.cloud.security.xsuaa.Assertions;
 import com.sap.cloud.security.xsuaa.client.*;
@@ -23,6 +24,7 @@ import static com.sap.cloud.security.config.Service.XSUAA;
 public class JwtValidatorBuilder {
 	private static Map<OAuth2ServiceConfiguration, JwtValidatorBuilder> instances = new HashMap<>();
 	private final Collection<Validator<Token>> validators = new ArrayList<>();
+	private final List<ValidationListener> validationListeners = new ArrayList<>();
 	private OAuth2ServiceConfiguration configuration;
 	private OidcConfigurationServiceWithCache oidcConfigurationService = null;
 	private OAuth2TokenKeyServiceWithCache tokenKeyService = null;
@@ -121,6 +123,18 @@ public class JwtValidatorBuilder {
 		return this;
 	}
 
+	/**
+	 * Adds the validation listener to the jwt validator that is being built.
+	 * 
+	 * @param validationListener
+	 *            the listener to be added to the validator.
+	 * @return this builder
+	 */
+	public JwtValidatorBuilder withValidatorListener(ValidationListener validationListener) {
+		validationListeners.add(validationListener);
+		return this;
+	}
+
 	/**
 	 * Builds the validators with the applied parameters.
 	 *
@@ -130,7 +144,9 @@ public class JwtValidatorBuilder {
 		List<Validator<Token>> allValidators = createDefaultValidators();
 		allValidators.addAll(validators);
 
-		return new CombiningValidator<>(allValidators);
+		CombiningValidator<Token> combiningValidator = new CombiningValidator<>(allValidators);
+		validationListeners.forEach(combiningValidator::registerValidationListener);
+		return combiningValidator;
 	}
 
 	private List<Validator<Token>> createDefaultValidators() {


Test patch:
diff --git a/java-security/src/test/java/com/sap/cloud/security/servlet/XsuaaTokenAuthenticatorTest.java b/java-security/src/test/java/com/sap/cloud/security/servlet/XsuaaTokenAuthenticatorTest.java
index 467cfc92..3eeb0823 100644
--- a/java-security/src/test/java/com/sap/cloud/security/servlet/XsuaaTokenAuthenticatorTest.java
+++ b/java-security/src/test/java/com/sap/cloud/security/servlet/XsuaaTokenAuthenticatorTest.java
@@ -6,7 +6,7 @@ import com.sap.cloud.security.config.Service;
 import com.sap.cloud.security.config.cf.CFConstants;
 import com.sap.cloud.security.token.SecurityContext;
 import com.sap.cloud.security.token.XsuaaToken;
-import com.sap.cloud.security.xsuaa.client.OAuth2ServiceException;
+import com.sap.cloud.security.token.validation.ValidationListener;
 import com.sap.cloud.security.xsuaa.client.OAuth2TokenKeyService;
 import com.sap.cloud.security.xsuaa.client.OAuth2TokenKeyServiceWithCache;
 import com.sap.cloud.security.xsuaa.http.HttpHeaders;
@@ -23,31 +23,36 @@ import java.io.IOException;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.when;
 
 public class XsuaaTokenAuthenticatorTest {
 
 	private final static HttpServletResponse HTTP_RESPONSE = Mockito.mock(HttpServletResponse.class);
 
-	private XsuaaToken xsuaaToken;
-	private OAuth2TokenKeyService tokenKeyService;
-	private OAuth2ServiceConfiguration oAuth2ServiceConfiguration;
+	private final XsuaaToken xsuaaToken;
+	private final XsuaaToken invalidSignatureToken;
 
 	private AbstractTokenAuthenticator cut;
 
+	public XsuaaTokenAuthenticatorTest() throws IOException {
+		xsuaaToken = new XsuaaToken(IOUtils.resourceToString("/xsuaaUserAccessTokenRSA256.txt", UTF_8));
+		invalidSignatureToken = new XsuaaToken(
+				IOUtils.resourceToString("/xsuaaInvalidSignatureTokenRSA256.txt", UTF_8));
+	}
+
 	@Before
 	public void setUp() throws IOException {
-		tokenKeyService = Mockito.mock(OAuth2TokenKeyService.class);
+		OAuth2TokenKeyService tokenKeyService = Mockito.mock(OAuth2TokenKeyService.class);
 		when(tokenKeyService.retrieveTokenKeys(any())).thenReturn(
 				JsonWebKeySetFactory.createFromJson(IOUtils.resourceToString("/jsonWebTokenKeys.json", UTF_8)));
-		oAuth2ServiceConfiguration = OAuth2ServiceConfigurationBuilder.forService(Service.XSUAA)
+		OAuth2ServiceConfiguration oAuth2ServiceConfiguration = OAuth2ServiceConfigurationBuilder
+				.forService(Service.XSUAA)
 				.withProperty(CFConstants.XSUAA.UAA_DOMAIN, "auth.com")
 				.withProperty(CFConstants.XSUAA.APP_ID, "appId")
 				.withClientId("clientId")
 				.build();
 
-		xsuaaToken = new XsuaaToken(IOUtils.resourceToString("/xsuaaUserAccessTokenRSA256.txt", UTF_8));
-
 		cut = new XsuaaTokenAuthenticator()
 				.withOAuth2TokenKeyService(
 						OAuth2TokenKeyServiceWithCache.getInstance().withTokenKeyService(tokenKeyService))
@@ -97,6 +102,38 @@ public class XsuaaTokenAuthenticatorTest {
 		assertThat(response.getToken()).isSameAs(SecurityContext.getToken());
 	}
 
+	@Test
+	public void validateRequest_validToken_listenerIsCalled() {
+		HttpServletRequest httpRequest = createRequestWithToken(xsuaaToken.getBearerAccessToken());
+		ValidationListener validationListener1 = Mockito.mock(ValidationListener.class);
+		ValidationListener validationListener2 = Mockito.mock(ValidationListener.class);
+
+		cut.withValidationListener(validationListener1)
+				.withValidationListener(validationListener2)
+				.validateRequest(httpRequest, HTTP_RESPONSE);
+
+		Mockito.verify(validationListener1, times(1)).onValidationSuccess();
+		Mockito.verify(validationListener2, times(1)).onValidationSuccess();
+		Mockito.verifyNoMoreInteractions(validationListener1);
+		Mockito.verifyNoMoreInteractions(validationListener2);
+	}
+
+	@Test
+	public void validateRequest_invalidToken_listenerIsCalled() {
+		HttpServletRequest httpRequest = createRequestWithToken(invalidSignatureToken.getBearerAccessToken());
+		ValidationListener validationListener1 = Mockito.mock(ValidationListener.class);
+		ValidationListener validationListener2 = Mockito.mock(ValidationListener.class);
+
+		cut.withValidationListener(validationListener1)
+				.withValidationListener(validationListener2)
+				.validateRequest(httpRequest, HTTP_RESPONSE);
+
+		Mockito.verify(validationListener1, times(1)).onValidationError(any());
+		Mockito.verify(validationListener2, times(1)).onValidationError(any());
+		Mockito.verifyNoMoreInteractions(validationListener2);
+		Mockito.verifyNoMoreInteractions(validationListener2);
+	}
+
 	private HttpServletRequest createRequestWithoutToken() {
 		return Mockito.mock(HttpServletRequest.class);
 	}
diff --git a/java-security/src/test/java/com/sap/cloud/security/token/validation/CombiningValidatorTest.java b/java-security/src/test/java/com/sap/cloud/security/token/validation/CombiningValidatorTest.java
index f16d21a8..0d12ecf6 100644
--- a/java-security/src/test/java/com/sap/cloud/security/token/validation/CombiningValidatorTest.java
+++ b/java-security/src/test/java/com/sap/cloud/security/token/validation/CombiningValidatorTest.java
@@ -1,8 +1,6 @@
 package com.sap.cloud.security.token.validation;
 
 import com.sap.cloud.security.token.Token;
-import com.sap.cloud.security.token.XsuaaToken;
-import com.sap.cloud.security.token.validation.*;
 import org.junit.Test;
 import org.mockito.Mockito;
 
@@ -40,7 +38,7 @@ public class CombiningValidatorTest {
 	@Test
 	public void validate_twoValidValidators_validResult() {
 		Validator<Token> combiningValidator = new CombiningValidator<>(
-				newArrayList(validValidator(), validValidator()));
+				newArrayList(TokenTestValidator.createValid(), TokenTestValidator.createValid()));
 
 		ValidationResult validationResult = combiningValidator.validate(TOKEN);
 
@@ -50,7 +48,7 @@ public class CombiningValidatorTest {
 	@Test
 	public void validate_twoInvalidValidators_invalidResult() {
 		Validator<Token> combiningValidator = new CombiningValidator<>(
-				newArrayList(invalidValidator(), invalidValidator()));
+				newArrayList(TokenTestValidator.createInvalid(), TokenTestValidator.createInvalid()));
 
 		ValidationResult validationResult = combiningValidator.validate(TOKEN);
 
@@ -60,8 +58,8 @@ public class CombiningValidatorTest {
 	@Test
 	public void validate_twoInvalidValidators_containsOnlyOneErrorMessages() {
 		Validator<Token> combiningValidator = new CombiningValidator<>(
-				newArrayList(validValidator(), invalidValidator(FIRST_ERROR_MESSAGE),
-						invalidValidator(SECOND_ERROR_MESSAGE)));
+				newArrayList(TokenTestValidator.createValid(), TokenTestValidator.createInvalid(FIRST_ERROR_MESSAGE),
+						TokenTestValidator.createInvalid(SECOND_ERROR_MESSAGE)));
 
 		String error = combiningValidator.validate(TOKEN).getErrorDescription();
 
@@ -70,7 +68,8 @@ public class CombiningValidatorTest {
 
 	@Test
 	public void registerValidationListener_validValidator_callsOnValidationSuccess() {
-		CombiningValidator<Token> combiningValidator = new CombiningValidator<>(newArrayList(validValidator()));
+		CombiningValidator<Token> combiningValidator = new CombiningValidator<>(newArrayList(
+				TokenTestValidator.createValid()));
 		ValidationListener validationListenerMock = Mockito.mock(ValidationListener.class);
 		combiningValidator.registerValidationListener(validationListenerMock);
 
@@ -82,7 +81,8 @@ public class CombiningValidatorTest {
 
 	@Test
 	public void registerValidationListener_invalidValidator_callsOnValidationSuccess() {
-		CombiningValidator<Token> combiningValidator = new CombiningValidator<>(newArrayList(invalidValidator()));
+		CombiningValidator<Token> combiningValidator = new CombiningValidator<>(newArrayList(
+				TokenTestValidator.createInvalid()));
 		ValidationListener validationListenerMock = Mockito.mock(ValidationListener.class);
 		combiningValidator.registerValidationListener(validationListenerMock);
 
@@ -94,7 +94,8 @@ public class CombiningValidatorTest {
 
 	@Test
 	public void registerValidationListener_listenerIsRemoved_isNotCalled() {
-		CombiningValidator<Token> combiningValidator = new CombiningValidator<>(newArrayList(validValidator()));
+		CombiningValidator<Token> combiningValidator = new CombiningValidator<>(newArrayList(
+				TokenTestValidator.createValid()));
 		ValidationListener validationListenerMock = Mockito.mock(ValidationListener.class);
 		combiningValidator.registerValidationListener(validationListenerMock);
 		combiningValidator.removeValidationListener(validationListenerMock);
@@ -107,29 +108,9 @@ public class CombiningValidatorTest {
 	@Test
 	public void toString_containsValidatorName() {
 		CombiningValidator<Token> combiningValidator = new CombiningValidator<>(
-				newArrayList(new MyTokenValidator()));
+				newArrayList(TokenTestValidator.createValid()));
 
-		assertThat(combiningValidator.toString()).contains(MyTokenValidator.class.getSimpleName());
-	}
-
-	private class MyTokenValidator implements Validator<Token> {
-		@Override
-		public ValidationResult validate(Token token) {
-			return ValidationResults.createValid();
-		}
-
-	}
-
-	private Validator<Token> validValidator() {
-		return (obj) -> ValidationResults.createValid();
-	}
-
-	private Validator<Token> invalidValidator() {
-		return invalidValidator(FIRST_ERROR_MESSAGE);
-	}
-
-	private Validator<Token> invalidValidator(String errorMessage) {
-		return (obj) -> ValidationResults.createInvalid(errorMessage);
+		assertThat(combiningValidator.toString()).contains(TokenTestValidator.class.getSimpleName());
 	}
 
 }
\ No newline at end of file
diff --git a/java-security/src/test/java/com/sap/cloud/security/token/validation/TokenTestValidator.java b/java-security/src/test/java/com/sap/cloud/security/token/validation/TokenTestValidator.java
new file mode 100644
index 00000000..2ffc1993
--- /dev/null
+++ b/java-security/src/test/java/com/sap/cloud/security/token/validation/TokenTestValidator.java
@@ -0,0 +1,32 @@
+package com.sap.cloud.security.token.validation;
+
+import com.sap.cloud.security.token.Token;
+
+public final class TokenTestValidator implements Validator<Token> {
+
+	public static final String DEFAULT_ERROR_DESCRIPTION = "test error description";
+
+	private ValidationResult validationResult;
+
+	private TokenTestValidator(ValidationResult validationResult) {
+		this.validationResult = validationResult;
+	}
+
+	public static TokenTestValidator createValid() {
+		return new TokenTestValidator(ValidationResults.createValid());
+	}
+
+	public static TokenTestValidator createInvalid() {
+		return TokenTestValidator.createInvalid(DEFAULT_ERROR_DESCRIPTION);
+	}
+
+	public static TokenTestValidator createInvalid(String errorDescription) {
+		return new TokenTestValidator(ValidationResults.createInvalid(errorDescription));
+	}
+
+	@Override
+	public ValidationResult validate(Token token) {
+		return validationResult;
+	}
+
+}
diff --git a/java-security/src/test/java/com/sap/cloud/security/token/validation/validators/JwtValidatorBuilderTest.java b/java-security/src/test/java/com/sap/cloud/security/token/validation/validators/JwtValidatorBuilderTest.java
index 1d9b3b33..7cb45de8 100644
--- a/java-security/src/test/java/com/sap/cloud/security/token/validation/validators/JwtValidatorBuilderTest.java
+++ b/java-security/src/test/java/com/sap/cloud/security/token/validation/validators/JwtValidatorBuilderTest.java
@@ -4,26 +4,35 @@ import com.sap.cloud.security.config.OAuth2ServiceConfiguration;
 import com.sap.cloud.security.config.Service;
 import com.sap.cloud.security.config.cf.CFConstants.XSUAA;
 import com.sap.cloud.security.token.Token;
-import com.sap.cloud.security.token.validation.ValidationResults;
-import com.sap.cloud.security.token.validation.Validator;
-import org.junit.Ignore;
+import com.sap.cloud.security.token.validation.*;
+import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
 
-import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.List;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.*;
 
+@RunWith(MockitoJUnitRunner.class)
 public class JwtValidatorBuilderTest {
 
 	public static final Token TOKEN = mock(Token.class);
 
+
+	@Before
+	public void setUp() {
+		Mockito.mockitoSession().initMocks();
+	}
+
 	@Test
 	public void sameServiceConfiguration_getSameInstance() throws URISyntaxException {
-		TokenValidator tokenValidatorMock = createTokenValidatorMock();
-		OAuth2ServiceConfiguration configuration = createMockConfiguration();
+		TokenTestValidator.createValid();
+		OAuth2ServiceConfiguration configuration = configuration();
 		JwtValidatorBuilder builder_1 = JwtValidatorBuilder.getInstance(configuration);
 		JwtValidatorBuilder builder_2 = JwtValidatorBuilder.getInstance(configuration);
 		assertThat(builder_1).isSameAs(builder_2);
@@ -31,18 +40,18 @@ public class JwtValidatorBuilderTest {
 
 	@Test
 	public void withAudienceValidator_overridesXsuaaJwtAudienceValidator() throws URISyntaxException {
-		TokenValidator tokenValidatorMock = createTokenValidatorMock();
-		List<Validator<Token>> validators = JwtValidatorBuilder.getInstance(createMockConfiguration())
-				.withAudienceValidator(tokenValidatorMock)
+		TokenTestValidator validator = TokenTestValidator.createValid();
+		List<Validator<Token>> validators = JwtValidatorBuilder.getInstance(configuration())
+				.withAudienceValidator(validator)
 				.build()
 				.getValidators();
-		assertThat(validators).contains(tokenValidatorMock)
+		assertThat(validators).contains(validator)
 				.doesNotHaveAnyElementsOfTypes(XsuaaJwtAudienceValidator.class);
 	}
 
 	@Test
 	public void build_containsAllDefaultValidators() throws URISyntaxException {
-		List<Validator<Token>> validators = JwtValidatorBuilder.getInstance(createMockConfiguration()).build()
+		List<Validator<Token>> validators = JwtValidatorBuilder.getInstance(configuration()).build()
 				.getValidators();
 
 		assertThat(validators)
@@ -54,21 +63,20 @@ public class JwtValidatorBuilderTest {
 
 	@Test
 	public void buildWithAnotherValidator_containsAddedValidator() throws URISyntaxException {
-		TokenValidator tokenValidatorMock = createTokenValidatorMock();
+		TokenTestValidator tokenValidator = TokenTestValidator.createValid();
 
-		List<Validator<Token>> validators = JwtValidatorBuilder.getInstance(createMockConfiguration())
-				.with(tokenValidatorMock)
+		List<Validator<Token>> validators = JwtValidatorBuilder.getInstance(configuration())
+				.with(tokenValidator)
 				.build()
 				.getValidators();
 
 		assertThat(validators)
 				.hasAtLeastOneElementOfType(JwtTimestampValidator.class)
-				.contains(tokenValidatorMock);
+				.contains(tokenValidator);
 	}
 
-	private OAuth2ServiceConfiguration createMockConfiguration() throws URISyntaxException {
+	private OAuth2ServiceConfiguration configuration() {
 		OAuth2ServiceConfiguration configuration = mock(OAuth2ServiceConfiguration.class);
-		when(configuration.getUrl()).thenReturn(new URI("https://my.auth.com"));
 		when(configuration.getClientId()).thenReturn("sb-test-app!t123");
 		when(configuration.getProperty(XSUAA.APP_ID)).thenReturn("test-app!t123");
 		when(configuration.getProperty(XSUAA.UAA_DOMAIN)).thenReturn("auth.com");
@@ -76,13 +84,4 @@ public class JwtValidatorBuilderTest {
 		return configuration;
 	}
 
-	private TokenValidator createTokenValidatorMock() {
-		TokenValidator tokenValidatorMock = mock(TokenValidator.class);
-		when(tokenValidatorMock.validate(TOKEN)).thenReturn(ValidationResults.createValid());
-		return tokenValidatorMock;
-	}
-
-	private interface TokenValidator extends Validator<Token> {
-
-	}
 }
\ No newline at end of file
diff --git a/java-security/src/test/resources/xsuaaInvalidSignatureTokenRSA256.txt b/java-security/src/test/resources/xsuaaInvalidSignatureTokenRSA256.txt
new file mode 100644
index 00000000..b5a610cc
--- /dev/null
+++ b/java-security/src/test/resources/xsuaaInvalidSignatureTokenRSA256.txt
@@ -0,0 +1,1 @@
+eyJhbGciOiJSUzI1NiJ9.eyJzY29wZSI6WyJvcGVuaWQiLCJhcHAxLnNjb3BlIl19.mXL5Zq4siZoxzpX0XW68rqLBXsxIE3fvf8x7HEg6QDe8UX5gm3vpH_b_eLqIWmOQczQvcKpx4hPIfmMOk38pwr2T8OK6STZDgOp4xFbQlNF0Ak1zEWwpnGVMxMMjqyG76J5dkynGCsKNTd0IxaXZS79zbo8og0RT1So4xUTNo84XKPkSUQjveo-Es5_J6ha3W6gBCnjem6L8eKLcPkiibtyYyWMcBHOmm5LYz5rgb2mjpgvTYQW6rrs4CoNRlKpKAPz7CweKIqiCxqqdU6Wac9G7QzmX3AV4T377RkokGqj395UzmWAWIxRWBmClnFd8jCS89qwyHKgp-pk0e8qffA==
\ No newline at end of file