Repository: SAP/cloud-security-xsuaa-integration
Pull request #105 (2019-07-08 16:46:44)

SecurityContext.init method
With version 1.4.0, Pull Request #74 the [SecurityContext ](https://github.com/SAP/cloud-security-xsuaa-integration/blob/master/spring-xsuaa/src/main/java/com/sap/xs2/security/container/SecurityContext.java) was enhanced by a method to initialize the SecurityContextHolder with a JWT token as documented here:
https://github.com/SAP/cloud-security-xsuaa-integration/blob/master/spring-xsuaa/README.md#setup-security-context-for-non-http-requests

I'm not that happy with the released API (as of version 1.4.0) because the JWT token must not necessarily be a validated one. Only when people follows the documentation, then it is guaranteed. Please have a look at my code change proposal.

Overload SecurityContext.init method:
`SpringSecurityContext.init(String encodedJwtToken, JwtDecoder jwtDecoder, AuthoritiesExtractor authoritiesExtractor)`

From customer perspective this would be also a simplification.

### incompatible change:
- we face issues, when microservice uses SAP-libs, that use sap-internal container lib, that's why we need to move our functionality to a separate class, namely `SpringSecurityContext`


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fe589e11..de1347f2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,12 @@
 
 All notable changes to this project will be documented in this file.
 
+## 1.6.0
+* Provides spring starter for spring-xsuaa
+* Supports reactive ServerHttpSecurity (Spring webflux). Have a look at the (webflux sample application)[samples/spring-webflux-security-xsuaa-usage/README.md]
+* Some enhancements for XSUAA integration
+* Improved SecurityContext.init() method as documented [here](spring-xsuaa/README.md)
+
 ## 1.5.0
 * Supports `jku` URI which is provided as part of the JSON Web Signature (JWS). The `jku` of the Jwt token header references the public key URI of the Xsuaa OAuth Authorization Server, and needs to match to the `xsuaa.uaadomain`.
 * Completely customizable auto-configurations so that apps can override the spring-xsuaa defaults:
diff --git a/README.md b/README.md
index a0642788..cda42fe6 100644
--- a/README.md
+++ b/README.md
@@ -38,6 +38,8 @@ See [spring-security-xsuaa-usage](samples/spring-security-xsuaa-usage) for an ex
 - [spring-xsuaa-mock](./spring-xsuaa-mock) offers a Mock web server to provide token keys for offline token validation in case xsuaa service (OAuth resource-server) is not in place. Its use is only intended for unit tests and when running your Spring boot application on a Webserver locally.
 - [spring-xsuaa-test](./spring-xsuaa-test) offers test utilities to generate custom JWT tokens for the purpose of tests.
 
