Skip to content

Commit d75f1de

Browse files
[Identity] update fix multiple accounts login logic in IBC (#32134)
1 parent 87ea695 commit d75f1de

File tree

7 files changed

+511
-441
lines changed

7 files changed

+511
-441
lines changed

common/config/rush/pnpm-lock.yaml

+422-412
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
/**
5+
* @summary Authenticates using persistent token caching with Interactive Browser Credential
6+
*/
7+
import {
8+
InteractiveBrowserCredential,
9+
useIdentityPlugin,
10+
AuthenticationRecord,
11+
} from "@azure/identity";
12+
import { cachePersistencePlugin } from "@azure/identity-cache-persistence";
13+
import path from "path";
14+
import fs from "fs";
15+
import dotenv from "dotenv";
16+
17+
useIdentityPlugin(cachePersistencePlugin);
18+
19+
dotenv.config();
20+
// The app registration client ID in the Microsoft Entra tenant
21+
const clientId = "APP-REGISTRATION-CLIENT-ID";
22+
// The tenant ID in Microsoft Entra ID
23+
const tenantId = "TENANT-ID";
24+
// The email address of the user
25+
const loginHint = `EMAIL-ADDRESS`;
26+
// The file path to save the authentication record
27+
const authenticationRecordFilePath = path.resolve(__dirname, "authenticationRecord.json");
28+
29+
async function main() {
30+
try {
31+
const fileContents = await fs.promises.readFile(authenticationRecordFilePath, "utf8");
32+
const authenticationRecord = JSON.parse(fileContents);
33+
await doAuthenticate(authenticationRecord);
34+
} catch (e) {
35+
console.error(e);
36+
await doAuthenticate();
37+
}
38+
}
39+
40+
async function doAuthenticate(authenticationRecord?: AuthenticationRecord): Promise<void> {
41+
const scopes = ["Mail.ReadWrite", "Calendars.Read"];
42+
const credential = new InteractiveBrowserCredential({
43+
tenantId,
44+
clientId,
45+
tokenCachePersistenceOptions: {
46+
enabled: true,
47+
},
48+
loginHint: loginHint,
49+
authenticationRecord: authenticationRecord,
50+
});
51+
52+
if (!authenticationRecord) {
53+
console.log("No authentication record found");
54+
authenticationRecord = await credential.authenticate(scopes);
55+
try {
56+
const jsonString = JSON.stringify(authenticationRecord);
57+
await fs.promises.writeFile(authenticationRecordFilePath, jsonString);
58+
console.log("The file has been saved!");
59+
} catch (error) {
60+
console.log(error);
61+
}
62+
} else {
63+
console.log("Authenticating with authentication record");
64+
await credential.getToken(scopes);
65+
console.log("login successful");
66+
}
67+
}
68+
69+
main().catch((err) => {
70+
console.error("The sample encountered an error:", err);
71+
});

sdk/identity/identity/CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
- Fixed the logic to return authority without the scheme and tenant ID [#31540](https://github.com/Azure/azure-sdk-for-js/pull/31540)
1212
- Fixed an issue where an incorrect tenant ID was presented in multi-tenant authentication errors [#32505](https://github.com/Azure/azure-sdk-for-js/pull/32505)
1313
- `ManagedIdentityCredential` now throws an error when attempting to pass a user-assigned Managed Identity in a ServiceFabric environment instead of silently ignoring it. [#32841](https://github.com/Azure/azure-sdk-for-js/pull/32841)
14+
- Fixed the bug in silent authentication behavior to happen only in scenarios where an account is present either in the persistent cache (if tokenCachePersistence is enabled and authentication record is provided) or the in-memory cache, instead of silently picking up the first account found in token cache. [#32134](https://github.com/Azure/azure-sdk-for-js/pull/32134)
15+
- Fixed the bug in interactive authentication request to account for the correct user login prompt based on the login hint provided, in case there are multiple accounts present. [#32134](https://github.com/Azure/azure-sdk-for-js/pull/32134)
16+
- Incorporated the [fix by @azure/msal-node (v 3.2.1)](https://github.com/AzureAD/microsoft-authentication-library-for-js/pull/7469) for silent authentication to do token lookup in persistent cache.
1417

1518
### Other Changes
1619

sdk/identity/identity/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@
8686
"@azure/core-tracing": "^1.0.0",
8787
"@azure/core-util": "^1.11.0",
8888
"@azure/logger": "^1.0.0",
89-
"@azure/msal-node": "^2.15.0",
90-
"@azure/msal-browser": "^4.0.2",
89+
"@azure/msal-node": "^3.2.1",
90+
"@azure/msal-browser": "^4.2.0",
9191
"events": "^3.0.0",
9292
"jws": "^4.0.0",
9393
"open": "^10.1.0",

sdk/identity/identity/samples/v4/javascript/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@
2929
},
3030
"homepage": "https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/identity/identity",
3131
"dependencies": {
32+
"@azure/core-rest-pipeline": "^1.19.0",
3233
"@azure/identity": "^4.2.1",
33-
"dotenv": "latest",
34-
"@azure/keyvault-keys": "^4.2.0",
35-
"@azure/core-rest-pipeline": "^1.1.0"
34+
"@azure/keyvault-keys": "latest",
35+
"dotenv": "latest"
3636
}
3737
}

sdk/identity/identity/samples/v4/typescript/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@
3333
},
3434
"homepage": "https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/identity/identity",
3535
"dependencies": {
36+
"@azure/core-rest-pipeline": "^1.1.0",
3637
"@azure/identity": "^4.2.1",
37-
"dotenv": "latest",
3838
"@azure/keyvault-keys": "^4.2.0",
39-
"@azure/core-rest-pipeline": "^1.1.0"
39+
"dotenv": "latest"
4040
},
4141
"devDependencies": {
4242
"@types/node": "^18.0.0",

sdk/identity/identity/src/msal/nodeFlows/msalClient.ts

+8-22
Original file line numberDiff line numberDiff line change
@@ -418,27 +418,8 @@ export function createMsalClient(
418418
options: GetTokenOptions = {},
419419
): Promise<msal.AuthenticationResult> {
420420
if (state.cachedAccount === null) {
421-
state.logger.getToken.info(
422-
"No cached account found in local state, attempting to load it from MSAL cache.",
423-
);
424-
const cache = app.getTokenCache();
425-
const accounts = await cache.getAllAccounts();
426-
427-
if (accounts === undefined || accounts.length === 0) {
428-
throw new AuthenticationRequiredError({ scopes });
429-
}
430-
431-
if (accounts.length > 1) {
432-
state.logger
433-
.info(`More than one account was found authenticated for this Client ID and Tenant ID.
434-
However, no "authenticationRecord" has been provided for this credential,
435-
therefore we're unable to pick between these accounts.
436-
A new login attempt will be requested, to ensure the correct account is picked.
437-
To work with multiple accounts for the same Client ID and Tenant ID, please provide an "authenticationRecord" when initializing a credential to prevent this from happening.`);
438-
throw new AuthenticationRequiredError({ scopes });
439-
}
440-
441-
state.cachedAccount = accounts[0];
421+
state.logger.getToken.info("No cached account found in local state.");
422+
throw new AuthenticationRequiredError({ scopes });
442423
}
443424

444425
// Keep track and reuse the claims we received across challenges
@@ -466,7 +447,11 @@ To work with multiple accounts for the same Client ID and Tenant ID, please prov
466447
silentRequest.resourceRequestUri = options.proofOfPossessionOptions.resourceRequestUrl;
467448
}
468449
state.logger.getToken.info("Attempting to acquire token silently");
469-
return app.acquireTokenSilent(silentRequest);
450+
try {
451+
return await app.acquireTokenSilent(silentRequest);
452+
} catch (err: any) {
453+
throw handleMsalError(scopes, err, options);
454+
}
470455
}
471456

472457
/**
@@ -835,6 +820,7 @@ To work with multiple accounts for the same Client ID and Tenant ID, please prov
835820
loginHint: options?.loginHint,
836821
errorTemplate: options?.browserCustomizationOptions?.errorMessage,
837822
successTemplate: options?.browserCustomizationOptions?.successMessage,
823+
prompt: options?.loginHint ? "login" : "select_account",
838824
};
839825
}
840826

0 commit comments

Comments
 (0)