Enable the broker in macOS (#261148)

* Enable the broker in macOS

Fixes https://github.com/microsoft/vscode/issues/260158

* for testing

* better globbing

* guh

* guh

* delete

* log it all

* let's just log everything

* Only do on supported OS/Arches

* Add a console.log

* look at VSCODE_ARCH

* add msal files

* add entitlement maybe here

* actually it's probably here

* build: bundle msal libs for x64 and arm64

* revert that

* try again

* try adding $(AppIdentifierPrefix)

* temp: add debuggee entitlements

* bump msal and pass in redirect uri on macOS

* revert entitlement files

* forgot the .helper

* Allow PII for the output channel only

* use unsigned option

---------

Co-authored-by: deepak1556 <hop2deep@gmail.com>
This commit is contained in:
Tyler James Leonhardt 2025-08-27 14:31:09 -07:00 committed by GitHub
parent 543ea0e80d
commit da3cf78129
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 178 additions and 93 deletions

View File

@ -29,7 +29,7 @@ async function main(buildDir) {
'**/Credits.rtf',
'**/policies/{*.mobileconfig,**/*.plist}',
// TODO: Should we consider expanding this to other files in this area?
'**/node_modules/@parcel/node-addon-api/nothing.target.mk'
'**/node_modules/@parcel/node-addon-api/nothing.target.mk',
];
await (0, vscode_universal_bundler_1.makeUniversalApp)({
x64AppPath,
@ -38,7 +38,7 @@ async function main(buildDir) {
outAppPath,
force: true,
mergeASARs: true,
x64ArchFiles: '*/kerberos.node',
x64ArchFiles: '{*/kerberos.node,**/extensions/microsoft-authentication/dist/libmsalruntime.dylib,**/extensions/microsoft-authentication/dist/msal-node-runtime.node}',
filesToSkipComparison: (file) => {
for (const expected of filesToSkip) {
if ((0, minimatch_1.default)(file, expected)) {

View File

@ -30,7 +30,7 @@ async function main(buildDir?: string) {
'**/Credits.rtf',
'**/policies/{*.mobileconfig,**/*.plist}',
// TODO: Should we consider expanding this to other files in this area?
'**/node_modules/@parcel/node-addon-api/nothing.target.mk'
'**/node_modules/@parcel/node-addon-api/nothing.target.mk',
];
await makeUniversalApp({
@ -40,7 +40,7 @@ async function main(buildDir?: string) {
outAppPath,
force: true,
mergeASARs: true,
x64ArchFiles: '*/kerberos.node',
x64ArchFiles: '{*/kerberos.node,**/extensions/microsoft-authentication/dist/libmsalruntime.dylib,**/extensions/microsoft-authentication/dist/msal-node-runtime.node}',
filesToSkipComparison: (file: string) => {
for (const expected of filesToSkip) {
if (minimatch(file, expected)) {

View File

@ -11,6 +11,7 @@ const assert_1 = __importDefault(require("assert"));
const path_1 = __importDefault(require("path"));
const promises_1 = require("fs/promises");
const cross_spawn_promise_1 = require("@malept/cross-spawn-promise");
const minimatch_1 = __importDefault(require("minimatch"));
const MACHO_PREFIX = 'Mach-O ';
const MACHO_64_MAGIC_LE = 0xfeedfacf;
const MACHO_UNIVERSAL_MAGIC_LE = 0xbebafeca;
@ -22,6 +23,15 @@ const MACHO_X86_64_CPU_TYPE = new Set([
0x07000001,
0x01000007,
]);
// Files to skip during architecture validation
const FILES_TO_SKIP = [
// MSAL runtime files are only present in ARM64 builds
'**/extensions/microsoft-authentication/dist/libmsalruntime.dylib',
'**/extensions/microsoft-authentication/dist/msal-node-runtime.node',
];
function isFileSkipped(file) {
return FILES_TO_SKIP.some(pattern => (0, minimatch_1.default)(file, pattern));
}
async function read(file, buf, offset, length, position) {
let filehandle;
try {
@ -109,11 +119,11 @@ const archToCheck = process.argv[2];
(0, assert_1.default)(process.env['APP_PATH'], 'APP_PATH not set');
(0, assert_1.default)(archToCheck === 'x64' || archToCheck === 'arm64' || archToCheck === 'universal', `Invalid architecture ${archToCheck} to check`);
checkMachOFiles(process.env['APP_PATH'], archToCheck).then(invalidFiles => {
if (invalidFiles.length > 0) {
console.error('\x1b[31mThe following files are built for the wrong architecture:\x1b[0m');
for (const file of invalidFiles) {
console.error(`\x1b[31m${file}\x1b[0m`);
}
// Filter out files that should be skipped
const actualInvalidFiles = invalidFiles.filter(file => !isFileSkipped(file));
if (actualInvalidFiles.length > 0) {
console.error('\x1b[31mThese files are built for the wrong architecture:\x1b[0m');
actualInvalidFiles.forEach(file => console.error(`\x1b[31m${file}\x1b[0m`));
process.exit(1);
}
else {

View File

@ -7,6 +7,7 @@ import assert from 'assert';
import path from 'path';
import { open, stat, readdir, realpath } from 'fs/promises';
import { spawn, ExitCodeError } from '@malept/cross-spawn-promise';
import minimatch from 'minimatch';
const MACHO_PREFIX = 'Mach-O ';
const MACHO_64_MAGIC_LE = 0xfeedfacf;
@ -20,6 +21,17 @@ const MACHO_X86_64_CPU_TYPE = new Set([
0x01000007,
]);
// Files to skip during architecture validation
const FILES_TO_SKIP = [
// MSAL runtime files are only present in ARM64 builds
'**/extensions/microsoft-authentication/dist/libmsalruntime.dylib',
'**/extensions/microsoft-authentication/dist/msal-node-runtime.node',
];
function isFileSkipped(file: string): boolean {
return FILES_TO_SKIP.some(pattern => minimatch(file, pattern));
}
async function read(file: string, buf: Buffer, offset: number, length: number, position: number) {
let filehandle;
try {
@ -105,11 +117,11 @@ const archToCheck = process.argv[2];
assert(process.env['APP_PATH'], 'APP_PATH not set');
assert(archToCheck === 'x64' || archToCheck === 'arm64' || archToCheck === 'universal', `Invalid architecture ${archToCheck} to check`);
checkMachOFiles(process.env['APP_PATH'], archToCheck).then(invalidFiles => {
if (invalidFiles.length > 0) {
console.error('\x1b[31mThe following files are built for the wrong architecture:\x1b[0m');
for (const file of invalidFiles) {
console.error(`\x1b[31m${file}\x1b[0m`);
}
// Filter out files that should be skipped
const actualInvalidFiles = invalidFiles.filter(file => !isFileSkipped(file));
if (actualInvalidFiles.length > 0) {
console.error('\x1b[31mThese files are built for the wrong architecture:\x1b[0m');
actualInvalidFiles.forEach(file => console.error(`\x1b[31m${file}\x1b[0m`));
process.exit(1);
} else {
console.log('\x1b[32mAll files are valid\x1b[0m');

View File

@ -8,6 +8,25 @@ import CopyWebpackPlugin from 'copy-webpack-plugin';
import path from 'path';
const isWindows = process.platform === 'win32';
const windowsArches = ['x64'];
const isMacOS = process.platform === 'darwin';
const macOSArches = ['arm64'];
const arch = process.arch;
console.log(`Building Microsoft Authentication Extension for ${process.platform} (${arch})`);
const plugins = [...nodePlugins(import.meta.dirname)];
if ((isWindows && windowsArches.includes(arch)) || (isMacOS && macOSArches.includes(arch))) {
plugins.push(new CopyWebpackPlugin({
patterns: [
{
// The native files we need to ship with the extension
from: '**/dist/(lib|)msal*.(node|dll|dylib)',
to: '[name][ext]'
}
]
}));
}
export default withDefaults({
context: import.meta.dirname,
@ -25,18 +44,5 @@ export default withDefaults({
'keytar': path.resolve(import.meta.dirname, 'packageMocks', 'keytar', 'index.js')
}
},
plugins: [
...nodePlugins(import.meta.dirname),
new CopyWebpackPlugin({
patterns: [
{
// The native files we need to ship with the extension
from: '**/dist/msal*.(node|dll)',
to: '[name][ext]',
// These will only be present on Windows for now
noErrorOnMissing: !isWindows
}
]
})
]
plugins
});

View File

@ -10,8 +10,8 @@
"license": "MIT",
"dependencies": {
"@azure/ms-rest-azure-env": "^2.0.0",
"@azure/msal-node": "^2.16.2",
"@azure/msal-node-extensions": "^1.5.0",
"@azure/msal-node": "^3.7.3",
"@azure/msal-node-extensions": "^1.5.22",
"@vscode/extension-telemetry": "^0.9.8",
"keytar": "file:./packageMocks/keytar",
"vscode-tas-client": "^0.1.84"
@ -33,21 +33,21 @@
"integrity": "sha512-dG76W7ElfLi+fbTjnZVGj+M9e0BIEJmRxU6fHaUQ12bZBe8EJKYb2GV50YWNaP2uJiVQ5+7nXEVj1VN1UQtaEw=="
},
"node_modules/@azure/msal-common": {
"version": "14.16.0",
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.16.0.tgz",
"integrity": "sha512-1KOZj9IpcDSwpNiQNjt0jDYZpQvNZay7QAEi/5DLubay40iGYtLzya/jbjRPLyOTZhEKyL1MzPuw2HqBCjceYA==",
"version": "15.12.0",
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.12.0.tgz",
"integrity": "sha512-4ucXbjVw8KJ5QBgnGJUeA07c8iznwlk5ioHIhI4ASXcXgcf2yRFhWzYOyWg/cI49LC9ekpFJeQtO3zjDTbl6TQ==",
"license": "MIT",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/@azure/msal-node": {
"version": "2.16.2",
"resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.16.2.tgz",
"integrity": "sha512-An7l1hEr0w1HMMh1LU+rtDtqL7/jw74ORlc9Wnh06v7TU/xpG39/Zdr1ZJu3QpjUfKJ+E0/OXMW8DRSWTlh7qQ==",
"version": "3.7.3",
"resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.7.3.tgz",
"integrity": "sha512-MoJxkKM/YpChfq4g2o36tElyzNUMG8mfD6u8NbuaPAsqfGpaw249khAcJYNoIOigUzRw45OjXCOrexE6ImdUxg==",
"license": "MIT",
"dependencies": {
"@azure/msal-common": "14.16.0",
"@azure/msal-common": "15.12.0",
"jsonwebtoken": "^9.0.0",
"uuid": "^8.3.0"
},
@ -56,14 +56,14 @@
}
},
"node_modules/@azure/msal-node-extensions": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@azure/msal-node-extensions/-/msal-node-extensions-1.5.0.tgz",
"integrity": "sha512-UfEyh2xmJHKH64zPS/SbN1bd9adV4ZWGp1j2OSwIuhVraqpUXyXZ1LpDpiUqg/peTgLLtx20qrHOzYT0kKzmxQ==",
"version": "1.5.22",
"resolved": "https://registry.npmjs.org/@azure/msal-node-extensions/-/msal-node-extensions-1.5.22.tgz",
"integrity": "sha512-pdHcoyxmCszjXhc59jZ+7WKH0ZqebH0ZVvKlBwFD57TQzAOnXPS6q76/dsVO/Y7EXAlvkoCmcebNc6eP4cQulA==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@azure/msal-common": "14.16.0",
"@azure/msal-node-runtime": "^0.17.1",
"@azure/msal-common": "15.12.0",
"@azure/msal-node-runtime": "^0.19.0",
"keytar": "^7.8.0"
},
"engines": {
@ -71,9 +71,9 @@
}
},
"node_modules/@azure/msal-node-runtime": {
"version": "0.18.2",
"resolved": "https://registry.npmjs.org/@azure/msal-node-runtime/-/msal-node-runtime-0.18.2.tgz",
"integrity": "sha512-v45fyBQp80BrjZAeGJXl+qggHcbylQiFBihr0ijO2eniDCW9tz5TZBKYsqzH06VuiRaVG/Sa0Hcn4pjhJqFSTw==",
"version": "0.19.4",
"resolved": "https://registry.npmjs.org/@azure/msal-node-runtime/-/msal-node-runtime-0.19.4.tgz",
"integrity": "sha512-v90QdV/VKG6gvHx2bQp82yRCJ8IGG7OOk6gDQgbKvoHtOs7mSEz2CIqWydUpwCryJkUwgNfgMPAMoGhe/a4fOw==",
"hasInstallScript": true,
"license": "MIT"
},

View File

@ -144,15 +144,12 @@
},
"dependencies": {
"@azure/ms-rest-azure-env": "^2.0.0",
"@azure/msal-node": "^2.16.2",
"@azure/msal-node-extensions": "^1.5.0",
"@azure/msal-node": "^3.7.3",
"@azure/msal-node-extensions": "^1.5.22",
"@vscode/extension-telemetry": "^0.9.8",
"keytar": "file:./packageMocks/keytar",
"vscode-tas-client": "^0.1.84"
},
"overrides": {
"@azure/msal-node-runtime": "^0.18.2"
},
"repository": {
"type": "git",
"url": "https://github.com/microsoft/vscode.git"

View File

@ -0,0 +1,19 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface IConfig {
// The macOS broker redirect URI which is dependent on the bundle identifier of the signed app.
// Other platforms do not require a redirect URI to be set. For unsigned apps, the unsigned
// format can be used.
// Example formats:
// msauth.com.msauth.unsignedapp://auth or msauth.<bundleId>://auth
macOSBrokerRedirectUri: string;
}
export const Config: IConfig = {
// This is replaced in the build with the correct bundle id for that distro.
macOSBrokerRedirectUri: 'msauth.com.msauth.unsignedapp://auth'
};

View File

@ -19,13 +19,7 @@ export class MsalLoggerOptions {
return this._toMsalLogLevel(env.logLevel);
}
loggerCallback(level: MsalLogLevel, message: string, containsPii: boolean): void {
if (containsPii) {
// TODO: Should we still log the message if it contains PII? It's just going to
// an output channel that doesn't leave the machine.
this._output.debug('Skipped logging message because it may contain PII');
return;
}
loggerCallback(level: MsalLogLevel, message: string, _containsPii: boolean): void {
// Log to output channel one level lower than the MSAL log level
switch (level) {

View File

@ -14,6 +14,7 @@ export interface ICachedPublicClientApplication {
removeAccount(account: AccountInfo): Promise<void>;
accounts: AccountInfo[];
clientId: string;
isBrokerAvailable: Readonly<boolean>;
}
export interface ICachedPublicClientApplicationManager {

View File

@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AuthError } from '@azure/msal-node';
import TelemetryReporter, { TelemetryEventProperties } from '@vscode/extension-telemetry';
import { IExperimentationTelemetry } from 'vscode-tas-client';
@ -75,20 +76,36 @@ export class MicrosoftAuthenticationTelemetryReporter implements IExperimentatio
}
sendTelemetryErrorEvent(error: unknown): void {
const errorMessage = error instanceof Error ? error.message : String(error);
const errorStack = error instanceof Error ? error.stack : undefined;
const errorName = error instanceof Error ? error.name : undefined;
let errorMessage: string | undefined;
let errorName: string | undefined;
let errorCode: string | undefined;
let errorCorrelationId: string | undefined;
if (typeof error === 'string') {
errorMessage = error;
} else {
const authError: AuthError = error as any;
// don't set error message or stack because it contains PII
errorCode = authError.errorCode;
errorCorrelationId = authError.correlationId;
errorName = authError.name;
}
/* __GDPR__
"msalError" : {
"owner": "TylerLeonhardt",
"comment": "Used to determine how often users run into issues with the login flow.",
"errorMessage": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The error message from the exception." },
"errorStack": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The stack trace from the exception." },
"errorName": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The name of the error." }
"errorMessage": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The error message." },
"errorName": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The name of the error." },
"errorCode": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The error code." },
"errorCorrelationId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The error correlation id." }
}
*/
this._telemetryReporter.sendTelemetryErrorEvent('msalError', { errorMessage, errorStack, errorName });
this._telemetryReporter.sendTelemetryErrorEvent('msalError', {
errorMessage,
errorName,
errorCode,
errorCorrelationId,
});
}
/**

View File

@ -213,6 +213,7 @@ export class MsalAuthProvider implements AuthenticationProvider {
extensionHost: isNodeEnvironment
? this._context.extension.extensionKind === ExtensionKind.UI ? ExtensionHost.Local : ExtensionHost.Remote
: ExtensionHost.WebWorker,
isBrokerSupported: cachedPca.isBrokerAvailable
});
const authority = new URL(scopeData.tenant, this._env.activeDirectoryEndpointUrl).toString();
@ -342,6 +343,7 @@ export class MsalAuthProvider implements AuthenticationProvider {
extensionHost: isNodeEnvironment
? this._context.extension.extensionKind === ExtensionKind.UI ? ExtensionHost.Local : ExtensionHost.Remote
: ExtensionHost.WebWorker,
isBrokerSupported: cachedPca.isBrokerAvailable
});
const authority = new URL(scopeData.tenant, this._env.activeDirectoryEndpointUrl).toString();

View File

@ -24,7 +24,7 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica
private readonly _secretStorageCachePlugin: SecretStorageCachePlugin;
// Broker properties
private readonly _isBrokerAvailable: boolean;
readonly isBrokerAvailable: boolean;
//#region Events
@ -51,14 +51,16 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica
const loggerOptions = new MsalLoggerOptions(_logger, telemetryReporter);
const nativeBrokerPlugin = new NativeBrokerPlugin();
this._isBrokerAvailable = nativeBrokerPlugin.isBrokerAvailable;
this.isBrokerAvailable = nativeBrokerPlugin.isBrokerAvailable;
this._pca = new PublicClientApplication({
auth: { clientId: _clientId },
system: {
loggerOptions: {
correlationId: _clientId,
loggerCallback: (level, message, containsPii) => loggerOptions.loggerCallback(level, message, containsPii),
logLevel: LogLevel.Trace
logLevel: LogLevel.Trace,
// Enable PII logging since it will only go to the output channel
piiLoggingEnabled: true
}
},
broker: { nativeBrokerPlugin },
@ -110,7 +112,7 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica
);
if (fiveMinutesBefore < new Date()) {
this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] id token is expired or about to expire. Forcing refresh...`);
const newRequest = this._isBrokerAvailable
const newRequest = this.isBrokerAvailable
// HACK: Broker doesn't support forceRefresh so we need to pass in claims which will force a refresh
? { ...request, claims: request.claims ?? '{ "id_token": {}}' }
: { ...request, forceRefresh: true };
@ -128,7 +130,7 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica
// HACK: Only for the Broker we try one more time with different claims to force a refresh. Why? We've seen the Broker caching tokens by the claims requested, thus
// there has been a situation where both tokens are expired.
if (this._isBrokerAvailable) {
if (this.isBrokerAvailable) {
this._logger.error(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] forcing refresh with different claims...`);
const newRequest = { ...request, claims: request.claims ?? '{ "access_token": {}}' };
result = await this._sequencer.queue(() => this._pca.acquireTokenSilent(newRequest));
@ -166,20 +168,25 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica
title: l10n.t('Signing in to Microsoft...')
},
(_process, token) => this._sequencer.queue(async () => {
const result = await raceCancellationAndTimeoutError(
this._pca.acquireTokenInteractive(request),
token,
1000 * 60 * 5
);
if (this._isBrokerAvailable) {
await this._accountAccess.setAllowedAccess(result.account!, true);
try {
const result = await raceCancellationAndTimeoutError(
this._pca.acquireTokenInteractive(request),
token,
1000 * 60 * 5
);
if (this.isBrokerAvailable) {
await this._accountAccess.setAllowedAccess(result.account!, true);
}
// Force an update so that the account cache is updated.
// TODO:@TylerLeonhardt The problem is, we use the sequencer for
// change events but we _don't_ use it for the accounts cache.
// We should probably use it for the accounts cache as well.
await this._update();
return result;
} catch (error) {
this._logger.error(`[acquireTokenInteractive] [${this._clientId}] [${request.authority}] [${request.scopes?.join(' ')}] error: ${error}`);
throw error;
}
// Force an update so that the account cache is updated.
// TODO:@TylerLeonhardt The problem is, we use the sequencer for
// change events but we _don't_ use it for the accounts cache.
// We should probably use it for the accounts cache as well.
await this._update();
return result;
})
);
}
@ -203,7 +210,7 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica
});
if (result) {
// this._setupRefresh(result);
if (this._isBrokerAvailable && result.account) {
if (this.isBrokerAvailable && result.account) {
await this._accountAccess.setAllowedAccess(result.account, true);
}
}
@ -211,14 +218,14 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica
}
removeAccount(account: AccountInfo): Promise<void> {
if (this._isBrokerAvailable) {
if (this.isBrokerAvailable) {
return this._accountAccess.setAllowedAccess(account, false);
}
return this._sequencer.queue(() => this._pca.getTokenCache().removeAccount(account));
}
private _registerOnSecretStorageChanged() {
if (this._isBrokerAvailable) {
if (this.isBrokerAvailable) {
return this._accountAccess.onDidAccountAccessChange(() => this._sequencer.queue(() => this._update()));
}
return this._secretStorageCachePlugin.onDidChange(() => this._sequencer.queue(() => this._update()));
@ -258,7 +265,7 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica
// Clear in-memory cache so we know we're getting account data from the SecretStorage
this._pca.clearCache();
let after = await this._pca.getAllAccounts();
if (this._isBrokerAvailable) {
if (this.isBrokerAvailable) {
after = after.filter(a => this._accountAccess.isAllowedAccess(a));
}
this._accounts = after;

View File

@ -9,8 +9,9 @@ import { ICachedPublicClientApplication } from '../common/publicClientCache';
import { UriHandlerLoopbackClient } from '../common/loopbackClientAndOpener';
import { UriEventHandler } from '../UriEventHandler';
import { loopbackTemplate } from './loopbackTemplate';
import { Config } from '../common/config';
const redirectUri = 'https://vscode.dev/redirect';
const DEFAULT_REDIRECT_URI = 'https://vscode.dev/redirect';
export const enum ExtensionHost {
WebWorker,
@ -49,6 +50,10 @@ class DefaultLoopbackFlow implements IMsalFlow {
async trigger({ cachedPca, authority, scopes, claims, loginHint, windowHandle, logger }: IMsalFlowTriggerOptions): Promise<AuthenticationResult> {
logger.info('Trying default msal flow...');
let redirectUri: string | undefined;
if (cachedPca.isBrokerAvailable && process.platform === 'darwin') {
redirectUri = Config.macOSBrokerRedirectUri;
}
return await cachedPca.acquireTokenInteractive({
openBrowser: async (url: string) => { await env.openExternal(Uri.parse(url)); },
scopes,
@ -58,7 +63,8 @@ class DefaultLoopbackFlow implements IMsalFlow {
loginHint,
prompt: loginHint ? undefined : 'select_account',
windowHandle,
claims
claims,
redirectUri
});
}
}
@ -72,7 +78,11 @@ class UrlHandlerFlow implements IMsalFlow {
async trigger({ cachedPca, authority, scopes, claims, loginHint, windowHandle, logger, uriHandler }: IMsalFlowTriggerOptions): Promise<AuthenticationResult> {
logger.info('Trying protocol handler flow...');
const loopbackClient = new UriHandlerLoopbackClient(uriHandler, redirectUri, logger);
const loopbackClient = new UriHandlerLoopbackClient(uriHandler, DEFAULT_REDIRECT_URI, logger);
let redirectUri: string | undefined;
if (cachedPca.isBrokerAvailable && process.platform === 'darwin') {
redirectUri = Config.macOSBrokerRedirectUri;
}
return await cachedPca.acquireTokenInteractive({
openBrowser: (url: string) => loopbackClient.openBrowser(url),
scopes,
@ -81,7 +91,8 @@ class UrlHandlerFlow implements IMsalFlow {
loginHint,
prompt: loginHint ? undefined : 'select_account',
windowHandle,
claims
claims,
redirectUri
});
}
}
@ -93,10 +104,12 @@ const allFlows: IMsalFlow[] = [
export interface IMsalFlowQuery {
extensionHost: ExtensionHost;
isBrokerSupported: boolean;
}
export function getMsalFlows(query: IMsalFlowQuery): IMsalFlow[] {
return allFlows.filter(flow => {
const flows = [];
for (const flow of allFlows) {
let useFlow: boolean = true;
switch (query.extensionHost) {
case ExtensionHost.Remote:
@ -106,6 +119,13 @@ export function getMsalFlows(query: IMsalFlowQuery): IMsalFlow[] {
useFlow &&= flow.options.supportsWebWorkerExtensionHost;
break;
}
return useFlow;
});
if (useFlow) {
flows.push(flow);
if (query.isBrokerSupported) {
// If broker is supported, only use the first valid flow
return flows;
}
}
}
return flows;
}