From e88f42357d07df47d46a12922ff724aef50d58d8 Mon Sep 17 00:00:00 2001 From: msivasubramaniaan Date: Tue, 18 Aug 2026 17:54:30 +0530 Subject: [PATCH 1/7] fix: invalidate restored device auth sessions after token removal --- .../che-github-authentication/src/github.ts | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/code/extensions/che-github-authentication/src/github.ts b/code/extensions/che-github-authentication/src/github.ts index 3d127c043603..a6705408e084 100644 --- a/code/extensions/che-github-authentication/src/github.ts +++ b/code/extensions/che-github-authentication/src/github.ts @@ -48,6 +48,7 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { private deviceAuthentication?: DeviceAuthentication; private readonly storageKey: string; + private readonly deviceAuthSessionStorageKey: string; constructor( @inject(Logger) private logger: Logger, @@ -57,6 +58,7 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { ) { const workspaceId = process.env.DEVWORKSPACE_ID || 'default'; this.storageKey = `sessions:${workspaceId}`; + this.deviceAuthSessionStorageKey = `device-auth-session-ids:${workspaceId}`; this.sessionsPromise = this.readSessions(); } @@ -83,6 +85,80 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { ]); let sessions = await this.sessionsPromise; + + const isDeviceAuthToken = await this.githubService.isDeviceAuthToken(); + + let deviceAuthSessionIds = await this.getDeviceAuthSessionIds(); + + /* + * While Device Authentication is active, remember which persisted + * sessions belong to Device Authentication. + */ + if (isDeviceAuthToken && sessions.length > 0) { + const currentToken = await this.githubService.getToken(); + + const currentDeviceAuthSessions = sessions + .filter((session) => session.accessToken === currentToken) + .map((session) => session.id); + + const updatedDeviceAuthSessionIds = [ + ...new Set([...deviceAuthSessionIds, ...currentDeviceAuthSessions]), + ]; + + if (updatedDeviceAuthSessionIds.length !== deviceAuthSessionIds.length) { + await this.storeDeviceAuthSessionIds(updatedDeviceAuthSessionIds); + + deviceAuthSessionIds = updatedDeviceAuthSessionIds; + } + } + + /* + * VS Code restores persisted authentication sessions when the + * workspace is restarted. + * + * If Device Authentication is no longer active, remove only + * the sessions that were previously created using Device Authentication. + */ + if (!isDeviceAuthToken && deviceAuthSessionIds.length > 0) { + const removed = sessions.filter((session) => + deviceAuthSessionIds.includes(session.id), + ); + + const kept = sessions.filter( + (session) => !deviceAuthSessionIds.includes(session.id), + ); + + if (removed.length > 0) { + this.logger.info( + `GitHubAuthProvider: removing ${removed.length} persisted Device Authentication session(s) because Device Authentication is no longer active`, + ); + + await this.storeSessions(kept); + + const removedIds = new Set(removed.map((session) => session.id)); + + await this.storeDeviceAuthSessionIds( + deviceAuthSessionIds.filter((id) => !removedIds.has(id)), + ); + + this.sessionChangeEmitter.fire({ + added: [], + removed, + changed: [], + }); + + sessions = kept; + + /* + * Do not immediately recreate a session using the fallback + * PAT/git-credential token. The user must authenticate again. + */ + return; + } + // Clean up stale session IDs. + await this.storeDeviceAuthSessionIds([]); + } + if (sessions.length > 0) { try { await this.githubService.getTokenScopes(sessions[0].accessToken); @@ -97,6 +173,7 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { this.logger.warn('GitHubAuthProvider: existing session token is not valid, clearing sessions'); const removed = [...sessions]; await this.storeSessions([]); + await this.storeDeviceAuthSessionIds([]); this.sessionChangeEmitter.fire({ added: [], removed, changed: [] }); sessions = []; } else { @@ -119,6 +196,38 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { ); } + private async getDeviceAuthSessionIds(): Promise { + const raw = await this.extensionContext + .getContext() + .secrets + .get(this.deviceAuthSessionStorageKey); + + if (!raw) { + return []; + } + + try { + return JSON.parse(raw) as string[]; + } catch { + this.logger.warn( + 'GitHubAuthProvider: failed to parse persisted device-auth session IDs', + ); + return []; + } + } + + private async storeDeviceAuthSessionIds( + sessionIds: string[], + ): Promise { + await this.extensionContext + .getContext() + .secrets + .store( + this.deviceAuthSessionStorageKey, + JSON.stringify(sessionIds), + ); + } + private async waitForToken(timeoutMs: number, intervalMs: number): Promise { const deadline = Date.now() + timeoutMs; while (Date.now() < deadline) { @@ -227,6 +336,18 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { scopes, }; + const isDeviceAuth = await this.githubService.isDeviceAuthToken(); + if (isDeviceAuth) { + const deviceAuthSessionIds = await this.getDeviceAuthSessionIds(); + + if (!deviceAuthSessionIds.includes(session.id)) { + await this.storeDeviceAuthSessionIds([ + ...deviceAuthSessionIds, + session.id, + ]); + } + } + const sessionIndex = sessions.findIndex(s => sessionMatchesRequestedScopes(s.scopes, sortedScopes)); const removed: vscode.AuthenticationSession[] = []; const updatedSessions = [...sessions]; @@ -303,6 +424,7 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { this.logger.info(`GitHubAuthProvider: clearing all ${sessions.length} sessions`); const removed = [...sessions]; await this.storeSessions([]); + await this.storeDeviceAuthSessionIds([]); this.sessionChangeEmitter.fire({ added: [], removed, changed: [] }); } @@ -326,6 +448,16 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { if (removed.length > 0) { this.logger.info(`GitHubAuthProvider: clearing ${removed.length} device-auth sessions, keeping ${kept.length} K8s sessions`); await this.storeSessions(kept); + const deviceAuthSessionIds = await this.getDeviceAuthSessionIds(); + + const removedIds = new Set(removed.map(session => session.id),); + + await this.storeDeviceAuthSessionIds( + deviceAuthSessionIds.filter( + id => !removedIds.has(id), + ), + ); + this.sessionChangeEmitter.fire({ added: [], removed, changed: [] }); } } catch { @@ -341,6 +473,12 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { if (session) { const updatedSessions = sessions.filter(s => s.id !== id); await this.storeSessions(updatedSessions); + const deviceAuthSessionIds = await this.getDeviceAuthSessionIds(); + if (deviceAuthSessionIds.includes(id)) { + await this.storeDeviceAuthSessionIds(deviceAuthSessionIds.filter( + sessionId => sessionId !== id, + )); + } this.sessionChangeEmitter.fire({ added: [], removed: [session], changed: [] }); this.logger.info(`GitHubAuthProvider: session was removed successfully! `); From 39f9b03f85ab1439886788e6db8e5508ed550ac6 Mon Sep 17 00:00:00 2001 From: msivasubramaniaan Date: Fri, 21 Aug 2026 20:13:40 +0530 Subject: [PATCH 2/7] addressed coderabbit-ai review comment --- .../che-github-authentication/src/github.ts | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/code/extensions/che-github-authentication/src/github.ts b/code/extensions/che-github-authentication/src/github.ts index a6705408e084..dab64cb32c2a 100644 --- a/code/extensions/che-github-authentication/src/github.ts +++ b/code/extensions/che-github-authentication/src/github.ts @@ -18,6 +18,7 @@ import { ErrorHandler } from './error-handler'; import { ExtensionContext } from './extension-context'; import { Logger } from './logger'; import { getMatchingHydrationScopeBundles, hasAllScopes, isUnauthorizedError, sessionMatchesRequestedScopes } from './utils'; +import { AuthenticationSession } from 'vscode'; export interface GithubUser { login: string; @@ -185,7 +186,13 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { try { const token = await this.githubService.getToken(); - await this.doHydrateWithToken(token); + const hydratedSessions = await this.doHydrateWithToken(token); + if (isDeviceAuthToken && hydratedSessions.length > 0) { + const hydratedSessionIds = hydratedSessions.map(session => session.id); + const updatedDeviceAuthSessionIds = [...new Set([...deviceAuthSessionIds, ...hydratedSessionIds])]; + await this.storeDeviceAuthSessionIds(updatedDeviceAuthSessionIds); + deviceAuthSessionIds = updatedDeviceAuthSessionIds; + } return; } catch { this.logger.info('GitHubAuthProvider: no token available after initialization'); @@ -207,7 +214,11 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { } try { - return JSON.parse(raw) as string[]; + const sessionIds: unknown = JSON.parse(raw); + if (!Array.isArray(sessionIds) || !sessionIds.every(id => typeof id === 'string')) { + throw new Error('Invalid device-auth session ID storage value'); + } + return sessionIds; } catch { this.logger.warn( 'GitHubAuthProvider: failed to parse persisted device-auth session IDs', @@ -249,19 +260,19 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { await this.doHydrateWithToken(token); } - private async doHydrateWithToken(token: string): Promise { + private async doHydrateWithToken(token: string): Promise { try { const tokenScopes = await this.githubService.getTokenScopes(token); if (tokenScopes.length === 0) { this.logger.info('GitHubAuthProvider: hydrate skipped, token has no scopes'); - return; + return []; } const githubUser = await this.githubService.getUser(); const matchingBundles = getMatchingHydrationScopeBundles(tokenScopes); if (matchingBundles.length === 0) { this.logger.info('GitHubAuthProvider: hydrate skipped, token scopes match no known bundle'); - return; + return []; } const account = { label: githubUser.login, id: githubUser.id.toString() }; @@ -275,12 +286,14 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { await this.storeSessions(hydratedSessions); this.sessionChangeEmitter.fire({ added: hydratedSessions, removed: [], changed: [] }); this.logger.info(`GitHubAuthProvider: hydrated ${hydratedSessions.length} session(s) from K8s token`); + return hydratedSessions; } catch (error) { if (isUnauthorizedError(error)) { this.logger.warn('GitHubAuthProvider: hydrate failed, token is not valid'); } else { this.logger.warn(`GitHubAuthProvider: hydrate failed: ${(error as Error).message}`); } + return []; } } From e0a120896bd1d7bbd90fe954b570edf7225b1051 Mon Sep 17 00:00:00 2001 From: msivasubramaniaan Date: Mon, 24 Aug 2026 17:18:38 +0530 Subject: [PATCH 3/7] addressed review comments --- .../che-github-authentication/src/github.ts | 153 +++++++++--------- 1 file changed, 80 insertions(+), 73 deletions(-) diff --git a/code/extensions/che-github-authentication/src/github.ts b/code/extensions/che-github-authentication/src/github.ts index dab64cb32c2a..b327b1c4ab72 100644 --- a/code/extensions/che-github-authentication/src/github.ts +++ b/code/extensions/che-github-authentication/src/github.ts @@ -87,78 +87,78 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { let sessions = await this.sessionsPromise; - const isDeviceAuthToken = await this.githubService.isDeviceAuthToken(); + const isDeviceAuthToken = await this.githubService.isDeviceAuthToken(); - let deviceAuthSessionIds = await this.getDeviceAuthSessionIds(); + let deviceAuthSessionIds = await this.getDeviceAuthSessionIds(); - /* - * While Device Authentication is active, remember which persisted - * sessions belong to Device Authentication. - */ - if (isDeviceAuthToken && sessions.length > 0) { - const currentToken = await this.githubService.getToken(); + /* + * While Device Authentication is active, remember which persisted + * sessions belong to Device Authentication. + */ + if (isDeviceAuthToken && sessions.length > 0) { + const currentToken = await this.githubService.getToken(); - const currentDeviceAuthSessions = sessions + const currentDeviceAuthSessions = sessions .filter((session) => session.accessToken === currentToken) .map((session) => session.id); - const updatedDeviceAuthSessionIds = [ - ...new Set([...deviceAuthSessionIds, ...currentDeviceAuthSessions]), - ]; + const updatedDeviceAuthSessionIds = [ + ...new Set([...deviceAuthSessionIds, ...currentDeviceAuthSessions]), + ]; - if (updatedDeviceAuthSessionIds.length !== deviceAuthSessionIds.length) { - await this.storeDeviceAuthSessionIds(updatedDeviceAuthSessionIds); + if (updatedDeviceAuthSessionIds.length !== deviceAuthSessionIds.length) { + await this.storeDeviceAuthSessionIds(updatedDeviceAuthSessionIds); - deviceAuthSessionIds = updatedDeviceAuthSessionIds; - } - } + deviceAuthSessionIds = updatedDeviceAuthSessionIds; + } + } - /* - * VS Code restores persisted authentication sessions when the - * workspace is restarted. - * + /* + * VS Code restores persisted authentication sessions when the + * workspace is restarted. + * * If Device Authentication is no longer active, remove only * the sessions that were previously created using Device Authentication. - */ - if (!isDeviceAuthToken && deviceAuthSessionIds.length > 0) { + */ + if (!isDeviceAuthToken && deviceAuthSessionIds.length > 0) { const removed = sessions.filter((session) => - deviceAuthSessionIds.includes(session.id), - ); + deviceAuthSessionIds.includes(session.id), + ); - const kept = sessions.filter( + const kept = sessions.filter( (session) => !deviceAuthSessionIds.includes(session.id), - ); + ); - if (removed.length > 0) { - this.logger.info( - `GitHubAuthProvider: removing ${removed.length} persisted Device Authentication session(s) because Device Authentication is no longer active`, - ); + if (removed.length > 0) { + this.logger.info( + `GitHubAuthProvider: removing ${removed.length} persisted Device Authentication session(s) because Device Authentication is no longer active`, + ); - await this.storeSessions(kept); + await this.storeSessions(kept); const removedIds = new Set(removed.map((session) => session.id)); - await this.storeDeviceAuthSessionIds( + await this.storeDeviceAuthSessionIds( deviceAuthSessionIds.filter((id) => !removedIds.has(id)), - ); - - this.sessionChangeEmitter.fire({ - added: [], - removed, - changed: [], - }); - - sessions = kept; - - /* - * Do not immediately recreate a session using the fallback - * PAT/git-credential token. The user must authenticate again. - */ - return; - } - // Clean up stale session IDs. - await this.storeDeviceAuthSessionIds([]); - } + ); + + this.sessionChangeEmitter.fire({ + added: [], + removed, + changed: [], + }); + + sessions = kept; + + /* + * Do not immediately recreate a session using the fallback + * PAT/git-credential token. The user must authenticate again. + */ + return; + } + // Clean up stale session IDs. + await this.storeDeviceAuthSessionIds([]); + } if (sessions.length > 0) { try { @@ -184,23 +184,39 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { } } - try { - const token = await this.githubService.getToken(); - const hydratedSessions = await this.doHydrateWithToken(token); - if (isDeviceAuthToken && hydratedSessions.length > 0) { - const hydratedSessionIds = hydratedSessions.map(session => session.id); - const updatedDeviceAuthSessionIds = [...new Set([...deviceAuthSessionIds, ...hydratedSessionIds])]; + const token = await this.githubService.getToken(); + + const hydratedSessions = await this.doHydrateWithToken(token); + + if (isDeviceAuthToken && hydratedSessions.length > 0) { + const hydratedSessionIds = hydratedSessions.map(session => session.id); + + const updatedDeviceAuthSessionIds = [ + ...new Set([...deviceAuthSessionIds, ...hydratedSessionIds]), + ]; + + try { await this.storeDeviceAuthSessionIds(updatedDeviceAuthSessionIds); deviceAuthSessionIds = updatedDeviceAuthSessionIds; + } catch (error) { + await this.rollbackHydratedSessions(hydratedSessions); + throw error; } - return; - } catch { - this.logger.info('GitHubAuthProvider: no token available after initialization'); } + } - this.doHydrate().catch(err => - this.logger.error(`GitHubAuthProvider: background hydration failed: ${(err as Error).message}`) - ); + private async rollbackHydratedSessions(hydratedSessions: AuthenticationSession[]): Promise { + const hydratedSessionIds = new Set(hydratedSessions.map(session => session.id)); + const sessions = await this.sessionsPromise; + const updatedSessions = sessions.filter(session => !hydratedSessionIds.has(session.id)); + + await this.storeSessions(updatedSessions); + + this.sessionChangeEmitter.fire({ + added: [], + removed: hydratedSessions, + changed: [], + }); } private async getDeviceAuthSessionIds(): Promise { @@ -251,15 +267,6 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { return undefined; } - private async doHydrate(): Promise { - const token = await this.waitForToken(30000, 500); - if (!token) { - this.logger.warn('GitHubAuthProvider: hydrate failed, token not available after 30s'); - return; - } - await this.doHydrateWithToken(token); - } - private async doHydrateWithToken(token: string): Promise { try { const tokenScopes = await this.githubService.getTokenScopes(token); From bb1e0af3c0b905c49cb2ad3907c078c5a0bf7e1d Mon Sep 17 00:00:00 2001 From: msivasubramaniaan Date: Mon, 24 Aug 2026 17:59:19 +0530 Subject: [PATCH 4/7] Added github auth msg --- .../vs/workbench/api/common/extHostChatAgents2.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/code/src/vs/workbench/api/common/extHostChatAgents2.ts b/code/src/vs/workbench/api/common/extHostChatAgents2.ts index a580f8cbc061..9cd5403f3485 100644 --- a/code/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/code/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -946,7 +946,9 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS if (!model) { model = await this._languageModels.getDefaultLanguageModel(extension); if (!model) { - throw new Error('Language model unavailable'); + const error = new Error('GitHub authentication is required'); + error.name = 'GitHubAuthenticationRequired'; + throw error; } } @@ -1077,6 +1079,16 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS const isQuotaExceeded = e instanceof Error && e.name === 'ChatQuotaExceeded'; const isRateLimited = e instanceof Error && e.name === 'ChatRateLimited'; const isExpectedError = e instanceof Error && e.name === 'ChatExpectedError'; + if (e instanceof Error && e.name === 'GitHubAuthenticationRequired') { + return { + errorDetails: { + message: 'GitHub authentication is required to use Copilot.', + responseIsIncomplete: true, + }, + errorCallstack: undefined, + errorName: e.name, + }; + } const { callstack: errorCallstack } = packErrorForTelemetry(e); const errorName = e instanceof Error ? e.name : undefined; return { errorDetails: { message: toErrorMessage(e), responseIsIncomplete: true, isQuotaExceeded, isRateLimited, isExpectedError }, errorCallstack, errorName }; From 6b16f9c21a1a0cf90d4466e4e1f1492664efab2e Mon Sep 17 00:00:00 2001 From: msivasubramaniaan Date: Mon, 24 Aug 2026 22:47:45 +0530 Subject: [PATCH 5/7] added confirmation btn --- .../che-github-authentication/src/github.ts | 58 ++++++++----------- .../api/common/extHostChatAgents2.ts | 33 +++++++---- 2 files changed, 46 insertions(+), 45 deletions(-) diff --git a/code/extensions/che-github-authentication/src/github.ts b/code/extensions/che-github-authentication/src/github.ts index b327b1c4ab72..caaca7585e8e 100644 --- a/code/extensions/che-github-authentication/src/github.ts +++ b/code/extensions/che-github-authentication/src/github.ts @@ -1,5 +1,5 @@ /********************************************************************** - * Copyright (c) 2023 Red Hat, Inc. + * Copyright (c) 2023-2026 Red Hat, Inc. * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -80,21 +80,13 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { } async hydrateFromK8sToken(): Promise { - await Promise.race([ - this.githubService.whenReady, - new Promise(resolve => setTimeout(resolve, 5000)) - ]); + await Promise.race([this.githubService.whenReady,new Promise(resolve => setTimeout(resolve, 5000))]); let sessions = await this.sessionsPromise; const isDeviceAuthToken = await this.githubService.isDeviceAuthToken(); - let deviceAuthSessionIds = await this.getDeviceAuthSessionIds(); - /* - * While Device Authentication is active, remember which persisted - * sessions belong to Device Authentication. - */ if (isDeviceAuthToken && sessions.length > 0) { const currentToken = await this.githubService.getToken(); @@ -108,7 +100,6 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { if (updatedDeviceAuthSessionIds.length !== deviceAuthSessionIds.length) { await this.storeDeviceAuthSessionIds(updatedDeviceAuthSessionIds); - deviceAuthSessionIds = updatedDeviceAuthSessionIds; } } @@ -125,8 +116,7 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { deviceAuthSessionIds.includes(session.id), ); - const kept = sessions.filter( - (session) => !deviceAuthSessionIds.includes(session.id), + const kept = sessions.filter((session) => !deviceAuthSessionIds.includes(session.id), ); if (removed.length > 0) { @@ -139,7 +129,7 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { const removedIds = new Set(removed.map((session) => session.id)); await this.storeDeviceAuthSessionIds( - deviceAuthSessionIds.filter((id) => !removedIds.has(id)), + deviceAuthSessionIds.filter((id) => !removedIds.has(id)), ); this.sessionChangeEmitter.fire({ @@ -148,15 +138,10 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { changed: [], }); - sessions = kept; - - /* - * Do not immediately recreate a session using the fallback - * PAT/git-credential token. The user must authenticate again. - */ + // Do not recreate a session using the fallback PAT. return; } - // Clean up stale session IDs. + await this.storeDeviceAuthSessionIds([]); } @@ -185,7 +170,6 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { } const token = await this.githubService.getToken(); - const hydratedSessions = await this.doHydrateWithToken(token); if (isDeviceAuthToken && hydratedSessions.length > 0) { @@ -255,18 +239,6 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { ); } - private async waitForToken(timeoutMs: number, intervalMs: number): Promise { - const deadline = Date.now() + timeoutMs; - while (Date.now() < deadline) { - try { - return await this.githubService.getToken(); - } catch { - await new Promise(resolve => setTimeout(resolve, intervalMs)); - } - } - return undefined; - } - private async doHydrateWithToken(token: string): Promise { try { const tokenScopes = await this.githubService.getTokenScopes(token); @@ -378,6 +350,24 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider { } await this.storeSessions(updatedSessions); + if (isDeviceAuth) { + const deviceAuthSessionIds = await this.getDeviceAuthSessionIds(); + + if (!deviceAuthSessionIds.includes(session.id)) { + try { + await this.storeDeviceAuthSessionIds([ + ...deviceAuthSessionIds, + session.id, + ]); + } catch (error) { + // Roll back the session because its Device Authentication + // tracking ID could not be persisted. + await this.storeSessions(sessions); + throw error; + } + } + } + this.sessionChangeEmitter.fire({ added: [session], removed, changed: [] }); this.logger.info(`GitHubAuthProvider: session was created successfully for scopes: ${JSON.stringify(scopes)}`); diff --git a/code/src/vs/workbench/api/common/extHostChatAgents2.ts b/code/src/vs/workbench/api/common/extHostChatAgents2.ts index 9cd5403f3485..1978737cbd02 100644 --- a/code/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/code/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -1079,19 +1079,30 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS const isQuotaExceeded = e instanceof Error && e.name === 'ChatQuotaExceeded'; const isRateLimited = e instanceof Error && e.name === 'ChatRateLimited'; const isExpectedError = e instanceof Error && e.name === 'ChatExpectedError'; - if (e instanceof Error && e.name === 'GitHubAuthenticationRequired') { - return { - errorDetails: { - message: 'GitHub authentication is required to use Copilot.', - responseIsIncomplete: true, - }, - errorCallstack: undefined, - errorName: e.name, - }; - } + const isGitHubAuthenticationRequired = e instanceof Error && e.name === 'GitHubAuthenticationRequired'; + const { callstack: errorCallstack } = packErrorForTelemetry(e); const errorName = e instanceof Error ? e.name : undefined; - return { errorDetails: { message: toErrorMessage(e), responseIsIncomplete: true, isQuotaExceeded, isRateLimited, isExpectedError }, errorCallstack, errorName }; + return { + errorDetails: { + message: isGitHubAuthenticationRequired ? 'GitHub authentication is required to use Copilot.' : toErrorMessage(e), + responseIsIncomplete: true, + isQuotaExceeded, + isRateLimited, + isExpectedError: + isExpectedError || isGitHubAuthenticationRequired, + confirmationButtons: isGitHubAuthenticationRequired + ? [{ + label: 'Device Authentication', + data: { + command: 'github-authentication.device-code-flow.authentication', + }, + }] + : undefined, + }, + errorCallstack, + errorName, + }; } finally { if (inFlightRequest) { From 741eb535938e805147810f7b5e68953bef55ba55 Mon Sep 17 00:00:00 2001 From: msivasubramaniaan Date: Tue, 25 Aug 2026 00:13:58 +0530 Subject: [PATCH 6/7] moved signin code into copilot --- .../vscode-node/copilotTokenManager.ts | 17 ++++++++++-- .../api/common/extHostChatAgents2.ts | 27 ++----------------- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/code/extensions/copilot/src/platform/authentication/vscode-node/copilotTokenManager.ts b/code/extensions/copilot/src/platform/authentication/vscode-node/copilotTokenManager.ts index 1e103997ca1a..39a074b363a0 100644 --- a/code/extensions/copilot/src/platform/authentication/vscode-node/copilotTokenManager.ts +++ b/code/extensions/copilot/src/platform/authentication/vscode-node/copilotTokenManager.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { env, window } from 'vscode'; +import { commands, env, window } from 'vscode'; import { TaskSingler } from '../../../util/common/taskSingler'; import { ConfigKey, IConfigurationService } from '../../configuration/common/configurationService'; import { ICAPIClientService } from '../../endpoint/common/capiClient'; @@ -139,7 +139,20 @@ export class VSCodeCopilotTokenManager extends BaseCopilotTokenManager { } if (tokenResult.kind === 'failure' && tokenResult.reason === 'GitHubLoginFailed') { - throw new GitHubLoginFailedError('GitHubLoginFailed'); + const message = 'GitHub authentication is required to use Copilot.'; + + window.showWarningMessage( + message, + 'Sign in to GitHub', + ).then(selection => { + if (selection === 'Sign in to GitHub') { + commands.executeCommand( + 'github-authentication.device-code-flow.authentication', + ); + } + }); + + throw new GitHubLoginFailedError(message); } if (tokenResult.kind === 'failure' && tokenResult.reason === 'RateLimited') { diff --git a/code/src/vs/workbench/api/common/extHostChatAgents2.ts b/code/src/vs/workbench/api/common/extHostChatAgents2.ts index 1978737cbd02..a580f8cbc061 100644 --- a/code/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/code/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -946,9 +946,7 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS if (!model) { model = await this._languageModels.getDefaultLanguageModel(extension); if (!model) { - const error = new Error('GitHub authentication is required'); - error.name = 'GitHubAuthenticationRequired'; - throw error; + throw new Error('Language model unavailable'); } } @@ -1079,30 +1077,9 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS const isQuotaExceeded = e instanceof Error && e.name === 'ChatQuotaExceeded'; const isRateLimited = e instanceof Error && e.name === 'ChatRateLimited'; const isExpectedError = e instanceof Error && e.name === 'ChatExpectedError'; - const isGitHubAuthenticationRequired = e instanceof Error && e.name === 'GitHubAuthenticationRequired'; - const { callstack: errorCallstack } = packErrorForTelemetry(e); const errorName = e instanceof Error ? e.name : undefined; - return { - errorDetails: { - message: isGitHubAuthenticationRequired ? 'GitHub authentication is required to use Copilot.' : toErrorMessage(e), - responseIsIncomplete: true, - isQuotaExceeded, - isRateLimited, - isExpectedError: - isExpectedError || isGitHubAuthenticationRequired, - confirmationButtons: isGitHubAuthenticationRequired - ? [{ - label: 'Device Authentication', - data: { - command: 'github-authentication.device-code-flow.authentication', - }, - }] - : undefined, - }, - errorCallstack, - errorName, - }; + return { errorDetails: { message: toErrorMessage(e), responseIsIncomplete: true, isQuotaExceeded, isRateLimited, isExpectedError }, errorCallstack, errorName }; } finally { if (inFlightRequest) { From 632952a4c917efdbc3bd7f7a0a7cd910805e7d0d Mon Sep 17 00:00:00 2001 From: msivasubramaniaan Date: Thu, 27 Aug 2026 00:45:29 +0530 Subject: [PATCH 7/7] revert the login btn --- .../vscode-node/copilotTokenManager.ts | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/code/extensions/copilot/src/platform/authentication/vscode-node/copilotTokenManager.ts b/code/extensions/copilot/src/platform/authentication/vscode-node/copilotTokenManager.ts index 39a074b363a0..1e103997ca1a 100644 --- a/code/extensions/copilot/src/platform/authentication/vscode-node/copilotTokenManager.ts +++ b/code/extensions/copilot/src/platform/authentication/vscode-node/copilotTokenManager.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { commands, env, window } from 'vscode'; +import { env, window } from 'vscode'; import { TaskSingler } from '../../../util/common/taskSingler'; import { ConfigKey, IConfigurationService } from '../../configuration/common/configurationService'; import { ICAPIClientService } from '../../endpoint/common/capiClient'; @@ -139,20 +139,7 @@ export class VSCodeCopilotTokenManager extends BaseCopilotTokenManager { } if (tokenResult.kind === 'failure' && tokenResult.reason === 'GitHubLoginFailed') { - const message = 'GitHub authentication is required to use Copilot.'; - - window.showWarningMessage( - message, - 'Sign in to GitHub', - ).then(selection => { - if (selection === 'Sign in to GitHub') { - commands.executeCommand( - 'github-authentication.device-code-flow.authentication', - ); - } - }); - - throw new GitHubLoginFailedError(message); + throw new GitHubLoginFailedError('GitHubLoginFailed'); } if (tokenResult.kind === 'failure' && tokenResult.reason === 'RateLimited') {