Repository: SAP/cloud-security-xsuaa-integration
Pull request #284 (2020-05-12 16:59:43)

TG.02: Basic support of Zones (token claim "zone_uuid")

Use getSubaccountId() only to fetch the subaccount id, e.g. for calling the metering API for user-based pricing.
In case you are interested in the customers tenant GUID make use of getZoneId() instead!
In upcoming releases - especially for new subaccounts - subaccount id will no longer match the tenant GUID which is provided via the xsuaa access token as zid claim or via the ias oidc token as zone_uuid claim.


**Enhance java-security:**

- new method Tenant.getZoneId() should return "zone_uuid" if available or as fallback "zid" for xsuaa tokens

**Enhance spring-xsuaa** - no incompatible change fur application developers

- deprecate XSUserInfo.getIdentityZone() currently returns "zid" => still ok
- add XSUserInfo.getZoneId() which should return "zid" for xsuaa tokens
- update **Token.getSubaccountId()** currently returns "zid" => should return "subaccountid" if available or as fallback "zid". 
- documents that **Token.getZoneId()** should be used to get tenant discriminator (tenant guid). For most of the old subaccounts this matches the subaccount id.


Patch:
diff --git a/api/src/main/java/com/sap/xsa/security/container/XSUserInfo.java b/api/src/main/java/com/sap/xsa/security/container/XSUserInfo.java
index 56267adb..ac0b77d0 100644
--- a/api/src/main/java/com/sap/xsa/security/container/XSUserInfo.java
+++ b/api/src/main/java/com/sap/xsa/security/container/XSUserInfo.java
@@ -78,24 +78,22 @@ public interface XSUserInfo {
 	String getOrigin() throws XSUserInfoException;
 
 	/**
-	 * Return identity zone which is the same like the subaccount id (tenant id).
+	 * Return identity zone which is in most cases same like the subaccount identifier.
 	 * 
-	 * @deprecated Can be replaced with
-	 *             {@code token.getClaimAsString(TokenClaims.XSUAA.ZONE_ID)} from
-	 *             the {@code com.sap.cloud.security.token} package.
+	 * @deprecated Have to be replaced with {@link #getZoneId()} or {@link #getSubaccountId()}.
 	 * @return identity zone
 	 * @throws XSUserInfoException
 	 *             if attribute is not available in the authentication token
 	 */
+	@Deprecated
 	String getIdentityZone() throws XSUserInfoException;
 
 	/**
-	 * Return subaccount identifier which is the same like the identity zone (tenant
-	 * id).
-	 * 
-	 * @deprecated Can be replaced with
-	 *             {@code token.getClaimAsString(TokenClaims.XSUAA.ZONE_ID)} from
-	 *             the {@code com.sap.cloud.security.token} package.
+	 * Return subaccount identifier which is in most cases same like the
+	 * identity zone.
+	 * DO only use this for metering purposes.
+	 * DO NOT longer use this method to get the unique tenant id! For that use {@link #getZoneId()}.
+	 *
 	 * @return subaccount identifier
 	 * @throws XSUserInfoException
 	 *             if attribute is not available in the authentication token
@@ -103,11 +101,23 @@ public interface XSUserInfo {
 	String getSubaccountId() throws XSUserInfoException;
 
 	/**
+	 * Return zone identifier which should be used as tenant discriminator (tenant id).
+	 * For most of the old subaccounts this matches the id returned by {@link #getSubaccountId()}.
 	 *
+	 * @deprecated Can be replaced with
+	 *             {@code token.getZoneId()} from
+	 *             the {@code com.sap.cloud.security.token} package.
+	 * @return zone identifier
+	 * @throws XSUserInfoException
+	 *             if attribute is not available in the authentication token
+	 */
+	String getZoneId() throws XSUserInfoException;
+
+	/**
 	 * Supported via {@code XSUserInfoAdapter} from the
 	 * {@code com.sap.cloud.security.adapter.xs} package. Also available on tokens
 	 * of type {@code XsuaaToken} from java-security.
-	 * 
+	 *
 	 * @return the subdomain
 	 * @throws XSUserInfoException
 	 *             if subdomain is not available in the authentication token
diff --git a/java-api/src/main/java/com/sap/cloud/security/token/Token.java b/java-api/src/main/java/com/sap/cloud/security/token/Token.java
index e0e614d3..87f8a2f1 100644
--- a/java-api/src/main/java/com/sap/cloud/security/token/Token.java
+++ b/java-api/src/main/java/com/sap/cloud/security/token/Token.java
@@ -140,4 +140,13 @@ public interface Token {
 	 * @return the audiences.
 	 **/
 	Set<String> getAudiences();
+
+
+	/**
+	 * Returns the Zone identifier, which can be used as tenant discriminator (tenant guid).
+	 *
+	 * @return the unique Zone identifier.
+	 */
+	String getZoneId();
+
 }
