From 0274b05a5a386f229661345a6666f51f018ae08c Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Tue, 21 Jul 2020 09:19:33 -0700 Subject: [PATCH 01/21] Classes for tenant profile functionality --- .../aad/msal4j/IMultiTenantAccount.java | 8 +++++++ .../microsoft/aad/msal4j/ITenantProfile.java | 5 +++++ .../aad/msal4j/MultiTenantAccount.java | 22 +++++++++++++++++++ .../microsoft/aad/msal4j/TenantProfile.java | 20 +++++++++++++++++ 4 files changed, 55 insertions(+) create mode 100644 src/main/java/com/microsoft/aad/msal4j/IMultiTenantAccount.java create mode 100644 src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java create mode 100644 src/main/java/com/microsoft/aad/msal4j/MultiTenantAccount.java create mode 100644 src/main/java/com/microsoft/aad/msal4j/TenantProfile.java diff --git a/src/main/java/com/microsoft/aad/msal4j/IMultiTenantAccount.java b/src/main/java/com/microsoft/aad/msal4j/IMultiTenantAccount.java new file mode 100644 index 00000000..5ba8f00f --- /dev/null +++ b/src/main/java/com/microsoft/aad/msal4j/IMultiTenantAccount.java @@ -0,0 +1,8 @@ +package com.microsoft.aad.msal4j; + +import java.util.Map; + +public interface IMultiTenantAccount extends IAccount { + + Map getTenantProfiles(); +} diff --git a/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java b/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java new file mode 100644 index 00000000..cfb8ad18 --- /dev/null +++ b/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java @@ -0,0 +1,5 @@ +package com.microsoft.aad.msal4j; + +public interface ITenantProfile extends IAccount { + +} diff --git a/src/main/java/com/microsoft/aad/msal4j/MultiTenantAccount.java b/src/main/java/com/microsoft/aad/msal4j/MultiTenantAccount.java new file mode 100644 index 00000000..be26d133 --- /dev/null +++ b/src/main/java/com/microsoft/aad/msal4j/MultiTenantAccount.java @@ -0,0 +1,22 @@ +package com.microsoft.aad.msal4j; + +import lombok.Getter; +import lombok.Setter; + +import java.util.Map; + +@Getter +@Setter +public class MultiTenantAccount extends Account implements IMultiTenantAccount { + + private Map tenantProfiles; + + public MultiTenantAccount(String homeAccountId, String environment, String username, Map idTokenClaims) { + super(homeAccountId, environment, username, idTokenClaims); + } + + @Override + public Map getTenantProfiles() { + return tenantProfiles; + } +} diff --git a/src/main/java/com/microsoft/aad/msal4j/TenantProfile.java b/src/main/java/com/microsoft/aad/msal4j/TenantProfile.java new file mode 100644 index 00000000..70a63d4f --- /dev/null +++ b/src/main/java/com/microsoft/aad/msal4j/TenantProfile.java @@ -0,0 +1,20 @@ +package com.microsoft.aad.msal4j; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.util.Map; + +@Accessors(fluent = true) +@Getter +@Setter +public class TenantProfile extends Account implements ITenantProfile{ + + String tenantId; + + public TenantProfile(String homeAccountId, String environment, String username, Map idTokenClaims, String tenantId) { + super(homeAccountId, environment, username, idTokenClaims); + this.tenantId = tenantId; + } +} From 741218066517d1dfd4ac6cc71f01d629237997ba Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Tue, 21 Jul 2020 09:29:21 -0700 Subject: [PATCH 02/21] Implement tenant profile feature --- .../com/microsoft/aad/msal4j/Account.java | 3 ++ .../aad/msal4j/AccountCacheEntity.java | 10 +++- .../com/microsoft/aad/msal4j/IAccount.java | 6 +++ .../com/microsoft/aad/msal4j/IdToken.java | 25 ++++++++++ .../com/microsoft/aad/msal4j/TokenCache.java | 48 ++++++++++++++++++- 5 files changed, 90 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/aad/msal4j/Account.java b/src/main/java/com/microsoft/aad/msal4j/Account.java index 821a8849..33e2d008 100644 --- a/src/main/java/com/microsoft/aad/msal4j/Account.java +++ b/src/main/java/com/microsoft/aad/msal4j/Account.java @@ -7,6 +7,7 @@ import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; +import java.util.Map; /** * Representation of a single user account. If modifying this object, ensure it is compliant with @@ -23,4 +24,6 @@ class Account implements IAccount { String environment; String username; + + Map idTokenClaims; } diff --git a/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java b/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java index 23ba4113..2cba6f2a 100644 --- a/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java +++ b/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java @@ -10,6 +10,7 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import java.util.Map; @Accessors(fluent = true) @Getter @@ -49,6 +50,8 @@ ClientInfo clientInfo() { @JsonProperty("authority_type") protected String authorityType; + protected Map idTokenClaims; + String getKey() { List keyParts = new ArrayList<>(); @@ -77,6 +80,7 @@ static AccountCacheEntity create(String clientInfoStr, Authority requestAuthorit account.localAccountId(localAccountId); account.username(idToken.preferredUsername); account.name(idToken.name); + account.idTokenClaims(idToken.tokenClaims()); } return account; @@ -101,6 +105,10 @@ static AccountCacheEntity create(String clientInfoStr, Authority requestAuthorit } IAccount toAccount(){ - return new Account(homeAccountId, environment, username); + if (homeAccountId.contains(localAccountId)) { + return new Account(homeAccountId, environment, username, idTokenClaims); + } else { + return new TenantProfile(homeAccountId, environment, username, idTokenClaims, localAccountId); + } } } diff --git a/src/main/java/com/microsoft/aad/msal4j/IAccount.java b/src/main/java/com/microsoft/aad/msal4j/IAccount.java index 7c43b5af..c248f985 100644 --- a/src/main/java/com/microsoft/aad/msal4j/IAccount.java +++ b/src/main/java/com/microsoft/aad/msal4j/IAccount.java @@ -3,6 +3,7 @@ package com.microsoft.aad.msal4j; +import java.util.Map; import java.util.Set; /** @@ -26,4 +27,9 @@ public interface IAccount { * @return account username */ String username(); + + /** + * @return claims in ID token + */ + Map idTokenClaims(); } diff --git a/src/main/java/com/microsoft/aad/msal4j/IdToken.java b/src/main/java/com/microsoft/aad/msal4j/IdToken.java index b5ee6a0a..2ac873c1 100644 --- a/src/main/java/com/microsoft/aad/msal4j/IdToken.java +++ b/src/main/java/com/microsoft/aad/msal4j/IdToken.java @@ -7,6 +7,8 @@ import com.nimbusds.jwt.JWTClaimsSet; import java.text.ParseException; +import java.util.HashMap; +import java.util.Map; class IdToken { @@ -59,6 +61,29 @@ class IdToken { @JsonProperty("unique_name") protected String uniqueName; + /** + * Used to attach all claims in an ID token to an account object + * + * @return map of key/value pairs of claims in ID token + */ + protected Map tokenClaims() { + Map idTokenClaims = new HashMap<>(); + idTokenClaims.put(ISSUER, this.issuer); + idTokenClaims.put(SUBJECT, this.subject); + idTokenClaims.put(AUDIENCE, this.audience); + idTokenClaims.put(EXPIRATION_TIME, this.expirationTime); + idTokenClaims.put(ISSUED_AT, this.issuedAt); + idTokenClaims.put(NOT_BEFORE, this.notBefore); + idTokenClaims.put(NAME, this.name); + idTokenClaims.put(PREFERRED_USERNAME, this.preferredUsername); + idTokenClaims.put(OBJECT_IDENTIFIER, this.objectIdentifier); + idTokenClaims.put(TENANT_IDENTIFIER, this.tenantIdentifier); + idTokenClaims.put(UPN, this.upn); + idTokenClaims.put(UNIQUE_NAME, this.uniqueName); + + return idTokenClaims; + } + static IdToken createFromJWTClaims(final JWTClaimsSet claims) throws ParseException { IdToken idToken = new IdToken(); diff --git a/src/main/java/com/microsoft/aad/msal4j/TokenCache.java b/src/main/java/com/microsoft/aad/msal4j/TokenCache.java index d95f2f66..a78d81a4 100644 --- a/src/main/java/com/microsoft/aad/msal4j/TokenCache.java +++ b/src/main/java/com/microsoft/aad/msal4j/TokenCache.java @@ -300,10 +300,56 @@ Set getAccounts(String clientId, Set environmentAliases) { build())) { try { lock.readLock().lock(); + Set rootAccounts = new HashSet<>(); - return accounts.values().stream(). + //Filter accounts map into two sets: a set of home accounts (local UID = home UID), + // and a set of tenant profiles (local UID != home UID) + Set homeAccounts = accounts.values().stream(). filter(acc -> environmentAliases.contains(acc.environment())). + filter(acc -> acc.homeAccountId().contains(acc.localAccountId())). collect(Collectors.mapping(AccountCacheEntity::toAccount, Collectors.toSet())); + Set tenantAccounts = accounts.values().stream(). + filter(acc -> environmentAliases.contains(acc.environment())). + filter(acc -> !acc.homeAccountId().contains(acc.localAccountId())). + collect(Collectors.mapping(AccountCacheEntity::toAccount, Collectors.toSet())); + + Iterator homeAcctsIterator = homeAccounts.iterator(); + Iterator tenantAcctsIterator; + Map tenantProfiles; + IAccount homeAcc, tenantAcc; + + while (homeAcctsIterator.hasNext()) { + homeAcc = homeAcctsIterator.next(); + tenantAcctsIterator = tenantAccounts.iterator(); + tenantProfiles = new HashMap<>(); + + while (tenantAcctsIterator.hasNext()) { + tenantAcc = tenantAcctsIterator.next(); + + //If the tenant account's home ID matches a home ID (UID) in the list of home accounts, it is a + //tenant profile of that home account + if (tenantAcc.homeAccountId().contains(homeAcc.homeAccountId().substring(0, homeAcc.homeAccountId().indexOf(".")))) { + TenantProfile profile = (TenantProfile) tenantAcc; + tenantProfiles.put(profile.tenantId(), profile); + tenantAcctsIterator.remove(); + } + } + if (tenantProfiles.isEmpty()) { + //If the home account has no tenant profiles, leave it as an Account object + rootAccounts.add(homeAcc); + } else { + //If the home account had some tenant profiles, transform the home Account object into a + //MultiTenantAccount object and attach its tenant profile list + MultiTenantAccount multAcct = new MultiTenantAccount( + homeAcc.homeAccountId(), homeAcc.environment(), homeAcc.username(), idTokens); + multAcct.setTenantProfiles(tenantProfiles); + rootAccounts.add(multAcct); + } + } + //Add any tenant accounts which did not have a corresponding home account to the list of root accounts + rootAccounts.addAll(tenantAccounts); + + return rootAccounts; } finally { lock.readLock().unlock(); } From 2982cb427a3dbfa21ec8f0b68a851ef95e7761c6 Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Tue, 21 Jul 2020 09:29:49 -0700 Subject: [PATCH 03/21] Tests for tenant profile feature --- .../com/microsoft/aad/msal4j/AccountTest.java | 76 +++++++++++++++++++ .../multi-tenant-account-cache.json | 37 +++++++++ 2 files changed, 113 insertions(+) create mode 100644 src/test/java/com/microsoft/aad/msal4j/AccountTest.java create mode 100644 src/test/resources/cache_data/multi-tenant-account-cache.json diff --git a/src/test/java/com/microsoft/aad/msal4j/AccountTest.java b/src/test/java/com/microsoft/aad/msal4j/AccountTest.java new file mode 100644 index 00000000..c5ec0949 --- /dev/null +++ b/src/test/java/com/microsoft/aad/msal4j/AccountTest.java @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j; + +import org.testng.Assert; +import org.testng.annotations.Test; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Iterator; +import java.util.Map; + +public class AccountTest { + + @Test + public void testMultiTenantAccount_ClassTypes() throws IOException, URISyntaxException { + + ITokenCacheAccessAspect accountCache = new CachePersistenceIT.TokenPersistence( + TestHelper.readResource(this.getClass(), + "/cache_data/multi-tenant-account-cache.json")); + + PublicClientApplication app = PublicClientApplication.builder("uid1") + .setTokenCacheAccessAspect(accountCache).build(); + + Assert.assertEquals(app.getAccounts().join().size(), 3); + Iterator acctIterator = app.getAccounts().join().iterator(); + + IAccount curAccount; + IAccount account = null, multiAccount = null, tenantProfileAccount = null; + while (acctIterator.hasNext()) { + curAccount = acctIterator.next(); + switch (curAccount.username()) { + case "Account": + account = curAccount; + break; + case "MultiAccount": + multiAccount = curAccount; + break; + case "TenantProfileNoHome": + tenantProfileAccount = curAccount; + break; + } + } + + Assert.assertTrue(account instanceof Account); + Assert.assertTrue(multiAccount instanceof MultiTenantAccount); + Assert.assertTrue(tenantProfileAccount instanceof TenantProfile); + } + + @Test + public void testMultiTenantAccount_AccessTenantProfile() throws IOException, URISyntaxException { + + ITokenCacheAccessAspect accountCache = new CachePersistenceIT.TokenPersistence( + TestHelper.readResource(this.getClass(), + "/cache_data/multi-tenant-account-cache.json")); + + PublicClientApplication app = PublicClientApplication.builder("uid1") + .setTokenCacheAccessAspect(accountCache).build(); + + Assert.assertEquals(app.getAccounts().join().size(), 3); + Iterator acctIterator = app.getAccounts().join().iterator(); + + IAccount curAccount; + while (acctIterator.hasNext()) { + curAccount = acctIterator.next(); + + if (curAccount.username().equals("MultiAccount")) { + Assert.assertTrue(curAccount instanceof MultiTenantAccount); + Map tenantProfiles = ((MultiTenantAccount) curAccount).getTenantProfiles(); + Assert.assertNotNull(tenantProfiles); + Assert.assertNotNull(tenantProfiles.get("tenantprofile1")); + Assert.assertEquals(tenantProfiles.get("tenantprofile1").username(), "TenantProfile"); + } + } + } +} diff --git a/src/test/resources/cache_data/multi-tenant-account-cache.json b/src/test/resources/cache_data/multi-tenant-account-cache.json new file mode 100644 index 00000000..becf51cb --- /dev/null +++ b/src/test/resources/cache_data/multi-tenant-account-cache.json @@ -0,0 +1,37 @@ +{ + "Account": { + "uid1.utid1-login.windows.net-contoso": { + "username": "MultiAccount", + "local_account_id": "uid1", + "realm": "contoso", + "environment": "login.windows.net", + "home_account_id": "uid1.utid1", + "authority_type": "MSSTS" + }, + "uid1.utid2-login.windows.net-contoso": { + "username": "TenantProfile", + "local_account_id": "tenantprofile1", + "realm": "contoso", + "environment": "login.windows.net", + "home_account_id": "uid1.utid1", + "authority_type": "MSSTS" + }, + "uid2.utid2-login.windows.net-contoso": { + "username": "TenantProfileNoHome", + "local_account_id": "tenantprofile2", + "realm": "contoso", + "environment": "login.windows.net", + "home_account_id": "uid2.utid2", + "authority_type": "MSSTS" + }, + "uid3.utid3-login.windows.net-contoso": { + "username": "Account", + "local_account_id": "uid3", + "realm": "contoso", + "environment": "login.windows.net", + "home_account_id": "uid3.utid3", + "authority_type": "MSSTS" + } + }, + "AppMetadata": {} +} From c7c53dc024fc74deba54f13b7cdd556905104204 Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Wed, 12 Aug 2020 13:08:54 -0700 Subject: [PATCH 04/21] Simplify tenant profile class structure --- .../com/microsoft/aad/msal4j/Account.java | 16 ++++++ .../aad/msal4j/AccountCacheEntity.java | 6 +-- .../com/microsoft/aad/msal4j/IAccount.java | 14 ++++- .../aad/msal4j/IMultiTenantAccount.java | 8 --- .../microsoft/aad/msal4j/ITenantProfile.java | 5 -- .../aad/msal4j/MultiTenantAccount.java | 22 -------- .../microsoft/aad/msal4j/TenantProfile.java | 20 ------- .../com/microsoft/aad/msal4j/TokenCache.java | 17 +++--- .../com/microsoft/aad/msal4j/AccountTest.java | 54 +++++-------------- .../multi-tenant-account-cache.json | 26 +++++---- 10 files changed, 67 insertions(+), 121 deletions(-) delete mode 100644 src/main/java/com/microsoft/aad/msal4j/IMultiTenantAccount.java delete mode 100644 src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java delete mode 100644 src/main/java/com/microsoft/aad/msal4j/MultiTenantAccount.java delete mode 100644 src/main/java/com/microsoft/aad/msal4j/TenantProfile.java diff --git a/src/main/java/com/microsoft/aad/msal4j/Account.java b/src/main/java/com/microsoft/aad/msal4j/Account.java index 33e2d008..2fdb88fb 100644 --- a/src/main/java/com/microsoft/aad/msal4j/Account.java +++ b/src/main/java/com/microsoft/aad/msal4j/Account.java @@ -25,5 +25,21 @@ class Account implements IAccount { String username; + String localAccountId; + Map idTokenClaims; + + Map tenantProfiles; + + public String getTenantId() { + return localAccountId; + } + + public Map getClaims() { + return idTokenClaims; + } + + public Map getTenantProfiles() { + return tenantProfiles; + } } diff --git a/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java b/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java index 2cba6f2a..167d57f4 100644 --- a/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java +++ b/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java @@ -105,10 +105,6 @@ static AccountCacheEntity create(String clientInfoStr, Authority requestAuthorit } IAccount toAccount(){ - if (homeAccountId.contains(localAccountId)) { - return new Account(homeAccountId, environment, username, idTokenClaims); - } else { - return new TenantProfile(homeAccountId, environment, username, idTokenClaims, localAccountId); - } + return new Account(homeAccountId, environment, username, localAccountId, idTokenClaims, null); } } diff --git a/src/main/java/com/microsoft/aad/msal4j/IAccount.java b/src/main/java/com/microsoft/aad/msal4j/IAccount.java index c248f985..b5be5909 100644 --- a/src/main/java/com/microsoft/aad/msal4j/IAccount.java +++ b/src/main/java/com/microsoft/aad/msal4j/IAccount.java @@ -29,7 +29,17 @@ public interface IAccount { String username(); /** - * @return claims in ID token + * @return claims in id token */ - Map idTokenClaims(); + Map getClaims(); + + /** + * @return tenant id + */ + String getTenantId(); + + /** + * @return tenant profiles + */ + Map getTenantProfiles(); } diff --git a/src/main/java/com/microsoft/aad/msal4j/IMultiTenantAccount.java b/src/main/java/com/microsoft/aad/msal4j/IMultiTenantAccount.java deleted file mode 100644 index 5ba8f00f..00000000 --- a/src/main/java/com/microsoft/aad/msal4j/IMultiTenantAccount.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.microsoft.aad.msal4j; - -import java.util.Map; - -public interface IMultiTenantAccount extends IAccount { - - Map getTenantProfiles(); -} diff --git a/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java b/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java deleted file mode 100644 index cfb8ad18..00000000 --- a/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.microsoft.aad.msal4j; - -public interface ITenantProfile extends IAccount { - -} diff --git a/src/main/java/com/microsoft/aad/msal4j/MultiTenantAccount.java b/src/main/java/com/microsoft/aad/msal4j/MultiTenantAccount.java deleted file mode 100644 index be26d133..00000000 --- a/src/main/java/com/microsoft/aad/msal4j/MultiTenantAccount.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.microsoft.aad.msal4j; - -import lombok.Getter; -import lombok.Setter; - -import java.util.Map; - -@Getter -@Setter -public class MultiTenantAccount extends Account implements IMultiTenantAccount { - - private Map tenantProfiles; - - public MultiTenantAccount(String homeAccountId, String environment, String username, Map idTokenClaims) { - super(homeAccountId, environment, username, idTokenClaims); - } - - @Override - public Map getTenantProfiles() { - return tenantProfiles; - } -} diff --git a/src/main/java/com/microsoft/aad/msal4j/TenantProfile.java b/src/main/java/com/microsoft/aad/msal4j/TenantProfile.java deleted file mode 100644 index 70a63d4f..00000000 --- a/src/main/java/com/microsoft/aad/msal4j/TenantProfile.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.microsoft.aad.msal4j; - -import lombok.Getter; -import lombok.Setter; -import lombok.experimental.Accessors; - -import java.util.Map; - -@Accessors(fluent = true) -@Getter -@Setter -public class TenantProfile extends Account implements ITenantProfile{ - - String tenantId; - - public TenantProfile(String homeAccountId, String environment, String username, Map idTokenClaims, String tenantId) { - super(homeAccountId, environment, username, idTokenClaims); - this.tenantId = tenantId; - } -} diff --git a/src/main/java/com/microsoft/aad/msal4j/TokenCache.java b/src/main/java/com/microsoft/aad/msal4j/TokenCache.java index a78d81a4..92572118 100644 --- a/src/main/java/com/microsoft/aad/msal4j/TokenCache.java +++ b/src/main/java/com/microsoft/aad/msal4j/TokenCache.java @@ -315,22 +315,21 @@ Set getAccounts(String clientId, Set environmentAliases) { Iterator homeAcctsIterator = homeAccounts.iterator(); Iterator tenantAcctsIterator; - Map tenantProfiles; - IAccount homeAcc, tenantAcc; + Map tenantProfiles; + Account homeAcc, tenantAcc; while (homeAcctsIterator.hasNext()) { - homeAcc = homeAcctsIterator.next(); + homeAcc = (Account) homeAcctsIterator.next(); tenantAcctsIterator = tenantAccounts.iterator(); tenantProfiles = new HashMap<>(); while (tenantAcctsIterator.hasNext()) { - tenantAcc = tenantAcctsIterator.next(); + tenantAcc = (Account) tenantAcctsIterator.next(); //If the tenant account's home ID matches a home ID (UID) in the list of home accounts, it is a //tenant profile of that home account if (tenantAcc.homeAccountId().contains(homeAcc.homeAccountId().substring(0, homeAcc.homeAccountId().indexOf(".")))) { - TenantProfile profile = (TenantProfile) tenantAcc; - tenantProfiles.put(profile.tenantId(), profile); + tenantProfiles.put(tenantAcc.getTenantId(), tenantAcc); tenantAcctsIterator.remove(); } } @@ -340,10 +339,8 @@ Set getAccounts(String clientId, Set environmentAliases) { } else { //If the home account had some tenant profiles, transform the home Account object into a //MultiTenantAccount object and attach its tenant profile list - MultiTenantAccount multAcct = new MultiTenantAccount( - homeAcc.homeAccountId(), homeAcc.environment(), homeAcc.username(), idTokens); - multAcct.setTenantProfiles(tenantProfiles); - rootAccounts.add(multAcct); + homeAcc.tenantProfiles(tenantProfiles); + rootAccounts.add(homeAcc); } } //Add any tenant accounts which did not have a corresponding home account to the list of root accounts diff --git a/src/test/java/com/microsoft/aad/msal4j/AccountTest.java b/src/test/java/com/microsoft/aad/msal4j/AccountTest.java index c5ec0949..fd282693 100644 --- a/src/test/java/com/microsoft/aad/msal4j/AccountTest.java +++ b/src/test/java/com/microsoft/aad/msal4j/AccountTest.java @@ -12,41 +12,6 @@ public class AccountTest { - @Test - public void testMultiTenantAccount_ClassTypes() throws IOException, URISyntaxException { - - ITokenCacheAccessAspect accountCache = new CachePersistenceIT.TokenPersistence( - TestHelper.readResource(this.getClass(), - "/cache_data/multi-tenant-account-cache.json")); - - PublicClientApplication app = PublicClientApplication.builder("uid1") - .setTokenCacheAccessAspect(accountCache).build(); - - Assert.assertEquals(app.getAccounts().join().size(), 3); - Iterator acctIterator = app.getAccounts().join().iterator(); - - IAccount curAccount; - IAccount account = null, multiAccount = null, tenantProfileAccount = null; - while (acctIterator.hasNext()) { - curAccount = acctIterator.next(); - switch (curAccount.username()) { - case "Account": - account = curAccount; - break; - case "MultiAccount": - multiAccount = curAccount; - break; - case "TenantProfileNoHome": - tenantProfileAccount = curAccount; - break; - } - } - - Assert.assertTrue(account instanceof Account); - Assert.assertTrue(multiAccount instanceof MultiTenantAccount); - Assert.assertTrue(tenantProfileAccount instanceof TenantProfile); - } - @Test public void testMultiTenantAccount_AccessTenantProfile() throws IOException, URISyntaxException { @@ -64,12 +29,21 @@ public void testMultiTenantAccount_AccessTenantProfile() throws IOException, URI while (acctIterator.hasNext()) { curAccount = acctIterator.next(); - if (curAccount.username().equals("MultiAccount")) { - Assert.assertTrue(curAccount instanceof MultiTenantAccount); - Map tenantProfiles = ((MultiTenantAccount) curAccount).getTenantProfiles(); + if (curAccount.username().equals("MultiTenantAccount")) { + Assert.assertEquals(curAccount.homeAccountId(), "uid1.utid1"); + Map tenantProfiles = curAccount.getTenantProfiles(); Assert.assertNotNull(tenantProfiles); - Assert.assertNotNull(tenantProfiles.get("tenantprofile1")); - Assert.assertEquals(tenantProfiles.get("tenantprofile1").username(), "TenantProfile"); + Assert.assertEquals(tenantProfiles.size(), 2); + Assert.assertNotNull(tenantProfiles.get("utid2")); + Assert.assertEquals(tenantProfiles.get("utid2").username(), "TenantProfile1"); + Assert.assertEquals(tenantProfiles.get("utid2").username(), "TenantProfile1"); + Assert.assertNotNull(tenantProfiles.get("utid3")); + Assert.assertEquals(tenantProfiles.get("utid3").username(), "TenantProfile2"); + } + else if (curAccount.username().equals("TenantProfileNoHome") || + curAccount.username().equals("SingleTenantAccount") ) { + Map tenantProfiles = curAccount.getTenantProfiles(); + Assert.assertNull(tenantProfiles); } } } diff --git a/src/test/resources/cache_data/multi-tenant-account-cache.json b/src/test/resources/cache_data/multi-tenant-account-cache.json index becf51cb..c1c72e76 100644 --- a/src/test/resources/cache_data/multi-tenant-account-cache.json +++ b/src/test/resources/cache_data/multi-tenant-account-cache.json @@ -1,32 +1,40 @@ { "Account": { "uid1.utid1-login.windows.net-contoso": { - "username": "MultiAccount", - "local_account_id": "uid1", + "username": "MultiTenantAccount", + "local_account_id": "utid1", "realm": "contoso", "environment": "login.windows.net", "home_account_id": "uid1.utid1", "authority_type": "MSSTS" }, "uid1.utid2-login.windows.net-contoso": { - "username": "TenantProfile", - "local_account_id": "tenantprofile1", + "username": "TenantProfile1", + "local_account_id": "utid2", "realm": "contoso", "environment": "login.windows.net", "home_account_id": "uid1.utid1", "authority_type": "MSSTS" }, - "uid2.utid2-login.windows.net-contoso": { + "uid1.utid3-login.windows.net-contoso": { + "username": "TenantProfile2", + "local_account_id": "utid3", + "realm": "contoso", + "environment": "login.windows.net", + "home_account_id": "uid1.utid1", + "authority_type": "MSSTS" + }, + "uid2.utid4-login.windows.net-contoso": { "username": "TenantProfileNoHome", - "local_account_id": "tenantprofile2", + "local_account_id": "utid4", "realm": "contoso", "environment": "login.windows.net", "home_account_id": "uid2.utid2", "authority_type": "MSSTS" }, - "uid3.utid3-login.windows.net-contoso": { - "username": "Account", - "local_account_id": "uid3", + "uid3.utid5-login.windows.net-contoso": { + "username": "SingleTenantAccount", + "local_account_id": "uid5", "realm": "contoso", "environment": "login.windows.net", "home_account_id": "uid3.utid3", From f92e16643fd99007c2fc57b9536c9a4d5e1ec846 Mon Sep 17 00:00:00 2001 From: SomkaPe Date: Mon, 17 Aug 2020 15:55:56 -0700 Subject: [PATCH 05/21] 1.6.2 release --- README.md | 6 ++--- changelog.txt | 7 ++++++ pom.xml | 3 +-- .../UsernamePasswordIT.java | 24 +++++++++++++++++++ .../msal4j/AadInstanceDiscoveryProvider.java | 17 ++++++++----- src/samples/msal-b2c-web-sample/pom.xml | 2 +- src/samples/msal-obo-sample/pom.xml | 2 +- src/samples/msal-web-sample/pom.xml | 2 +- 8 files changed, 49 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 2bf9c77a..1dde6686 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Quick links: The library supports the following Java environments: - Java 8 (or higher) -Current version - 1.6.1 +Current version - 1.6.2 You can find the changes for each version in the [change log](https://github.com/AzureAD/microsoft-authentication-library-for-java/blob/master/changelog.txt). @@ -28,13 +28,13 @@ Find [the latest package in the Maven repository](https://mvnrepository.com/arti com.microsoft.azure msal4j - 1.6.1 + 1.6.2 ``` ### Gradle ``` -compile group: 'com.microsoft.azure', name: 'msal4j', version: '1.6.1' +compile group: 'com.microsoft.azure', name: 'msal4j', version: '1.6.2' ``` ## Usage diff --git a/changelog.txt b/changelog.txt index fd6fdd46..289c6602 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,10 @@ +Version 1.6.2 +============= +- Fix for "NullPointerException during accessing B2C authority aliases" +- Adding extraScopesToConsent parameter to AuthorizationRequestUrlParameters builder. + Can be used to request the end user to consent upfront, + in addition to scopes which the application is requesting access to. + Version 1.6.1 ============= - Compatibility with json-smart [1.3.1 - 2.3] diff --git a/pom.xml b/pom.xml index e6a76d6f..53decf5b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.microsoft.azure msal4j - 1.6.1 + 1.6.2 jar msal4j @@ -55,7 +55,6 @@ 2.10.1 - org.apache.commons diff --git a/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java b/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java index 0a42217c..b0b2c69f 100644 --- a/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java +++ b/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java @@ -149,6 +149,18 @@ public void acquireTokenWithUsernamePassword_B2C_CustomAuthority() throws Except Assert.assertNotNull(result); Assert.assertNotNull(result.accessToken()); Assert.assertNotNull(result.idToken()); + + IAccount account = pca.getAccounts().join().iterator().next(); + SilentParameters.builder(Collections.singleton(TestConstants.B2C_READ_SCOPE), account); + + result = pca.acquireTokenSilently( + SilentParameters.builder(Collections.singleton(TestConstants.B2C_READ_SCOPE), account) + .build()) + .get(); + + Assert.assertNotNull(result); + Assert.assertNotNull(result.accessToken()); + Assert.assertNotNull(result.idToken()); } @Test @@ -173,5 +185,17 @@ public void acquireTokenWithUsernamePassword_B2C_LoginMicrosoftOnline() throws E Assert.assertNotNull(result); Assert.assertNotNull(result.accessToken()); Assert.assertNotNull(result.idToken()); + + IAccount account = pca.getAccounts().join().iterator().next(); + SilentParameters.builder(Collections.singleton(TestConstants.B2C_READ_SCOPE), account); + + result = pca.acquireTokenSilently( + SilentParameters.builder(Collections.singleton(TestConstants.B2C_READ_SCOPE), account) + .build()) + .get(); + + Assert.assertNotNull(result); + Assert.assertNotNull(result.accessToken()); + Assert.assertNotNull(result.idToken()); } } diff --git a/src/main/java/com/microsoft/aad/msal4j/AadInstanceDiscoveryProvider.java b/src/main/java/com/microsoft/aad/msal4j/AadInstanceDiscoveryProvider.java index 84bab38d..0286ec8a 100644 --- a/src/main/java/com/microsoft/aad/msal4j/AadInstanceDiscoveryProvider.java +++ b/src/main/java/com/microsoft/aad/msal4j/AadInstanceDiscoveryProvider.java @@ -74,7 +74,7 @@ static AadInstanceDiscoveryResponse parseInstanceDiscoveryMetadata(String instan static void cacheInstanceDiscoveryMetadata(String host, AadInstanceDiscoveryResponse aadInstanceDiscoveryResponse) { - if (aadInstanceDiscoveryResponse.metadata() != null) { + if (aadInstanceDiscoveryResponse != null && aadInstanceDiscoveryResponse.metadata() != null) { for (InstanceDiscoveryMetadataEntry entry : aadInstanceDiscoveryResponse.metadata()) { for (String alias: entry.aliases()) { cache.put(alias, entry); @@ -83,7 +83,9 @@ static void cacheInstanceDiscoveryMetadata(String host, } cache.putIfAbsent(host, InstanceDiscoveryMetadataEntry.builder(). preferredCache(host). - preferredNetwork(host).build()); + preferredNetwork(host). + aliases(Collections.singleton(host)). + build()); } private static String getAuthorizeEndpoint(String host, String tenant) { @@ -127,11 +129,14 @@ private static void doInstanceDiscoveryAndCache(URL authorityUrl, MsalRequest msalRequest, ServiceBundle serviceBundle) { - AadInstanceDiscoveryResponse aadInstanceDiscoveryResponse = - sendInstanceDiscoveryRequest(authorityUrl, msalRequest, serviceBundle); + AadInstanceDiscoveryResponse aadInstanceDiscoveryResponse = null; - if (validateAuthority) { - validate(aadInstanceDiscoveryResponse); + if(msalRequest.application().authenticationAuthority.authorityType.equals(AuthorityType.AAD)) { + aadInstanceDiscoveryResponse = sendInstanceDiscoveryRequest(authorityUrl, msalRequest, serviceBundle); + + if (validateAuthority) { + validate(aadInstanceDiscoveryResponse); + } } cacheInstanceDiscoveryMetadata(authorityUrl.getAuthority(), aadInstanceDiscoveryResponse); diff --git a/src/samples/msal-b2c-web-sample/pom.xml b/src/samples/msal-b2c-web-sample/pom.xml index b747cb15..84354b3f 100644 --- a/src/samples/msal-b2c-web-sample/pom.xml +++ b/src/samples/msal-b2c-web-sample/pom.xml @@ -23,7 +23,7 @@ com.microsoft.azure msal4j - 1.6.1 + 1.6.2 com.nimbusds diff --git a/src/samples/msal-obo-sample/pom.xml b/src/samples/msal-obo-sample/pom.xml index 7ffdeb03..964b7fa4 100644 --- a/src/samples/msal-obo-sample/pom.xml +++ b/src/samples/msal-obo-sample/pom.xml @@ -23,7 +23,7 @@ com.microsoft.azure msal4j - 1.6.1 + 1.6.2 com.nimbusds diff --git a/src/samples/msal-web-sample/pom.xml b/src/samples/msal-web-sample/pom.xml index 84cdedb9..94243108 100644 --- a/src/samples/msal-web-sample/pom.xml +++ b/src/samples/msal-web-sample/pom.xml @@ -23,7 +23,7 @@ com.microsoft.azure msal4j - 1.6.1 + 1.6.2 com.nimbusds From 00c920c8bb3990a0502b4f113612544342773201 Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Fri, 21 Aug 2020 08:44:07 -0700 Subject: [PATCH 06/21] Classes for tenant profile redesign --- .../microsoft/aad/msal4j/ITenantProfile.java | 30 +++++++++++++++ .../microsoft/aad/msal4j/TenantProfile.java | 37 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java create mode 100644 src/main/java/com/microsoft/aad/msal4j/TenantProfile.java diff --git a/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java b/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java new file mode 100644 index 00000000..eeab57fe --- /dev/null +++ b/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j; + +import java.util.Map; + +/** + * Interface representing a single tenant profile. ITenantProfiles are made available through the + * {@link IAccount#getTenantProfiles()} method of an Account + * + */ +public interface ITenantProfile { + + /** + * @return local OID + */ + String getId(); + + /** + * @return local tenant ID + */ + String getTenantId(); + + /** + * @return claims in id token + */ + Map getClaims(); + +} diff --git a/src/main/java/com/microsoft/aad/msal4j/TenantProfile.java b/src/main/java/com/microsoft/aad/msal4j/TenantProfile.java new file mode 100644 index 00000000..53d5e2a4 --- /dev/null +++ b/src/main/java/com/microsoft/aad/msal4j/TenantProfile.java @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import java.util.Map; + +/** + * Representation of a single tenant profile + */ +@Accessors(fluent = true) +@Getter +@Setter +@AllArgsConstructor +class TenantProfile implements ITenantProfile { + String objectID; + + String tenantID; + + Map idTokenClaims; + + public String getId() { + return objectID; + } + + public String getTenantId() { + return tenantID; + } + + public Map getClaims() { + return idTokenClaims; + } +} From 42a8fc776409f75800e92c37301b8ae66c70b9c7 Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Fri, 21 Aug 2020 08:45:02 -0700 Subject: [PATCH 07/21] Tests for tenant profile redesign --- .../com/microsoft/aad/msal4j/AccountTest.java | 13 ++-- .../multi-tenant-account-cache.json | 77 ++++++++++++++----- 2 files changed, 65 insertions(+), 25 deletions(-) diff --git a/src/test/java/com/microsoft/aad/msal4j/AccountTest.java b/src/test/java/com/microsoft/aad/msal4j/AccountTest.java index fd282693..a19f5018 100644 --- a/src/test/java/com/microsoft/aad/msal4j/AccountTest.java +++ b/src/test/java/com/microsoft/aad/msal4j/AccountTest.java @@ -19,7 +19,7 @@ public void testMultiTenantAccount_AccessTenantProfile() throws IOException, URI TestHelper.readResource(this.getClass(), "/cache_data/multi-tenant-account-cache.json")); - PublicClientApplication app = PublicClientApplication.builder("uid1") + PublicClientApplication app = PublicClientApplication.builder("client_id") .setTokenCacheAccessAspect(accountCache).build(); Assert.assertEquals(app.getAccounts().join().size(), 3); @@ -31,19 +31,18 @@ public void testMultiTenantAccount_AccessTenantProfile() throws IOException, URI if (curAccount.username().equals("MultiTenantAccount")) { Assert.assertEquals(curAccount.homeAccountId(), "uid1.utid1"); - Map tenantProfiles = curAccount.getTenantProfiles(); + Map tenantProfiles = curAccount.getTenantProfiles(); Assert.assertNotNull(tenantProfiles); Assert.assertEquals(tenantProfiles.size(), 2); Assert.assertNotNull(tenantProfiles.get("utid2")); - Assert.assertEquals(tenantProfiles.get("utid2").username(), "TenantProfile1"); - Assert.assertEquals(tenantProfiles.get("utid2").username(), "TenantProfile1"); + Assert.assertNotNull(tenantProfiles.get("utid2").getClaims()); Assert.assertNotNull(tenantProfiles.get("utid3")); - Assert.assertEquals(tenantProfiles.get("utid3").username(), "TenantProfile2"); + Assert.assertNotNull(tenantProfiles.get("utid3").getClaims()); } else if (curAccount.username().equals("TenantProfileNoHome") || curAccount.username().equals("SingleTenantAccount") ) { - Map tenantProfiles = curAccount.getTenantProfiles(); - Assert.assertNull(tenantProfiles); + Assert.assertNull(curAccount.getTenantProfiles()); + Assert.assertNotNull(curAccount.getClaims()); } } } diff --git a/src/test/resources/cache_data/multi-tenant-account-cache.json b/src/test/resources/cache_data/multi-tenant-account-cache.json index c1c72e76..cd4f1962 100644 --- a/src/test/resources/cache_data/multi-tenant-account-cache.json +++ b/src/test/resources/cache_data/multi-tenant-account-cache.json @@ -1,45 +1,86 @@ { "Account": { - "uid1.utid1-login.windows.net-contoso": { + "uid1.utid1-login.windows.net-utid1": { "username": "MultiTenantAccount", - "local_account_id": "utid1", - "realm": "contoso", + "local_account_id": "uid1", + "realm": "utid1", "environment": "login.windows.net", "home_account_id": "uid1.utid1", "authority_type": "MSSTS" }, - "uid1.utid2-login.windows.net-contoso": { + "uid1.utid1-login.windows.net-utid2": { "username": "TenantProfile1", - "local_account_id": "utid2", - "realm": "contoso", + "local_account_id": "uid2", + "realm": "utid2", "environment": "login.windows.net", "home_account_id": "uid1.utid1", "authority_type": "MSSTS" }, - "uid1.utid3-login.windows.net-contoso": { + "uid1.utid1-login.windows.net-utid3": { "username": "TenantProfile2", - "local_account_id": "utid3", - "realm": "contoso", + "local_account_id": "uid3", + "realm": "utid3", "environment": "login.windows.net", "home_account_id": "uid1.utid1", "authority_type": "MSSTS" }, - "uid2.utid4-login.windows.net-contoso": { + "uid5.utid4-login.windows.net-utid4": { "username": "TenantProfileNoHome", - "local_account_id": "utid4", - "realm": "contoso", + "local_account_id": "uid4", + "realm": "utid4", "environment": "login.windows.net", - "home_account_id": "uid2.utid2", + "home_account_id": "uid5.utid4", "authority_type": "MSSTS" }, - "uid3.utid5-login.windows.net-contoso": { + "uid6.utid5-login.windows.net-utid5": { "username": "SingleTenantAccount", - "local_account_id": "uid5", - "realm": "contoso", + "local_account_id": "uid6", + "realm": "utid5", "environment": "login.windows.net", - "home_account_id": "uid3.utid3", + "home_account_id": "uid6.utid5", "authority_type": "MSSTS" } }, - "AppMetadata": {} + "IdToken": { + "uid1.utid1-login.windows.net-idtoken-client_id-utid1-": { + "realm": "utid1", + "environment": "login.windows.net", + "credential_type": "IdToken", + "secret": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSJ9.eyJhdWQiOiJhNmUzN2FkYy05MTJlLTRlMjEtYmE2My1mN2FiZjIwYmExNmYiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vOTE3N2RjZmItZmE5Ny00YWJmLWI3NDQtODk5NjNmZGFiMWZjL3YyLjAiLCJpYXQiOjE1OTQ3NDg2NjEsIm5iZiI6MTU5NDc0ODY2MSwiZXhwIjoxNTk0NzUyNTYxLCJmYW1pbHlfbmFtZSI6IlRlc3QiLCJuYW1lIjoiYXZkZXZ0ZXN0Iiwib2lkIjoiNzExNjExYzEtM2VlOC00ZmI0LTgyMTYtNjA4ZWQ2ZmM1ZmM2IiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYXZkZXZ0ZXN0QGF2ZXJ5ZGV2ZWxvcG1lbnQub25taWNyb3NvZnQuY29tIiwic3ViIjoiVkw3M2l2eEh2SWNISGJVcVAyZUNkRjgtb1pTbFF0bXZUZU9hVEdxV19mNCIsInRpZCI6IjkxNzdkY2ZiLWZhOTctNGFiZi1iNzQ0LTg5OTYzZmRhYjFmYyIsInV0aSI6InRGU3ZtLVZzMFVhMGYxd0FLS0FqQVEiLCJ2ZXIiOiIyLjAifQ.rbzIYQgTsSihcN3LC6bXJqcIyjq_vMZiRxBBHCqsQky0kCsauOlBGolN9EbVa0evgAgfy9_5sTV3cuTbQ69BkPSjL2Bq3cm1oih7zC3Y-p7nHrxfHzoWaIylpHpe3s0MfEQX3lSuF_2n6jf1d_lIs8E1FqBYIyUteDFoxwDKn-gChoAFQtpo2xJz9Lm21rIjWQIKxfEYknBZinJQCU1ECZYT9cLUJBxwD7P_hoXhwM8gC7WcVMK2lhyCUq8K4ot_x4DUZJfpcb6Lrf5liUU10Zoro5qS8hANipTmtaCWUV95m2NrwlXJeH0UodcBmVkmUAkLCqmPss7Oy46R6AmG4A", + "client_id": "client_id", + "home_account_id": "uid.utid1" + }, + "uid1.utid1-login.windows.net-idtoken-client_id-utid2-": { + "realm": "utid2", + "environment": "login.windows.net", + "credential_type": "IdToken", + "secret": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSJ9.eyJhdWQiOiJhNmUzN2FkYy05MTJlLTRlMjEtYmE2My1mN2FiZjIwYmExNmYiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vOTE3N2RjZmItZmE5Ny00YWJmLWI3NDQtODk5NjNmZGFiMWZjL3YyLjAiLCJpYXQiOjE1OTQ3NDg2NjEsIm5iZiI6MTU5NDc0ODY2MSwiZXhwIjoxNTk0NzUyNTYxLCJmYW1pbHlfbmFtZSI6IlRlc3QiLCJuYW1lIjoiYXZkZXZ0ZXN0Iiwib2lkIjoiNzExNjExYzEtM2VlOC00ZmI0LTgyMTYtNjA4ZWQ2ZmM1ZmM2IiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYXZkZXZ0ZXN0QGF2ZXJ5ZGV2ZWxvcG1lbnQub25taWNyb3NvZnQuY29tIiwic3ViIjoiVkw3M2l2eEh2SWNISGJVcVAyZUNkRjgtb1pTbFF0bXZUZU9hVEdxV19mNCIsInRpZCI6IjkxNzdkY2ZiLWZhOTctNGFiZi1iNzQ0LTg5OTYzZmRhYjFmYyIsInV0aSI6InRGU3ZtLVZzMFVhMGYxd0FLS0FqQVEiLCJ2ZXIiOiIyLjAifQ.rbzIYQgTsSihcN3LC6bXJqcIyjq_vMZiRxBBHCqsQky0kCsauOlBGolN9EbVa0evgAgfy9_5sTV3cuTbQ69BkPSjL2Bq3cm1oih7zC3Y-p7nHrxfHzoWaIylpHpe3s0MfEQX3lSuF_2n6jf1d_lIs8E1FqBYIyUteDFoxwDKn-gChoAFQtpo2xJz9Lm21rIjWQIKxfEYknBZinJQCU1ECZYT9cLUJBxwD7P_hoXhwM8gC7WcVMK2lhyCUq8K4ot_x4DUZJfpcb6Lrf5liUU10Zoro5qS8hANipTmtaCWUV95m2NrwlXJeH0UodcBmVkmUAkLCqmPss7Oy46R6AmG4A", + "client_id": "client_id", + "home_account_id": "uid.utid1" + }, + "uid1.utid1-login.windows.net-idtoken-client_id-utid3-": { + "realm": "utid3", + "environment": "login.windows.net", + "credential_type": "IdToken", + "secret": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSJ9.eyJhdWQiOiJhNmUzN2FkYy05MTJlLTRlMjEtYmE2My1mN2FiZjIwYmExNmYiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vOTE3N2RjZmItZmE5Ny00YWJmLWI3NDQtODk5NjNmZGFiMWZjL3YyLjAiLCJpYXQiOjE1OTQ3NDg2NjEsIm5iZiI6MTU5NDc0ODY2MSwiZXhwIjoxNTk0NzUyNTYxLCJmYW1pbHlfbmFtZSI6IlRlc3QiLCJuYW1lIjoiYXZkZXZ0ZXN0Iiwib2lkIjoiNzExNjExYzEtM2VlOC00ZmI0LTgyMTYtNjA4ZWQ2ZmM1ZmM2IiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYXZkZXZ0ZXN0QGF2ZXJ5ZGV2ZWxvcG1lbnQub25taWNyb3NvZnQuY29tIiwic3ViIjoiVkw3M2l2eEh2SWNISGJVcVAyZUNkRjgtb1pTbFF0bXZUZU9hVEdxV19mNCIsInRpZCI6IjkxNzdkY2ZiLWZhOTctNGFiZi1iNzQ0LTg5OTYzZmRhYjFmYyIsInV0aSI6InRGU3ZtLVZzMFVhMGYxd0FLS0FqQVEiLCJ2ZXIiOiIyLjAifQ.rbzIYQgTsSihcN3LC6bXJqcIyjq_vMZiRxBBHCqsQky0kCsauOlBGolN9EbVa0evgAgfy9_5sTV3cuTbQ69BkPSjL2Bq3cm1oih7zC3Y-p7nHrxfHzoWaIylpHpe3s0MfEQX3lSuF_2n6jf1d_lIs8E1FqBYIyUteDFoxwDKn-gChoAFQtpo2xJz9Lm21rIjWQIKxfEYknBZinJQCU1ECZYT9cLUJBxwD7P_hoXhwM8gC7WcVMK2lhyCUq8K4ot_x4DUZJfpcb6Lrf5liUU10Zoro5qS8hANipTmtaCWUV95m2NrwlXJeH0UodcBmVkmUAkLCqmPss7Oy46R6AmG4A", + "client_id": "client_id", + "home_account_id": "uid.utid1" + }, + "uid5.utid4-login.windows.net-idtoken-client_id-utid4-": { + "realm": "utid4", + "environment": "login.windows.net", + "credential_type": "IdToken", + "secret": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSJ9.eyJhdWQiOiJhNmUzN2FkYy05MTJlLTRlMjEtYmE2My1mN2FiZjIwYmExNmYiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vOTE3N2RjZmItZmE5Ny00YWJmLWI3NDQtODk5NjNmZGFiMWZjL3YyLjAiLCJpYXQiOjE1OTQ3NDg2NjEsIm5iZiI6MTU5NDc0ODY2MSwiZXhwIjoxNTk0NzUyNTYxLCJmYW1pbHlfbmFtZSI6IlRlc3QiLCJuYW1lIjoiYXZkZXZ0ZXN0Iiwib2lkIjoiNzExNjExYzEtM2VlOC00ZmI0LTgyMTYtNjA4ZWQ2ZmM1ZmM2IiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYXZkZXZ0ZXN0QGF2ZXJ5ZGV2ZWxvcG1lbnQub25taWNyb3NvZnQuY29tIiwic3ViIjoiVkw3M2l2eEh2SWNISGJVcVAyZUNkRjgtb1pTbFF0bXZUZU9hVEdxV19mNCIsInRpZCI6IjkxNzdkY2ZiLWZhOTctNGFiZi1iNzQ0LTg5OTYzZmRhYjFmYyIsInV0aSI6InRGU3ZtLVZzMFVhMGYxd0FLS0FqQVEiLCJ2ZXIiOiIyLjAifQ.rbzIYQgTsSihcN3LC6bXJqcIyjq_vMZiRxBBHCqsQky0kCsauOlBGolN9EbVa0evgAgfy9_5sTV3cuTbQ69BkPSjL2Bq3cm1oih7zC3Y-p7nHrxfHzoWaIylpHpe3s0MfEQX3lSuF_2n6jf1d_lIs8E1FqBYIyUteDFoxwDKn-gChoAFQtpo2xJz9Lm21rIjWQIKxfEYknBZinJQCU1ECZYT9cLUJBxwD7P_hoXhwM8gC7WcVMK2lhyCUq8K4ot_x4DUZJfpcb6Lrf5liUU10Zoro5qS8hANipTmtaCWUV95m2NrwlXJeH0UodcBmVkmUAkLCqmPss7Oy46R6AmG4A", + "client_id": "client_id", + "home_account_id": "uid5.utid4" + }, + "uid6.utid5-login.windows.net-idtoken-client_id-utid5-": { + "realm": "utid5", + "environment": "login.windows.net", + "credential_type": "IdToken", + "secret": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSJ9.eyJhdWQiOiJhNmUzN2FkYy05MTJlLTRlMjEtYmE2My1mN2FiZjIwYmExNmYiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vOTE3N2RjZmItZmE5Ny00YWJmLWI3NDQtODk5NjNmZGFiMWZjL3YyLjAiLCJpYXQiOjE1OTQ3NDg2NjEsIm5iZiI6MTU5NDc0ODY2MSwiZXhwIjoxNTk0NzUyNTYxLCJmYW1pbHlfbmFtZSI6IlRlc3QiLCJuYW1lIjoiYXZkZXZ0ZXN0Iiwib2lkIjoiNzExNjExYzEtM2VlOC00ZmI0LTgyMTYtNjA4ZWQ2ZmM1ZmM2IiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYXZkZXZ0ZXN0QGF2ZXJ5ZGV2ZWxvcG1lbnQub25taWNyb3NvZnQuY29tIiwic3ViIjoiVkw3M2l2eEh2SWNISGJVcVAyZUNkRjgtb1pTbFF0bXZUZU9hVEdxV19mNCIsInRpZCI6IjkxNzdkY2ZiLWZhOTctNGFiZi1iNzQ0LTg5OTYzZmRhYjFmYyIsInV0aSI6InRGU3ZtLVZzMFVhMGYxd0FLS0FqQVEiLCJ2ZXIiOiIyLjAifQ.rbzIYQgTsSihcN3LC6bXJqcIyjq_vMZiRxBBHCqsQky0kCsauOlBGolN9EbVa0evgAgfy9_5sTV3cuTbQ69BkPSjL2Bq3cm1oih7zC3Y-p7nHrxfHzoWaIylpHpe3s0MfEQX3lSuF_2n6jf1d_lIs8E1FqBYIyUteDFoxwDKn-gChoAFQtpo2xJz9Lm21rIjWQIKxfEYknBZinJQCU1ECZYT9cLUJBxwD7P_hoXhwM8gC7WcVMK2lhyCUq8K4ot_x4DUZJfpcb6Lrf5liUU10Zoro5qS8hANipTmtaCWUV95m2NrwlXJeH0UodcBmVkmUAkLCqmPss7Oy46R6AmG4A", + "client_id": "client_id", + "home_account_id": "uid6.utid5" + } + } } From 7a0dfa44a5dbbf227c455dc2a91d5c53b1efd091 Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Fri, 21 Aug 2020 08:50:04 -0700 Subject: [PATCH 08/21] Adjust sample cached ID tokens to have realistic headers --- src/samples/cache/sample_cache.json | 2 +- src/test/resources/cache_data/serialized_cache.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/samples/cache/sample_cache.json b/src/samples/cache/sample_cache.json index 6334ff43..8e2d4b62 100644 --- a/src/samples/cache/sample_cache.json +++ b/src/samples/cache/sample_cache.json @@ -38,7 +38,7 @@ "realm": "contoso", "environment": "login.windows.net", "credential_type": "IdToken", - "secret": "header.eyJvaWQiOiAib2JqZWN0MTIzNCIsICJwcmVmZXJyZWRfdXNlcm5hbWUiOiAiSm9obiBEb2UiLCAic3ViIjogInN1YiJ9.signature", + "secret": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSJ9.eyJvaWQiOiAib2JqZWN0MTIzNCIsICJwcmVmZXJyZWRfdXNlcm5hbWUiOiAiSm9obiBEb2UiLCAic3ViIjogInN1YiJ9.signature", "client_id": "my_client_id", "home_account_id": "uid.utid" } diff --git a/src/test/resources/cache_data/serialized_cache.json b/src/test/resources/cache_data/serialized_cache.json index b2ad552c..978b5f9c 100644 --- a/src/test/resources/cache_data/serialized_cache.json +++ b/src/test/resources/cache_data/serialized_cache.json @@ -41,7 +41,7 @@ "realm": "contoso", "environment": "login.windows.net", "credential_type": "IdToken", - "secret": "header.eyJvaWQiOiAib2JqZWN0MTIzNCIsICJwcmVmZXJyZWRfdXNlcm5hbWUiOiAiSm9obiBEb2UiLCAic3ViIjogInN1YiJ9.signature", + "secret": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSJ9.eyJvaWQiOiAib2JqZWN0MTIzNCIsICJwcmVmZXJyZWRfdXNlcm5hbWUiOiAiSm9obiBEb2UiLCAic3ViIjogInN1YiJ9.signature", "client_id": "my_client_id", "home_account_id": "uid.utid" } From bd0cc29212a6cb76719179dc7e665c63cf82aed2 Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Fri, 21 Aug 2020 08:51:20 -0700 Subject: [PATCH 09/21] Redesign how Tenant Pofiles are added to Accounts --- .../com/microsoft/aad/msal4j/TokenCache.java | 89 +++++++++++++------ 1 file changed, 62 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/microsoft/aad/msal4j/TokenCache.java b/src/main/java/com/microsoft/aad/msal4j/TokenCache.java index 92572118..2a9fcaa9 100644 --- a/src/main/java/com/microsoft/aad/msal4j/TokenCache.java +++ b/src/main/java/com/microsoft/aad/msal4j/TokenCache.java @@ -6,8 +6,10 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.nimbusds.jwt.JWTParser; import java.io.IOException; +import java.text.ParseException; import java.util.*; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -302,57 +304,90 @@ Set getAccounts(String clientId, Set environmentAliases) { lock.readLock().lock(); Set rootAccounts = new HashSet<>(); - //Filter accounts map into two sets: a set of home accounts (local UID = home UID), - // and a set of tenant profiles (local UID != home UID) - Set homeAccounts = accounts.values().stream(). + //Filter accounts map into two sets: a set of home accounts (local OID = home OID), + // and a set of tenant profiles (local OID != home OID) + Set homeAccounts = accounts.values().stream(). filter(acc -> environmentAliases.contains(acc.environment())). filter(acc -> acc.homeAccountId().contains(acc.localAccountId())). - collect(Collectors.mapping(AccountCacheEntity::toAccount, Collectors.toSet())); - Set tenantAccounts = accounts.values().stream(). + collect(Collectors.toSet()); + Set tenantAccounts = accounts.values().stream(). filter(acc -> environmentAliases.contains(acc.environment())). filter(acc -> !acc.homeAccountId().contains(acc.localAccountId())). - collect(Collectors.mapping(AccountCacheEntity::toAccount, Collectors.toSet())); + collect(Collectors.toSet()); - Iterator homeAcctsIterator = homeAccounts.iterator(); - Iterator tenantAcctsIterator; - Map tenantProfiles; - Account homeAcc, tenantAcc; + for (AccountCacheEntity homeAccCached : homeAccounts) { + IdTokenCacheEntity homeAcctIdToken = idTokens.get(getIdTokenKey( + homeAccCached.homeAccountId(), + homeAccCached.environment(), + clientId, + homeAccCached.realm())); - while (homeAcctsIterator.hasNext()) { - homeAcc = (Account) homeAcctsIterator.next(); - tenantAcctsIterator = tenantAccounts.iterator(); - tenantProfiles = new HashMap<>(); + Account homeAcc = (Account) homeAccCached.toAccount(); + homeAcc.idTokenClaims = JWTParser.parse(homeAcctIdToken.secret).getJWTClaimsSet().getClaims(); - while (tenantAcctsIterator.hasNext()) { - tenantAcc = (Account) tenantAcctsIterator.next(); + Iterator tenantAcctsIterator = tenantAccounts.iterator(); + Map tenantProfiles = new HashMap<>(); + while (tenantAcctsIterator.hasNext()) { + AccountCacheEntity tenantAccCached = tenantAcctsIterator.next(); //If the tenant account's home ID matches a home ID (UID) in the list of home accounts, it is a //tenant profile of that home account - if (tenantAcc.homeAccountId().contains(homeAcc.homeAccountId().substring(0, homeAcc.homeAccountId().indexOf(".")))) { - tenantProfiles.put(tenantAcc.getTenantId(), tenantAcc); + if (tenantAccCached.homeAccountId.equals(homeAccCached.homeAccountId())) { + IdTokenCacheEntity token = idTokens.get(getIdTokenKey( + tenantAccCached.homeAccountId(), + tenantAccCached.environment(), + clientId, + tenantAccCached.realm())); + + ITenantProfile profile = new TenantProfile(tenantAccCached.localAccountId(), + tenantAccCached.realm(), + JWTParser.parse(token.secret()).getJWTClaimsSet().getClaims()); + tenantProfiles.put(tenantAccCached.realm(), profile); tenantAcctsIterator.remove(); } } - if (tenantProfiles.isEmpty()) { - //If the home account has no tenant profiles, leave it as an Account object - rootAccounts.add(homeAcc); - } else { - //If the home account had some tenant profiles, transform the home Account object into a - //MultiTenantAccount object and attach its tenant profile list + + if (tenantProfiles.size() > 0) { homeAcc.tenantProfiles(tenantProfiles); - rootAccounts.add(homeAcc); } + + rootAccounts.add(homeAcc); + } + //Create Accounts from any tenant profile which did not have a corresponding home account, + // and add it to the list of root accounts + for (AccountCacheEntity tenantProfileNoHome : tenantAccounts) { + IdTokenCacheEntity token = idTokens.get(getIdTokenKey( + tenantProfileNoHome.homeAccountId(), + tenantProfileNoHome.environment(), + clientId, + tenantProfileNoHome.realm())); + Account account = (Account) tenantProfileNoHome.toAccount(); + account.idTokenClaims(JWTParser.parse(token.secret).getJWTClaimsSet().getClaims()); + rootAccounts.add(account); } - //Add any tenant accounts which did not have a corresponding home account to the list of root accounts - rootAccounts.addAll(tenantAccounts); return rootAccounts; + } catch (ParseException e) { + throw new MsalClientException("Cached JWT could not be parsed: " + e.getMessage(), AuthenticationErrorCode.INVALID_JWT); } finally { lock.readLock().unlock(); } } } + /** + * Returns a String representing a key of a cached ID token, formatted in the same way as {@link IdTokenCacheEntity#getKey} + * + * @return String representing a possible key of a cached ID token + */ + private String getIdTokenKey(String homeAccountId, String environment, String clientId, String realm) { + return String.join(Constants.CACHE_KEY_SEPARATOR, + Arrays.asList(homeAccountId, + environment, + "idtoken", clientId, + realm, "")).toLowerCase(); + } + /** * @return familyId status of application */ From 925461522a80095a7be7be29d24c39fd97b56be8 Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Fri, 21 Aug 2020 08:52:00 -0700 Subject: [PATCH 10/21] New error code for JWT parse exceptions --- .../com/microsoft/aad/msal4j/AuthenticationErrorCode.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/microsoft/aad/msal4j/AuthenticationErrorCode.java b/src/main/java/com/microsoft/aad/msal4j/AuthenticationErrorCode.java index 61e7f3ac..45f82eb8 100644 --- a/src/main/java/com/microsoft/aad/msal4j/AuthenticationErrorCode.java +++ b/src/main/java/com/microsoft/aad/msal4j/AuthenticationErrorCode.java @@ -100,4 +100,9 @@ public class AuthenticationErrorCode { * A JSON processing failure, indicating the JSON provided to MSAL is of invalid format. */ public final static String INVALID_JSON = "invalid_json"; + + /** + * A JWT parsing failure, indicating the JWT provided to MSAL is of invalid format. + */ + public final static String INVALID_JWT = "invalid_jwt"; } From 1f898f658d8e3e9c7ef8374206db073043c9f7c3 Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Fri, 21 Aug 2020 08:54:38 -0700 Subject: [PATCH 11/21] Add claims and tenant profiles fields to Account --- src/main/java/com/microsoft/aad/msal4j/Account.java | 8 ++------ .../com/microsoft/aad/msal4j/AccountCacheEntity.java | 5 +---- .../com/microsoft/aad/msal4j/AuthenticationResult.java | 10 +++++++++- src/main/java/com/microsoft/aad/msal4j/IAccount.java | 8 +------- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/microsoft/aad/msal4j/Account.java b/src/main/java/com/microsoft/aad/msal4j/Account.java index 2fdb88fb..39bcab41 100644 --- a/src/main/java/com/microsoft/aad/msal4j/Account.java +++ b/src/main/java/com/microsoft/aad/msal4j/Account.java @@ -29,17 +29,13 @@ class Account implements IAccount { Map idTokenClaims; - Map tenantProfiles; - - public String getTenantId() { - return localAccountId; - } + Map tenantProfiles; public Map getClaims() { return idTokenClaims; } - public Map getTenantProfiles() { + public Map getTenantProfiles() { return tenantProfiles; } } diff --git a/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java b/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java index 167d57f4..6af25b84 100644 --- a/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java +++ b/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java @@ -50,8 +50,6 @@ ClientInfo clientInfo() { @JsonProperty("authority_type") protected String authorityType; - protected Map idTokenClaims; - String getKey() { List keyParts = new ArrayList<>(); @@ -80,7 +78,6 @@ static AccountCacheEntity create(String clientInfoStr, Authority requestAuthorit account.localAccountId(localAccountId); account.username(idToken.preferredUsername); account.name(idToken.name); - account.idTokenClaims(idToken.tokenClaims()); } return account; @@ -105,6 +102,6 @@ static AccountCacheEntity create(String clientInfoStr, Authority requestAuthorit } IAccount toAccount(){ - return new Account(homeAccountId, environment, username, localAccountId, idTokenClaims, null); + return new Account(homeAccountId, environment, username, localAccountId, null, null); } } diff --git a/src/main/java/com/microsoft/aad/msal4j/AuthenticationResult.java b/src/main/java/com/microsoft/aad/msal4j/AuthenticationResult.java index 44885c2e..84581df6 100644 --- a/src/main/java/com/microsoft/aad/msal4j/AuthenticationResult.java +++ b/src/main/java/com/microsoft/aad/msal4j/AuthenticationResult.java @@ -63,7 +63,15 @@ private IAccount getAccount() { if (accountCacheEntity == null) { return null; } - return accountCacheEntity.toAccount(); + IAccount account = accountCacheEntity.toAccount(); + try { + if (idToken != null) { + ((Account) account).idTokenClaims(JWTParser.parse(idToken).getJWTClaimsSet().getClaims()); + } + } catch (ParseException e) { + throw new MsalClientException("Cached JWT could not be parsed: " + e.getMessage(), AuthenticationErrorCode.INVALID_JWT); + } + return account; } private String environment; diff --git a/src/main/java/com/microsoft/aad/msal4j/IAccount.java b/src/main/java/com/microsoft/aad/msal4j/IAccount.java index b5be5909..58ab31a4 100644 --- a/src/main/java/com/microsoft/aad/msal4j/IAccount.java +++ b/src/main/java/com/microsoft/aad/msal4j/IAccount.java @@ -4,7 +4,6 @@ package com.microsoft.aad.msal4j; import java.util.Map; -import java.util.Set; /** * Interface representing a single user account. An IAccount is returned in the {@link IAuthenticationResult} @@ -33,13 +32,8 @@ public interface IAccount { */ Map getClaims(); - /** - * @return tenant id - */ - String getTenantId(); - /** * @return tenant profiles */ - Map getTenantProfiles(); + Map getTenantProfiles(); } From 0e496323eb90cfe08ae16f30ef172c5bbee391ef Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Fri, 21 Aug 2020 08:55:18 -0700 Subject: [PATCH 12/21] Remove annotation excluding realm field from comparisons --- src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java b/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java index 6af25b84..c4d6320c 100644 --- a/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java +++ b/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java @@ -27,7 +27,6 @@ class AccountCacheEntity implements Serializable { @JsonProperty("environment") protected String environment; - @EqualsAndHashCode.Exclude @JsonProperty("realm") protected String realm; From ed16f2491510ff1c6ba1e9e984508afeded7574b Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Fri, 21 Aug 2020 10:23:36 -0700 Subject: [PATCH 13/21] Use more generic token --- .../cache_data/multi-tenant-account-cache.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/resources/cache_data/multi-tenant-account-cache.json b/src/test/resources/cache_data/multi-tenant-account-cache.json index cd4f1962..bf711e8e 100644 --- a/src/test/resources/cache_data/multi-tenant-account-cache.json +++ b/src/test/resources/cache_data/multi-tenant-account-cache.json @@ -46,7 +46,7 @@ "realm": "utid1", "environment": "login.windows.net", "credential_type": "IdToken", - "secret": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSJ9.eyJhdWQiOiJhNmUzN2FkYy05MTJlLTRlMjEtYmE2My1mN2FiZjIwYmExNmYiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vOTE3N2RjZmItZmE5Ny00YWJmLWI3NDQtODk5NjNmZGFiMWZjL3YyLjAiLCJpYXQiOjE1OTQ3NDg2NjEsIm5iZiI6MTU5NDc0ODY2MSwiZXhwIjoxNTk0NzUyNTYxLCJmYW1pbHlfbmFtZSI6IlRlc3QiLCJuYW1lIjoiYXZkZXZ0ZXN0Iiwib2lkIjoiNzExNjExYzEtM2VlOC00ZmI0LTgyMTYtNjA4ZWQ2ZmM1ZmM2IiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYXZkZXZ0ZXN0QGF2ZXJ5ZGV2ZWxvcG1lbnQub25taWNyb3NvZnQuY29tIiwic3ViIjoiVkw3M2l2eEh2SWNISGJVcVAyZUNkRjgtb1pTbFF0bXZUZU9hVEdxV19mNCIsInRpZCI6IjkxNzdkY2ZiLWZhOTctNGFiZi1iNzQ0LTg5OTYzZmRhYjFmYyIsInV0aSI6InRGU3ZtLVZzMFVhMGYxd0FLS0FqQVEiLCJ2ZXIiOiIyLjAifQ.rbzIYQgTsSihcN3LC6bXJqcIyjq_vMZiRxBBHCqsQky0kCsauOlBGolN9EbVa0evgAgfy9_5sTV3cuTbQ69BkPSjL2Bq3cm1oih7zC3Y-p7nHrxfHzoWaIylpHpe3s0MfEQX3lSuF_2n6jf1d_lIs8E1FqBYIyUteDFoxwDKn-gChoAFQtpo2xJz9Lm21rIjWQIKxfEYknBZinJQCU1ECZYT9cLUJBxwD7P_hoXhwM8gC7WcVMK2lhyCUq8K4ot_x4DUZJfpcb6Lrf5liUU10Zoro5qS8hANipTmtaCWUV95m2NrwlXJeH0UodcBmVkmUAkLCqmPss7Oy46R6AmG4A", + "secret": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSJ9.eyJvaWQiOiAib2JqZWN0MTIzNCIsICJwcmVmZXJyZWRfdXNlcm5hbWUiOiAiSm9obiBEb2UiLCAic3ViIjogInN1YiJ9.signature", "client_id": "client_id", "home_account_id": "uid.utid1" }, @@ -54,7 +54,7 @@ "realm": "utid2", "environment": "login.windows.net", "credential_type": "IdToken", - "secret": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSJ9.eyJhdWQiOiJhNmUzN2FkYy05MTJlLTRlMjEtYmE2My1mN2FiZjIwYmExNmYiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vOTE3N2RjZmItZmE5Ny00YWJmLWI3NDQtODk5NjNmZGFiMWZjL3YyLjAiLCJpYXQiOjE1OTQ3NDg2NjEsIm5iZiI6MTU5NDc0ODY2MSwiZXhwIjoxNTk0NzUyNTYxLCJmYW1pbHlfbmFtZSI6IlRlc3QiLCJuYW1lIjoiYXZkZXZ0ZXN0Iiwib2lkIjoiNzExNjExYzEtM2VlOC00ZmI0LTgyMTYtNjA4ZWQ2ZmM1ZmM2IiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYXZkZXZ0ZXN0QGF2ZXJ5ZGV2ZWxvcG1lbnQub25taWNyb3NvZnQuY29tIiwic3ViIjoiVkw3M2l2eEh2SWNISGJVcVAyZUNkRjgtb1pTbFF0bXZUZU9hVEdxV19mNCIsInRpZCI6IjkxNzdkY2ZiLWZhOTctNGFiZi1iNzQ0LTg5OTYzZmRhYjFmYyIsInV0aSI6InRGU3ZtLVZzMFVhMGYxd0FLS0FqQVEiLCJ2ZXIiOiIyLjAifQ.rbzIYQgTsSihcN3LC6bXJqcIyjq_vMZiRxBBHCqsQky0kCsauOlBGolN9EbVa0evgAgfy9_5sTV3cuTbQ69BkPSjL2Bq3cm1oih7zC3Y-p7nHrxfHzoWaIylpHpe3s0MfEQX3lSuF_2n6jf1d_lIs8E1FqBYIyUteDFoxwDKn-gChoAFQtpo2xJz9Lm21rIjWQIKxfEYknBZinJQCU1ECZYT9cLUJBxwD7P_hoXhwM8gC7WcVMK2lhyCUq8K4ot_x4DUZJfpcb6Lrf5liUU10Zoro5qS8hANipTmtaCWUV95m2NrwlXJeH0UodcBmVkmUAkLCqmPss7Oy46R6AmG4A", + "secret": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSJ9.eyJvaWQiOiAib2JqZWN0MTIzNCIsICJwcmVmZXJyZWRfdXNlcm5hbWUiOiAiSm9obiBEb2UiLCAic3ViIjogInN1YiJ9.signature", "client_id": "client_id", "home_account_id": "uid.utid1" }, @@ -62,7 +62,7 @@ "realm": "utid3", "environment": "login.windows.net", "credential_type": "IdToken", - "secret": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSJ9.eyJhdWQiOiJhNmUzN2FkYy05MTJlLTRlMjEtYmE2My1mN2FiZjIwYmExNmYiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vOTE3N2RjZmItZmE5Ny00YWJmLWI3NDQtODk5NjNmZGFiMWZjL3YyLjAiLCJpYXQiOjE1OTQ3NDg2NjEsIm5iZiI6MTU5NDc0ODY2MSwiZXhwIjoxNTk0NzUyNTYxLCJmYW1pbHlfbmFtZSI6IlRlc3QiLCJuYW1lIjoiYXZkZXZ0ZXN0Iiwib2lkIjoiNzExNjExYzEtM2VlOC00ZmI0LTgyMTYtNjA4ZWQ2ZmM1ZmM2IiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYXZkZXZ0ZXN0QGF2ZXJ5ZGV2ZWxvcG1lbnQub25taWNyb3NvZnQuY29tIiwic3ViIjoiVkw3M2l2eEh2SWNISGJVcVAyZUNkRjgtb1pTbFF0bXZUZU9hVEdxV19mNCIsInRpZCI6IjkxNzdkY2ZiLWZhOTctNGFiZi1iNzQ0LTg5OTYzZmRhYjFmYyIsInV0aSI6InRGU3ZtLVZzMFVhMGYxd0FLS0FqQVEiLCJ2ZXIiOiIyLjAifQ.rbzIYQgTsSihcN3LC6bXJqcIyjq_vMZiRxBBHCqsQky0kCsauOlBGolN9EbVa0evgAgfy9_5sTV3cuTbQ69BkPSjL2Bq3cm1oih7zC3Y-p7nHrxfHzoWaIylpHpe3s0MfEQX3lSuF_2n6jf1d_lIs8E1FqBYIyUteDFoxwDKn-gChoAFQtpo2xJz9Lm21rIjWQIKxfEYknBZinJQCU1ECZYT9cLUJBxwD7P_hoXhwM8gC7WcVMK2lhyCUq8K4ot_x4DUZJfpcb6Lrf5liUU10Zoro5qS8hANipTmtaCWUV95m2NrwlXJeH0UodcBmVkmUAkLCqmPss7Oy46R6AmG4A", + "secret": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSJ9.eyJvaWQiOiAib2JqZWN0MTIzNCIsICJwcmVmZXJyZWRfdXNlcm5hbWUiOiAiSm9obiBEb2UiLCAic3ViIjogInN1YiJ9.signature", "client_id": "client_id", "home_account_id": "uid.utid1" }, @@ -70,7 +70,7 @@ "realm": "utid4", "environment": "login.windows.net", "credential_type": "IdToken", - "secret": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSJ9.eyJhdWQiOiJhNmUzN2FkYy05MTJlLTRlMjEtYmE2My1mN2FiZjIwYmExNmYiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vOTE3N2RjZmItZmE5Ny00YWJmLWI3NDQtODk5NjNmZGFiMWZjL3YyLjAiLCJpYXQiOjE1OTQ3NDg2NjEsIm5iZiI6MTU5NDc0ODY2MSwiZXhwIjoxNTk0NzUyNTYxLCJmYW1pbHlfbmFtZSI6IlRlc3QiLCJuYW1lIjoiYXZkZXZ0ZXN0Iiwib2lkIjoiNzExNjExYzEtM2VlOC00ZmI0LTgyMTYtNjA4ZWQ2ZmM1ZmM2IiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYXZkZXZ0ZXN0QGF2ZXJ5ZGV2ZWxvcG1lbnQub25taWNyb3NvZnQuY29tIiwic3ViIjoiVkw3M2l2eEh2SWNISGJVcVAyZUNkRjgtb1pTbFF0bXZUZU9hVEdxV19mNCIsInRpZCI6IjkxNzdkY2ZiLWZhOTctNGFiZi1iNzQ0LTg5OTYzZmRhYjFmYyIsInV0aSI6InRGU3ZtLVZzMFVhMGYxd0FLS0FqQVEiLCJ2ZXIiOiIyLjAifQ.rbzIYQgTsSihcN3LC6bXJqcIyjq_vMZiRxBBHCqsQky0kCsauOlBGolN9EbVa0evgAgfy9_5sTV3cuTbQ69BkPSjL2Bq3cm1oih7zC3Y-p7nHrxfHzoWaIylpHpe3s0MfEQX3lSuF_2n6jf1d_lIs8E1FqBYIyUteDFoxwDKn-gChoAFQtpo2xJz9Lm21rIjWQIKxfEYknBZinJQCU1ECZYT9cLUJBxwD7P_hoXhwM8gC7WcVMK2lhyCUq8K4ot_x4DUZJfpcb6Lrf5liUU10Zoro5qS8hANipTmtaCWUV95m2NrwlXJeH0UodcBmVkmUAkLCqmPss7Oy46R6AmG4A", + "secret": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSJ9.eyJvaWQiOiAib2JqZWN0MTIzNCIsICJwcmVmZXJyZWRfdXNlcm5hbWUiOiAiSm9obiBEb2UiLCAic3ViIjogInN1YiJ9.signature", "client_id": "client_id", "home_account_id": "uid5.utid4" }, @@ -78,7 +78,7 @@ "realm": "utid5", "environment": "login.windows.net", "credential_type": "IdToken", - "secret": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSJ9.eyJhdWQiOiJhNmUzN2FkYy05MTJlLTRlMjEtYmE2My1mN2FiZjIwYmExNmYiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vOTE3N2RjZmItZmE5Ny00YWJmLWI3NDQtODk5NjNmZGFiMWZjL3YyLjAiLCJpYXQiOjE1OTQ3NDg2NjEsIm5iZiI6MTU5NDc0ODY2MSwiZXhwIjoxNTk0NzUyNTYxLCJmYW1pbHlfbmFtZSI6IlRlc3QiLCJuYW1lIjoiYXZkZXZ0ZXN0Iiwib2lkIjoiNzExNjExYzEtM2VlOC00ZmI0LTgyMTYtNjA4ZWQ2ZmM1ZmM2IiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYXZkZXZ0ZXN0QGF2ZXJ5ZGV2ZWxvcG1lbnQub25taWNyb3NvZnQuY29tIiwic3ViIjoiVkw3M2l2eEh2SWNISGJVcVAyZUNkRjgtb1pTbFF0bXZUZU9hVEdxV19mNCIsInRpZCI6IjkxNzdkY2ZiLWZhOTctNGFiZi1iNzQ0LTg5OTYzZmRhYjFmYyIsInV0aSI6InRGU3ZtLVZzMFVhMGYxd0FLS0FqQVEiLCJ2ZXIiOiIyLjAifQ.rbzIYQgTsSihcN3LC6bXJqcIyjq_vMZiRxBBHCqsQky0kCsauOlBGolN9EbVa0evgAgfy9_5sTV3cuTbQ69BkPSjL2Bq3cm1oih7zC3Y-p7nHrxfHzoWaIylpHpe3s0MfEQX3lSuF_2n6jf1d_lIs8E1FqBYIyUteDFoxwDKn-gChoAFQtpo2xJz9Lm21rIjWQIKxfEYknBZinJQCU1ECZYT9cLUJBxwD7P_hoXhwM8gC7WcVMK2lhyCUq8K4ot_x4DUZJfpcb6Lrf5liUU10Zoro5qS8hANipTmtaCWUV95m2NrwlXJeH0UodcBmVkmUAkLCqmPss7Oy46R6AmG4A", + "secret": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSJ9.eyJvaWQiOiAib2JqZWN0MTIzNCIsICJwcmVmZXJyZWRfdXNlcm5hbWUiOiAiSm9obiBEb2UiLCAic3ViIjogInN1YiJ9.signature", "client_id": "client_id", "home_account_id": "uid6.utid5" } From f92983d8ed5105261a14861240395075a0eb7d99 Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Tue, 25 Aug 2020 08:54:33 -0700 Subject: [PATCH 14/21] Remove ID token claims field from Account --- src/main/java/com/microsoft/aad/msal4j/Account.java | 8 +------- .../com/microsoft/aad/msal4j/AccountCacheEntity.java | 2 +- .../com/microsoft/aad/msal4j/AuthenticationResult.java | 10 +--------- src/main/java/com/microsoft/aad/msal4j/IAccount.java | 5 ----- 4 files changed, 3 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/microsoft/aad/msal4j/Account.java b/src/main/java/com/microsoft/aad/msal4j/Account.java index 39bcab41..54562360 100644 --- a/src/main/java/com/microsoft/aad/msal4j/Account.java +++ b/src/main/java/com/microsoft/aad/msal4j/Account.java @@ -25,16 +25,10 @@ class Account implements IAccount { String username; - String localAccountId; - - Map idTokenClaims; + private String localAccountId; Map tenantProfiles; - public Map getClaims() { - return idTokenClaims; - } - public Map getTenantProfiles() { return tenantProfiles; } diff --git a/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java b/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java index c4d6320c..4039758b 100644 --- a/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java +++ b/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java @@ -101,6 +101,6 @@ static AccountCacheEntity create(String clientInfoStr, Authority requestAuthorit } IAccount toAccount(){ - return new Account(homeAccountId, environment, username, localAccountId, null, null); + return new Account(homeAccountId, environment, username, localAccountId, null); } } diff --git a/src/main/java/com/microsoft/aad/msal4j/AuthenticationResult.java b/src/main/java/com/microsoft/aad/msal4j/AuthenticationResult.java index 84581df6..44885c2e 100644 --- a/src/main/java/com/microsoft/aad/msal4j/AuthenticationResult.java +++ b/src/main/java/com/microsoft/aad/msal4j/AuthenticationResult.java @@ -63,15 +63,7 @@ private IAccount getAccount() { if (accountCacheEntity == null) { return null; } - IAccount account = accountCacheEntity.toAccount(); - try { - if (idToken != null) { - ((Account) account).idTokenClaims(JWTParser.parse(idToken).getJWTClaimsSet().getClaims()); - } - } catch (ParseException e) { - throw new MsalClientException("Cached JWT could not be parsed: " + e.getMessage(), AuthenticationErrorCode.INVALID_JWT); - } - return account; + return accountCacheEntity.toAccount(); } private String environment; diff --git a/src/main/java/com/microsoft/aad/msal4j/IAccount.java b/src/main/java/com/microsoft/aad/msal4j/IAccount.java index 58ab31a4..ed63d8e2 100644 --- a/src/main/java/com/microsoft/aad/msal4j/IAccount.java +++ b/src/main/java/com/microsoft/aad/msal4j/IAccount.java @@ -27,11 +27,6 @@ public interface IAccount { */ String username(); - /** - * @return claims in id token - */ - Map getClaims(); - /** * @return tenant profiles */ From 1f66818ce6ce000e83eac9f50dbf39ce20360ac0 Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Tue, 25 Aug 2020 08:55:16 -0700 Subject: [PATCH 15/21] Minor changes for clarity --- .../java/com/microsoft/aad/msal4j/ITenantProfile.java | 10 +++++++--- .../java/com/microsoft/aad/msal4j/TenantProfile.java | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java b/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java index eeab57fe..458b0986 100644 --- a/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java +++ b/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java @@ -13,17 +13,21 @@ public interface ITenantProfile { /** - * @return local OID + * This value corresponds to the 'oid' key of an ID token + * + * @return String local OID */ String getId(); /** - * @return local tenant ID + * This value corresponds to the 'realm' key of an ID token + * + * @return String local tenant ID */ String getTenantId(); /** - * @return claims in id token + * @return Map claims in id token */ Map getClaims(); diff --git a/src/main/java/com/microsoft/aad/msal4j/TenantProfile.java b/src/main/java/com/microsoft/aad/msal4j/TenantProfile.java index 53d5e2a4..04b1a84f 100644 --- a/src/main/java/com/microsoft/aad/msal4j/TenantProfile.java +++ b/src/main/java/com/microsoft/aad/msal4j/TenantProfile.java @@ -17,14 +17,14 @@ @Setter @AllArgsConstructor class TenantProfile implements ITenantProfile { - String objectID; + String id; String tenantID; Map idTokenClaims; public String getId() { - return objectID; + return id; } public String getTenantId() { From 36e2638a186a12e447721e1433d2c4382198e870 Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Tue, 25 Aug 2020 08:56:10 -0700 Subject: [PATCH 16/21] Adjust tests for tenant profile design refactor --- .../TokenCacheIT.java | 2 +- .../com/microsoft/aad/msal4j/AccountTest.java | 46 +++++++++++++------ 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java b/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java index ec631476..17793ff9 100644 --- a/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java +++ b/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java @@ -146,7 +146,7 @@ public void twoAccountsInCache_SameUserDifferentTenants_RemoveAccountTest() thro .get(); // There should be two tokens in cache, with same accounts except for tenant - Assert.assertEquals(pca2.getAccounts().join().size() , 2); + Assert.assertEquals(pca2.getAccounts().join().iterator().next().getTenantProfiles().size() , 2); IAccount account = pca2.getAccounts().get().iterator().next(); diff --git a/src/test/java/com/microsoft/aad/msal4j/AccountTest.java b/src/test/java/com/microsoft/aad/msal4j/AccountTest.java index a19f5018..ed063ded 100644 --- a/src/test/java/com/microsoft/aad/msal4j/AccountTest.java +++ b/src/test/java/com/microsoft/aad/msal4j/AccountTest.java @@ -29,20 +29,38 @@ public void testMultiTenantAccount_AccessTenantProfile() throws IOException, URI while (acctIterator.hasNext()) { curAccount = acctIterator.next(); - if (curAccount.username().equals("MultiTenantAccount")) { - Assert.assertEquals(curAccount.homeAccountId(), "uid1.utid1"); - Map tenantProfiles = curAccount.getTenantProfiles(); - Assert.assertNotNull(tenantProfiles); - Assert.assertEquals(tenantProfiles.size(), 2); - Assert.assertNotNull(tenantProfiles.get("utid2")); - Assert.assertNotNull(tenantProfiles.get("utid2").getClaims()); - Assert.assertNotNull(tenantProfiles.get("utid3")); - Assert.assertNotNull(tenantProfiles.get("utid3").getClaims()); - } - else if (curAccount.username().equals("TenantProfileNoHome") || - curAccount.username().equals("SingleTenantAccount") ) { - Assert.assertNull(curAccount.getTenantProfiles()); - Assert.assertNotNull(curAccount.getClaims()); + switch (curAccount.username()) { + case "MultiTenantAccount": { + Assert.assertEquals(curAccount.homeAccountId(), "uid1.utid1"); + Map tenantProfiles = curAccount.getTenantProfiles(); + Assert.assertNotNull(tenantProfiles); + Assert.assertEquals(tenantProfiles.size(), 3); + Assert.assertNotNull(tenantProfiles.get("utid1")); + Assert.assertNotNull(tenantProfiles.get("utid1").getClaims()); + Assert.assertNotNull(tenantProfiles.get("utid2")); + Assert.assertNotNull(tenantProfiles.get("utid2").getClaims()); + Assert.assertNotNull(tenantProfiles.get("utid3")); + Assert.assertNotNull(tenantProfiles.get("utid3").getClaims()); + break; + } + case "SingleTenantAccount": { + Assert.assertEquals(curAccount.homeAccountId(), "uid6.utid5"); + Map tenantProfiles = curAccount.getTenantProfiles(); + Assert.assertNotNull(tenantProfiles); + Assert.assertEquals(tenantProfiles.size(), 1); + Assert.assertNotNull(tenantProfiles.get("utid5")); + Assert.assertNotNull(tenantProfiles.get("utid5").getClaims()); + break; + } + case "TenantProfileNoHome": { + Assert.assertEquals(curAccount.homeAccountId(), "uid5.utid4"); + Map tenantProfiles = curAccount.getTenantProfiles(); + Assert.assertNotNull(tenantProfiles); + Assert.assertEquals(tenantProfiles.size(), 1); + Assert.assertNotNull(tenantProfiles.get("utid4")); + Assert.assertNotNull(tenantProfiles.get("utid4").getClaims()); + break; + } } } } From 8424a43dc6acc3c5abaf7aa40ea9524619e57608 Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Tue, 25 Aug 2020 08:57:09 -0700 Subject: [PATCH 17/21] Refactor tenant profile structure --- .../com/microsoft/aad/msal4j/TokenCache.java | 83 +++++++------------ 1 file changed, 28 insertions(+), 55 deletions(-) diff --git a/src/main/java/com/microsoft/aad/msal4j/TokenCache.java b/src/main/java/com/microsoft/aad/msal4j/TokenCache.java index 2a9fcaa9..802076d2 100644 --- a/src/main/java/com/microsoft/aad/msal4j/TokenCache.java +++ b/src/main/java/com/microsoft/aad/msal4j/TokenCache.java @@ -302,71 +302,44 @@ Set getAccounts(String clientId, Set environmentAliases) { build())) { try { lock.readLock().lock(); - Set rootAccounts = new HashSet<>(); + Map rootAccounts = new HashMap<>(); - //Filter accounts map into two sets: a set of home accounts (local OID = home OID), - // and a set of tenant profiles (local OID != home OID) - Set homeAccounts = accounts.values().stream(). + Set accountsCached = accounts.values().stream(). filter(acc -> environmentAliases.contains(acc.environment())). - filter(acc -> acc.homeAccountId().contains(acc.localAccountId())). - collect(Collectors.toSet()); - Set tenantAccounts = accounts.values().stream(). - filter(acc -> environmentAliases.contains(acc.environment())). - filter(acc -> !acc.homeAccountId().contains(acc.localAccountId())). collect(Collectors.toSet()); - for (AccountCacheEntity homeAccCached : homeAccounts) { - IdTokenCacheEntity homeAcctIdToken = idTokens.get(getIdTokenKey( - homeAccCached.homeAccountId(), - homeAccCached.environment(), + for (AccountCacheEntity accCached : accountsCached) { + + IdTokenCacheEntity idToken = idTokens.get(getIdTokenKey( + accCached.homeAccountId(), + accCached.environment(), clientId, - homeAccCached.realm())); - - Account homeAcc = (Account) homeAccCached.toAccount(); - homeAcc.idTokenClaims = JWTParser.parse(homeAcctIdToken.secret).getJWTClaimsSet().getClaims(); - - Iterator tenantAcctsIterator = tenantAccounts.iterator(); - Map tenantProfiles = new HashMap<>(); - - while (tenantAcctsIterator.hasNext()) { - AccountCacheEntity tenantAccCached = tenantAcctsIterator.next(); - //If the tenant account's home ID matches a home ID (UID) in the list of home accounts, it is a - //tenant profile of that home account - if (tenantAccCached.homeAccountId.equals(homeAccCached.homeAccountId())) { - IdTokenCacheEntity token = idTokens.get(getIdTokenKey( - tenantAccCached.homeAccountId(), - tenantAccCached.environment(), - clientId, - tenantAccCached.realm())); - - ITenantProfile profile = new TenantProfile(tenantAccCached.localAccountId(), - tenantAccCached.realm(), - JWTParser.parse(token.secret()).getJWTClaimsSet().getClaims()); - tenantProfiles.put(tenantAccCached.realm(), profile); - tenantAcctsIterator.remove(); - } - } + accCached.realm())); - if (tenantProfiles.size() > 0) { - homeAcc.tenantProfiles(tenantProfiles); + Map idTokenClaims = null; + if (idToken != null) { + idTokenClaims = JWTParser.parse(idToken.secret()).getJWTClaimsSet().getClaims(); } - rootAccounts.add(homeAcc); - } - //Create Accounts from any tenant profile which did not have a corresponding home account, - // and add it to the list of root accounts - for (AccountCacheEntity tenantProfileNoHome : tenantAccounts) { - IdTokenCacheEntity token = idTokens.get(getIdTokenKey( - tenantProfileNoHome.homeAccountId(), - tenantProfileNoHome.environment(), - clientId, - tenantProfileNoHome.realm())); - Account account = (Account) tenantProfileNoHome.toAccount(); - account.idTokenClaims(JWTParser.parse(token.secret).getJWTClaimsSet().getClaims()); - rootAccounts.add(account); + ITenantProfile profile = new TenantProfile(accCached.localAccountId(), + accCached.realm(), + idTokenClaims); + + if (rootAccounts.get(accCached.homeAccountId()) == null) { + IAccount acc = accCached.toAccount(); + ((Account) acc).tenantProfiles = new HashMap<>(); + ((Account) acc).tenantProfiles().put(accCached.realm(), profile); + + rootAccounts.put(accCached.homeAccountId(), acc); + } else { + ((Account)rootAccounts.get(accCached.homeAccountId())).tenantProfiles.put(accCached.realm(), profile); + if (accCached.homeAccountId().contains(accCached.localAccountId())) { + ((Account) rootAccounts.get(accCached.homeAccountId())).username(accCached.username()); + } + } } - return rootAccounts; + return new HashSet<>(rootAccounts.values()); } catch (ParseException e) { throw new MsalClientException("Cached JWT could not be parsed: " + e.getMessage(), AuthenticationErrorCode.INVALID_JWT); } finally { From f778f31c6438eb62cec274adeef475a22d2fc414 Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Tue, 25 Aug 2020 13:52:36 -0700 Subject: [PATCH 18/21] Minor fixes --- .../com/microsoft/aad/msal4j/Account.java | 2 -- .../microsoft/aad/msal4j/ITenantProfile.java | 4 +++- .../com/microsoft/aad/msal4j/IdToken.java | 23 ------------------- 3 files changed, 3 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/microsoft/aad/msal4j/Account.java b/src/main/java/com/microsoft/aad/msal4j/Account.java index 54562360..2c222a54 100644 --- a/src/main/java/com/microsoft/aad/msal4j/Account.java +++ b/src/main/java/com/microsoft/aad/msal4j/Account.java @@ -25,8 +25,6 @@ class Account implements IAccount { String username; - private String localAccountId; - Map tenantProfiles; public Map getTenantProfiles() { diff --git a/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java b/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java index 458b0986..ac92522b 100644 --- a/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java +++ b/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java @@ -22,11 +22,13 @@ public interface ITenantProfile { /** * This value corresponds to the 'realm' key of an ID token * - * @return String local tenant ID + * @return String tenant ID */ String getTenantId(); /** + * A map of claims taken from an ID token. Keys and values will follow the structure of a JSON Web Token + * * @return Map claims in id token */ Map getClaims(); diff --git a/src/main/java/com/microsoft/aad/msal4j/IdToken.java b/src/main/java/com/microsoft/aad/msal4j/IdToken.java index 2ac873c1..c0930530 100644 --- a/src/main/java/com/microsoft/aad/msal4j/IdToken.java +++ b/src/main/java/com/microsoft/aad/msal4j/IdToken.java @@ -61,29 +61,6 @@ class IdToken { @JsonProperty("unique_name") protected String uniqueName; - /** - * Used to attach all claims in an ID token to an account object - * - * @return map of key/value pairs of claims in ID token - */ - protected Map tokenClaims() { - Map idTokenClaims = new HashMap<>(); - idTokenClaims.put(ISSUER, this.issuer); - idTokenClaims.put(SUBJECT, this.subject); - idTokenClaims.put(AUDIENCE, this.audience); - idTokenClaims.put(EXPIRATION_TIME, this.expirationTime); - idTokenClaims.put(ISSUED_AT, this.issuedAt); - idTokenClaims.put(NOT_BEFORE, this.notBefore); - idTokenClaims.put(NAME, this.name); - idTokenClaims.put(PREFERRED_USERNAME, this.preferredUsername); - idTokenClaims.put(OBJECT_IDENTIFIER, this.objectIdentifier); - idTokenClaims.put(TENANT_IDENTIFIER, this.tenantIdentifier); - idTokenClaims.put(UPN, this.upn); - idTokenClaims.put(UNIQUE_NAME, this.uniqueName); - - return idTokenClaims; - } - static IdToken createFromJWTClaims(final JWTClaimsSet claims) throws ParseException { IdToken idToken = new IdToken(); From ae56c041a2551d247a99765cfa04726ca5cd1c11 Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Tue, 25 Aug 2020 14:11:26 -0700 Subject: [PATCH 19/21] Minor fixes --- src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java b/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java index 4039758b..44417bf7 100644 --- a/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java +++ b/src/main/java/com/microsoft/aad/msal4j/AccountCacheEntity.java @@ -101,6 +101,6 @@ static AccountCacheEntity create(String clientInfoStr, Authority requestAuthorit } IAccount toAccount(){ - return new Account(homeAccountId, environment, username, localAccountId, null); + return new Account(homeAccountId, environment, username, null); } } From 9a4b4f2d89389b6ea0205152bc16c15990848e00 Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Tue, 25 Aug 2020 14:35:43 -0700 Subject: [PATCH 20/21] Minor fixes --- src/main/java/com/microsoft/aad/msal4j/IAccount.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/microsoft/aad/msal4j/IAccount.java b/src/main/java/com/microsoft/aad/msal4j/IAccount.java index ed63d8e2..5bd6f6ef 100644 --- a/src/main/java/com/microsoft/aad/msal4j/IAccount.java +++ b/src/main/java/com/microsoft/aad/msal4j/IAccount.java @@ -28,6 +28,9 @@ public interface IAccount { String username(); /** + * Map of {@link ITenantProfile} objects related to this account. The keys of the map are tenant ID values, which + * are taken from {@link AccountCacheEntity#realm} + * * @return tenant profiles */ Map getTenantProfiles(); From d9fbf46d2828ac2fa0a2c1960f9e134600f51493 Mon Sep 17 00:00:00 2001 From: Avery-Dunn Date: Mon, 31 Aug 2020 08:18:44 -0700 Subject: [PATCH 21/21] Simplify tenant profile class --- .../java/com/microsoft/aad/msal4j/IAccount.java | 4 ++-- .../com/microsoft/aad/msal4j/ITenantProfile.java | 14 -------------- .../com/microsoft/aad/msal4j/TenantProfile.java | 11 ----------- .../java/com/microsoft/aad/msal4j/TokenCache.java | 4 +--- 4 files changed, 3 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/microsoft/aad/msal4j/IAccount.java b/src/main/java/com/microsoft/aad/msal4j/IAccount.java index 5bd6f6ef..2e87fad7 100644 --- a/src/main/java/com/microsoft/aad/msal4j/IAccount.java +++ b/src/main/java/com/microsoft/aad/msal4j/IAccount.java @@ -28,8 +28,8 @@ public interface IAccount { String username(); /** - * Map of {@link ITenantProfile} objects related to this account. The keys of the map are tenant ID values, which - * are taken from {@link AccountCacheEntity#realm} + * Map of {@link ITenantProfile} objects related to this account, the keys of the map are the tenant ID values and + * match the 'realm' key of an ID token * * @return tenant profiles */ diff --git a/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java b/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java index ac92522b..a8818be5 100644 --- a/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java +++ b/src/main/java/com/microsoft/aad/msal4j/ITenantProfile.java @@ -12,20 +12,6 @@ */ public interface ITenantProfile { - /** - * This value corresponds to the 'oid' key of an ID token - * - * @return String local OID - */ - String getId(); - - /** - * This value corresponds to the 'realm' key of an ID token - * - * @return String tenant ID - */ - String getTenantId(); - /** * A map of claims taken from an ID token. Keys and values will follow the structure of a JSON Web Token * diff --git a/src/main/java/com/microsoft/aad/msal4j/TenantProfile.java b/src/main/java/com/microsoft/aad/msal4j/TenantProfile.java index 04b1a84f..dfeab3cb 100644 --- a/src/main/java/com/microsoft/aad/msal4j/TenantProfile.java +++ b/src/main/java/com/microsoft/aad/msal4j/TenantProfile.java @@ -17,20 +17,9 @@ @Setter @AllArgsConstructor class TenantProfile implements ITenantProfile { - String id; - - String tenantID; Map idTokenClaims; - public String getId() { - return id; - } - - public String getTenantId() { - return tenantID; - } - public Map getClaims() { return idTokenClaims; } diff --git a/src/main/java/com/microsoft/aad/msal4j/TokenCache.java b/src/main/java/com/microsoft/aad/msal4j/TokenCache.java index 802076d2..d2887d64 100644 --- a/src/main/java/com/microsoft/aad/msal4j/TokenCache.java +++ b/src/main/java/com/microsoft/aad/msal4j/TokenCache.java @@ -321,9 +321,7 @@ Set getAccounts(String clientId, Set environmentAliases) { idTokenClaims = JWTParser.parse(idToken.secret()).getJWTClaimsSet().getClaims(); } - ITenantProfile profile = new TenantProfile(accCached.localAccountId(), - accCached.realm(), - idTokenClaims); + ITenantProfile profile = new TenantProfile(idTokenClaims); if (rootAccounts.get(accCached.homeAccountId()) == null) { IAccount acc = accCached.toAccount();