Improvements in access token flows (#17670)
This commit is contained in:
Родитель
e033fbfc00
Коммит
e7a978d143
|
@ -134,6 +134,9 @@
|
|||
<trans-unit id="azureConsentDialogBody">
|
||||
<source xml:lang="en">Your tenant '{0} ({1})' requires you to re-authenticate again to access {2} resources. Press Open to start the authentication process.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="azureConsentDialogBodyAccount">
|
||||
<source xml:lang="en">Your account needs re-authentication to access {0} resources. Press Open to start the authentication process.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="azureMicrosoftCorpAccount">
|
||||
<source xml:lang="en">Microsoft Corp</source>
|
||||
</trans-unit>
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ITenant } from '../models/contracts/azure';
|
||||
|
||||
export const serviceName = 'Code';
|
||||
|
||||
export const httpConfigSectionName = 'http';
|
||||
|
@ -47,6 +49,41 @@ export const azureTenantConfigSection = azureSection + '.' + tenantSection + '.'
|
|||
|
||||
export const oldMsalCacheFileName = 'azureTokenCacheMsal-azure_publicCloud';
|
||||
|
||||
/////// MSAL ERROR CODES, ref: https://learn.microsoft.com/en-us/azure/active-directory/develop/reference-aadsts-error-codes
|
||||
/**
|
||||
* The refresh token has expired or is invalid due to sign-in frequency checks by conditional access.
|
||||
* The token was issued on {issueDate} and the maximum allowed lifetime for this request is {time}.
|
||||
*/
|
||||
export const AADSTS70043 = 'AADSTS70043';
|
||||
/**
|
||||
* FreshTokenNeeded - The provided grant has expired due to it being revoked, and a fresh auth token is needed.
|
||||
* Either an admin or a user revoked the tokens for this user, causing subsequent token refreshes to fail and
|
||||
* require reauthentication. Have the user sign in again.
|
||||
*/
|
||||
export const AADSTS50173 = 'AADSTS50173';
|
||||
/**
|
||||
* User account 'user@domain.com' from identity provider {IdentityProviderURL} does not exist in tenant {ResourceTenantName}.
|
||||
* This error occurs when account is authenticated without a tenant id, which happens when tenant Id is not available in connection profile.
|
||||
* We have the user sign in again when this error occurs.
|
||||
*/
|
||||
export const AADSTS50020 = 'AADSTS50020';
|
||||
/**
|
||||
* Error thrown from STS - indicates user account not found in MSAL cache.
|
||||
* We request user to sign in again.
|
||||
*/
|
||||
export const mdsUserAccountNotFound = `User account '{0}' not found in MSAL cache, please add linked account or refresh account credentials.`;
|
||||
/**
|
||||
* Error thrown from STS - indicates user account info not received from connection profile.
|
||||
* This is possible when account info is not available when populating user's preferred name in connection profile.
|
||||
* We request user to sign in again, to refresh their account credentials.
|
||||
*/
|
||||
export const mdsUserAccountNotReceived = 'User account not received.';
|
||||
/**
|
||||
* This error is thrown by MSAL when user account is not received in silent authentication request.
|
||||
* Thrown by TS layer, indicates user account hint not provided. We request user to reauthenticate when this error occurs.
|
||||
*/
|
||||
export const noAccountInSilentRequestError = 'no_account_in_silent_request';
|
||||
|
||||
/** MSAL Account version */
|
||||
export const accountVersion = '2.0';
|
||||
|
||||
|
@ -59,6 +96,15 @@ export const s256CodeChallengeMethod = 'S256';
|
|||
|
||||
export const selectAccount = 'select_account';
|
||||
|
||||
export const commonTenant: ITenant = {
|
||||
id: 'common',
|
||||
displayName: 'common'
|
||||
};
|
||||
|
||||
export const organizationTenant: ITenant = {
|
||||
id: 'organizations',
|
||||
displayName: 'organizations'
|
||||
};
|
||||
/**
|
||||
* Account issuer as received from access token
|
||||
*/
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Resource } from '@azure/arm-resources';
|
||||
import { AccountInfo, AuthenticationResult, InteractionRequiredAuthError, PublicClientApplication, SilentFlowRequest } from '@azure/msal-node';
|
||||
import { AccountInfo, AuthError, AuthenticationResult, InteractionRequiredAuthError, PublicClientApplication, SilentFlowRequest } from '@azure/msal-node';
|
||||
import * as url from 'url';
|
||||
import * as vscode from 'vscode';
|
||||
import * as LocalizedConstants from '../../constants/localizedConstants';
|
||||
|
@ -21,8 +21,6 @@ import { HttpClient } from './httpClient';
|
|||
// tslint:disable:no-null-keyword
|
||||
export abstract class MsalAzureAuth {
|
||||
protected readonly loginEndpointUrl: string;
|
||||
public readonly commonTenant: ITenant;
|
||||
public readonly organizationTenant: ITenant;
|
||||
protected readonly redirectUri: string;
|
||||
protected readonly scopes: string[];
|
||||
protected readonly scopesString: string;
|
||||
|
@ -39,14 +37,6 @@ export abstract class MsalAzureAuth {
|
|||
protected readonly logger: Logger
|
||||
) {
|
||||
this.loginEndpointUrl = this.providerSettings.loginEndpoint ?? 'https://login.microsoftonline.com/';
|
||||
this.commonTenant = {
|
||||
id: 'common',
|
||||
displayName: 'common'
|
||||
};
|
||||
this.organizationTenant = {
|
||||
id: 'organizations',
|
||||
displayName: 'organizations'
|
||||
};
|
||||
// Use localhost for MSAL instead of this.providerSettings.redirectUri (kept as-is for ADAL only);
|
||||
this.redirectUri = 'http://localhost';
|
||||
this.clientId = this.providerSettings.clientId;
|
||||
|
@ -62,7 +52,7 @@ export abstract class MsalAzureAuth {
|
|||
if (!this.providerSettings.resources.windowsManagementResource) {
|
||||
throw new Error(Utils.formatString(LocalizedConstants.azureNoMicrosoftResource, this.providerSettings.displayName));
|
||||
}
|
||||
const result = await this.login(this.organizationTenant);
|
||||
const result = await this.login(Constants.organizationTenant);
|
||||
loginComplete = result.authComplete;
|
||||
if (!result?.response || !result.response?.account) {
|
||||
this.logger.error(`Authentication failed: ${loginComplete}`);
|
||||
|
@ -150,7 +140,7 @@ export abstract class MsalAzureAuth {
|
|||
return await this.clientApplication.acquireTokenSilent(tokenRequest);
|
||||
} catch (e) {
|
||||
this.logger.error('Failed to acquireTokenSilent', e);
|
||||
if (e instanceof InteractionRequiredAuthError) {
|
||||
if (e instanceof AuthError && this.accountNeedsRefresh(e)) {
|
||||
// build refresh token request
|
||||
const tenant: ITenant = {
|
||||
id: tenantId,
|
||||
|
@ -158,13 +148,26 @@ export abstract class MsalAzureAuth {
|
|||
};
|
||||
return this.handleInteractionRequired(tenant, settings);
|
||||
} else if (e.name === 'ClientAuthError') {
|
||||
this.logger.error(e.message);
|
||||
this.logger.verbose('[ClientAuthError] Failed to silently acquire token');
|
||||
}
|
||||
|
||||
this.logger.error(`Failed to silently acquire token, not InteractionRequiredAuthError: ${e.message}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the account needs to be refreshed based on received error instance
|
||||
* and STS error codes from errorMessage.
|
||||
* @param error AuthError instance
|
||||
*/
|
||||
private accountNeedsRefresh(error: AuthError): boolean {
|
||||
return error instanceof InteractionRequiredAuthError
|
||||
|| error.errorMessage.includes(Constants.AADSTS70043)
|
||||
|| error.errorMessage.includes(Constants.AADSTS50020)
|
||||
|| error.errorMessage.includes(Constants.AADSTS50173);
|
||||
}
|
||||
|
||||
public async refreshAccessToken(account: IAccount, tenantId: string, settings: IAADResource): Promise<IAccount | undefined> {
|
||||
if (account) {
|
||||
try {
|
||||
|
@ -329,7 +332,9 @@ export abstract class MsalAzureAuth {
|
|||
}
|
||||
|
||||
};
|
||||
const messageBody = Utils.formatString(LocalizedConstants.azureConsentDialogBody, tenant.displayName, tenant.id, settings.id);
|
||||
const messageBody = (tenant.id === Constants.organizationTenant.id)
|
||||
? Utils.formatString(LocalizedConstants.azureConsentDialogBody, tenant.displayName, tenant.id, settings.id)
|
||||
: Utils.formatString(LocalizedConstants.azureConsentDialogBodyAccount, settings.id);
|
||||
const result = await vscode.window.showInformationMessage(messageBody, { modal: true }, openItem, closeItem, dontAskAgainItem);
|
||||
|
||||
if (result?.action) {
|
||||
|
@ -362,7 +367,7 @@ export abstract class MsalAzureAuth {
|
|||
const name = tokenClaims.name ?? tokenClaims.preferred_username ?? tokenClaims.email ?? tokenClaims.unique_name;
|
||||
const email = tokenClaims.preferred_username ?? tokenClaims.email ?? tokenClaims.unique_name;
|
||||
|
||||
let owningTenant: ITenant = this.commonTenant; // default to common tenant
|
||||
let owningTenant: ITenant = Constants.commonTenant; // default to common tenant
|
||||
|
||||
// Read more about tid > https://learn.microsoft.com/azure/active-directory/develop/id-tokens
|
||||
if (tokenClaims.tid) {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ILoggerCallback, LogLevel as MsalLogLevel } from '@azure/msal-common';
|
||||
import { ClientAuthError, ILoggerCallback, LogLevel as MsalLogLevel } from '@azure/msal-common';
|
||||
import { Configuration, PublicClientApplication } from '@azure/msal-node';
|
||||
import * as Constants from '../../constants/constants';
|
||||
import * as LocalizedConstants from '../../constants/localizedConstants';
|
||||
|
@ -19,7 +19,7 @@ import { MsalAzureDeviceCode } from './msalAzureDeviceCode';
|
|||
import { MsalCachePluginProvider } from './msalCachePlugin';
|
||||
import { promises as fsPromises } from 'fs';
|
||||
import * as path from 'path';
|
||||
import { oldMsalCacheFileName } from '../constants';
|
||||
import * as AzureConstants from '../constants';
|
||||
|
||||
export class MsalAzureController extends AzureController {
|
||||
private _authMappings = new Map<AzureAuthType, MsalAzureAuth>();
|
||||
|
@ -69,7 +69,7 @@ export class MsalAzureController extends AzureController {
|
|||
* Clears old cache file that is no longer needed on system.
|
||||
*/
|
||||
private async clearOldCacheIfExists(): Promise<void> {
|
||||
let filePath = path.join(await this.findOrMakeStoragePath(), oldMsalCacheFileName);
|
||||
let filePath = path.join(await this.findOrMakeStoragePath(), AzureConstants.oldMsalCacheFileName);
|
||||
try {
|
||||
await fsPromises.access(filePath);
|
||||
await fsPromises.rm(filePath);
|
||||
|
@ -134,9 +134,10 @@ export class MsalAzureController extends AzureController {
|
|||
|
||||
public async refreshAccessToken(account: IAccount, accountStore: AccountStore, tenantId: string | undefined,
|
||||
settings: IAADResource): Promise<IToken | undefined> {
|
||||
let newAccount: IAccount;
|
||||
try {
|
||||
let azureAuth = await this.getAzureAuthInstance(getAzureActiveDirectoryConfig());
|
||||
let newAccount = await azureAuth!.refreshAccessToken(account, 'organizations',
|
||||
newAccount = await azureAuth!.refreshAccessToken(account, AzureConstants.organizationTenant.id,
|
||||
this._providerSettings.resources.windowsManagementResource);
|
||||
|
||||
if (newAccount!.isStale === true) {
|
||||
|
@ -145,10 +146,26 @@ export class MsalAzureController extends AzureController {
|
|||
|
||||
await accountStore.addAccount(newAccount!);
|
||||
return await this.getAccountSecurityToken(
|
||||
account, tenantId!, settings
|
||||
account, tenantId ?? account.properties.owningTenant.id, settings
|
||||
);
|
||||
} catch (ex) {
|
||||
this._vscodeWrapper.showErrorMessage(ex);
|
||||
if (ex instanceof ClientAuthError && ex.errorCode === AzureConstants.noAccountInSilentRequestError) {
|
||||
try {
|
||||
// Account needs re-authentication
|
||||
newAccount = await this.login(account.properties.azureAuthType);
|
||||
if (newAccount!.isStale === true) {
|
||||
return undefined;
|
||||
}
|
||||
await accountStore.addAccount(newAccount!);
|
||||
return await this.getAccountSecurityToken(
|
||||
account, tenantId ?? account.properties.owningTenant.id, settings
|
||||
);
|
||||
} catch (ex) {
|
||||
this._vscodeWrapper.showErrorMessage(ex);
|
||||
}
|
||||
} else {
|
||||
this._vscodeWrapper.showErrorMessage(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -122,8 +122,7 @@ export class MsalCachePluginProvider {
|
|||
retryAttempt++;
|
||||
this._logger.verbose(`MsalCachePlugin: Failed to acquire lock on cache file. Retrying in ${retryWait} ms.`);
|
||||
|
||||
// tslint:disable:no-empty
|
||||
setTimeout(() => { }, retryWait);
|
||||
await new Promise(resolve => setTimeout(() => resolve, retryWait));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -799,12 +799,12 @@ export default class ConnectionManager {
|
|||
} else {
|
||||
throw new Error(LocalizedConstants.cannotConnect);
|
||||
}
|
||||
// Always set username
|
||||
connectionCreds.user = account.displayInfo.displayName;
|
||||
connectionCreds.email = account.displayInfo.email;
|
||||
profile.user = account.displayInfo.displayName;
|
||||
profile.email = account.displayInfo.email;
|
||||
if (!this.azureController.isSqlAuthProviderEnabled()) {
|
||||
if (account) {
|
||||
// Always set username
|
||||
connectionCreds.user = account.displayInfo.displayName;
|
||||
connectionCreds.email = account.displayInfo.email;
|
||||
profile.user = account.displayInfo.displayName;
|
||||
profile.email = account.displayInfo.email;
|
||||
let azureAccountToken = await this.azureController.refreshAccessToken(account!,
|
||||
this.accountStore, profile.tenantId, providerSettings.resources.databaseResource!);
|
||||
if (!azureAccountToken) {
|
||||
|
@ -821,6 +821,8 @@ export default class ConnectionManager {
|
|||
connectionCreds.azureAccountToken = azureAccountToken.token;
|
||||
connectionCreds.expiresOn = azureAccountToken.expiresOn;
|
||||
}
|
||||
} else {
|
||||
throw new Error(LocalizedConstants.msgAccountNotFound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -231,6 +231,10 @@ export function isSameProfile(currentProfile: IConnectionProfile, expectedProfil
|
|||
} else if (currentProfile.connectionString || expectedProfile.connectionString) {
|
||||
// If either profile uses connection strings, compare them directly
|
||||
return currentProfile.connectionString === expectedProfile.connectionString;
|
||||
} else if (currentProfile.authenticationType === Constants.azureMfa && expectedProfile.authenticationType === Constants.azureMfa) {
|
||||
return expectedProfile.server === currentProfile.server
|
||||
&& isSameDatabase(expectedProfile.database, currentProfile.database)
|
||||
&& isSameAccountKey(expectedProfile.accountId, currentProfile.accountId);
|
||||
}
|
||||
return expectedProfile.server === currentProfile.server
|
||||
&& isSameDatabase(expectedProfile.database, currentProfile.database)
|
||||
|
@ -249,14 +253,20 @@ export function isSameProfile(currentProfile: IConnectionProfile, expectedProfil
|
|||
*/
|
||||
export function isSameConnection(conn: IConnectionInfo, expectedConn: IConnectionInfo): boolean {
|
||||
return (conn.connectionString || expectedConn.connectionString) ? conn.connectionString === expectedConn.connectionString :
|
||||
expectedConn.server === conn.server
|
||||
&& isSameDatabase(expectedConn.database, conn.database)
|
||||
&& isSameAuthenticationType(expectedConn.authenticationType, conn.authenticationType)
|
||||
&& (conn.authenticationType === Constants.sqlAuthentication ?
|
||||
conn.user === expectedConn.user :
|
||||
isEmpty(conn.user) === isEmpty(expectedConn.user))
|
||||
&& (<IConnectionProfile>conn).savePassword ===
|
||||
(<IConnectionProfile>expectedConn).savePassword;
|
||||
// Azure MFA connections
|
||||
((expectedConn.authenticationType === Constants.azureMfa && conn.authenticationType === Constants.azureMfa)
|
||||
? expectedConn.server === conn.server
|
||||
&& isSameDatabase(expectedConn.database, conn.database)
|
||||
&& isSameAccountKey(expectedConn.accountId, conn.accountId)
|
||||
// Not Azure MFA connections
|
||||
: expectedConn.server === conn.server
|
||||
&& isSameDatabase(expectedConn.database, conn.database)
|
||||
&& isSameAuthenticationType(expectedConn.authenticationType, conn.authenticationType)
|
||||
&& (conn.authenticationType === Constants.sqlAuthentication ?
|
||||
conn.user === expectedConn.user :
|
||||
isEmpty(conn.user) === isEmpty(expectedConn.user))
|
||||
&& (<IConnectionProfile>conn).savePassword ===
|
||||
(<IConnectionProfile>expectedConn).savePassword);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,6 +26,8 @@ import { ConnectionCredentials } from '../models/connectionCredentials';
|
|||
import { ConnectionProfile } from '../models/connectionProfile';
|
||||
import providerSettings from '../azure/providerSettings';
|
||||
import { IConnectionInfo } from 'vscode-mssql';
|
||||
import { IAccount } from '../models/contracts/azure';
|
||||
import * as AzureConstants from '../azure/constants';
|
||||
|
||||
function getParentNode(node: TreeNodeType): TreeNodeInfo {
|
||||
node = node.parentNode;
|
||||
|
@ -133,18 +135,16 @@ export class ObjectExplorerService {
|
|||
if (errorNumber === Constants.errorSSLCertificateValidationFailed) {
|
||||
self._connectionManager.showInstructionTextAsWarning(self._currentNode.connectionInfo,
|
||||
async updatedProfile => {
|
||||
self.currentNode.connectionInfo = updatedProfile;
|
||||
self.updateNode(self._currentNode);
|
||||
let fileUri = ObjectExplorerUtils.getNodeUri(self._currentNode);
|
||||
if (await self._connectionManager.connectionStore.saveProfile(updatedProfile as IConnectionProfile)) {
|
||||
const res = await self._connectionManager.connect(fileUri, updatedProfile);
|
||||
if (await self._connectionManager.handleConnectionResult(res, fileUri, updatedProfile)) {
|
||||
self.refreshNode(self._currentNode);
|
||||
}
|
||||
} else {
|
||||
self._connectionManager.vscodeWrapper.showErrorMessage(LocalizedConstants.msgPromptProfileUpdateFailed);
|
||||
}
|
||||
self.reconnectProfile(self._currentNode, updatedProfile);
|
||||
});
|
||||
} else if (self._currentNode.connectionInfo.authenticationType === Constants.azureMfa
|
||||
&& self.needsAccountRefresh(result, self._currentNode.connectionInfo.user)) {
|
||||
let profile = self._currentNode.connectionInfo;
|
||||
let account = this._connectionManager.accountStore.getAccount(profile.accountId);
|
||||
await this.refreshAccount(account, profile);
|
||||
// Do not await when performing reconnect to allow
|
||||
// OE node to expand after connection is established.
|
||||
this.reconnectProfile(self._currentNode, profile);
|
||||
} else {
|
||||
self._connectionManager.vscodeWrapper.showErrorMessage(error);
|
||||
}
|
||||
|
@ -169,6 +169,29 @@ export class ObjectExplorerService {
|
|||
return handler;
|
||||
}
|
||||
|
||||
private async reconnectProfile(node: TreeNodeInfo, profile: IConnectionInfo): Promise<void> {
|
||||
node.connectionInfo = profile;
|
||||
this.updateNode(node);
|
||||
let fileUri = ObjectExplorerUtils.getNodeUri(node);
|
||||
if (await this._connectionManager.connectionStore.saveProfile(profile as IConnectionProfile)) {
|
||||
const res = await this._connectionManager.connect(fileUri, profile);
|
||||
if (await this._connectionManager.handleConnectionResult(res, fileUri, profile)) {
|
||||
this.refreshNode(node);
|
||||
}
|
||||
} else {
|
||||
this._connectionManager.vscodeWrapper.showErrorMessage(LocalizedConstants.msgPromptProfileUpdateFailed);
|
||||
}
|
||||
}
|
||||
|
||||
private needsAccountRefresh(result: SessionCreatedParameters, username: string): boolean {
|
||||
let email = username?.includes(' - ') ? username.substring(username.indexOf('-') + 2) : username;
|
||||
return result.errorMessage.includes(AzureConstants.AADSTS70043)
|
||||
|| result.errorMessage.includes(AzureConstants.AADSTS50173)
|
||||
|| result.errorMessage.includes(AzureConstants.AADSTS50020)
|
||||
|| result.errorMessage.includes(AzureConstants.mdsUserAccountNotReceived)
|
||||
|| result.errorMessage.includes(Utils.formatString(AzureConstants.mdsUserAccountNotFound, email));
|
||||
}
|
||||
|
||||
private getParentFromExpandParams(params: ExpandParams): TreeNodeInfo | undefined {
|
||||
for (let key of this._expandParamsToTreeNodeInfoMap.keys()) {
|
||||
if (key.sessionId === params.sessionId &&
|
||||
|
@ -458,38 +481,20 @@ export class ObjectExplorerService {
|
|||
} else if (connectionCredentials.authenticationType === Constants.azureMfa) {
|
||||
let azureController = this._connectionManager.azureController;
|
||||
let account = this._connectionManager.accountStore.getAccount(connectionCredentials.accountId);
|
||||
let profile = new ConnectionProfile(connectionCredentials);
|
||||
let needsRefresh: boolean = false;
|
||||
if (azureController.isSqlAuthProviderEnabled()) {
|
||||
this._client.logger.verbose('SQL Authentication provider is enabled for Azure MFA connections, skipping token acquiry in extension.');
|
||||
if (!account) {
|
||||
needsRefresh = true;
|
||||
} else if (azureController.isSqlAuthProviderEnabled()) {
|
||||
connectionCredentials.user = account.displayInfo.displayName;
|
||||
connectionCredentials.email = account.displayInfo.email;
|
||||
// Update profile after updating user/email
|
||||
await this._connectionManager.connectionUI.saveProfile(connectionCredentials as IConnectionProfile);
|
||||
if (!azureController.isAccountInCache(account)) {
|
||||
needsRefresh = true;
|
||||
}
|
||||
}
|
||||
if (!connectionCredentials.azureAccountToken && (!azureController.isSqlAuthProviderEnabled() || needsRefresh)) {
|
||||
let azureAccountToken = await azureController.refreshAccessToken(
|
||||
account, this._connectionManager.accountStore, connectionCredentials.tenantId, providerSettings.resources.databaseResource);
|
||||
if (!azureAccountToken) {
|
||||
this._client.logger.verbose('Access token could not be refreshed for connection profile.');
|
||||
let errorMessage = LocalizedConstants.msgAccountRefreshFailed;
|
||||
await this._connectionManager.vscodeWrapper.showErrorMessage(
|
||||
errorMessage, LocalizedConstants.refreshTokenLabel).then(async result => {
|
||||
if (result === LocalizedConstants.refreshTokenLabel) {
|
||||
let updatedProfile = await azureController.populateAccountProperties(
|
||||
profile, this._connectionManager.accountStore, providerSettings.resources.databaseResource);
|
||||
connectionCredentials.azureAccountToken = updatedProfile.azureAccountToken;
|
||||
connectionCredentials.expiresOn = updatedProfile.expiresOn;
|
||||
} else {
|
||||
this._client.logger.error('Credentials not refreshed by user.');
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
connectionCredentials.azureAccountToken = azureAccountToken.token;
|
||||
connectionCredentials.expiresOn = azureAccountToken.expiresOn;
|
||||
}
|
||||
this.refreshAccount(account, connectionCredentials);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -510,6 +515,31 @@ export class ObjectExplorerService {
|
|||
}
|
||||
}
|
||||
|
||||
private async refreshAccount(account: IAccount, connectionCredentials: ConnectionCredentials): Promise<void> {
|
||||
let azureController = this._connectionManager.azureController;
|
||||
let profile = new ConnectionProfile(connectionCredentials);
|
||||
let azureAccountToken = await azureController.refreshAccessToken(
|
||||
account, this._connectionManager.accountStore, connectionCredentials.tenantId, providerSettings.resources.databaseResource);
|
||||
if (!azureAccountToken) {
|
||||
this._client.logger.verbose('Access token could not be refreshed for connection profile.');
|
||||
let errorMessage = LocalizedConstants.msgAccountRefreshFailed;
|
||||
await this._connectionManager.vscodeWrapper.showErrorMessage(
|
||||
errorMessage, LocalizedConstants.refreshTokenLabel).then(async result => {
|
||||
if (result === LocalizedConstants.refreshTokenLabel) {
|
||||
let updatedProfile = await azureController.populateAccountProperties(
|
||||
profile, this._connectionManager.accountStore, providerSettings.resources.databaseResource);
|
||||
connectionCredentials.azureAccountToken = updatedProfile.azureAccountToken;
|
||||
connectionCredentials.expiresOn = updatedProfile.expiresOn;
|
||||
} else {
|
||||
this._client.logger.error('Credentials not refreshed by user.');
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
connectionCredentials.azureAccountToken = azureAccountToken.token;
|
||||
connectionCredentials.expiresOn = azureAccountToken.expiresOn;
|
||||
}
|
||||
}
|
||||
public getConnectionCredentials(sessionId: string): IConnectionInfo {
|
||||
if (this._sessionIdToConnectionCredentialsMap.has(sessionId)) {
|
||||
return this._sessionIdToConnectionCredentialsMap.get(sessionId);
|
||||
|
|
Загрузка…
Ссылка в новой задаче