Skip to content

Commit 738ade6

Browse files
check account loginHint before idTokenClaims setting logoutHint (#8591)
This pull request updates the logic for setting the `logoutHint` during logout in the `StandardInteractionClient`. The main improvement is that the code now prioritizes using the `loginHint` from the account object before falling back to extracting the hint from the ID Token Claims. Enhancement to logout hint resolution: * Updated the logic in `StandardInteractionClient` (`StandardInteractionClient.ts`) so that when a logout request is made without a `logoutHint`, the code first checks for a `loginHint` on the account and uses it if present; if not, it falls back to extracting the hint from the ID Token Claims. This ensures the most direct hint is used for logout and improves reliability. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 1fff290 commit 738ade6

3 files changed

Lines changed: 132 additions & 5 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "patch",
3+
"comment": "Fix logoutHint to check account loginHint before falling back to idTokenClaims [#8591](https://github.com/AzureAD/microsoft-authentication-library-for-js/pull/8591)",
4+
"packageName": "@azure/msal-browser",
5+
"email": "lalimasharda@microsoft.com",
6+
"dependentChangeType": "patch"
7+
}

lib/msal-browser/src/interaction_client/StandardInteractionClient.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,17 @@ export abstract class StandardInteractionClient extends BaseInteractionClient {
7373
* and logoutHint attribute wasn't manually set in logout request
7474
*/
7575
if (logoutRequest) {
76-
// If logoutHint isn't set and an account was passed in, try to extract logoutHint from ID Token Claims
76+
// If logoutHint isn't set and an account was passed in, try to extract logoutHint from account loginHint or ID Token Claims
7777
if (!logoutRequest.logoutHint) {
7878
if (logoutRequest.account) {
79-
const logoutHint = this.getLogoutHintFromIdTokenClaims(
80-
logoutRequest.account
81-
);
79+
const logoutHint =
80+
logoutRequest.account.loginHint ||
81+
this.getLogoutHintFromIdTokenClaims(
82+
logoutRequest.account
83+
);
8284
if (logoutHint) {
8385
this.logger.verbose(
84-
"Setting logoutHint to login_hint ID Token Claim value for the account provided",
86+
"Setting logoutHint value to loginHint of the account provided",
8587
this.correlationId
8688
);
8789
validLogoutRequest.logoutHint = logoutHint;
@@ -167,6 +169,10 @@ export abstract class StandardInteractionClient extends BaseInteractionClient {
167169
const idTokenClaims: IdTokenClaims | undefined = account.idTokenClaims;
168170
if (idTokenClaims) {
169171
if (idTokenClaims.login_hint) {
172+
this.logger.verbose(
173+
"Extracted login_hint claim from account ID Token Claims to be used as logoutHint",
174+
this.correlationId
175+
);
170176
return idTokenClaims.login_hint;
171177
} else {
172178
this.logger.verbose(

lib/msal-browser/test/interaction_client/RedirectClient.spec.ts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2595,6 +2595,120 @@ describe("RedirectClient", () => {
25952595
);
25962596
});
25972597

2598+
it("gets logouthint from account loginhint first before checking idtokenclaims", (done) => {
2599+
const logoutHint = "accountloginhint@user.com";
2600+
const testIdTokenClaims: TokenClaims = {
2601+
ver: "2.0",
2602+
iss: "https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0",
2603+
sub: "AAAAAAAAAAAAAAAAAAAAAIkzqFVrSaSaFHy782bbtaQ",
2604+
name: "Abe Lincoln",
2605+
preferred_username: "AbeLi@microsoft.com",
2606+
oid: "00000000-0000-0000-66f3-3332eca7ea81",
2607+
tid: "3338040d-6c67-4c5b-b112-36a304b66dad",
2608+
nonce: "123523",
2609+
};
2610+
2611+
const testAccountInfo: AccountInfo = {
2612+
homeAccountId: TEST_DATA_CLIENT_INFO.TEST_HOME_ACCOUNT_ID,
2613+
localAccountId: TEST_DATA_CLIENT_INFO.TEST_UID,
2614+
environment: "login.windows.net",
2615+
tenantId: testIdTokenClaims.tid || "",
2616+
username: testIdTokenClaims.preferred_username || "",
2617+
loginHint: logoutHint,
2618+
idTokenClaims: testIdTokenClaims,
2619+
};
2620+
2621+
const testAccount: AccountEntity = {
2622+
homeAccountId: testAccountInfo.homeAccountId,
2623+
localAccountId: testAccountInfo.localAccountId,
2624+
environment: testAccountInfo.environment,
2625+
realm: testAccountInfo.tenantId,
2626+
username: testAccountInfo.username,
2627+
name: testAccountInfo.name,
2628+
authorityType: "MSSTS",
2629+
clientInfo: TEST_DATA_CLIENT_INFO.TEST_CLIENT_INFO_B64ENCODED,
2630+
lastUpdatedAt: Date.now().toString(),
2631+
};
2632+
2633+
jest.spyOn(
2634+
NavigationClient.prototype,
2635+
"navigateExternal"
2636+
).mockImplementation(
2637+
(
2638+
urlNavigate: string,
2639+
options: NavigationOptions
2640+
): Promise<boolean> => {
2641+
expect(urlNavigate).toContain(
2642+
`logout_hint=${encodeURIComponent(logoutHint)}`
2643+
);
2644+
done();
2645+
return Promise.resolve(true);
2646+
}
2647+
);
2648+
browserStorage
2649+
.setAccount(testAccount, TEST_CONFIG.CORRELATION_ID, true, 0)
2650+
.then(() =>
2651+
redirectClient.logout({ account: testAccountInfo })
2652+
);
2653+
});
2654+
2655+
it("falls back to idTokenClaims login_hint when account loginHint is not available", (done) => {
2656+
const idTokenLoginHint = "idtoken@user.com";
2657+
const testIdTokenClaims: TokenClaims = {
2658+
ver: "2.0",
2659+
iss: "https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0",
2660+
sub: "AAAAAAAAAAAAAAAAAAAAAIkzqFVrSaSaFHy782bbtaQ",
2661+
name: "Abe Lincoln",
2662+
preferred_username: "AbeLi@microsoft.com",
2663+
oid: "00000000-0000-0000-66f3-3332eca7ea81",
2664+
tid: "3338040d-6c67-4c5b-b112-36a304b66dad",
2665+
nonce: "123523",
2666+
login_hint: idTokenLoginHint,
2667+
};
2668+
2669+
const testAccountInfo: AccountInfo = {
2670+
homeAccountId: TEST_DATA_CLIENT_INFO.TEST_HOME_ACCOUNT_ID,
2671+
localAccountId: TEST_DATA_CLIENT_INFO.TEST_UID,
2672+
environment: "login.windows.net",
2673+
tenantId: testIdTokenClaims.tid || "",
2674+
username: testIdTokenClaims.preferred_username || "",
2675+
idTokenClaims: testIdTokenClaims,
2676+
};
2677+
2678+
const testAccount: AccountEntity = {
2679+
homeAccountId: testAccountInfo.homeAccountId,
2680+
localAccountId: testAccountInfo.localAccountId,
2681+
environment: testAccountInfo.environment,
2682+
realm: testAccountInfo.tenantId,
2683+
username: testAccountInfo.username,
2684+
name: testAccountInfo.name,
2685+
authorityType: "MSSTS",
2686+
clientInfo: TEST_DATA_CLIENT_INFO.TEST_CLIENT_INFO_B64ENCODED,
2687+
lastUpdatedAt: Date.now().toString(),
2688+
};
2689+
2690+
jest.spyOn(
2691+
NavigationClient.prototype,
2692+
"navigateExternal"
2693+
).mockImplementation(
2694+
(
2695+
urlNavigate: string,
2696+
options: NavigationOptions
2697+
): Promise<boolean> => {
2698+
expect(urlNavigate).toContain(
2699+
`logout_hint=${encodeURIComponent(idTokenLoginHint)}`
2700+
);
2701+
done();
2702+
return Promise.resolve(true);
2703+
}
2704+
);
2705+
browserStorage
2706+
.setAccount(testAccount, TEST_CONFIG.CORRELATION_ID, true, 0)
2707+
.then(() =>
2708+
redirectClient.logout({ account: testAccountInfo })
2709+
);
2710+
});
2711+
25982712
it("logoutHint attribute takes precedence over ID Token Claims from provided account when setting logout_hint", (done) => {
25992713
const logoutHint = "test@user.com";
26002714
const loginHint = "anothertest@user.com";

0 commit comments

Comments
 (0)