Handle authority & redirectUri in acquireTokenByRefreshToken (#263958)

Handle authority in acquireTokenByRefreshToken

This fixes the migration logic for clients that that moving from MSAL to MSAL+Broker
This commit is contained in:
Tyler James Leonhardt
2025-08-28 21:27:54 -07:00
committed by GitHub
parent 5b25d491ca
commit 2e43a0c0d6
4 changed files with 35 additions and 13 deletions

View File

@@ -4,6 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { Uri } from 'vscode';
export const DEFAULT_REDIRECT_URI = 'https://vscode.dev/redirect';
const VALID_DESKTOP_CALLBACK_SCHEMES = [
'vscode',
'vscode-insiders',

View File

@@ -19,6 +19,6 @@ export interface ICachedPublicClientApplication {
export interface ICachedPublicClientApplicationManager {
onDidAccountsChange: Event<{ added: AccountInfo[]; changed: AccountInfo[]; deleted: AccountInfo[] }>;
getOrCreate(clientId: string, refreshTokensToMigrate?: string[]): Promise<ICachedPublicClientApplication>;
getOrCreate(clientId: string, migrate?: { refreshTokensToMigrate?: string[]; tenant: string }): Promise<ICachedPublicClientApplication>;
getAll(): ICachedPublicClientApplication[];
}

View File

@@ -15,8 +15,9 @@ import { BetterTokenStorage } from '../betterSecretStorage';
import { IStoredSession } from '../AADHelper';
import { ExtensionHost, getMsalFlows } from './flows';
import { base64Decode } from './buffer';
import { Config } from '../common/config';
import { DEFAULT_REDIRECT_URI } from '../common/env';
const redirectUri = 'https://vscode.dev/redirect';
const MSA_TID = '9188040d-6c67-4c5b-b112-36a304b66dad';
const MSA_PASSTHRU_TID = 'f8cdef31-a31e-4b4a-93e4-5f571e91255a';
@@ -87,7 +88,7 @@ export class MsalAuthProvider implements AuthenticationProvider {
uriHandler: UriEventHandler,
env: Environment = Environment.AzureCloud
): Promise<MsalAuthProvider> {
const publicClientManager = await CachedPublicClientApplicationManager.create(context.secrets, logger, telemetryReporter, env.name);
const publicClientManager = await CachedPublicClientApplicationManager.create(context.secrets, logger, telemetryReporter, env);
context.subscriptions.push(publicClientManager);
const authProvider = new MsalAuthProvider(context, telemetryReporter, logger, uriHandler, publicClientManager, env);
await authProvider.initialize();
@@ -117,8 +118,8 @@ export class MsalAuthProvider implements AuthenticationProvider {
clientTenantMap.get(key)!.refreshTokens.push(session.refreshToken);
}
for (const { clientId, refreshTokens } of clientTenantMap.values()) {
await this._publicClientManager.getOrCreate(clientId, refreshTokens);
for (const { clientId, tenant, refreshTokens } of clientTenantMap.values()) {
await this._publicClientManager.getOrCreate(clientId, { refreshTokensToMigrate: refreshTokens, tenant });
}
}
@@ -483,6 +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,

View File

@@ -9,6 +9,9 @@ import { ICachedPublicClientApplication, ICachedPublicClientApplicationManager }
import { CachedPublicClientApplication } from './cachedPublicClientApplication';
import { IAccountAccess, ScopedAccountAccess } from '../common/accountAccess';
import { MicrosoftAuthenticationTelemetryReporter } from '../common/telemetryReporter';
import { Environment } from '@azure/ms-rest-azure-env';
import { Config } from '../common/config';
import { DEFAULT_REDIRECT_URI } from '../common/env';
export interface IPublicClientApplicationInfo {
clientId: string;
@@ -26,6 +29,7 @@ export class CachedPublicClientApplicationManager implements ICachedPublicClient
readonly onDidAccountsChange = this._onDidAccountsChangeEmitter.event;
private constructor(
private readonly _env: Environment,
private readonly _pcasSecretStorage: IPublicClientApplicationSecretStorage,
private readonly _accountAccess: IAccountAccess,
private readonly _secretStorage: SecretStorage,
@@ -44,13 +48,13 @@ export class CachedPublicClientApplicationManager implements ICachedPublicClient
secretStorage: SecretStorage,
logger: LogOutputChannel,
telemetryReporter: MicrosoftAuthenticationTelemetryReporter,
cloudName: string
env: Environment
): Promise<CachedPublicClientApplicationManager> {
const pcasSecretStorage = await PublicClientApplicationsSecretStorage.create(secretStorage, cloudName);
const pcasSecretStorage = await PublicClientApplicationsSecretStorage.create(secretStorage, env.name);
// TODO: Remove the migrations in a version
const migrations = await pcasSecretStorage.getOldValue();
const accountAccess = await ScopedAccountAccess.create(secretStorage, cloudName, logger, migrations);
const manager = new CachedPublicClientApplicationManager(pcasSecretStorage, accountAccess, secretStorage, logger, telemetryReporter, [pcasSecretStorage, accountAccess]);
const accountAccess = await ScopedAccountAccess.create(secretStorage, env.name, logger, migrations);
const manager = new CachedPublicClientApplicationManager(env, pcasSecretStorage, accountAccess, secretStorage, logger, telemetryReporter, [pcasSecretStorage, accountAccess]);
await manager.initialize();
return manager;
}
@@ -110,7 +114,7 @@ export class CachedPublicClientApplicationManager implements ICachedPublicClient
Disposable.from(...this._pcaDisposables.values()).dispose();
}
async getOrCreate(clientId: string, refreshTokensToMigrate?: string[]): Promise<ICachedPublicClientApplication> {
async getOrCreate(clientId: string, migrate?: { refreshTokensToMigrate?: string[]; tenant: string }): Promise<ICachedPublicClientApplication> {
let pca = this._pcas.get(clientId);
if (pca) {
this._logger.debug(`[getOrCreate] [${clientId}] PublicClientApplicationManager cache hit`);
@@ -122,13 +126,24 @@ export class CachedPublicClientApplicationManager implements ICachedPublicClient
}
// TODO: MSAL Migration. Remove this when we remove the old flow.
if (refreshTokensToMigrate?.length) {
if (migrate?.refreshTokensToMigrate?.length) {
this._logger.debug(`[getOrCreate] [${clientId}] Migrating refresh tokens to PCA...`);
for (const refreshToken of refreshTokensToMigrate) {
const authority = new URL(migrate.tenant, this._env.activeDirectoryEndpointUrl).toString();
let redirectUri = DEFAULT_REDIRECT_URI;
if (pca.isBrokerAvailable && process.platform === 'darwin') {
redirectUri = Config.macOSBrokerRedirectUri;
}
for (const refreshToken of migrate.refreshTokensToMigrate) {
try {
// Use the refresh token to acquire a result. This will cache the refresh token for future operations.
// The scopes don't matter here since we can create any token from the refresh token.
const result = await pca.acquireTokenByRefreshToken({ refreshToken, forceCache: true, scopes: [] });
const result = await pca.acquireTokenByRefreshToken({
refreshToken,
forceCache: true,
scopes: [],
authority,
redirectUri
});
if (result?.account) {
this._logger.debug(`[getOrCreate] [${clientId}] Refresh token migrated to PCA.`);
}