diff --git a/java-api/src/main/java/com/sap/cloud/security/token/TokenClaims.java b/java-api/src/main/java/com/sap/cloud/security/token/TokenClaims.java
index 872ef606..cfbbbc99 100644
--- a/java-api/src/main/java/com/sap/cloud/security/token/TokenClaims.java
+++ b/java-api/src/main/java/com/sap/cloud/security/token/TokenClaims.java
@@ -19,7 +19,7 @@ public final class TokenClaims {
 	public static final String FAMILY_NAME = "family_name";
 	public static final String EMAIL = "email";
 	public static final String SAP_GLOBAL_USER_ID = "user_uuid";
-	// public static final String SAP_ZONE_ID = "sap_zid"; // public static final
+	public static final String SAP_GLOBAL_ZONE_ID = "zone_uuid"; // tenant GUID
 
 	public final class XSUAA {
 
@@ -28,7 +28,7 @@ public final class TokenClaims {
 
 		public static final String ORIGIN = "origin";
 		public static final String GRANT_TYPE = "grant_type"; // OAuth grant type used for token creation
-		public static final String ZONE_ID = "zid"; // tenant GUID, identity zone id, subaccount id
+		public static final String ZONE_ID = "zid"; // tenant GUID -> SAP_GLOBAL_ZONE_ID
 		public static final String CLIENT_ID = "cid"; // OAuth client identifier
 		public static final String SCOPES = "scope"; // list of scopes including app id, e.g. "my-app!t123.Display"
 		public static final String ISSUED_AT = "iat";
diff --git a/java-security/src/main/java/com/sap/cloud/security/adapter/xs/XSUserInfoAdapter.java b/java-security/src/main/java/com/sap/cloud/security/adapter/xs/XSUserInfoAdapter.java
index 38a53efe..698a7c85 100644
--- a/java-security/src/main/java/com/sap/cloud/security/adapter/xs/XSUserInfoAdapter.java
+++ b/java-security/src/main/java/com/sap/cloud/security/adapter/xs/XSUserInfoAdapter.java
@@ -7,7 +7,6 @@ import com.sap.cloud.security.json.JsonObject;
 import com.sap.cloud.security.json.JsonParsingException;
 import com.sap.cloud.security.token.AccessToken;
 import com.sap.cloud.security.token.GrantType;
-import com.sap.cloud.security.token.TokenClaims;
 import com.sap.cloud.security.xsuaa.Assertions;
 import com.sap.cloud.security.xsuaa.client.*;
 import com.sap.cloud.security.xsuaa.tokenflows.TokenFlowException;
@@ -42,7 +41,9 @@ public class XSUserInfoAdapter implements XSUserInfo {
 	static final String EXTERNAL_CONTEXT = "ext_ctx";
 	static final String CLAIM_ADDITIONAL_AZ_ATTR = "az_attr";
 	static final String XS_USER_ATTRIBUTES = "xs.user.attributes";
+	// new with SECAUTH-806
 	static final String XS_SYSTEM_ATTRIBUTES = "xs.system.attributes";
+	static final String CLAIM_SUBACCOUNT_ID = "subaccountid";
 	static final String HDB_NAMEDUSER_SAML = "hdb.nameduser.saml";
 	static final String SERVICEINSTANCEID = "serviceinstanceid";
 	static final String ZDN = "zdn";
@@ -122,12 +123,18 @@ public class XSUserInfoAdapter implements XSUserInfo {
 
 	@Override
 	public String getIdentityZone() {
-		return getClaimValue(TokenClaims.XSUAA.ZONE_ID);
+		return getClaimValue(ZONE_ID);
 	}
 
 	@Override
 	public String getSubaccountId() {
-		return getIdentityZone();
+		String subaccountId = getAttributeFromClaimAsString(XS_SYSTEM_ATTRIBUTES, CLAIM_SUBACCOUNT_ID);
+		return (subaccountId == null || "".equals(subaccountId)) ? getClaimValue(ZONE_ID) : subaccountId;
+	}
+
+	@Override
+	public String getZoneId() {
+		return accessToken.hasClaim(SAP_GLOBAL_ZONE_ID) ? accessToken.getClaimAsString(SAP_GLOBAL_ZONE_ID) : getClaimValue(ZONE_ID);
 	}
 
 	@Override
diff --git a/java-security/src/main/java/com/sap/cloud/security/token/AbstractToken.java b/java-security/src/main/java/com/sap/cloud/security/token/AbstractToken.java
index c1096a22..f80dc479 100644
--- a/java-security/src/main/java/com/sap/cloud/security/token/AbstractToken.java
+++ b/java-security/src/main/java/com/sap/cloud/security/token/AbstractToken.java
@@ -163,4 +163,9 @@ public abstract class AbstractToken implements Token {
 	public int hashCode() {
 		return Objects.hash(getTokenValue());
 	}
+
+	@Override
+	public String getZoneId() {
+		return getClaimAsString(TokenClaims.SAP_GLOBAL_ZONE_ID);
+	}
 }
diff --git a/java-security/src/main/java/com/sap/cloud/security/token/XsuaaToken.java b/java-security/src/main/java/com/sap/cloud/security/token/XsuaaToken.java
index b89c7386..548cdfb5 100644
--- a/java-security/src/main/java/com/sap/cloud/security/token/XsuaaToken.java
+++ b/java-security/src/main/java/com/sap/cloud/security/token/XsuaaToken.java
@@ -10,6 +10,7 @@ import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import java.security.Principal;
 import java.util.LinkedHashSet;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 
@@ -165,4 +166,8 @@ public class XsuaaToken extends AbstractToken implements AccessToken {
 				.orElse(null);
 	}
 
+	@Override
+	public String getZoneId() {
+		return Objects.nonNull(super.getZoneId()) ? super.getZoneId() : getClaimAsString(ZONE_ID);
+	}
 }
diff --git a/spring-xsuaa/Migration_JavaContainerSecurityProjects.md b/spring-xsuaa/Migration_JavaContainerSecurityProjects.md
index e6af583b..7f695cca 100644
--- a/spring-xsuaa/Migration_JavaContainerSecurityProjects.md
+++ b/spring-xsuaa/Migration_JavaContainerSecurityProjects.md
@@ -183,7 +183,8 @@ See the following table for methods that are not available anymore and workaroun
 | `getAttribute`          | Use `getXSUserAttribute`.                                                                        |
 | `getDBToken`            | Not implemented.                                                                                 |
 | `getHdbToken`           | Not implemented.                                                                                 |
-| `getIdentityZone`       | Use `getSubaccountId`.                                                                 |
+| `getIdentityZone`       | Use `getZoneId` to get the tenant GUID or use `getSubaccountId` to get subaccount id, e.g. to provide it to the metering API.  
+                                                             |
 | `getJsonValue`          | Use `containsClaim` and `getClaimAsString`. See section [XsuaaToken](#xsuaatoken).                                                                                |
 | `getSystemAttribute`    | This extracts data from `xs.system.attributes` claim. See section [XsuaaToken](#xsuaatoken).    |
 | `getToken`              | Not implemented.                                                                                 |
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java
index 8c403737..4f440ae7 100644
--- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/Token.java
@@ -20,12 +20,23 @@ public interface Token extends UserDetails {
 	static final String GRANTTYPE_CLIENTCREDENTIAL = "client_credentials";
 
 	/**
-	 * Returns the subaccount identifier, which can be used as tenant GUID.
+	 * Return subaccount identifier which is in most cases same like the
+	 * identity zone.
+	 * DO only use this for metering purposes.
+	 * DO NOT longer use this method to get the unique tenant id! For that use {@link #getZoneId()}.
 	 *
 	 * @return the subaccount identifier.
 	 */
 	String getSubaccountId();
 
+	/**
+	 * Return zone identifier which should be used as tenant discriminator (tenant id).
+	 * For most of the old subaccounts this matches the id returned by {@link #getSubaccountId()}.
+	 *
+	 * @return the zone identifier.
+	 */
+	String getZoneId();
+
 	/**
 	 * Returns the subdomain of the calling tenant's subaccount.
 	 *
@@ -57,8 +68,7 @@ public interface Token extends UserDetails {
 	 * <li><b>user login name:</b> name of the user in an identity provider,
 	 * provided by this method.
 	 * <li><b>origin:</b> alias to an identity provider, see {@link #getOrigin()}.
-	 * <li><b>subaccount id:</b> identifier for the subaccount, see
-	 * {@link #getSubaccountId()}.
+	 * <li><b>zone id:</b> identifier for the zone, see {@link #getZoneId()}.
 	 * </ul>
 	 *
 	 * @return unique principal name or null if it can not be determined.
diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java
index d09882f5..db984f77 100644
--- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java
+++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/XsuaaToken.java
@@ -13,6 +13,7 @@ import java.time.Instant;
 import java.util.*;
 
 import static com.sap.cloud.security.xsuaa.token.TokenClaims.*;
+import static org.springframework.util.StringUtils.isEmpty;
 
 import com.sap.cloud.security.xsuaa.client.OAuth2TokenServiceConstants;
 
@@ -30,6 +31,10 @@ public class XsuaaToken extends Jwt implements Token {
 	static final String CLAIM_ADDITIONAL_AZ_ATTR = "az_attr";
 	static final String CLAIM_EXTERNAL_ATTR = "ext_attr";
 	static final String CLAIM_EXTERNAL_CONTEXT = "ext_ctx";
+	// new with SECAUTH-806
+	static final String CLAIM_XS_SYSTEM_ATTRIBUTES = "xs.system.attributes";
+	static final String CLAIM_SUBACCOUNT_ID = "subaccountid";
+	//
 	private static final long serialVersionUID = -836947635254353927L;
 	private static final Logger logger = LoggerFactory.getLogger(XsuaaToken.class);
 	private Collection<GrantedAuthority> authorities = Collections.emptyList();
@@ -117,7 +122,7 @@ public class XsuaaToken extends Jwt implements Token {
 
 		if (origin.contains("/")) {
 			logger.warn(
-					"Illegal '/' character detected in origin claim of JWT. Cannot create unique user name. Returing null.");
+					"Illegal '/' character detected in origin claim of JWT. Cannot create unique user name. Returning null.");
 			return null;
 		}
 
@@ -170,6 +175,12 @@ public class XsuaaToken extends Jwt implements Token {
 
 	@Override
 	public String getSubaccountId() {
+		String subaccountId = getStringAttributeFromClaim(CLAIM_SUBACCOUNT_ID, CLAIM_XS_SYSTEM_ATTRIBUTES);
+		return isEmpty(subaccountId) ? getClaimAsString(CLAIM_ZONE_ID) : subaccountId;
+	}
+
+	@Override
+	public String getZoneId() {
 		return getClaimAsString(CLAIM_ZONE_ID);
 	}
 


Test patch:
diff --git a/java-security-test/src/test/resources/claims.json b/java-security-test/src/test/resources/claims.json
index f3d62728..ae6e5efd 100644
--- a/java-security-test/src/test/resources/claims.json
+++ b/java-security-test/src/test/resources/claims.json
@@ -12,5 +12,6 @@
   "grant_type": "urn:ietf:params:oauth:grant-type:saml2-bearer",
   "email": "test@uaa.org",
   "exp": 1542416800,
-  "az_attr": {"external_id":"abcd1234"}
+  "az_attr": {"external_id":"abcd1234"},
+  "xs.system.attributes": {"subaccountid": "test-subaccount"}
 }
\ No newline at end of file
diff --git a/java-security/src/test/java/com/sap/cloud/security/adapter/xs/XSUserInfoAdapterIntegrationTest.java b/java-security/src/test/java/com/sap/cloud/security/adapter/xs/XSUserInfoAdapterIntegrationTest.java
index f1bde09e..74ff0a39 100644
--- a/java-security/src/test/java/com/sap/cloud/security/adapter/xs/XSUserInfoAdapterIntegrationTest.java
+++ b/java-security/src/test/java/com/sap/cloud/security/adapter/xs/XSUserInfoAdapterIntegrationTest.java
@@ -125,7 +125,7 @@ public class XSUserInfoAdapterIntegrationTest {
 	@Test
 	public void getIdentityZone() {
 		assertEquals("uaa", infoUser.getIdentityZone());
-		assertEquals("uaa", infoUser.getSubaccountId());
+		assertEquals("uaa", infoUser.getZoneId());
 	}
 
 	@Test
diff --git a/java-security/src/test/java/com/sap/cloud/security/adapter/xs/XSUserInfoAdapterTest.java b/java-security/src/test/java/com/sap/cloud/security/adapter/xs/XSUserInfoAdapterTest.java
index aa2540ce..ff070e6b 100644
--- a/java-security/src/test/java/com/sap/cloud/security/adapter/xs/XSUserInfoAdapterTest.java
+++ b/java-security/src/test/java/com/sap/cloud/security/adapter/xs/XSUserInfoAdapterTest.java
@@ -280,6 +280,13 @@ public class XSUserInfoAdapterTest {
 		assertThat(cut.getSubaccountId()).isEqualTo("paas");
 	}
 
+	@Test
+	public void testGetSubaccountIdFromSystemAttributes() throws XSUserInfoException {
+		String token = "eyJqa3UiOiJodHRwOi8vbG9jYWxob3N0Iiwia2lkIjoiZGVmYXVsdC1raWQiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOlsic2ItY2xpZW50SWQhdDA4MTUiXSwiZXh0X2F0dHIiOnsiZW5oYW5jZXIiOiJYU1VBQSIsInpkbiI6InVhYSIsImFjbCI6WyJhcHAxIXQyMyJdLCJzZXJ2aWNlaW5zdGFuY2VpZCI6ImJyb2tlckNsb25lU2VydmljZUluc3RhbmNlSWQifSwiYXpfYXR0ciI6eyJleHRlcm5hbF9pZCI6ImFiY2QxMjM0In0sImdyYW50X3R5cGUiOiJ1cm46aWV0ZjpwYXJhbXM6b2F1dGg6Z3JhbnQtdHlwZTpzYW1sMi1iZWFyZXIiLCJzY29wZSI6WyJvcGVuaWQiLCJ0ZXN0U2NvcGUiLCJ0ZXN0QXBwLmxvY2FsU2NvcGUiXSwiZW1wdHlfbGlzdCI6W10sInhzLnN5c3RlbS5hdHRyaWJ1dGVzIjp7InN1YmFjY291bnRpZCI6InRlc3Qtc3ViYWNjb3VudCJ9LCJleHAiOjE1NDI0MTY4MDAsImVtYWlsIjoidGVzdEB1YWEub3JnIiwiY2lkIjoic2ItY2xpZW50SWQhdDA4MTUifQ.dbCiJFDooPvJ0Sla3pnVXn5exNcVw7dKPgaDF3w_mFS6HqkzhxSwujy0PHtolHlaD1PztZ-6Lyi_eWqlIP3QjIxXKAkf2b3BcAF6WDRX3p5h8Noql9UDoNvYSNImdQcDWN2cZ5q2TzIv2X46HwW0cFP86oKFykALvp_CHlW1GKeJ8VdSWHoMJlOsdoKrLe68HKkIot1yjLsVr9i5LlsQm1EwR_GijLZeDHQNnYL2i_XWOhz3o6bjlcXicdhUaXEBp9CRvb0zucSYbTWJB5tV4e4i0GShXioX6qJG5NOgBZqsigUEaWACfO7Cmdzbr7AsVTlxb_mww9fF7wSAzrW1zA";
+		cut = new XSUserInfoAdapter(new XsuaaToken(token));
+		assertThat(cut.getSubaccountId()).isEqualTo("test-subaccount");
+	}
+
 	@Test
 	public void testGetOrigin() throws XSUserInfoException {
 		assertThat(cut.getOrigin()).isEqualTo("useridp");
diff --git a/samples/spring-security-basic-auth/src/main/java/sample/spring/xsuaa/TestController.java b/samples/spring-security-basic-auth/src/main/java/sample/spring/xsuaa/TestController.java
index 55f0556c..48e1f0e4 100644
--- a/samples/spring-security-basic-auth/src/main/java/sample/spring/xsuaa/TestController.java
+++ b/samples/spring-security-basic-auth/src/main/java/sample/spring/xsuaa/TestController.java
@@ -18,6 +18,7 @@ public class TestController {
 		result.put("grant type", token.getGrantType());
 		result.put("client id", token.getClientId());
 		result.put("subaccount id", token.getSubaccountId());
+		result.put("zone id", token.getZoneId());
 		result.put("logon name", token.getLogonName());
 		result.put("family name", token.getFamilyName());
 		result.put("given name", token.getGivenName());
diff --git a/samples/spring-security-xsuaa-usage/src/main/java/sample/spring/xsuaa/TestController.java b/samples/spring-security-xsuaa-usage/src/main/java/sample/spring/xsuaa/TestController.java
index c8acafbb..a9b61523 100644
--- a/samples/spring-security-xsuaa-usage/src/main/java/sample/spring/xsuaa/TestController.java
+++ b/samples/spring-security-xsuaa-usage/src/main/java/sample/spring/xsuaa/TestController.java
@@ -58,6 +58,7 @@ public class TestController {
         result.put("grant type", token.getGrantType());
         result.put("client id", token.getClientId());
         result.put("subaccount id", token.getSubaccountId());
+        result.put("zone id", token.getZoneId());
         result.put("logon name", token.getLogonName());
         result.put("family name", token.getFamilyName());
         result.put("given name", token.getGivenName());
diff --git a/spring-xsuaa-it/src/main/java/testservice/api/v1/TestController.java b/spring-xsuaa-it/src/main/java/testservice/api/v1/TestController.java
index 0da7b100..ad70bfaa 100644
--- a/spring-xsuaa-it/src/main/java/testservice/api/v1/TestController.java
+++ b/spring-xsuaa-it/src/main/java/testservice/api/v1/TestController.java
@@ -60,7 +60,7 @@ public class TestController {
 		// email
 		Assert.assertEquals("max@example.com", token.getEmail());
 		// zone
-		Assert.assertTrue(token.getSubaccountId().endsWith("domain-id"));
+		Assert.assertTrue(token.getZoneId().endsWith("domain-id"));
 		// ext attr
 		Assert.assertEquals("domain\\group1", token.getAdditionalAuthAttribute("external_group"));
 		Assert.assertEquals("abcd1234", token.getAdditionalAuthAttribute("external_id"));
diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/SpringSecurityContextTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/SpringSecurityContextTest.java
index 585d4427..000014c1 100644
--- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/SpringSecurityContextTest.java
+++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/SpringSecurityContextTest.java
@@ -74,10 +74,10 @@ public class SpringSecurityContextTest {
 		});
 
 		assertEquals(SUBDOMAIN_1, future_1.get().getSubdomain());
-		assertEquals(SUBDOMAIN_1 + "-id", future_1.get().getSubaccountId());
+		assertEquals(SUBDOMAIN_1 + "-id", future_1.get().getZoneId());
 
 		assertEquals(SUBDOMAIN_2, future_2.get().getSubdomain());
-		assertEquals(SUBDOMAIN_2 + "-id", future_2.get().getSubaccountId());
+		assertEquals(SUBDOMAIN_2 + "-id", future_2.get().getZoneId());
 	}
 
 	private static void initSecurityContextWithToken(Jwt token) throws InterruptedException {
diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/XsuaaTokenTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/XsuaaTokenTest.java
index e156a3fa..82f18c9f 100644
--- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/XsuaaTokenTest.java
+++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/XsuaaTokenTest.java
@@ -32,7 +32,6 @@ public class XsuaaTokenTest {
 	private XsuaaToken token;
 	private Jwt jwtSaml;
 	private Jwt jwtCC;
-	private Jwt jwtCCNoAttributes;
 	private JWTClaimsSet.Builder claimsSetBuilder = null;
 	private String xsAppName = "my-app-name!t400";
 	private String scopeRead = xsAppName + "." + "Read";
@@ -53,7 +52,6 @@ public class XsuaaTokenTest {
 
 		jwtSaml = new JwtGenerator().createFromTemplate("/saml.txt");
 		jwtCC = JwtGenerator.createFromFile("/token_cc.txt");
-		jwtCCNoAttributes = JwtGenerator.createFromFile("/token_cc_noattr.txt");
 	}
 
 	@Test
@@ -68,6 +66,7 @@ public class XsuaaTokenTest {
 		assertThat(token.getFamilyName(), nullValue());
 		assertThat(token.getGivenName(), nullValue());
 		assertThat(token.getEmail(), is("testUser@test.org"));
+		assertThat(token.getZoneId(), is(zoneId));
 		assertThat(token.getSubaccountId(), is(zoneId));
 		assertThat(token.isAccountNonLocked(), is(true));
 		assertThat(token.isAccountNonExpired(), is(true));
@@ -138,6 +137,13 @@ public class XsuaaTokenTest {
 		token = createToken(claimsSetBuilder);
 
 		assertThat(token.getSubaccountId(), is(zoneId));
+		assertThat(token.getZoneId(), is(zoneId));
+	}
+
+	@Test
+	public void getSubaccountIdFromSystemAttributes() throws IOException {
+		Token token = new XsuaaToken(jwtSaml);
+		Assert.assertEquals("test-subaccount", token.getSubaccountId());
 	}
 
 	@Test
diff --git a/spring-xsuaa/src/test/resources/saml.txt b/spring-xsuaa/src/test/resources/saml.txt
index f3e1da8a..18e84ea2 100644
--- a/spring-xsuaa/src/test/resources/saml.txt
+++ b/spring-xsuaa/src/test/resources/saml.txt
@@ -13,7 +13,8 @@
     "xs.saml.groups": [
       "g1"
     ],
-    "xs.rolecollections": ["rc1"]
+    "xs.rolecollections": ["rc1"],
+    "subaccountid": "test-subaccount"
   },
   "sub": "1002191",
   "scope": [