Handle multiple matching tokens error (#23441)

Surfaces when fetching subscriptions
This commit is contained in:
Cheena Malhotra
2023-06-27 10:48:10 -07:00
committed by GitHub
parent 92935429ca
commit fcb56da720
3 changed files with 66 additions and 43 deletions

View File

@@ -332,46 +332,52 @@ export abstract class AzureAuth implements vscode.Disposable {
// Resource endpoint must end with '/' to form a valid scope for MSAL token request.
const endpoint = resource.endpoint.endsWith('/') ? resource.endpoint : resource.endpoint + '/';
let account: AccountInfo | null = await this.getAccountFromMsalCache(accountId);
if (!account) {
Logger.error('Error: Could not fetch account when acquiring token');
throw new Error(localize('msal.accountNotFoundError', `Unable to find account info when acquiring token.`));
}
let account: AccountInfo | null;
let newScope;
if (resource.azureResourceId === azdata.AzureResource.ResourceManagement) {
newScope = [`${endpoint}user_impersonation`];
} else {
newScope = [`${endpoint}.default`];
}
// construct request
// forceRefresh needs to be set true here in order to fetch the correct token, due to this issue
// https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/3687
// Even for full tenants, access token is often received expired - force refresh is necessary when token expires.
const tokenRequest = {
account: account,
authority: `${this.loginEndpointUrl}${tenantId}`,
scopes: newScope,
forceRefresh: true
};
try {
return await this.clientApplication.acquireTokenSilent(tokenRequest);
} catch (e) {
Logger.error('Failed to acquireTokenSilent', e);
if (e instanceof AuthError && this.accountNeedsRefresh(e)) {
// build refresh token request
const tenant: Tenant = {
id: tenantId,
displayName: ''
};
return this.handleInteractionRequiredMsal(tenant, resource);
} else {
if (e.name === 'ClientAuthError') {
Logger.verbose('[ClientAuthError] Failed to silently acquire token');
}
return errorToPromptFailedResult(e);
account = await this.getAccountFromMsalCache(accountId);
if (!account) {
Logger.error('Error: Could not fetch account when acquiring token');
throw new Error(localize('msal.accountNotFoundError', `Unable to find account info when acquiring token, please remove account and add again.`));
}
if (resource.azureResourceId === azdata.AzureResource.ResourceManagement) {
newScope = [`${endpoint}user_impersonation`];
} else {
newScope = [`${endpoint}.default`];
}
// construct request
// forceRefresh needs to be set true here in order to fetch the correct token, due to this issue
// https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/3687
// Even for full tenants, access token is often received expired - force refresh is necessary when token expires.
const tokenRequest = {
account: account,
authority: `${this.loginEndpointUrl}${tenantId}`,
scopes: newScope,
forceRefresh: true
};
try {
return await this.clientApplication.acquireTokenSilent(tokenRequest);
} catch (e) {
Logger.error('Failed to acquireTokenSilent', e);
if (e instanceof AuthError && this.accountNeedsRefresh(e)) {
// build refresh token request
const tenant: Tenant = {
id: tenantId,
displayName: ''
};
return this.handleInteractionRequiredMsal(tenant, resource);
} else {
if (e.name === 'ClientAuthError') {
Logger.verbose('[ClientAuthError] Failed to silently acquire token');
}
return errorToPromptFailedResult(e);
}
}
} catch (error) {
Logger.error(`[ClientAuthError] Failed to find account: ${error}`);
return errorToPromptFailedResult(error);
}
}
@@ -904,12 +910,16 @@ export abstract class AzureAuth implements vscode.Disposable {
private async deleteAccountCacheMsal(accountKey: azdata.AccountKey): Promise<void> {
const tokenCache = this.clientApplication.getTokenCache();
let msalAccount: AccountInfo | null = await this.getAccountFromMsalCache(accountKey.accountId);
if (!msalAccount) {
Logger.error(`MSAL: Unable to find account ${accountKey.accountId} for removal`);
throw Error(`Unable to find account ${accountKey.accountId}`);
try {
let msalAccount: AccountInfo | null = await this.getAccountFromMsalCache(accountKey.accountId);
if (!msalAccount) {
Logger.error(`MSAL: Unable to find account ${accountKey.accountId} for removal`);
throw Error(`Unable to find account ${accountKey.accountId}`);
}
await tokenCache.removeAccount(msalAccount);
} catch (error) {
Logger.error(`[ClientAuthError] Failed to find account: ${error}`);
}
await tokenCache.removeAccount(msalAccount);
await this.msalCacheProvider.clearAccountFromLocalCache(accountKey.accountId);
}

View File

@@ -15,6 +15,7 @@ import { Logger } from '../../utils/Logger';
import * as nls from 'vscode-nls';
import { TenantIgnoredError } from '../../utils/TenantIgnoredError';
import { multiple_matching_tokens_error } from '../../constants';
const localize = nls.loadMessageBundle();
export class AzureResourceSubscriptionService implements IAzureResourceSubscriptionService {
@@ -52,8 +53,12 @@ export class AzureResourceSubscriptionService implements IAzureResourceSubscript
}
} catch (error) {
if (!account.isStale && !(error instanceof TenantIgnoredError)) {
const errorMsg = localize('azure.resource.tenantSubscriptionsError', "Failed to get subscriptions for account {0} (tenant '{1}'). {2}", account.displayInfo.displayName, tenantId, AzureResourceErrorMessageUtil.getErrorMessage(error));
Logger.error(`Failed to get subscriptions for account ${account.displayInfo.displayName} (tenant '${tenantId}'). ${AzureResourceErrorMessageUtil.getErrorMessage(error)}`);
const msg = AzureResourceErrorMessageUtil.getErrorMessage(error);
let errorMsg = localize('azure.resource.tenantSubscriptionsError', "Failed to get subscriptions for account {0} (tenant '{1}'). {2}", account.displayInfo.displayName, tenantId, msg);
if (msg.includes(multiple_matching_tokens_error)) {
errorMsg = errorMsg.concat(` To resolve this error, please clear token cache, and refresh account credentials.`);
}
Logger.error(`Failed to get subscriptions for account ${account.displayInfo.displayName} (tenant '${tenantId}'). ${msg}`);
errors.push(error);
void vscode.window.showWarningMessage(errorMsg);
}

View File

@@ -93,6 +93,14 @@ export const AADSTS70043 = 'AADSTS70043';
*/
export const AADSTS50173 = 'AADSTS50173';
/**
* multiple_matching_tokens error can occur in scenarios when users try to run ADS as different users, reference issue:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/5134
* Error message: multiple_matching_tokens The cache contains multiple tokens satisfying the requirements.
* Call AcquireToken again providing more requirements such as authority or account.
*/
export const multiple_matching_tokens_error = 'multiple_matching_tokens';
export enum BuiltInCommands {
SetContext = 'setContext'
}