Skip to content

Commit

Permalink
Create default tenant profile without idToken (#7197)
Browse files Browse the repository at this point in the history
Account lookup doesn't work at all if the cached accounts don't have a
tenant profile, which can happen if there is no id token yet. This PR
updates account generation to build a default tenant profile when an
idToken is not present
  • Loading branch information
tnorling authored Jul 15, 2024
1 parent 0ba711a commit aa24330
Show file tree
Hide file tree
Showing 13 changed files with 224 additions and 152 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Generate tenantProfile even without idTokenClaims",
"packageName": "@azure/msal-browser",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Generate tenantProfile even without idTokenClaims",
"packageName": "@azure/msal-common",
"email": "[email protected]",
"dependentChangeType": "patch"
}
6 changes: 4 additions & 2 deletions lib/msal-browser/src/naa/mapping/NestedAppAuthAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
IdTokenEntity,
AccessTokenEntity,
TenantProfile,
buildTenantProfileFromIdTokenClaims,
buildTenantProfile,
} from "@azure/msal-common";
import { isBridgeError } from "../BridgeError";
import { BridgeStatusCode } from "../BridgeStatusCode";
Expand Down Expand Up @@ -192,8 +192,10 @@ export class NestedAppAuthAdapter {

const tenantProfiles = new Map<string, TenantProfile>();

const tenantProfile = buildTenantProfileFromIdTokenClaims(
const tenantProfile = buildTenantProfile(
homeAccountId,
localAccountId,
tenantId,
effectiveIdTokenClaims
);
tenantProfiles.set(tenantId, tenantProfile);
Expand Down
5 changes: 0 additions & 5 deletions lib/msal-browser/test/cache/TestStorageManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ import {
ValidCredentialType,
TokenKeys,
CacheHelpers,
TokenClaims,
CredentialType,
buildTenantProfileFromIdTokenClaims,
TenantProfile,
AccountInfo,
} from "@azure/msal-common";

const ACCOUNT_KEYS = "ACCOUNT_KEYS";
Expand Down
14 changes: 14 additions & 0 deletions lib/msal-browser/test/cache/TokenCache.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
BrowserAuthError,
BrowserAuthErrorCodes,
BrowserAuthErrorMessage,
PublicClientApplication,
SilentRequest,
} from "../../src";
import { base64Decode } from "../../src/encode/Base64Decode";
Expand Down Expand Up @@ -465,6 +466,19 @@ describe("TokenCache tests", () => {
options
);

// Validate account can be retrieved
const pca = new PublicClientApplication(configuration);
expect(pca.getAllAccounts()).toHaveLength(1);
expect(
pca.getAccount({
localAccountId: result.account.localAccountId,
homeAccountId: result.account.homeAccountId,
realm: result.account.tenantId,
environment: result.account.environment,
})
).toEqual(result.account);

// Validate tokens can be retrieved
expect(
browserStorage.getRefreshTokenCredential(refreshTokenKey)
).toEqual(refreshTokenEntity);
Expand Down
70 changes: 35 additions & 35 deletions lib/msal-common/apiReview/msal-common.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -671,10 +671,10 @@ export function buildClientInfoFromHomeAccountId(homeAccountId: string): ClientI
// @public (undocumented)
export function buildStaticAuthorityOptions(authOptions: Partial<AuthorityOptions>): StaticAuthorityOptions;

// Warning: (ae-missing-release-tag) "buildTenantProfileFromIdTokenClaims" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
// Warning: (ae-missing-release-tag) "buildTenantProfile" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function buildTenantProfileFromIdTokenClaims(homeAccountId: string, idTokenClaims: TokenClaims): TenantProfile;
// @public
export function buildTenantProfile(homeAccountId: string, localAccountId: string, tenantId: string, idTokenClaims?: TokenClaims): TenantProfile;

// Warning: (ae-missing-release-tag) "CacheAccountType" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
// Warning: (ae-missing-release-tag) "CacheAccountType" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
Expand Down Expand Up @@ -4208,40 +4208,40 @@ const X_MS_LIB_CAPABILITY = "x-ms-lib-capability";
// src/authority/AuthorityOptions.ts:26:5 - (ae-forgotten-export) The symbol "CloudInstanceDiscoveryResponse" needs to be exported by the entry point index.d.ts
// src/cache/CacheManager.ts:297:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:298:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:581:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1649:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1650:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1664:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1665:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:571:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1639:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1640:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1654:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1655:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1675:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1676:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1685:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1686:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1695:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1696:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1712:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1713:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1727:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1728:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1761:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1762:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1776:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1777:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1788:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1789:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1800:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1801:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1812:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1813:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1830:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1831:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1855:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1856:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1875:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1876:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1895:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1896:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1907:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1908:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1916:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1702:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1703:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1717:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1718:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1751:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1752:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1766:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1767:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1778:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1779:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1790:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1791:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1802:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1803:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1820:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1821:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1845:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1846:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1865:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1866:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1885:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1886:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1897:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1898:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/CacheManager.ts:1906:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// src/cache/utils/CacheTypes.ts:94:53 - (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag
// src/cache/utils/CacheTypes.ts:94:43 - (tsdoc-malformed-html-name) Invalid HTML element: An HTML name must be an ASCII letter followed by zero or more letters, digits, or hyphens
// src/client/AuthorizationCodeClient.ts:228:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
Expand Down
54 changes: 37 additions & 17 deletions lib/msal-common/src/account/AccountInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,26 +75,44 @@ export function tenantIdMatchesHomeTenant(
);
}

export function buildTenantProfileFromIdTokenClaims(
/**
* Build tenant profile
* @param homeAccountId - Home account identifier for this account object
* @param localAccountId - Local account identifer for this account object
* @param tenantId - Full tenant or organizational id that this account belongs to
* @param idTokenClaims - Claims from the ID token
* @returns
*/
export function buildTenantProfile(
homeAccountId: string,
idTokenClaims: TokenClaims
localAccountId: string,
tenantId: string,
idTokenClaims?: TokenClaims
): TenantProfile {
const { oid, sub, tid, name, tfp, acr } = idTokenClaims;
if (idTokenClaims) {
const { oid, sub, tid, name, tfp, acr } = idTokenClaims;

/**
* Since there is no way to determine if the authority is AAD or B2C, we exhaust all the possible claims that can serve as tenant ID with the following precedence:
* tid - TenantID claim that identifies the tenant that issued the token in AAD. Expected in all AAD ID tokens, not present in B2C ID Tokens.
* tfp - Trust Framework Policy claim that identifies the policy that was used to authenticate the user. Functions as tenant for B2C scenarios.
* acr - Authentication Context Class Reference claim used only with older B2C policies. Fallback in case tfp is not present, but likely won't be present anyway.
*/
const tenantId = tid || tfp || acr || "";
/**
* Since there is no way to determine if the authority is AAD or B2C, we exhaust all the possible claims that can serve as tenant ID with the following precedence:
* tid - TenantID claim that identifies the tenant that issued the token in AAD. Expected in all AAD ID tokens, not present in B2C ID Tokens.
* tfp - Trust Framework Policy claim that identifies the policy that was used to authenticate the user. Functions as tenant for B2C scenarios.
* acr - Authentication Context Class Reference claim used only with older B2C policies. Fallback in case tfp is not present, but likely won't be present anyway.
*/
const tenantId = tid || tfp || acr || "";

return {
tenantId: tenantId,
localAccountId: oid || sub || "",
name: name,
isHomeTenant: tenantIdMatchesHomeTenant(tenantId, homeAccountId),
};
return {
tenantId: tenantId,
localAccountId: oid || sub || "",
name: name,
isHomeTenant: tenantIdMatchesHomeTenant(tenantId, homeAccountId),
};
} else {
return {
tenantId,
localAccountId,
isHomeTenant: tenantIdMatchesHomeTenant(tenantId, homeAccountId),
};
}
}

/**
Expand Down Expand Up @@ -122,8 +140,10 @@ export function updateAccountTenantProfileData(
// Ignore isHomeTenant, loginHint, and sid which are part of tenant profile but not base account info
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { isHomeTenant, ...claimsSourcedTenantProfile } =
buildTenantProfileFromIdTokenClaims(
buildTenantProfile(
baseAccountInfo.homeAccountId,
baseAccountInfo.localAccountId,
baseAccountInfo.tenantId,
idTokenClaims
);

Expand Down
14 changes: 2 additions & 12 deletions lib/msal-common/src/cache/CacheManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,24 +303,14 @@ export abstract class CacheManager implements ICacheManager {
accountFilter?: AccountFilter
): AccountInfo[] {
return cachedAccounts.flatMap((accountEntity) => {
return this.getAccountInfoForTenantProfiles(
return this.getTenantProfilesFromAccountEntity(
accountEntity,
accountFilter?.tenantId,
accountFilter
);
});
}

private getAccountInfoForTenantProfiles(
accountEntity: AccountEntity,
accountFilter?: AccountFilter
): AccountInfo[] {
return this.getTenantProfilesFromAccountEntity(
accountEntity,
accountFilter?.tenantId,
accountFilter
);
}

private getTenantedAccountInfoByFilter(
accountInfo: AccountInfo,
tokenKeys: TokenKeys,
Expand Down
18 changes: 8 additions & 10 deletions lib/msal-common/src/cache/entities/AccountEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ClientInfo, buildClientInfo } from "../../account/ClientInfo";
import {
AccountInfo,
TenantProfile,
buildTenantProfileFromIdTokenClaims,
buildTenantProfile,
} from "../../account/AccountInfo";
import {
createClientAuthError,
Expand Down Expand Up @@ -214,15 +214,13 @@ export class AccountEntity {
if (accountDetails.tenantProfiles) {
account.tenantProfiles = accountDetails.tenantProfiles;
} else {
const tenantProfiles = [];
if (accountDetails.idTokenClaims) {
const tenantProfile = buildTenantProfileFromIdTokenClaims(
accountDetails.homeAccountId,
accountDetails.idTokenClaims
);
tenantProfiles.push(tenantProfile);
}
account.tenantProfiles = tenantProfiles;
const tenantProfile = buildTenantProfile(
accountDetails.homeAccountId,
account.localAccountId,
account.realm,
accountDetails.idTokenClaims
);
account.tenantProfiles = [tenantProfile];
}

return account;
Expand Down
2 changes: 1 addition & 1 deletion lib/msal-common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export {
TenantProfile,
updateAccountTenantProfileData,
tenantIdMatchesHomeTenant,
buildTenantProfileFromIdTokenClaims,
buildTenantProfile,
} from "./account/AccountInfo";
export { AuthToken };
export {
Expand Down
Loading

0 comments on commit aa24330

Please sign in to comment.