Add new msal-no-broker implementation option to allow for getting rid of classic soon (#263966)

Also, removes the redirectUri from acquireTokenSilent because MSAL was throwing because of it.
This commit is contained in:
Tyler James Leonhardt
2025-08-29 00:01:02 -07:00
committed by GitHub
parent c281e3ea7e
commit 7e8f8e9230
6 changed files with 59 additions and 61 deletions

View File

@@ -110,10 +110,12 @@
"default": "msal",
"enum": [
"msal",
"msal-no-broker",
"classic"
],
"enumDescriptions": [
"%microsoft-authentication.implementation.enumDescriptions.msal%",
"%microsoft-authentication.implementation.enumDescriptions.msal-no-broker%",
"%microsoft-authentication.implementation.enumDescriptions.classic%"
],
"markdownDescription": "%microsoft-authentication.implementation.description%",

View File

@@ -4,13 +4,14 @@
"signIn": "Sign In",
"signOut": "Sign Out",
"microsoft-authentication.implementation.description": {
"message": "The authentication implementation to use for signing in with a Microsoft account.\n\n*NOTE: The `classic` implementation is deprecated and will be removed, along with this setting, in a future release. If only the `classic` implementation works for you, please [open an issue](command:workbench.action.openIssueReporter) and explain what you are trying to log in to.*",
"message": "The authentication implementation to use for signing in with a Microsoft account.\n\n*NOTE: The `classic` implementation is deprecated and will be removed in a future release. If the `msal` implementation does not work for you, please [open an issue](command:workbench.action.openIssueReporter) and explain what you are trying to log in to.*",
"comment": [
"{Locked='[(command:workbench.action.openIssueReporter)]'}",
"The `command:` syntax will turn into a link. Do not translate it."
]
},
"microsoft-authentication.implementation.enumDescriptions.msal": "Use the Microsoft Authentication Library (MSAL) to sign in with a Microsoft account.",
"microsoft-authentication.implementation.enumDescriptions.msal-no-broker": "Use the Microsoft Authentication Library (MSAL) to sign in with a Microsoft account using a browser. This is useful if you are having issues with the native broker.",
"microsoft-authentication.implementation.enumDescriptions.classic": "(deprecated) Use the classic authentication flow to sign in with a Microsoft account.",
"microsoft-sovereign-cloud.environment.description": {
"message": "The Sovereign Cloud to use for authentication. If you select `custom`, you must also set the `#microsoft-sovereign-cloud.customEnvironment#` setting.",

View File

@@ -36,11 +36,22 @@ export class MicrosoftAuthenticationTelemetryReporter implements IExperimentatio
);
}
sendActivatedWithClassicImplementationEvent(): void {
sendActivatedWithMsalNoBrokerEvent(): void {
/* __GDPR__
"activatingClassic" : { "owner": "TylerLeonhardt", "comment": "Used to determine how often users use the classic login flow." }
"activatingMsalNoBroker" : { "owner": "TylerLeonhardt", "comment": "Used to determine how often users use the msal-no-broker login flow. This only fires if the user explictly opts in to this." }
*/
this._telemetryReporter.sendTelemetryEvent('activatingClassic');
this._telemetryReporter.sendTelemetryEvent('activatingmsalnobroker');
}
sendActivatedWithClassicImplementationEvent(reason: 'setting' | 'web'): void {
/* __GDPR__
"activatingClassic" : {
"owner": "TylerLeonhardt",
"comment": "Used to determine how often users use the classic login flow.",
"reason": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Why classic was used" },
}
*/
this._telemetryReporter.sendTelemetryEvent('activatingClassic', { reason });
}
sendLoginEvent(scopes: readonly string[]): void {

View File

@@ -3,60 +3,28 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { commands, env, ExtensionContext, l10n, window, workspace } from 'vscode';
import { commands, ExtensionContext, l10n, window, workspace } from 'vscode';
import * as extensionV1 from './extensionV1';
import * as extensionV2 from './extensionV2';
import { createExperimentationService } from './common/experimentation';
import { MicrosoftAuthenticationTelemetryReporter } from './common/telemetryReporter';
import { IExperimentationService } from 'vscode-tas-client';
import Logger from './logger';
function shouldUseMsal(expService: IExperimentationService): boolean {
// First check if there is a setting value to allow user to override the default
const inspect = workspace.getConfiguration('microsoft-authentication').inspect<'msal' | 'classic'>('implementation');
if (inspect?.workspaceFolderValue !== undefined) {
Logger.info(`Acquired MSAL enablement value from 'workspaceFolderValue'. Value: ${inspect.workspaceFolderValue}`);
return inspect.workspaceFolderValue === 'msal';
}
if (inspect?.workspaceValue !== undefined) {
Logger.info(`Acquired MSAL enablement value from 'workspaceValue'. Value: ${inspect.workspaceValue}`);
return inspect.workspaceValue === 'msal';
}
if (inspect?.globalValue !== undefined) {
Logger.info(`Acquired MSAL enablement value from 'globalValue'. Value: ${inspect.globalValue}`);
return inspect.globalValue === 'msal';
}
let implementation: 'msal' | 'msal-no-broker' | 'classic' = 'msal';
const getImplementation = () => workspace.getConfiguration('microsoft-authentication').get<'msal' | 'msal-no-broker' | 'classic'>('implementation') ?? 'msal';
// Then check if the experiment value
const expValue = expService.getTreatmentVariable<boolean>('vscode', 'microsoft.useMsal');
if (expValue !== undefined) {
Logger.info(`Acquired MSAL enablement value from 'exp'. Value: ${expValue}`);
return expValue;
}
Logger.info('Acquired MSAL enablement value from default. Value: true');
// If no setting or experiment value is found, default to true
return true;
}
let useMsal: boolean | undefined;
export async function activate(context: ExtensionContext) {
const mainTelemetryReporter = new MicrosoftAuthenticationTelemetryReporter(context.extension.packageJSON.aiKey);
const expService = await createExperimentationService(
context,
mainTelemetryReporter,
env.uriScheme !== 'vscode', // isPreRelease
);
useMsal = shouldUseMsal(expService);
implementation = getImplementation();
context.subscriptions.push(workspace.onDidChangeConfiguration(async e => {
if (!e.affectsConfiguration('microsoft-authentication')) {
return;
}
if (useMsal === shouldUseMsal(expService)) {
if (implementation === getImplementation()) {
return;
}
// Allow for the migration to be re-attempted if the user switches back to the MSAL implementation
context.globalState.update('msalMigration', undefined);
const reload = l10n.t('Reload');
const result = await window.showInformationMessage(
'Reload required',
@@ -72,17 +40,31 @@ export async function activate(context: ExtensionContext) {
}
}));
const isNodeEnvironment = typeof process !== 'undefined' && typeof process?.versions?.node === 'string';
// Only activate the new extension if we are not running in a browser environment
if (useMsal && isNodeEnvironment) {
await extensionV2.activate(context, mainTelemetryReporter);
} else {
mainTelemetryReporter.sendActivatedWithClassicImplementationEvent();
await extensionV1.activate(context, mainTelemetryReporter.telemetryReporter);
if (!isNodeEnvironment) {
mainTelemetryReporter.sendActivatedWithClassicImplementationEvent('web');
return await extensionV1.activate(context, mainTelemetryReporter.telemetryReporter);
}
switch (implementation) {
case 'msal-no-broker':
mainTelemetryReporter.sendActivatedWithMsalNoBrokerEvent();
await extensionV2.activate(context, mainTelemetryReporter);
break;
case 'classic':
mainTelemetryReporter.sendActivatedWithClassicImplementationEvent('setting');
await extensionV1.activate(context, mainTelemetryReporter.telemetryReporter);
break;
case 'msal':
default:
await extensionV2.activate(context, mainTelemetryReporter);
break;
}
}
export function deactivate() {
if (useMsal) {
if (implementation !== 'classic') {
extensionV2.deactivate();
} else {
extensionV1.deactivate();

View File

@@ -484,15 +484,10 @@ export class MsalAuthProvider implements AuthenticationProvider {
forceRefresh = true;
claims = scopeData.claims;
}
let redirectUri = DEFAULT_REDIRECT_URI;
if (cachedPca.isBrokerAvailable && process.platform === 'darwin') {
redirectUri = Config.macOSBrokerRedirectUri;
}
const result = await cachedPca.acquireTokenSilent({
account,
authority,
scopes: scopeData.scopesToSend,
redirectUri,
claims,
forceRefresh
});

View File

@@ -3,9 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { PublicClientApplication, AccountInfo, SilentFlowRequest, AuthenticationResult, InteractiveRequest, LogLevel, RefreshTokenRequest } from '@azure/msal-node';
import { PublicClientApplication, AccountInfo, SilentFlowRequest, AuthenticationResult, InteractiveRequest, LogLevel, RefreshTokenRequest, BrokerOptions } from '@azure/msal-node';
import { NativeBrokerPlugin } from '@azure/msal-node-extensions';
import { Disposable, SecretStorage, LogOutputChannel, window, ProgressLocation, l10n, EventEmitter } from 'vscode';
import { Disposable, SecretStorage, LogOutputChannel, window, ProgressLocation, l10n, EventEmitter, workspace } from 'vscode';
import { raceCancellationAndTimeoutError } from '../common/async';
import { SecretStorageCachePlugin } from '../common/cachePlugin';
import { MsalLoggerOptions } from '../common/loggerOptions';
@@ -24,7 +24,7 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica
private readonly _secretStorageCachePlugin: SecretStorageCachePlugin;
// Broker properties
readonly isBrokerAvailable: boolean;
readonly isBrokerAvailable: boolean = false;
//#region Events
@@ -50,8 +50,15 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica
);
const loggerOptions = new MsalLoggerOptions(_logger, telemetryReporter);
const nativeBrokerPlugin = new NativeBrokerPlugin();
this.isBrokerAvailable = nativeBrokerPlugin.isBrokerAvailable;
let broker: BrokerOptions | undefined;
if (workspace.getConfiguration('microsoft-authentication').get<'msal' | 'msal-no-broker'>('implementation') !== 'msal-no-broker') {
const nativeBrokerPlugin = new NativeBrokerPlugin();
this.isBrokerAvailable = nativeBrokerPlugin.isBrokerAvailable;
this._logger.info(`[${this._clientId}] Native Broker enabled: ${this.isBrokerAvailable}`);
broker = { nativeBrokerPlugin };
} else {
this._logger.info(`[${this._clientId}] Native Broker disabled via settings`);
}
this._pca = new PublicClientApplication({
auth: { clientId: _clientId },
system: {
@@ -63,7 +70,7 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica
piiLoggingEnabled: true
}
},
broker: { nativeBrokerPlugin },
broker,
cache: { cachePlugin: this._secretStorageCachePlugin }
});
this._disposable = Disposable.from(