Path: blob/main/extensions/copilot/src/platform/authentication/vscode-node/session.ts
13401 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { authentication, AuthenticationGetSessionOptions, AuthenticationSession, AuthenticationSessionsChangeEvent } from 'vscode';6import { mixin } from '../../../util/vs/base/common/objects';7import { URI } from '../../../util/vs/base/common/uri';8import { AuthPermissionMode, ConfigKey, IConfigurationService } from '../../configuration/common/configurationService';9import { authProviderId, GITHUB_SCOPE_ALIGNED, GITHUB_SCOPE_READ_USER, GITHUB_SCOPE_USER_EMAIL, MinimalModeError } from '../common/authentication';1011export const SESSION_LOGIN_MESSAGE = 'You are not signed in to GitHub. Please sign in to use Copilot.';12// These types are subsets of the "real" types AuthenticationSessionAccountInformation and13// AuthenticationSession. They allow us to use the type system to validate which fields14// are actually needed and hence which ones need values when we construct fake session.15type CopilotAuthenticationSessionAccountInformation = {16label: string;17};1819export type CopilotAuthenticationSession = {20accessToken: string;21account: CopilotAuthenticationSessionAccountInformation;22};2324async function getAuthSession(providerId: string, defaultScopes: string[], getSilentSession: () => Promise<AuthenticationSession | undefined>, options: AuthenticationGetSessionOptions = {}) {25const accounts = await authentication.getAccounts(providerId);26if (!accounts.length) {27return await authentication.getSession(providerId, defaultScopes, options);28}2930if (options.forceNewSession) {31const session = await authentication.getSession(providerId, defaultScopes, {32...options,33forceNewSession: mixin({ learnMore: URI.parse('https://aka.ms/copilotRepoScope') }, options.forceNewSession),34// When GitHub becomes a true multi-account provider, we won't have to clearSessionPreference.35clearSessionPreference: true36});37return session;38}3940const silentSession = await getSilentSession();41if (silentSession) {42return silentSession;43}4445if (options.createIfNone) {46// This will force GitHub auth to present a picker to choose which account you want to log in to if there47// are multiple accounts.48// When GitHub becomes a true multi-account provider, we can change this to just createIfNone: true.49const session = await authentication.getSession(providerId, defaultScopes, options);50return session;51}52// Pass the options in as they are53return await authentication.getSession(providerId, defaultScopes, options);54}5556/**57* Cast a wide net to get a session with any of the scopes that Copilot needs.58* @param configurationService for determining the auth provider59* @returns an auth session with any of the scopes that Copilot needs, or undefined if none is found60* @deprecated use `IAuthenticationService` instead61*/62export function getAnyAuthSession(configurationService: IConfigurationService, options?: AuthenticationGetSessionOptions): Promise<AuthenticationSession | undefined> {63const providerId = authProviderId(configurationService);6465return getAuthSession(66providerId,67GITHUB_SCOPE_USER_EMAIL,68async () => {69// Ask for aligned scopes first, since that's what we want to use going forward.70if (configurationService.getConfig(ConfigKey.Shared.AuthPermissions) !== AuthPermissionMode.Minimal) {71const permissive = await authentication.getSession(providerId, GITHUB_SCOPE_ALIGNED, { silent: true });72if (permissive) {73return permissive;74}75}76const minimal = await authentication.getSession(providerId, GITHUB_SCOPE_USER_EMAIL, { silent: true });77if (minimal) {78return minimal;79}80// This is what Completions extension use to ask for and is here mostly for backwards compatibility.81const fallback = await authentication.getSession(providerId, GITHUB_SCOPE_READ_USER, { silent: true });82if (fallback) {83return fallback;84}85return undefined;86},87options88);89}9091/**92* Get a session with an access token that has the same scopes as other GitHub extensions like GitHub Pull Requests.93* @param configurationService for determining the auth provider94* @param options what get passed in to getSession95* @returns an auth session with a token with the aligned scopes, or undefined if none is found96* @deprecated use `IAuthenticationService` instead97*/98export function getAlignedSession(configurationService: IConfigurationService, options: AuthenticationGetSessionOptions): Promise<AuthenticationSession | undefined> {99if (configurationService.getConfig(ConfigKey.Shared.AuthPermissions) === AuthPermissionMode.Minimal) {100if (options.createIfNone || options.forceNewSession) {101throw new MinimalModeError();102}103return Promise.resolve(undefined);104}105const providerId = authProviderId(configurationService);106return getAuthSession(107providerId,108GITHUB_SCOPE_ALIGNED,109async () => await authentication.getSession(providerId, GITHUB_SCOPE_ALIGNED, { silent: true }),110options111);112}113114export function authChangeAffectsCopilot(event: AuthenticationSessionsChangeEvent, configurationService: IConfigurationService): boolean {115const provider = event.provider;116const providerId = authProviderId(configurationService);117return provider.id === providerId;118}119120121