Add prefer device flow config for GitHub auth (#271838)

* Initial plan

* Add forceDeviceCodeFlow setting for GitHub authentication

Co-authored-by: pwang347 <8560030+pwang347@users.noreply.github.com>

* modifications

* nit

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: pwang347 <8560030+pwang347@users.noreply.github.com>
This commit is contained in:
Paul
2025-10-16 17:06:01 -07:00
committed by GitHub
parent af6adc61ce
commit 55cdeb5771
4 changed files with 85 additions and 3 deletions

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import { ProgressLocation, Uri, commands, env, l10n, window } from 'vscode';
import { ProgressLocation, Uri, commands, env, l10n, window, workspace } from 'vscode';
import { Log } from './common/logger';
import { Config } from './config';
import { UriEventHandler } from './github';
@@ -612,7 +612,7 @@ const allFlows: IFlow[] = [
];
export function getFlows(query: IFlowQuery) {
return allFlows.filter(flow => {
const validFlows = allFlows.filter(flow => {
let useFlow: boolean = true;
switch (query.target) {
case GitHubTarget.DotCom:
@@ -648,6 +648,16 @@ export function getFlows(query: IFlowQuery) {
}
return useFlow;
});
const preferDeviceCodeFlow = workspace.getConfiguration('github-authentication').get<boolean>('preferDeviceCodeFlow', false);
if (preferDeviceCodeFlow) {
return [
...validFlows.filter(flow => flow instanceof DeviceCodeFlow),
...validFlows.filter(flow => !(flow instanceof DeviceCodeFlow))
];
}
return validFlows;
}
/**

View File

@@ -6,6 +6,7 @@
import * as assert from 'assert';
import { ExtensionHost, GitHubTarget, IFlowQuery, getFlows } from '../flows';
import { Config } from '../config';
import * as vscode from 'vscode';
const enum Flows {
UrlHandlerFlow = 'url handler',
@@ -193,4 +194,68 @@ suite('getFlows', () => {
}
});
}
suite('preferDeviceCodeFlow configuration', () => {
let originalConfig: boolean | undefined;
suiteSetup(async () => {
const config = vscode.workspace.getConfiguration('github-authentication');
originalConfig = config.get<boolean>('preferDeviceCodeFlow');
});
suiteTeardown(async () => {
const config = vscode.workspace.getConfiguration('github-authentication');
await config.update('preferDeviceCodeFlow', originalConfig, vscode.ConfigurationTarget.Global);
});
test('returns device code flow first when preferDeviceCodeFlow is true - VS Code Desktop', async () => {
const config = vscode.workspace.getConfiguration('github-authentication');
await config.update('preferDeviceCodeFlow', true, vscode.ConfigurationTarget.Global);
const flows = getFlows({
extensionHost: ExtensionHost.Local,
isSupportedClient: true,
target: GitHubTarget.DotCom
});
// Should return device code flow first, then other flows
assert.strictEqual(flows.length, 3, `Expected 3 flows, got ${flows.length}: ${flows.map(f => f.label).join(',')}`);
assert.strictEqual(flows[0].label, Flows.DeviceCodeFlow);
// Other flows should still be available
assert.strictEqual(flows[1].label, Flows.LocalServerFlow);
assert.strictEqual(flows[2].label, Flows.UrlHandlerFlow);
});
test('returns device code flow first when preferDeviceCodeFlow is true - Remote', async () => {
const config = vscode.workspace.getConfiguration('github-authentication');
await config.update('preferDeviceCodeFlow', true, vscode.ConfigurationTarget.Global);
const flows = getFlows({
extensionHost: ExtensionHost.Remote,
isSupportedClient: true,
target: GitHubTarget.DotCom
});
// Should return device code flow first, then other flows
assert.strictEqual(flows.length, 2, `Expected 2 flows, got ${flows.length}: ${flows.map(f => f.label).join(',')}`);
assert.strictEqual(flows[0].label, Flows.DeviceCodeFlow);
assert.strictEqual(flows[1].label, Flows.UrlHandlerFlow);
});
test('returns normal flows when preferDeviceCodeFlow is true but device code flow is not supported - WebWorker', async () => {
const config = vscode.workspace.getConfiguration('github-authentication');
await config.update('preferDeviceCodeFlow', true, vscode.ConfigurationTarget.Global);
const flows = getFlows({
extensionHost: ExtensionHost.WebWorker,
isSupportedClient: true,
target: GitHubTarget.DotCom
});
// WebWorker doesn't support DeviceCodeFlow, so should return normal flows
// Based on the original logic, WebWorker + DotCom should return UrlHandlerFlow
assert.strictEqual(flows.length, 1, `Expected 1 flow for WebWorker configuration, got ${flows.length}: ${flows.map(f => f.label).join(',')}`);
assert.strictEqual(flows[0].label, Flows.UrlHandlerFlow);
});
});
});