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

@@ -62,6 +62,12 @@
"default": true,
"scope": "application",
"markdownDescription": "%config.github-authentication.useElectronFetch.description%"
},
"github-authentication.preferDeviceCodeFlow": {
"type": "boolean",
"default": false,
"scope": "application",
"markdownDescription": "%config.github-authentication.preferDeviceCodeFlow.description%"
}
}
}

View File

@@ -3,5 +3,6 @@
"description": "GitHub Authentication Provider",
"config.github-enterprise.title": "GHE.com & GitHub Enterprise Server Authentication",
"config.github-enterprise.uri.description": "The URI for your GHE.com or GitHub Enterprise Server instance.\n\nExamples:\n* GHE.com: `https://octocat.ghe.com`\n* GitHub Enterprise Server: `https://github.octocat.com`\n\n> **Note:** This should _not_ be set to a GitHub.com URI. If your account exists on GitHub.com or is a GitHub Enterprise Managed User, you do not need any additional configuration and can simply log in to GitHub.",
"config.github-authentication.useElectronFetch.description": "When true, uses Electron's built-in fetch function for HTTP requests. When false, uses the Node.js global fetch function. This setting only applies when running in the Electron environment. **Note:** A restart is required for this setting to take effect."
"config.github-authentication.useElectronFetch.description": "When true, uses Electron's built-in fetch function for HTTP requests. When false, uses the Node.js global fetch function. This setting only applies when running in the Electron environment. **Note:** A restart is required for this setting to take effect.",
"config.github-authentication.preferDeviceCodeFlow.description": "When true, prioritize the device code flow for authentication instead of other available flows. This is useful for environments like WSL where the local server or URL handler flows may not work as expected."
}

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);
});
});
});