mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Handle multiple matching tokens error (#23441)
Surfaces when fetching subscriptions
This commit is contained in:
@@ -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.
|
// Resource endpoint must end with '/' to form a valid scope for MSAL token request.
|
||||||
const endpoint = resource.endpoint.endsWith('/') ? resource.endpoint : resource.endpoint + '/';
|
const endpoint = resource.endpoint.endsWith('/') ? resource.endpoint : resource.endpoint + '/';
|
||||||
|
let account: AccountInfo | null;
|
||||||
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 newScope;
|
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 {
|
try {
|
||||||
return await this.clientApplication.acquireTokenSilent(tokenRequest);
|
account = await this.getAccountFromMsalCache(accountId);
|
||||||
} catch (e) {
|
if (!account) {
|
||||||
Logger.error('Failed to acquireTokenSilent', e);
|
Logger.error('Error: Could not fetch account when acquiring token');
|
||||||
if (e instanceof AuthError && this.accountNeedsRefresh(e)) {
|
throw new Error(localize('msal.accountNotFoundError', `Unable to find account info when acquiring token, please remove account and add again.`));
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
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> {
|
private async deleteAccountCacheMsal(accountKey: azdata.AccountKey): Promise<void> {
|
||||||
const tokenCache = this.clientApplication.getTokenCache();
|
const tokenCache = this.clientApplication.getTokenCache();
|
||||||
let msalAccount: AccountInfo | null = await this.getAccountFromMsalCache(accountKey.accountId);
|
try {
|
||||||
if (!msalAccount) {
|
let msalAccount: AccountInfo | null = await this.getAccountFromMsalCache(accountKey.accountId);
|
||||||
Logger.error(`MSAL: Unable to find account ${accountKey.accountId} for removal`);
|
if (!msalAccount) {
|
||||||
throw Error(`Unable to find account ${accountKey.accountId}`);
|
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);
|
await this.msalCacheProvider.clearAccountFromLocalCache(accountKey.accountId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { Logger } from '../../utils/Logger';
|
|||||||
|
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { TenantIgnoredError } from '../../utils/TenantIgnoredError';
|
import { TenantIgnoredError } from '../../utils/TenantIgnoredError';
|
||||||
|
import { multiple_matching_tokens_error } from '../../constants';
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
export class AzureResourceSubscriptionService implements IAzureResourceSubscriptionService {
|
export class AzureResourceSubscriptionService implements IAzureResourceSubscriptionService {
|
||||||
@@ -52,8 +53,12 @@ export class AzureResourceSubscriptionService implements IAzureResourceSubscript
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (!account.isStale && !(error instanceof TenantIgnoredError)) {
|
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));
|
const msg = AzureResourceErrorMessageUtil.getErrorMessage(error);
|
||||||
Logger.error(`Failed to get subscriptions for account ${account.displayInfo.displayName} (tenant '${tenantId}'). ${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);
|
errors.push(error);
|
||||||
void vscode.window.showWarningMessage(errorMsg);
|
void vscode.window.showWarningMessage(errorMsg);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,6 +93,14 @@ export const AADSTS70043 = 'AADSTS70043';
|
|||||||
*/
|
*/
|
||||||
export const AADSTS50173 = 'AADSTS50173';
|
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 {
|
export enum BuiltInCommands {
|
||||||
SetContext = 'setContext'
|
SetContext = 'setContext'
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user