+Find [here](https://github.com/SAP/cloud-application-security-sample/tree/master/spring-security-basis) a more sophisticated sample.
+
 # Download and Installation
 Build results are published to maven central: https://search.maven.org/search?q=com.sap.cloud.security 
 
diff --git a/api/pom.xml b/api/pom.xml
index c74b7991..16d53d33 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -4,12 +4,12 @@
 
 	<groupId>com.sap.cloud.security.xsuaa</groupId>
 	<artifactId>api</artifactId>
-	<version>1.5.0</version>
+	<version>1.6.0</version>
 
 	<parent>
 		<groupId>com.sap.cloud.security.xsuaa</groupId>
 		<artifactId>parent</artifactId>
-		<version>1.5.0</version>
+		<version>1.6.0</version>
 	</parent>
 
 	<packaging>jar</packaging>
diff --git a/etc/update_version.sh b/etc/update_version.sh
deleted file mode 100644
index 3db01db1..00000000
--- a/etc/update_version.sh
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/bash
-
-pomVersion=0.0.0
-if [ "$#" -eq 1 ]; then
-  echo "Update com.sap.cloud.security.xsuaa version to: " $1
-  pomVersion=$1
-else
-  echo ""
-  echo "Call this script with a version, e.g. see pom.xml and use 1.n.0 (n=n+1)"
-  echo "Example: ./update_version.sh 1.6.0"
-  echo ""
-  exit 1
-fi
-################################# CONFIGURATION #################################
-cd ..
-git remote add sec-lib-spring git@github.com:SAP/cloud-security-xsuaa-integration.git
-git fetch sec-lib-spring
-git checkout -B master_version sec-lib-spring/master
-git reset --hard sec-lib-spring/master
-git clean -d -x -f
-sed -i 's/## master[\n]*$/## master\n\n## '$pomVersion'\n\n### Changed\n - \n/g' CHANGELOG.md
-mvn versions:set -DgenerateBackupPoms=false -DnewVersion=$pomVersion
-mvn clean install -DskipTests=true -Dmaven.test.skip=true
-cd spring-xsuaa-it
-mvn versions:set -DgenerateBackupPoms=false -DnewVersion=$pomVersion
-cd ..
-git commit -a -m "com.sap.cloud.security.xsuaa version $pomVersion"
-git push sec-lib-spring master_version
-echo "Next Steps:"
-echo "1. Update version in samples Version to $pomVersion"
-echo "2. git push sec-lib-spring master_version"
-echo "3. start PR from master_version"
-
diff --git a/pom.xml b/pom.xml
index 1c4dd92d..e7a609d6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
 
 	<groupId>com.sap.cloud.security.xsuaa</groupId>
 	<artifactId>parent</artifactId>
-	<version>1.5.0</version>
+	<version>1.6.0</version>
 	<packaging>pom</packaging>
 
 	<name>parent</name>
diff --git a/samples/sap-java-buildpack-api-usage/pom.xml b/samples/sap-java-buildpack-api-usage/pom.xml
index 7db7acc9..570ffc12 100755
--- a/samples/sap-java-buildpack-api-usage/pom.xml
+++ b/samples/sap-java-buildpack-api-usage/pom.xml
@@ -21,7 +21,7 @@
 		<dependency>
 			<groupId>com.sap.cloud.security.xsuaa</groupId>
 			<artifactId>api</artifactId>
-			<version>1.5.0</version>
+			<version>1.6.0</version>
 			<scope>provided</scope>
 		</dependency>
 		<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
diff --git a/samples/spring-security-basic-auth/README.md b/samples/spring-security-basic-auth/README.md
index 3241937b..e2caf334 100644
--- a/samples/spring-security-basic-auth/README.md
+++ b/samples/spring-security-basic-auth/README.md
@@ -1,5 +1,5 @@
 # Description
-In some situations, the client does not support OAuth protocols so you need to fall back to basic authentication. This sample uses a implementation of the [bearerTokenResolver](https://docs.spring.io/spring-security/site/docs/5.1.1.RELEASE/api/org/springframework/security/oauth2/server/resource/web/BearerTokenResolver.html). Depending on the configuration, this resolver will
+In some situations, the client does not support OAuth protocols so you need to fall back to basic authentication. This sample uses a implementation of the [BearerTokenResolver](https://docs.spring.io/spring-security/site/docs/5.1.1.RELEASE/api/org/springframework/security/oauth2/server/resource/web/BearerTokenResolver.html). Depending on the configuration, this resolver will
 - Support OAuth JWT tokens
 - exchange incoming credentials using the OAuth password grant flow
 - exchange incoming credentials using the OAuth client credential flow
diff --git a/samples/spring-security-basic-auth/pom.xml b/samples/spring-security-basic-auth/pom.xml
index 626be093..367e5d66 100644
--- a/samples/spring-security-basic-auth/pom.xml
+++ b/samples/spring-security-basic-auth/pom.xml
@@ -11,7 +11,7 @@
 	</parent>
 
 	<artifactId>spring-security-basic-auth</artifactId>
-	<version>1.5.0</version>
+	<version>1.6.0</version>
 	<name>spring-security-basic-auth</name>
 
 	<properties>
@@ -40,7 +40,7 @@
 		<dependency>
 			<groupId>com.sap.cloud.security.xsuaa</groupId>
 			<artifactId>spring-xsuaa</artifactId>
-			<version>1.5.0</version>
+			<version>1.6.0</version>
 		</dependency>
 	</dependencies>
 
diff --git a/samples/spring-security-xsuaa-usage/pom.xml b/samples/spring-security-xsuaa-usage/pom.xml
index f1a91e06..5cb200ae 100644
--- a/samples/spring-security-xsuaa-usage/pom.xml
+++ b/samples/spring-security-xsuaa-usage/pom.xml
@@ -14,7 +14,7 @@
 
 	<groupId>com.sap.cloud.security.samples</groupId>
 	<artifactId>spring-security-xsuaa-usage</artifactId>
-	<version>1.5.0</version>
+	<version>1.6.0</version>
 	<name>spring-security-xsuaa-usage</name>
 
 	<properties>
@@ -57,7 +57,7 @@
 		<dependency>
 			<groupId>com.sap.cloud.security.xsuaa</groupId>
 			<artifactId>spring-xsuaa</artifactId>
-			<version>1.5.0</version>
+			<version>1.6.0</version>
 		</dependency>
 
 	</dependencies>
diff --git a/spring-xsuaa-it/pom.xml b/spring-xsuaa-it/pom.xml
index c00305da..617c43a9 100644
--- a/spring-xsuaa-it/pom.xml
+++ b/spring-xsuaa-it/pom.xml
@@ -11,7 +11,7 @@
 	</parent>
 
 	<artifactId>spring-xsuaa-it</artifactId>
-	<version>1.5.0</version>
+	<version>1.6.0</version>
 	<name>spring-xsuaa-it</name>
 
 	<properties>
diff --git a/spring-xsuaa-mock/README.md b/spring-xsuaa-mock/README.md
index 6013e053..c74b05d0 100644
--- a/spring-xsuaa-mock/README.md
+++ b/spring-xsuaa-mock/README.md
@@ -17,7 +17,7 @@ The default implementation offers already valid *token_keys* for JWT tokens, tha
 <dependency>
     <groupId>com.sap.cloud.security.xsuaa</groupId>
     <artifactId>spring-xsuaa-mock</artifactId>
-    <version>1.5.0</version>
+    <version>1.6.0</version>
 </dependency>
 <dependency> <!-- new with version 1.5.0 - provided with org.springframework.boot:spring-boot-starter:jar -->
     <groupId>org.springframework.boot</groupId>
diff --git a/spring-xsuaa-mock/pom.xml b/spring-xsuaa-mock/pom.xml
index 5f104f0d..0ecd31e7 100644
--- a/spring-xsuaa-mock/pom.xml
+++ b/spring-xsuaa-mock/pom.xml
@@ -7,7 +7,7 @@
 	<parent>
 		<groupId>com.sap.cloud.security.xsuaa</groupId>
 		<artifactId>parent</artifactId>
-		<version>1.5.0</version>
+		<version>1.6.0</version>
 	</parent>
 
 	<artifactId>spring-xsuaa-mock</artifactId>
diff --git a/spring-xsuaa/README.md b/spring-xsuaa/README.md
index e4176263..a08a296b 100644
--- a/spring-xsuaa/README.md
+++ b/spring-xsuaa/README.md
@@ -23,7 +23,7 @@ This library enhances the [spring-security](https://github.com/spring-projects/s
 <dependency>
     <groupId>com.sap.cloud.security.xsuaa</groupId>
     <artifactId>spring-xsuaa</artifactId>
-    <version>1.5.0</version>
+    <version>1.6.0</version>
 </dependency>
 <dependency> <!-- new with version 1.5.0 - provided with org.springframework.boot:spring-boot-starter:jar -->
     <groupId>org.springframework.boot</groupId>
@@ -86,29 +86,30 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
 
 
 ### Setup Security Context for non-HTTP requests
-In case of non-HTTP requests, you may need to initialize the Spring `SecurityContext` with a JWT token you've received from a message / event or you've requested from XSUAA directly:
+In case of non-HTTP requests, you may need to initialize the Spring Security Context with a JWT token you've received from a message / event or you've requested from XSUAA directly:
 
 ```java
+@Autowired 
+XsuaaServiceConfiguration xsuaaServiceConfiguration;
+
 @Autowired
 JwtDecoder jwtDecoder;
 
-@Value("${xsuaa.xsappname}")
-String xsappname;
-
 public void onEvent(String myEncodedJwtToken) {
-    Jwt jwtToken = jwtDecoder.decode(myEncodedJwtToken);
-    SecurityContext.init(xsappname, myEncodedJwtToken, true);
+    if (myEncodedJwtToken != null) {
+        SpringSecurityContext.init(myEncodedJwtToken, jwtDecoder, new LocalAuthoritiesExtractor(xsuaaServiceConfiguration.getAppId()));
+    }
     try {
-        // ... handle event
+        handleEvent();
     } finally {
-        SecurityContext.clear();
+        SpringSecurityContext.clear();
     }
 }
 ```
 
-In detail `com.sap.xs2.security.container.SecurityContext` wraps the Spring `SecurityContext`, which stores by default the information in `ThreadLocal`s. In order to avoid memory leaks it is recommended to remove the current thread's value for garbage collection.
+In detail `com.sap.cloud.security.xsuaa.token.SpringSecurityContext` wraps the Spring Security Context (namely `SecurityContextHolder.getContext()`), which stores by default the information in `ThreadLocal`s. In order to avoid memory leaks it is recommended to remove the current thread's value for garbage collection.
 
-Note that Spring `SecurityContext` is thread-bound and is NOT propagated to child-threads. This [Baeldung tutorial: Spring Security Context Propagation article](https://www.baeldung.com/spring-security-async-principal-propagation) provides more information on how to propagate the context.
+Note that Spring Security Context is thread-bound and is NOT propagated to child-threads. This [Baeldung tutorial: Spring Security Context Propagation article](https://www.baeldung.com/spring-security-async-principal-propagation) provides more information on how to propagate the context.
 
 ## Usage
 
@@ -125,7 +126,7 @@ public Map<String, String> message(@AuthenticationPrincipal Token token) {
 Or alternatively:
 ```java
 public Map<String, String> message() {
-    Token token = SecurityContext.getToken();
+    Token token = SpringSecurityContext.getToken();
     token.getGivenName();
 }
 ```
diff --git a/spring-xsuaa/pom.xml b/spring-xsuaa/pom.xml
index e0e0c843..36973a29 100644
--- a/spring-xsuaa/pom.xml
+++ b/spring-xsuaa/pom.xml
@@ -7,7 +7,7 @@
 	<parent>
 		<groupId>com.sap.cloud.security.xsuaa</groupId>
 		<artifactId>parent</artifactId>
-		<version>1.5.0</version>
+		<version>1.6.0</version>
 	</parent>
 
 	<artifactId>spring-xsuaa</artifactId>
@@ -60,6 +60,22 @@
 			<artifactId>caffeine</artifactId>
 			<version>${caffeine.version}</version>
 		</dependency>
+		<dependency>
+			<groupId>org.springframework.security.oauth</groupId>
+			<artifactId>spring-security-oauth2</artifactId>
+			<version>2.3.6.RELEASE</version>
+			<scope>compile</scope>
+			<exclusions>
+				<exclusion>
+					<artifactId>spring-beans</artifactId>
+					<groupId>org.springframework</groupId>
+				</exclusion>
+				<exclusion>
+					<artifactId>spring-context</artifactId>
+					<groupId>org.springframework</groupId>
+				</exclusion>
+			</exclusions>
+		</dependency>
 
 		<dependency>
 			<groupId>junit</groupId>
@@ -97,7 +113,6 @@
 			<version>${project.version}</version>
 			<scope>test</scope>
 		</dependency>
-
 	</dependencies>
 
 	<build>
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/DummyXsuaaServiceConfiguration.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/DummyXsuaaServiceConfiguration.java
new file mode 100644
index 00000000..9d34345f
--- /dev/null
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/DummyXsuaaServiceConfiguration.java
@@ -0,0 +1,46 @@
+package com.sap.cloud.security.xsuaa;
+
+public class DummyXsuaaServiceConfiguration implements XsuaaServiceConfiguration {
+
+	private String clientId;
+	private String uaaDomain;
+	private String appId;
+
+	public DummyXsuaaServiceConfiguration() {
+	}
+
+	public DummyXsuaaServiceConfiguration(String clientId, String appId) {
+		this.clientId = clientId;
+		this.appId = appId;
+	}
+
+	@Override
+	public String getClientId() {
+		return clientId;
+	}
+
+	@Override
+	public String getClientSecret() {
+		return null;
+	}
+
+	@Override
+	public String getUaaUrl() {
+		return null;
+	}
+
+	@Override
+	public String getTokenKeyUrl(String zid, String subdomain) {
+		return null;
+	}
+
+	@Override
+	public String getAppId() {
+		return appId;
+	}
+
+	@Override
+	public String getUaaDomain() {
+		return uaaDomain;
+	}
+}
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaResourceServerJwkAutoConfiguration.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaResourceServerJwkAutoConfiguration.java
index 1a1362c7..2347d9f5 100644
--- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaResourceServerJwkAutoConfiguration.java
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/autoconfiguration/XsuaaResourceServerJwkAutoConfiguration.java
@@ -1,7 +1,5 @@
 package com.sap.cloud.security.xsuaa.autoconfiguration;
 
-import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration;
-import com.sap.cloud.security.xsuaa.token.authentication.XsuaaJwtDecoderBuilder;
 import org.springframework.boot.autoconfigure.AutoConfigureBefore;
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -14,6 +12,9 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.security.oauth2.jwt.Jwt;
 import org.springframework.security.oauth2.jwt.JwtDecoder;
 
+import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration;
+import com.sap.cloud.security.xsuaa.token.authentication.XsuaaJwtDecoderBuilder;
+
 /**
  * {@link EnableAutoConfiguration Auto-configuration} that exposes a
  * {@link JwtDecoder}, which has the standard Spring Security Jwt validators as
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/AuthoritiesExtractor.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/AuthoritiesExtractor.java
index e43399a2..6ce5a524 100644
--- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/AuthoritiesExtractor.java
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/extractor/AuthoritiesExtractor.java
@@ -5,14 +5,15 @@ import java.util.Collection;
 import com.sap.cloud.security.xsuaa.token.XsuaaToken;
 import org.springframework.security.core.GrantedAuthority;
 
+/**
+ * Extracts the authorities from the Jwt token. Can use this method to map /
+ * manipulate scopes, e.g. by changing their prefix, etc.
+ */
 public interface AuthoritiesExtractor {
 	/**
 	 * Returns the granted authorities based on the information in the Jwt. A
 	 * standard implementation will base the granted authorities on the scopes.
 	 *
-	 * Implementations can use this method to map / manipulate scopes, e.g. by
-	 * changing their prefix, etc.
-	 *
 	 * @param jwt
 	 *            the Jwt to extract the authorities from.
 	 * @return the collection of granted authorities.
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/AuthenticationToken.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/AuthenticationToken.java
index 8142156d..0a2ed2fc 100644
--- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/AuthenticationToken.java
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/AuthenticationToken.java
@@ -15,17 +15,25 @@ import org.springframework.security.oauth2.server.resource.authentication.JwtAut
  */
 public class AuthenticationToken extends JwtAuthenticationToken {
 
+	private Token token;
+
 	public AuthenticationToken(Jwt jwt, Collection<GrantedAuthority> authorities) {
 		super(jwt, authorities);
-	}
 
-	@Override
-	public Object getPrincipal() {
 		// Here is where the actual magic happens.
 		// The Jwt is exchanged for another implementation.
 		XsuaaToken token = new XsuaaToken(getToken());
 		token.setAuthorities(this.getAuthorities());
+		this.token = token;
+	}
+
+	@Override
+	public Object getPrincipal() {
 		return token;
 	}
 
+	@Override
+	public String getName() {
+		return token.getUsername();
+	}
 }
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/OAuth2AuthenticationConverter.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/OAuth2AuthenticationConverter.java
new file mode 100644
index 00000000..0c79c318
--- /dev/null
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/OAuth2AuthenticationConverter.java
@@ -0,0 +1,35 @@
+package com.sap.cloud.security.xsuaa.token;
+
+import static com.sap.cloud.security.xsuaa.token.TokenClaims.CLAIM_CLIENT_ID;
+
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.provider.AuthorizationRequest;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+
+import com.sap.cloud.security.xsuaa.extractor.AuthoritiesExtractor;
+
+/**
+ * An authentication converter that sets a OAuth2Authentication object. Required
+ * to keep compatibility with UAA.
+ */
+public class OAuth2AuthenticationConverter extends TokenAuthenticationConverter {
+
+	public OAuth2AuthenticationConverter(AuthoritiesExtractor authoritiesExtractor) {
+		super(authoritiesExtractor);
+	}
+
+	@Override
+	public OAuth2Authentication convert(Jwt jwt) {
+		AuthenticationToken authenticationToken = (AuthenticationToken) super.convert(jwt);
+		String clientId = jwt.getClaimAsString(CLAIM_CLIENT_ID);
+		AuthorizationRequest authorizationRequest = new AuthorizationRequest(clientId,
+				authenticationToken.getAuthorities().stream().map(Objects::toString).collect(Collectors.toList()));
+		authorizationRequest.setApproved(true);
+		authorizationRequest.setAuthorities(authenticationToken.getAuthorities());
+
+		return new OAuth2Authentication(authorizationRequest.createOAuth2Request(), authenticationToken);
+	}
+}
\ No newline at end of file
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/SpringSecurityContext.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/SpringSecurityContext.java
new file mode 100644
index 00000000..f4b4932c
--- /dev/null
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/SpringSecurityContext.java
@@ -0,0 +1,71 @@
+package com.sap.cloud.security.xsuaa.token;
+
+import com.sap.cloud.security.xsuaa.extractor.AuthoritiesExtractor;
+import com.sap.cloud.security.xsuaa.token.authentication.XsuaaJwtDecoder;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.jwt.JwtDecoder;
+import org.springframework.util.Assert;
+
+public class SpringSecurityContext {
+
+	/**
+	 * Obtain the Token object from the Spring Security Context
+	 * {@link SecurityContextHolder}
+	 *
+	 * @return Token object
+	 * @throws AccessDeniedException
+	 *             in case there is no token, user is not authenticated
+	 *             <p>
+	 *             Note: This method is introduced with xsuaa spring client lib.
+	 */
+	static public Token getToken() {
+		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+
+		if (authentication == null) {
+			throw new AccessDeniedException("Access forbidden: not authenticated");
+		}
+
+		Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+		Assert.state(principal != null, "Principal must not be null");
+		Assert.state(principal instanceof Token, "Unexpected principal type");
+
+		return (Token) principal;
+	}
+
+	/**
+	 * Initializes the Spring Security Context {@link SecurityContextHolder} and
+	 * extracts the authorities. With version 1.5.0 you can configure your own
+	 * {@link AuthoritiesExtractor} to specify how to extract the authorities.
+	 *
+	 * @param encodedJwtToken
+	 *            the jwt token that is decoded with the given JwtDecoder
+	 * @param xsuaaJwtDecoder
+	 *            the decoder of type {@link XsuaaJwtDecoder}
+	 * @param authoritiesExtractor
+	 *            the extractor used to turn Jwt scopes into Spring Security
+	 *            authorities.
+	 */
+	static public void init(String encodedJwtToken, JwtDecoder xsuaaJwtDecoder,
+			AuthoritiesExtractor authoritiesExtractor) {
+		Assert.isInstanceOf(XsuaaJwtDecoder.class, xsuaaJwtDecoder,
+				"Passed JwtDecoder instance must be of type 'XsuaaJwtDecoder'");
+		Jwt jwtToken = xsuaaJwtDecoder.decode(encodedJwtToken);
+
+		TokenAuthenticationConverter authenticationConverter = new TokenAuthenticationConverter(authoritiesExtractor);
+		Authentication authentication = authenticationConverter.convert(jwtToken);
+
+		SecurityContextHolder.createEmptyContext();
+		SecurityContextHolder.getContext().setAuthentication(authentication);
+	}
+
+	/**
+	 * Cleans up the Spring Security Context {@link SecurityContextHolder} and
+	 * release thread locals for Garbage Collector to avoid memory leaks resources.
+	 */
+	static public void clear() {
+		SecurityContextHolder.clearContext();
+	}
+}
\ No newline at end of file
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/TokenAuthenticationConverter.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/TokenAuthenticationConverter.java
index f7750acd..5daf3bbd 100644
--- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/TokenAuthenticationConverter.java
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/TokenAuthenticationConverter.java
@@ -1,19 +1,19 @@
 package com.sap.cloud.security.xsuaa.token;
 
+import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration;
 import com.sap.cloud.security.xsuaa.extractor.AuthoritiesExtractor;
 import com.sap.cloud.security.xsuaa.extractor.DefaultAuthoritiesExtractor;
 import com.sap.cloud.security.xsuaa.extractor.LocalAuthoritiesExtractor;
 import org.springframework.core.convert.converter.Converter;
 import org.springframework.security.authentication.AbstractAuthenticationToken;
 import org.springframework.security.oauth2.jwt.Jwt;
-
-import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration;
 import org.springframework.util.Assert;
 
 /**
- * An authentication converter that removes the ugly application id prefix (e.g.
- * my-application-demo!t1229) from the scopes in the JWT.
- *
+ * An authentication converter that extracts authorization related information
+ * from the Jwt token. For example the{@link LocalAuthoritiesExtractor} can
+ * remove the ugly application id prefix (e.g.my-application-demo!t1229) from
+ * the scopes in the JWT.
  */
 public class TokenAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
 
@@ -22,7 +22,7 @@ public class TokenAuthenticationConverter implements Converter<Jwt, AbstractAuth
 
 	/**
 	 * Creates a new converter with the given {@link AuthoritiesExtractor}.
-	 * 
+	 *
 	 * @param authoritiesExtractor
 	 *            - the extractor used to turn Jwt scopes into Spring Security
 	 *            authorities.
@@ -34,7 +34,7 @@ public class TokenAuthenticationConverter implements Converter<Jwt, AbstractAuth
 	/**
 	 * Creates a new converter with a new {@link DefaultAuthoritiesExtractor}
 	 * instance as default authorities extractor.
-	 * 
+	 *
 	 * @param appId
 	 *            e.g. myXsAppname!t123
 	 */
@@ -71,9 +71,13 @@ public class TokenAuthenticationConverter implements Converter<Jwt, AbstractAuth
 	 *            e.g. "Display".
 	 */
 	public void setLocalScopeAsAuthorities(boolean extractLocalScopesOnly) {
-		Assert.state(appId != null,
-				"For local Scope extraction 'appId' must be provided to `TokenAuthenticationConverter`");
-		authoritiesExtractor = new LocalAuthoritiesExtractor(appId);
+		if (extractLocalScopesOnly) {
+			Assert.state(appId != null,
+					"For local Scope extraction 'appId' must be provided to `TokenAuthenticationConverter`");
+			authoritiesExtractor = new LocalAuthoritiesExtractor(appId);
+		} else {
+			authoritiesExtractor = new DefaultAuthoritiesExtractor();
+		}
 	}
 
 }
\ No newline at end of file
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/TokenClaims.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/TokenClaims.java
index 3393247e..b7de400b 100644
--- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/TokenClaims.java
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/TokenClaims.java
@@ -19,4 +19,8 @@ public final class TokenClaims {
 	public static final String CLAIM_GRANT_TYPE = "grant_type";
 	public static final String CLAIM_ZDN = "zdn";
 	public static final String CLAIM_ZONE_ID = "zid";
+	public static final String CLAIM_ISSUER = "iss";
+	public static final String CLAIM_JKU = "jku";
+	public static final String CLAIM_KID = "kid";
+
 }
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/PostValidationAction.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/PostValidationAction.java
new file mode 100644
index 00000000..36b76f76
--- /dev/null
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/PostValidationAction.java
@@ -0,0 +1,8 @@
+package com.sap.cloud.security.xsuaa.token.authentication;
+
+import org.springframework.security.oauth2.jwt.Jwt;
+
+public interface PostValidationAction {
+
+	void perform(Jwt token);
+}
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/TokenInfoExtractor.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/TokenInfoExtractor.java
new file mode 100644
index 00000000..26f5b55b
--- /dev/null
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/TokenInfoExtractor.java
@@ -0,0 +1,16 @@
+package com.sap.cloud.security.xsuaa.token.authentication;
+
+import com.nimbusds.jwt.JWT;
+
+/**
+ * Responsible to extract information out of the token and provide it to the
+ * JwtDecoder.
+ */
+public interface TokenInfoExtractor {
+
+	String getJku(JWT jwt);
+
+	String getKid(JWT jwt);
+
+	String getUaaDomain(JWT jwt);
+}
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 ee1dba33..fe05bb34 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,9 +1,13 @@
 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.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
@@ -27,15 +31,38 @@ public class XsuaaJwtDecoder implements JwtDecoder {
 	private final Logger logger = LoggerFactory.getLogger(getClass());
 
 	Cache<String, JwtDecoder> cache;
-	private String uaaDomain;
 	private OAuth2TokenValidator<Jwt> tokenValidators;
+	private Collection<PostValidationAction> postValidationActions;
+	private TokenInfoExtractor tokenInfoExtractor;
 
 	XsuaaJwtDecoder(XsuaaServiceConfiguration xsuaaServiceConfiguration, int cacheValidityInSeconds, int cacheSize,
 			OAuth2TokenValidator<Jwt> tokenValidators) {
 		cache = Caffeine.newBuilder().expireAfterWrite(cacheValidityInSeconds, TimeUnit.SECONDS).maximumSize(cacheSize)
 				.build();
-		this.uaaDomain = xsuaaServiceConfiguration.getUaaDomain();
 		this.tokenValidators = tokenValidators;
+
+		this.tokenInfoExtractor = new TokenInfoExtractor() {
+			@Override
+			public String getJku(JWT jwt) {
+				return (String) jwt.getHeader().toJSONObject().getOrDefault(CLAIM_JKU, null);
+			}
+
+			@Override
+			public String getKid(JWT jwt) {
+				return (String) jwt.getHeader().toJSONObject().getOrDefault(CLAIM_KID, null);
+			}
+
+			@Override
+			public String getUaaDomain(JWT jwt) {
+				return xsuaaServiceConfiguration.getUaaDomain();
+			}
+		};
+	}
+
+	XsuaaJwtDecoder(XsuaaServiceConfiguration xsuaaServiceConfiguration, int cacheValidityInSeconds, int cacheSize,
+			OAuth2TokenValidator<Jwt> tokenValidators, Collection<PostValidationAction> postValidationActions) {
+		this(xsuaaServiceConfiguration, cacheValidityInSeconds, cacheSize, tokenValidators);
+		this.postValidationActions = postValidationActions;
 	}
 
 	@Override
@@ -49,13 +76,19 @@ public class XsuaaJwtDecoder implements JwtDecoder {
 			throw new JwtException("Error initializing JWT decoder: " + ex.getMessage());
 		}
 
-		String jku = (String) jwt.getHeader().toJSONObject().getOrDefault("jku", null);
-		String kid = (String) jwt.getHeader().toJSONObject().getOrDefault("kid", null);
+		String jku = tokenInfoExtractor.getJku(jwt);
+		String kid = tokenInfoExtractor.getKid(jwt);
+		String uaaDomain = tokenInfoExtractor.getUaaDomain(jwt);
 
 		try {
 			canVerifyWithOnlineKey(jku, kid, uaaDomain);
 			validateJKU(jku, uaaDomain);
-			return verifyWithOnlineKey(token, jku, kid);
+			Jwt verifiedToken = verifyWithOnlineKey(token, jku, kid);
+
+			if (postValidationActions != null) {
+				postValidationActions.forEach(act -> act.perform(verifiedToken));
+			}
+			return verifiedToken;
 		} catch (JwtValidationException ex) {
 			throw ex;
 		} catch (JwtException ex) {
@@ -106,4 +139,8 @@ public class XsuaaJwtDecoder implements JwtDecoder {
 		decoder.setJwtValidator(tokenValidators);
 		return decoder;
 	}
+
+	public void setTokenInfoExtractor(TokenInfoExtractor tokenInfoExtractor) {
+		this.tokenInfoExtractor = tokenInfoExtractor;
+	}
 }
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 f4439fab..cd76f0b2 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,5 +1,8 @@
 package com.sap.cloud.security.xsuaa.token.authentication;
 
+import java.util.Arrays;
+import java.util.Collection;
+
 import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration;
 import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
 import org.springframework.security.oauth2.core.OAuth2TokenValidator;
@@ -14,6 +17,7 @@ public class XsuaaJwtDecoderBuilder {
 	int decoderCacheSize;
 	OAuth2TokenValidator<Jwt> xsuaaTokenValidators;
 	OAuth2TokenValidator<Jwt> defaultTokenValidators;
+	Collection<PostValidationAction> postValidationActions;
 
 	/**
 	 * Utility for building a JWT decoder configuration
@@ -38,7 +42,8 @@ public class XsuaaJwtDecoderBuilder {
 		DelegatingOAuth2TokenValidator<Jwt> combinedTokenValidators = new DelegatingOAuth2TokenValidator<>(
 				defaultTokenValidators,
 				xsuaaTokenValidators);
-		return new XsuaaJwtDecoder(configuration, decoderCacheValidity, decoderCacheSize, combinedTokenValidators);
+		return new XsuaaJwtDecoder(configuration, decoderCacheValidity, decoderCacheSize,
+				combinedTokenValidators, postValidationActions);
 	}
 
 	/**
@@ -67,6 +72,19 @@ public class XsuaaJwtDecoderBuilder {
 		return this;
 	}
 
+	/**
+	 * Sets the PostValidationActions that are executed after successful
+	 * verification and validation of the token.
+	 *
+	 * @param postValidationActions
+	 *            the PostValidationActions
+	 * @return this
+	 */
+	public XsuaaJwtDecoderBuilder withPostValidationActions(PostValidationAction... postValidationActions) {
+		this.postValidationActions = Arrays.asList(postValidationActions);
+		return this;
+	}
+
 	/**
 	 * Configures clone token validator, in case of two xsuaa bindings (application
 	 * and broker plan).
diff --git a/spring-xsuaa/src/main/java/com/sap/xs2/security/container/SecurityContext.java b/spring-xsuaa/src/main/java/com/sap/xs2/security/container/SecurityContext.java
index e96b1a9e..6185a3a0 100644
--- a/spring-xsuaa/src/main/java/com/sap/xs2/security/container/SecurityContext.java
+++ b/spring-xsuaa/src/main/java/com/sap/xs2/security/container/SecurityContext.java
@@ -1,17 +1,19 @@
 package com.sap.xs2.security.container;
 
+import com.sap.cloud.security.xsuaa.extractor.AuthoritiesExtractor;
+import com.sap.cloud.security.xsuaa.extractor.LocalAuthoritiesExtractor;
+import com.sap.cloud.security.xsuaa.token.SpringSecurityContext;
+import com.sap.cloud.security.xsuaa.token.Token;
 import com.sap.cloud.security.xsuaa.token.TokenAuthenticationConverter;
 import org.springframework.security.access.AccessDeniedException;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.oauth2.jwt.Jwt;
-import org.springframework.util.Assert;
-
-import com.sap.cloud.security.xsuaa.token.Token;
+import org.springframework.security.oauth2.jwt.JwtDecoder;
 
 public class SecurityContext {
 	/**
-	 * Obtain the UserInfo object from the Spring SecurityContext
+	 * Obtain the UserInfo object from the Spring Security Context
 	 *
 	 * @return UserInfo object
 	 * @throws UserInfoException
@@ -32,36 +34,54 @@ public class SecurityContext {
 	}
 
 	/**
-	 * Obtain the Token object from the Spring SecurityContext
+	 * Obtain the Token object from the Spring Security Context
 	 *
 	 * @return Token object
 	 * @throws AccessDeniedException
 	 *             in case there is no token, user is not authenticated
+	 *             <p>
+	 *             Note: This method is introduced with xsuaa spring client lib.
+	 * @deprecated method is moved to {@link SpringSecurityContext#getToken()}
 	 */
+	@Deprecated
 	static public Token getToken() {
-		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
-
-		if (authentication == null) {
-			throw new AccessDeniedException("Access forbidden: not authenticated");
-		}
-
-		Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
-		Assert.state(principal != null, "Principal must not be null");
-		Assert.state(principal instanceof Token, "Unexpected principal type");
-
-		return (Token) principal;
+		return SpringSecurityContext.getToken();
 	}
 
+	/**
+	 * Initializes the Spring Security Context and extracts the authorities.
+	 *
+	 * @param appId
+	 *            the application id e.g. myXsAppname!t123
+	 * @param token
+	 *            the jwt token
+	 * @param extractLocalScopesOnly
+	 *            true when {@link Token#getAuthorities()} should only extract local
+	 *            scopes. Local scopes means that non-application specific scopes
+	 *            are filtered out and scopes are returned without appId prefix,
+	 *            e.g. "Display".
+	 * @deprecated use method
+	 *             {@link SpringSecurityContext#init(String, JwtDecoder, AuthoritiesExtractor)}
+	 *             instead
+	 */
+	@Deprecated
 	static public void init(String appId, Jwt token, boolean extractLocalScopesOnly) {
-		TokenAuthenticationConverter authenticationConverter = new TokenAuthenticationConverter(appId);
-		authenticationConverter.setLocalScopeAsAuthorities(extractLocalScopesOnly);
+		TokenAuthenticationConverter authenticationConverter = new TokenAuthenticationConverter(
+				new LocalAuthoritiesExtractor(appId));
 		Authentication authentication = authenticationConverter.convert(token);
 
 		SecurityContextHolder.createEmptyContext();
 		SecurityContextHolder.getContext().setAuthentication(authentication);
 	}
 
+	/**
+	 * Cleans up the Spring Security Context and release thread locals for Garbage
+	 * Collector to avoid memory leaks resources.
+	 *
+	 * @deprecated method is moved to {@link SpringSecurityContext#clear()}
+	 */
+	@Deprecated
 	static public void clear() {
-		SecurityContextHolder.clearContext();
+		SpringSecurityContext.clear();
 	}
-}
+}
\ No newline at end of file
diff --git a/spring-xsuaa/src/main/java/com/sap/xs2/security/container/UserInfo.java b/spring-xsuaa/src/main/java/com/sap/xs2/security/container/UserInfo.java
index 7ede0eec..2c48d215 100644
--- a/spring-xsuaa/src/main/java/com/sap/xs2/security/container/UserInfo.java
+++ b/spring-xsuaa/src/main/java/com/sap/xs2/security/container/UserInfo.java
@@ -257,7 +257,7 @@ public class UserInfo implements XSUserInfo {
 	@Override
 	public String getToken(String namespace, String name) throws UserInfoException {
 		if (!(getGrantType().equals(GRANTTYPE_CLIENTCREDENTIAL)) && hasAttributes() && isInForeignMode()) {
-			throw new UserInfoException("The SecurityContext has been initialized with an access token of a\n"
+			throw new UserInfoException("The Spring Security Context has been initialized with an access token of a\n"
 					+ "foreign OAuth Client Id and/or Identity Zone. Furthermore, the\n"
 					+ "access token contains attributes. Due to the fact that we want to\n"
 					+ "restrict attribute access to the application that provided the \n"
diff --git a/troubleshooting/logcollector/README.md b/troubleshooting/logcollector/README.md
index 2d0109af..bb6459a1 100644
--- a/troubleshooting/logcollector/README.md
+++ b/troubleshooting/logcollector/README.md
@@ -3,7 +3,7 @@
 ## Description
 The logcollector scripts help you setting the right log levels for troubleshooting your application and application-router.
 
-Therefore it logs you in to your Cloud Foundry Space, sets the log levels for a given application and application router to the values recommended [here](https://wiki.wdf.sap.corp/wiki/display/NWCUIAMSIM/Increase+log+verbosity+for+detailed+error+analysis) and restarts the given apps.
+Therefore it logs you in to your Cloud Foundry Space, sets the log levels for a given application and application router to the values recommended [here (step 12)](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/f22d5100b3a243af88e3edf5311754fc.html) and restarts the given apps.
 Then you can replay your scenario. After that the script restores the log levels and restarts the apps again to make sure the changes take effect. It zips the aggregated logs in one file and prints the location to the output.
 
 
diff --git a/troubleshooting/logcollector/cf-logcollector.sh b/troubleshooting/logcollector/cf-logcollector.sh
index 69e9cb24..87c348fb 100755
--- a/troubleshooting/logcollector/cf-logcollector.sh
+++ b/troubleshooting/logcollector/cf-logcollector.sh
@@ -45,8 +45,8 @@ echo -e "\nSuccessfully logged in, will continue...\n"
 checkappname "$appname"
 checkappname "$approutername"
 
-printf "\nThis will restart your application \e[36m\e[1m%s\e[0m and your application router \e[36m\e[1m%s\e[0m twice. \nAre you sure (y/n)?" "$appname" "$approutername"
-read -rs -n 1 -p "" answer
+printf "\nThis will restart your application \033[36m\033[1m%s\033[0m and your application router \033[36m\033[1m%s\033[0m twice. \nAre you sure (Yy/Nn)?" "$appname" "$approutername"
+read -r -p "" answer
 if [ "$answer" != "${answer#[Yy]}" ]
 then
     true
@@ -67,12 +67,16 @@ cf restart "$approutername"
 cf restart "$appname"
 
 #Creating, collecting and compressing the logs
-echo -e "\n\e[36m\e[1mNow please repeat your scenario (e.g. try to login to your app or similar)...\e[0m\n"
+echo -e "\n\033[36m\033[1mNow please repeat your scenario (e.g. try to login to your app or similar)...\033[0m\n"
 read -rp "When you are done please press ENTER to collect the logs:"
 
 echo -e "\nCollecting the logs..."
 
-{ echo -e "Approuter logs:\n\n"; cf logs "$approutername" --recent; echo -e "\n\nApp logs:\n\n"; cf logs "$appname" --recent; } | zip -q "$logszip" -
+{ echo -e "Approuter logs:\n\n"; cf logs "$approutername" --recent; echo -e "\n\nApp logs:\n\n"; cf logs "$appname" --recent; } > /tmp/cflogsoutput
+
+zip -q "$logszip" /tmp/cflogsoutput
+
+rm /tmp/cflogsoutput
 
 #Unsetting env variables and restarting apps
 echo -e "\nRestoring log levels...\n"
@@ -86,4 +90,4 @@ cf restart "$approutername"
 cf restart "$appname"
 
 #End
-echo -e "\n\e[32m\e[1mAll done.\e[0m Your file is here:" && readlink -f "$logszip"
+echo -e "\n\033[32m\033[1mAll done.\033[0m Your file is here: " && echo "$logszip"


Test patch:
diff --git a/spring-xsuaa-it/src/main/java/testservice/api/nohttp/MyEventHandler.java b/spring-xsuaa-it/src/main/java/testservice/api/nohttp/MyEventHandler.java
index 1f6c0d5b..35b15fa9 100644
--- a/spring-xsuaa-it/src/main/java/testservice/api/nohttp/MyEventHandler.java
+++ b/spring-xsuaa-it/src/main/java/testservice/api/nohttp/MyEventHandler.java
@@ -14,6 +14,8 @@ import org.springframework.security.oauth2.jwt.Jwt;
 import org.springframework.security.oauth2.jwt.JwtDecoder;
 import org.springframework.stereotype.Service;
 
+import com.sap.cloud.security.xsuaa.extractor.LocalAuthoritiesExtractor;
+import com.sap.cloud.security.xsuaa.token.SpringSecurityContext;
 import com.sap.xs2.security.container.SecurityContext;
 
 @Service
@@ -22,25 +24,36 @@ public class MyEventHandler {
 	private static final Logger LOGGER = LoggerFactory.getLogger(MyEventHandler.class);
 
 	@Value("${xsuaa.xsappname}")
-	String xsappname;
+	String appId;
 
 	@Autowired
 	JwtDecoder jwtDecoder;
 
-	public void onEvent(String myEncodedJwtToken) {
+	public void onEvent_deprecated(String myEncodedJwtToken) {
 		if (myEncodedJwtToken != null) {
 			Jwt jwtToken = jwtDecoder.decode(myEncodedJwtToken);
-			SecurityContext.init(xsappname, jwtToken, true);
+			SecurityContext.init(appId, jwtToken, true);
+		}
+		try {
+			handleEvent();
+		} finally {
+			SpringSecurityContext.clear();
+		}
+	}
+
+	public void onEvent(String myEncodedJwtToken) {
+		if (myEncodedJwtToken != null) {
+			SpringSecurityContext.init(myEncodedJwtToken, jwtDecoder, new LocalAuthoritiesExtractor(appId));
 		}
 		try {
 			handleEvent();
 		} finally {
-			SecurityContext.clear();
+			SpringSecurityContext.clear();
 		}
 	}
 
 	void handleEvent() {
-		Collection<GrantedAuthority> authorities = (Collection<GrantedAuthority>) SecurityContext.getToken()
+		Collection<GrantedAuthority> authorities = (Collection<GrantedAuthority>) SpringSecurityContext.getToken()
 				.getAuthorities();
 		if (!authorities.contains(new SimpleGrantedAuthority("Display"))) {
 			throw new AccessDeniedException("Missing Authorization.");
diff --git a/spring-xsuaa-it/src/test/java/testservice/api/InitializeSecurityContextTest.java b/spring-xsuaa-it/src/test/java/testservice/api/InitializeSpringSecurityContextTest.java
similarity index 81%
rename from spring-xsuaa-it/src/test/java/testservice/api/InitializeSecurityContextTest.java
rename to spring-xsuaa-it/src/test/java/testservice/api/InitializeSpringSecurityContextTest.java
index e4b34537..d5ff187e 100644
--- a/spring-xsuaa-it/src/test/java/testservice/api/InitializeSecurityContextTest.java
+++ b/spring-xsuaa-it/src/test/java/testservice/api/InitializeSpringSecurityContextTest.java
@@ -32,10 +32,11 @@ import org.springframework.test.context.junit4.SpringRunner;
 
 import com.sap.cloud.security.xsuaa.autoconfiguration.XsuaaAutoConfiguration;
 import com.sap.cloud.security.xsuaa.autoconfiguration.XsuaaResourceServerJwkAutoConfiguration;
+import com.sap.cloud.security.xsuaa.extractor.LocalAuthoritiesExtractor;
 import com.sap.cloud.security.xsuaa.mock.XsuaaRequestDispatcher;
 import com.sap.cloud.security.xsuaa.test.JwtGenerator;
+import com.sap.cloud.security.xsuaa.token.SpringSecurityContext;
 import com.sap.cloud.security.xsuaa.token.Token;
-import com.sap.xs2.security.container.SecurityContext;
 
 import testservice.api.nohttp.MyEventHandler;
 import testservice.api.nohttp.SecurityConfiguration;
@@ -45,12 +46,12 @@ import testservice.api.nohttp.SecurityConfiguration;
 		XsuaaAutoConfiguration.class,
 		XsuaaResourceServerJwkAutoConfiguration.class })
 @ActiveProfiles({ "test.api.nohttp", "uaamock" })
-public class InitializeSecurityContextTest {
+public class InitializeSpringSecurityContextTest {
 	@Value("${xsuaa.clientid}")
 	String clientId;
 
 	@Value("${xsuaa.xsappname}")
-	String xsappname;
+	String appId;
 
 	@Autowired
 	JwtDecoder jwtDecoder;
@@ -61,12 +62,12 @@ public class InitializeSecurityContextTest {
 	@Test
 	public void initializeSecurityContext_succeeds() {
 		String jwt = new JwtGenerator(clientId, "subdomain")
-				.addScopes("openid", xsappname + ".Display", "otherXSAPP.Display")
+				.addScopes("openid", appId + ".Display", "otherXSAPP.Display")
 				.deriveAudiences(true).getToken().getTokenValue();
 
 		assertThat(SecurityContextHolder.getContext().getAuthentication(), is(nullValue()));
 
-		SecurityContext.init(xsappname, jwtDecoder.decode(jwt), true);
+		SpringSecurityContext.init(jwt, jwtDecoder, new LocalAuthoritiesExtractor(appId));
 
 		// test authentication - isAuthenticated()
 		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
@@ -89,8 +90,8 @@ public class InitializeSecurityContextTest {
 	public void clearSecurityContext_succeeds() {
 		String jwt = new JwtGenerator(clientId, "subdomain").deriveAudiences(true).getToken().getTokenValue();
 
-		SecurityContext.init(xsappname, jwtDecoder.decode(jwt), true);
-		SecurityContext.clear();
+		SpringSecurityContext.init(jwt, jwtDecoder, new LocalAuthoritiesExtractor(appId));
+		SpringSecurityContext.clear();
 
 		assertThat(SecurityContextHolder.getContext().getAuthentication(), is(nullValue()));
 	}
@@ -131,7 +132,16 @@ public class InitializeSecurityContextTest {
 	@Test
 	public void callEventWithSufficientAuthorization_succeeds() {
 		String jwt = new JwtGenerator(clientId, "subdomain")
-				.addScopes("openid", xsappname + ".Display")
+				.addScopes("openid", appId + ".Display")
+				.deriveAudiences(true).getToken().getTokenValue();
+
+		eventHandler.onEvent(jwt);
+	}
+
+	@Test
+	public void callEventWithSufficientAuthorization_succeeds_2() {
+		String jwt = new JwtGenerator(clientId, "subdomain")
+				.addScopes("openid", appId + ".Display")
 				.deriveAudiences(true).getToken().getTokenValue();
 
 		eventHandler.onEvent(jwt);
@@ -145,9 +155,22 @@ public class InitializeSecurityContextTest {
 		eventHandler.onEvent(jwt);
 	}
 
+	@Test(expected = AccessDeniedException.class)
+	public void callEventWithInsufficientAuthorization_raisesAccessDeniedException_2() {
+		String jwt = new JwtGenerator(clientId, "subdomain")
+				.deriveAudiences(true).getToken().getTokenValue();
+
+		eventHandler.onEvent(jwt);
+	}
+
 	@Test(expected = AccessDeniedException.class)
 	public void callEventWithNoJwtToken_raisesAccessDeniedException() {
 		eventHandler.onEvent(null);
 	}
 
+	@Test(expected = AccessDeniedException.class)
+	public void callEventWithNoJwtToken_raisesAccessDeniedException_2() {
+		eventHandler.onEvent(null);
+	}
+
 }
diff --git a/spring-xsuaa-it/src/test/java/testservice/api/XsuaaJwtDecoderTest.java b/spring-xsuaa-it/src/test/java/testservice/api/XsuaaJwtDecoderTest.java
new file mode 100644
index 00000000..cf6255af
--- /dev/null
+++ b/spring-xsuaa-it/src/test/java/testservice/api/XsuaaJwtDecoderTest.java
@@ -0,0 +1,73 @@
+package testservice.api;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.security.oauth2.jwt.JwtDecoder;
+import org.springframework.security.oauth2.jwt.JwtException;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration;
+import com.sap.cloud.security.xsuaa.autoconfiguration.XsuaaAutoConfiguration;
+import com.sap.cloud.security.xsuaa.autoconfiguration.XsuaaResourceServerJwkAutoConfiguration;
+import com.sap.cloud.security.xsuaa.test.JwtGenerator;
+import com.sap.cloud.security.xsuaa.token.authentication.XsuaaJwtDecoderBuilder;
+
+import testservice.api.nohttp.MyEventHandler;
+import testservice.api.nohttp.SecurityConfiguration;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = { SecurityConfiguration.class, MyEventHandler.class,
+		XsuaaAutoConfiguration.class,
+		XsuaaResourceServerJwkAutoConfiguration.class })
+@ActiveProfiles({ "test.api.nohttp", "uaamock" })
+public class XsuaaJwtDecoderTest {
+
+	boolean postActionExecuted;
+	JwtDecoder jwtDecoderWithPostAction;
+
+	@Value("${xsuaa.clientid}")
+	String clientId;
+
+	@Value("${xsuaa.xsappname}")
+	String xsappname;
+
+	@Autowired
+	XsuaaServiceConfiguration serviceConfiguration;
+
+	@Autowired
+	MyEventHandler eventHandler;
+
+	@Before
+	public void setUp() {
+		postActionExecuted = false;
+		jwtDecoderWithPostAction = new XsuaaJwtDecoderBuilder(serviceConfiguration)
+				.withPostValidationActions(token -> postActionExecuted = true).build();
+	}
+
+	@Test
+	public void postValidationActionIsExecutedIfSuccess() {
+		String jwt = new JwtGenerator(clientId, "subdomain").deriveAudiences(true)
+				.setJwtHeaderKeyId("legacy-token-key").getToken().getTokenValue();
+
+		jwtDecoderWithPostAction.decode(jwt);
+		Assert.assertTrue(postActionExecuted);
+	}
+
+	@Test
+	public void postValidationActionIsNotExecutedIfFail() {
+		String jwt = new JwtGenerator(clientId, "subdomain").deriveAudiences(true)
+				.setJwtHeaderKeyId("legacy-token-key").setJku(null).getToken().getTokenValue();
+		try {
+			jwtDecoderWithPostAction.decode(jwt);
+			Assert.fail();
+		} catch (JwtException e) {
+			Assert.assertFalse(postActionExecuted);
+		}
+	}
+}
diff --git a/spring-xsuaa-test/README.md b/spring-xsuaa-test/README.md
index 835817e8..0c075021 100644
--- a/spring-xsuaa-test/README.md
+++ b/spring-xsuaa-test/README.md
@@ -27,8 +27,11 @@ This includes for example a `JwtGenerator` that generates JSON Web Tokens (JWT)
 <dependency>
     <groupId>com.sap.cloud.security.xsuaa</groupId>
     <artifactId>spring-xsuaa-test</artifactId>
-    <version>1.5.0</version>
+    <version>1.6.0</version>
+    <scope>test</scope>
 </dependency>
+
+<!-- only if not already included -->
 <dependency>
     <groupId>commons-io</groupId>
     <artifactId>commons-io</artifactId>
diff --git a/spring-xsuaa-test/pom.xml b/spring-xsuaa-test/pom.xml
index a37ea876..1359148c 100644
--- a/spring-xsuaa-test/pom.xml
+++ b/spring-xsuaa-test/pom.xml
@@ -7,7 +7,7 @@
 	<parent>
 		<groupId>com.sap.cloud.security.xsuaa</groupId>
 		<artifactId>parent</artifactId>
-		<version>1.5.0</version>
+		<version>1.6.0</version>
 	</parent>
 
 	<artifactId>spring-xsuaa-test</artifactId>
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 37cce87d..c770d600 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
@@ -6,6 +6,7 @@ import static org.hamcrest.Matchers.not;
 import static org.hamcrest.Matchers.nullValue;
 import static org.junit.Assert.assertThat;
 
+import com.sap.cloud.security.xsuaa.DummyXsuaaServiceConfiguration;
 import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration;
 import com.sap.cloud.security.xsuaa.XsuaaServiceConfigurationDefault;
 import org.junit.Test;
@@ -22,7 +23,7 @@ import org.springframework.security.oauth2.jwt.Jwt;
 import org.springframework.test.context.junit4.SpringRunner;
 
 @RunWith(SpringRunner.class)
-@SpringBootTest(classes = XsuaaAutoConfiguration.class)
+@SpringBootTest(classes = { XsuaaAutoConfiguration.class, DummyXsuaaServiceConfiguration.class })
 public class XsuaaAutoConfigurationTest {
 
 	// create an ApplicationContextRunner that will create a context with the
@@ -84,7 +85,7 @@ public class XsuaaAutoConfigurationTest {
 					assertThat(context.containsBean("xsuaaServiceConfiguration"), is(false));
 					assertThat(context.containsBean("customServiceConfiguration"), is(true));
 					assertThat(context.getBean("customServiceConfiguration"),
-							instanceOf(CustomXsuaaConfiguration.class));
+							instanceOf(DummyXsuaaServiceConfiguration.class));
 				});
 	}
 
@@ -93,40 +94,7 @@ public class XsuaaAutoConfigurationTest {
 
 		@Bean
 		public XsuaaServiceConfiguration customServiceConfiguration() {
-			return new CustomXsuaaConfiguration();
-		}
-	}
-
-	static class CustomXsuaaConfiguration implements XsuaaServiceConfiguration {
-
-		@Override
-		public String getClientId() {
-			return null;
-		}
-
-		@Override
-		public String getClientSecret() {
-			return null;
-		}
-
-		@Override
-		public String getUaaUrl() {
-			return null;
-		}
-
-		@Override
-		public String getTokenKeyUrl(String zid, String subdomain) {
-			return null;
-		}
-
-		@Override
-		public String getAppId() {
-			return null;
-		}
-
-		@Override
-		public String getUaaDomain() {
-			return null;
+			return new DummyXsuaaServiceConfiguration();
 		}
 	}
 }
diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/OAuth2TokenAuthenticationConverterTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/OAuth2TokenAuthenticationConverterTest.java
new file mode 100644
index 00000000..310c8140
--- /dev/null
+++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/OAuth2TokenAuthenticationConverterTest.java
@@ -0,0 +1,43 @@
+package com.sap.cloud.security.xsuaa.token;
+
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+
+import com.sap.cloud.security.xsuaa.extractor.DefaultAuthoritiesExtractor;
+import com.sap.cloud.security.xsuaa.test.JwtGenerator;
+
+public class OAuth2TokenAuthenticationConverterTest {
+	private String xsAppName = "my-app-name!400";
+	private OAuth2AuthenticationConverter tokenConverterOauth2;
+	String scopeAdmin = xsAppName + "." + "Admin";
+	String scopeRead = xsAppName + "." + "Read";
+	String scopeOther = "other-app!234" + "." + "Other";
+
+	@Before
+	public void setup() {
+		tokenConverterOauth2 = new OAuth2AuthenticationConverter(new DefaultAuthoritiesExtractor());
+	}
+
+	@Test
+	public void extractAuthoritiesWithScopesOAuth2Authentication() {
+		Jwt jwt = new JwtGenerator().addScopes(scopeAdmin, scopeRead, scopeOther).getToken();
+
+		AbstractAuthenticationToken authenticationToken = tokenConverterOauth2.convert(jwt);
+		assertThat(authenticationToken.getAuthorities().size(), is(3));
+		assertThat(authenticationToken.getAuthorities(), hasItem(new SimpleGrantedAuthority(scopeRead)));
+		assertThat(authenticationToken.getAuthorities(), hasItem(new SimpleGrantedAuthority(scopeAdmin)));
+		assertThat(authenticationToken.getAuthorities(), hasItem(new SimpleGrantedAuthority(scopeOther)));
+
+		assertTrue(authenticationToken instanceof OAuth2Authentication);
+		assertThat(((OAuth2Authentication) authenticationToken).getOAuth2Request().getScope(), hasItem(scopeRead));
+	}
+}
diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaAudienceValidatorForCloneTokenTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaAudienceValidatorForCloneTokenTest.java
index d29e4aa7..8b1654ec 100644
--- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaAudienceValidatorForCloneTokenTest.java
+++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaAudienceValidatorForCloneTokenTest.java
@@ -7,9 +7,9 @@ import java.util.Date;
 import java.util.List;
 
 import com.nimbusds.jwt.JWTClaimsSet;
+import com.sap.cloud.security.xsuaa.DummyXsuaaServiceConfiguration;
 import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration;
 import com.sap.cloud.security.xsuaa.test.JwtGenerator;
-import com.sap.cloud.security.xsuaa.token.Token;
 import com.sap.cloud.security.xsuaa.token.TokenClaims;
 import org.junit.Assert;
 import org.junit.Before;
@@ -27,8 +27,7 @@ public class XsuaaAudienceValidatorForCloneTokenTest {
 
 	@Before
 	public void setup() {
-		XsuaaServiceConfiguration serviceConfiguration = new XsuaaAudienceValidatorTest.DummyXsuaaServiceConfiguration(
-				"sb-test1!t1", "test1!t1");
+		XsuaaServiceConfiguration serviceConfiguration = new DummyXsuaaServiceConfiguration("sb-test1!t1", "test1!t1");
 		cut = new XsuaaAudienceValidator(serviceConfiguration);
 		cut.configureAnotherXsuaaInstance(XSUAA_BROKER_XSAPPNAME, XSUAA_BROKER_CLIENT_ID);
 
diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaAudienceValidatorTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaAudienceValidatorTest.java
index fa02ce5f..e7b918a3 100644
--- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaAudienceValidatorTest.java
+++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaAudienceValidatorTest.java
@@ -9,9 +9,9 @@ import java.util.Date;
 import java.util.List;
 
 import com.nimbusds.jwt.JWTClaimsSet;
+import com.sap.cloud.security.xsuaa.DummyXsuaaServiceConfiguration;
 import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration;
 import com.sap.cloud.security.xsuaa.test.JwtGenerator;
-import com.sap.cloud.security.xsuaa.token.Token;
 import com.sap.cloud.security.xsuaa.token.TokenClaims;
 import org.junit.Assert;
 import org.junit.Before;
@@ -168,45 +168,4 @@ public class XsuaaAudienceValidatorTest {
 		Assert.assertFalse(result.hasErrors());
 	}
 
-	public static class DummyXsuaaServiceConfiguration implements XsuaaServiceConfiguration {
-
-		String clientId;
-		String xsAppId;
-
-		public DummyXsuaaServiceConfiguration(String clientId, String xsAppId) {
-			this.clientId = clientId;
-			this.xsAppId = xsAppId;
-		}
-
-		@Override
-		public String getClientId() {
-			return clientId;
-		}
-
-		@Override
-		public String getClientSecret() {
-			return null;
-		}
-
-		@Override
-		public String getUaaUrl() {
-			return null;
-		}
-
-		@Override
-		public String getTokenKeyUrl(String zid, String subdomain) {
-			return null;
-		}
-
-		@Override
-		public String getAppId() {
-			return xsAppId;
-		}
-
-		@Override
-		public String getUaaDomain() {
-			return null;
-		}
-
-	}
 }
diff --git a/spring-xsuaa/src/test/java/com/sap/xs2/security/container/SpringSecurityContextTest.java b/spring-xsuaa/src/test/java/com/sap/xs2/security/container/SpringSecurityContextTest.java
new file mode 100644
index 00000000..a6557f6f
--- /dev/null
+++ b/spring-xsuaa/src/test/java/com/sap/xs2/security/container/SpringSecurityContextTest.java
@@ -0,0 +1,106 @@
+package com.sap.xs2.security.container;
+
+import com.sap.cloud.security.xsuaa.extractor.DefaultAuthoritiesExtractor;
+import com.sap.cloud.security.xsuaa.test.JwtGenerator;
+import com.sap.cloud.security.xsuaa.token.SpringSecurityContext;
+import com.sap.cloud.security.xsuaa.token.Token;
+import com.sap.cloud.security.xsuaa.token.authentication.XsuaaJwtDecoder;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.jwt.JwtDecoder;
+import org.springframework.security.oauth2.jwt.JwtException;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+public class SpringSecurityContextTest {
+
+	private static final int NUM_OF_THREADS = 2;
+	private static int countThreads;
+
+	private Jwt token_1;
+	private Jwt token_2;
+
+	private static final String SUBDOMAIN_1 = "subdomain-1";
+	private static final String SUBDOMAIN_2 = "subdomain-2";
+	private static final String CLIENT_ID = "sb-xsappname!t123";
+
+	ExecutorService executor = Executors.newFixedThreadPool(10);
+
+	@Before
+	public void setup() {
+		token_1 = new JwtGenerator(CLIENT_ID, SUBDOMAIN_1).getToken();
+		token_2 = new JwtGenerator(CLIENT_ID, SUBDOMAIN_2).getToken();
+	}
+
+	@Test(expected = AccessDeniedException.class) // Access forbidden: not authenticated
+	public void getSecurityContextRaiseAccessDeniedExceptionIfNotInitialized() {
+		SecurityContext.getToken();
+	}
+
+	@Test(expected = IllegalArgumentException.class) // Passed JwtDecoder instance must be of type 'XsuaaJwtDecoder'
+	public void initSecurityContextRaiseExceptionIfNotXsuaaJwtDecoder() {
+		String message = "";
+		SpringSecurityContext.init(token_1.getTokenValue(), new JwtDecoder() {
+			@Override
+			public Jwt decode(String s) throws JwtException {
+				return token_1;
+			}
+		}, new DefaultAuthoritiesExtractor());
+	}
+
+	/**
+	 * This test case spawns two parallel Threads. Each Thread uses a token and
+	 * invokes SpringSecurityContext.init(...). A Token object is retrieved by the
+	 * SpringSecurityContext and returned to the main Thread. On the main Thread, we
+	 * assert that both Threads have been in the appropriate Tenant Context.
+	 *
+	 * @throws InterruptedException
+	 * @throws ExecutionException
+	 */
+	@Test
+	public void setSecurityContext() throws InterruptedException, ExecutionException {
+		SpringSecurityContextTest.countThreads = 0;
+
+		Future<Token> future_1 = executor.submit(() -> {
+			initSecurityContextWithToken(token_1);
+			return SecurityContext.getToken();
+		});
+		Future<Token> future_2 = executor.submit(() -> {
+			initSecurityContextWithToken(token_2);
+			return SecurityContext.getToken();
+		});
+
+		while (!future_1.isDone() || !future_2.isDone()) {
+			Thread.sleep(100);
+		}
+		assertEquals(SUBDOMAIN_1, future_1.get().getSubdomain());
+		assertEquals(SUBDOMAIN_1 + "-id", future_1.get().getSubaccountId());
+
+		assertEquals(SUBDOMAIN_2, future_2.get().getSubdomain());
+		assertEquals(SUBDOMAIN_2 + "-id", future_2.get().getSubaccountId());
+	}
+
+	private static void initSecurityContextWithToken(Jwt token) throws InterruptedException {
+		XsuaaJwtDecoder mockXsuaaJwtDecoder = Mockito.mock(XsuaaJwtDecoder.class);
+		when(mockXsuaaJwtDecoder.decode(token.getTokenValue())).thenReturn(token);
+
+		// initialize SpringSecurityContext with provided token
+		SpringSecurityContext.init(token.getTokenValue(), mockXsuaaJwtDecoder, new DefaultAuthoritiesExtractor());
+
+		// wait on other threads
+		SpringSecurityContextTest.countThreads++;
+		while (SpringSecurityContextTest.countThreads < NUM_OF_THREADS) {
+			Thread.sleep(100);
+		}
+		// now all threads have invoked the init(...) method
+	}
+}
\ No newline at end of file