diff --git a/src/sql/workbench/services/connection/browser/connectionManagementService.ts b/src/sql/workbench/services/connection/browser/connectionManagementService.ts index b912b2ab62..4fdcffa5d2 100644 --- a/src/sql/workbench/services/connection/browser/connectionManagementService.ts +++ b/src/sql/workbench/services/connection/browser/connectionManagementService.ts @@ -873,6 +873,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti && connection.authenticationType !== Constants.AuthenticationType.AzureMFAAndUser && connection.authenticationType !== Constants.AuthenticationType.DSTSAuth) { connection.options['azureAccountToken'] = undefined; + connection.options['expiresOn'] = undefined; return true; } @@ -883,6 +884,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti let dstsAccounts = accounts.filter(a => a.key.providerId.startsWith('dstsAuth')); if (dstsAccounts.length <= 0) { connection.options['azureAccountToken'] = undefined; + connection.options['expiresOn'] = undefined; return false; } diff --git a/src/sql/workbench/services/connection/test/browser/connectionManagementService.test.ts b/src/sql/workbench/services/connection/test/browser/connectionManagementService.test.ts index 6234487ffc..8389c21567 100644 --- a/src/sql/workbench/services/connection/test/browser/connectionManagementService.test.ts +++ b/src/sql/workbench/services/connection/test/browser/connectionManagementService.test.ts @@ -1771,6 +1771,81 @@ suite('SQL ConnectionManagementService tests', () => { assert.strictEqual(newProfile2.options['expiresOn'], freshToken.expiresOn); }); + test('refreshAzureAccountTokenIfNecessary does not refresh Azure access token for non-Azure authentication', async () => { + const uri: string = 'Editor Uri'; + // Set up a connection profile that uses Azure + const sqlAuthConnectionProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, connectionProfile); + sqlAuthConnectionProfile.authenticationType = Constants.AuthenticationType.SqlLogin; + sqlAuthConnectionProfile.userName = 'testuser'; + sqlAuthConnectionProfile.password = 'testpassword'; + + // For mocking purpose only. + const username = 'testuser@microsoft.com'; + const providerId = 'azure_PublicCloud'; + + const expiredToken = { + token: 'expiredToken', + tokenType: 'Bearer', + expiresOn: 0, + }; + + const freshToken = { + token: 'freshToken', + tokenType: 'Bearer', + expiresOn: new Date().getTime() / 1000 + 7200, + }; + + // every connectionStatusManager.connect will call accountManagementService.getAccountSecurityToken twice + accountManagementService.setup(x => x.getAccountSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(expiredToken)); + accountManagementService.setup(x => x.getAccountSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(expiredToken)); + accountManagementService.setup(x => x.getAccountSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(freshToken)); + accountManagementService.setup(x => x.getAccountSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(freshToken)); + accountManagementService.setup(x => x.getAccountSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(expiredToken)); + accountManagementService.setup(x => x.getAccountSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(expiredToken)); + + accountManagementService.setup(x => x.getAccounts()).returns(() => { + return Promise.resolve([ + { + key: { + accountId: username, + providerId: providerId + }, + displayInfo: undefined, + isStale: false, + properties: undefined + } + ]); + }); + + connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is(profile => profile.authenticationType === Constants.AuthenticationType.AzureMFA))).returns(profile => Promise.resolve({ + profile: profile, + savedCred: false + })); + + (connectionManagementService as any)._connectionStatusManager = connectionStatusManager; + await connect(uri, undefined, false, sqlAuthConnectionProfile); + + const oldProfile = connectionStatusManager.getConnectionProfile(uri); + assert.strictEqual(oldProfile.options['expiresOn'], undefined); + assert.strictEqual(oldProfile.options['azureAccountToken'], undefined); + + const refreshRes1 = await connectionManagementService.refreshAzureAccountTokenIfNecessary(uri); + assert.strictEqual(refreshRes1, false); + + // refresh should not populate expiresOn or token + const newProfile1 = connectionStatusManager.getConnectionProfile(uri); + assert.strictEqual(newProfile1.options['expiresOn'], undefined); + assert.strictEqual(newProfile1.options['azureAccountToken'], undefined); + + const refreshRes2 = await connectionManagementService.refreshAzureAccountTokenIfNecessary(uri); + assert.strictEqual(refreshRes2, false); + + // second refresh should be a no-op - no change on properties + const newProfile2 = connectionStatusManager.getConnectionProfile(uri); + assert.strictEqual(newProfile2.options['expiresOn'], undefined); + assert.strictEqual(newProfile1.options['azureAccountToken'], undefined); + }); + test('addSavedPassword fills in Azure access token for selected tenant', async () => { // Set up a connection profile that uses Azure let azureConnectionProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, connectionProfile);