Path: blob/main/src/vs/workbench/services/authentication/common/authentication.ts
5252 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*--------------------------------------------------------------------------------------------*/4import { Event } from '../../../../base/common/event.js';5import { IDisposable } from '../../../../base/common/lifecycle.js';6import { IAuthenticationChallenge, IAuthorizationProtectedResourceMetadata, IAuthorizationServerMetadata } from '../../../../base/common/oauth.js';7import { URI } from '../../../../base/common/uri.js';8import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';910/**11* Use this if you don't want the onDidChangeSessions event to fire in the extension host12*/13export const INTERNAL_AUTH_PROVIDER_PREFIX = '__';1415export interface AuthenticationSessionAccount {16label: string;17id: string;18}1920export interface AuthenticationSession {21id: string;22accessToken: string;23account: AuthenticationSessionAccount;24scopes: ReadonlyArray<string>;25idToken?: string;26}2728export interface AuthenticationSessionsChangeEvent {29added: ReadonlyArray<AuthenticationSession> | undefined;30removed: ReadonlyArray<AuthenticationSession> | undefined;31changed: ReadonlyArray<AuthenticationSession> | undefined;32}3334export interface AuthenticationProviderInformation {35id: string;36label: string;37authorizationServerGlobs?: ReadonlyArray<string>;38}3940/**41* Options for creating an authentication session via the service.42*/43export interface IAuthenticationCreateSessionOptions {44activateImmediate?: boolean;45/**46* The account that is being asked about. If this is passed in, the provider should47* attempt to return the sessions that are only related to this account.48*/49account?: AuthenticationSessionAccount;50/**51* The authorization server URI to use for this creation request. If passed in, first we validate that52* the provider can use this authorization server, then it is passed down to the auth provider.53*/54authorizationServer?: URI;55/**56* Allows the authentication provider to take in additional parameters.57* It is up to the provider to define what these parameters are and handle them.58* This is useful for passing in additional information that is specific to the provider59* and not part of the standard authentication flow.60*/61[key: string]: any;62}6364export interface IAuthenticationWwwAuthenticateRequest {65/**66* The raw WWW-Authenticate header value that triggered this challenge.67* This will be parsed by the authentication provider to extract the necessary68* challenge information.69*/70readonly wwwAuthenticate: string;7172/**73* Optional scopes for the session. If not provided, the authentication provider74* may use default scopes or extract them from the challenge.75*/76readonly fallbackScopes?: readonly string[];77}7879export function isAuthenticationWwwAuthenticateRequest(obj: unknown): obj is IAuthenticationWwwAuthenticateRequest {80return typeof obj === 'object'81&& obj !== null82&& 'wwwAuthenticate' in obj83&& (typeof obj.wwwAuthenticate === 'string');84}8586/**87* Represents constraints for authentication, including challenges and optional scopes.88* This is used when creating or retrieving sessions that must satisfy specific authentication89* requirements from WWW-Authenticate headers.90*/91export interface IAuthenticationConstraint {92/**93* Array of authentication challenges parsed from WWW-Authenticate headers.94*/95readonly challenges: readonly IAuthenticationChallenge[];9697/**98* Optional scopes for the session. If not provided, the authentication provider99* may extract scopes from the challenges or use default scopes.100*/101readonly fallbackScopes?: readonly string[];102}103104/**105* Options for getting authentication sessions via the service.106*/107export interface IAuthenticationGetSessionsOptions {108/**109* The account that is being asked about. If this is passed in, the provider should110* attempt to return the sessions that are only related to this account.111*/112account?: AuthenticationSessionAccount;113/**114* The authorization server URI to use for this request. If passed in, first we validate that115* the provider can use this authorization server, then it is passed down to the auth provider.116*/117authorizationServer?: URI;118/**119* Allows the authentication provider to take in additional parameters.120* It is up to the provider to define what these parameters are and handle them.121* This is useful for passing in additional information that is specific to the provider122* and not part of the standard authentication flow.123*/124[key: string]: any;125}126127export interface AllowedExtension {128id: string;129name: string;130/**131* If true or undefined, the extension is allowed to use the account132* If false, the extension is not allowed to use the account133* TODO: undefined shouldn't be a valid value, but it is for now134*/135allowed?: boolean;136lastUsed?: number;137// If true, this comes from the product.json138trusted?: boolean;139}140141export interface IAuthenticationProviderHostDelegate {142/** Priority for this delegate, delegates are tested in descending priority order */143readonly priority: number;144create(authorizationServer: URI, serverMetadata: IAuthorizationServerMetadata, resource: IAuthorizationProtectedResourceMetadata | undefined): Promise<string>;145}146147export const IAuthenticationService = createDecorator<IAuthenticationService>('IAuthenticationService');148149export interface IAuthenticationService {150readonly _serviceBrand: undefined;151152/**153* Fires when an authentication provider has been registered154*/155readonly onDidRegisterAuthenticationProvider: Event<AuthenticationProviderInformation>;156/**157* Fires when an authentication provider has been unregistered158*/159readonly onDidUnregisterAuthenticationProvider: Event<AuthenticationProviderInformation>;160161/**162* Fires when the list of sessions for a provider has been added, removed or changed163*/164readonly onDidChangeSessions: Event<{ providerId: string; label: string; event: AuthenticationSessionsChangeEvent }>;165166/**167* Fires when the list of declaredProviders has changed168*/169readonly onDidChangeDeclaredProviders: Event<void>;170171/**172* All providers that have been statically declared by extensions. These may not actually be registered or active yet.173*/174readonly declaredProviders: AuthenticationProviderInformation[];175176/**177* Registers that an extension has declared an authentication provider in their package.json178* @param provider The provider information to register179*/180registerDeclaredAuthenticationProvider(provider: AuthenticationProviderInformation): void;181182/**183* Unregisters a declared authentication provider184* @param id The id of the provider to unregister185*/186unregisterDeclaredAuthenticationProvider(id: string): void;187188/**189* Checks if an authentication provider has been registered190* @param id The id of the provider to check191*/192isAuthenticationProviderRegistered(id: string): boolean;193194/**195* Checks if an authentication provider is dynamic196* @param id The id of the provider to check197*/198isDynamicAuthenticationProvider(id: string): boolean;199200/**201* Registers an authentication provider202* @param id The id of the provider203* @param provider The implementation of the provider204*/205registerAuthenticationProvider(id: string, provider: IAuthenticationProvider): void;206207/**208* Unregisters an authentication provider209* @param id The id of the provider to unregister210*/211unregisterAuthenticationProvider(id: string): void;212213/**214* Gets the provider ids of all registered authentication providers215*/216getProviderIds(): string[];217218/**219* Gets the provider with the given id.220* @param id The id of the provider to get221* @throws if the provider is not registered222*/223getProvider(id: string): IAuthenticationProvider;224225/**226* Gets all accounts that are currently logged in across all sessions227* @param id The id of the provider to ask for accounts228* @returns A promise that resolves to an array of accounts229*/230getAccounts(id: string): Promise<ReadonlyArray<AuthenticationSessionAccount>>;231232/**233* Gets all sessions that satisfy the given scopes from the provider with the given id234* @param id The id of the provider to ask for a session235* @param scopes The scopes for the session236* @param options Additional options for getting sessions237* @param activateImmediate If true, the provider should activate immediately if it is not already238*/239getSessions(id: string, scopeListOrRequest?: ReadonlyArray<string> | IAuthenticationWwwAuthenticateRequest, options?: IAuthenticationGetSessionsOptions, activateImmediate?: boolean): Promise<ReadonlyArray<AuthenticationSession>>;240241/**242* Creates an AuthenticationSession with the given provider and scopes243* @param providerId The id of the provider244* @param scopes The scopes to request245* @param options Additional options for creating the session246*/247createSession(providerId: string, scopeListOrRequest: ReadonlyArray<string> | IAuthenticationWwwAuthenticateRequest, options?: IAuthenticationCreateSessionOptions): Promise<AuthenticationSession>;248249/**250* Removes the session with the given id from the provider with the given id251* @param providerId The id of the provider252* @param sessionId The id of the session to remove253*/254removeSession(providerId: string, sessionId: string): Promise<void>;255256/**257* Gets a provider id for a specified authorization server258* @param authorizationServer The authorization server url that this provider is responsible for259* @param resourceServer The resource server URI that should match the provider's resourceServer (if defined)260*/261getOrActivateProviderIdForServer(authorizationServer: URI, resourceServer?: URI): Promise<string | undefined>;262263/**264* Allows the ability register a delegate that will be used to start authentication providers265* @param delegate The delegate to register266*/267registerAuthenticationProviderHostDelegate(delegate: IAuthenticationProviderHostDelegate): IDisposable;268269/**270* Creates a dynamic authentication provider for the given server metadata271* @param serverMetadata The metadata for the server that is being authenticated against272*/273createDynamicAuthenticationProvider(authorizationServer: URI, serverMetadata: IAuthorizationServerMetadata, resourceMetadata: IAuthorizationProtectedResourceMetadata | undefined): Promise<IAuthenticationProvider | undefined>;274}275276export function isAuthenticationSession(thing: unknown): thing is AuthenticationSession {277if (typeof thing !== 'object' || !thing) {278return false;279}280const maybe = thing as AuthenticationSession;281if (typeof maybe.id !== 'string') {282return false;283}284if (typeof maybe.accessToken !== 'string') {285return false;286}287if (typeof maybe.account !== 'object' || !maybe.account) {288return false;289}290if (typeof maybe.account.label !== 'string') {291return false;292}293if (typeof maybe.account.id !== 'string') {294return false;295}296if (!Array.isArray(maybe.scopes)) {297return false;298}299if (maybe.idToken && typeof maybe.idToken !== 'string') {300return false;301}302return true;303}304305// TODO: Move this into MainThreadAuthentication306export const IAuthenticationExtensionsService = createDecorator<IAuthenticationExtensionsService>('IAuthenticationExtensionsService');307export interface IAuthenticationExtensionsService {308readonly _serviceBrand: undefined;309310/**311* Fires when an account preference for a specific provider has changed for the specified extensions. Does not fire when:312* * An account preference is removed313* * A session preference is changed (because it's deprecated)314* * A session preference is removed (because it's deprecated)315*/316readonly onDidChangeAccountPreference: Event<{ extensionIds: string[]; providerId: string }>;317/**318* Returns the accountName (also known as account.label) to pair with `IAuthenticationAccessService` to get the account preference319* @param providerId The authentication provider id320* @param extensionId The extension id to get the preference for321* @returns The accountName of the preference, or undefined if there is no preference set322*/323getAccountPreference(extensionId: string, providerId: string): string | undefined;324/**325* Sets the account preference for the given provider and extension326* @param providerId The authentication provider id327* @param extensionId The extension id to set the preference for328* @param account The account to set the preference to329*/330updateAccountPreference(extensionId: string, providerId: string, account: AuthenticationSessionAccount): void;331/**332* Removes the account preference for the given provider and extension333* @param providerId The authentication provider id334* @param extensionId The extension id to remove the preference for335*/336removeAccountPreference(extensionId: string, providerId: string): void;337/**338* @deprecated Sets the session preference for the given provider and extension339* @param providerId340* @param extensionId341* @param session342*/343updateSessionPreference(providerId: string, extensionId: string, session: AuthenticationSession): void;344/**345* @deprecated Gets the session preference for the given provider and extension346* @param providerId347* @param extensionId348* @param scopes349*/350getSessionPreference(providerId: string, extensionId: string, scopes: string[]): string | undefined;351/**352* @deprecated Removes the session preference for the given provider and extension353* @param providerId354* @param extensionId355* @param scopes356*/357removeSessionPreference(providerId: string, extensionId: string, scopes: string[]): void;358selectSession(providerId: string, extensionId: string, extensionName: string, scopeListOrRequest: ReadonlyArray<string> | IAuthenticationWwwAuthenticateRequest, possibleSessions: readonly AuthenticationSession[]): Promise<AuthenticationSession>;359requestSessionAccess(providerId: string, extensionId: string, extensionName: string, scopeListOrRequest: ReadonlyArray<string> | IAuthenticationWwwAuthenticateRequest, possibleSessions: readonly AuthenticationSession[]): void;360requestNewSession(providerId: string, scopeListOrRequest: ReadonlyArray<string> | IAuthenticationWwwAuthenticateRequest, extensionId: string, extensionName: string): Promise<void>;361updateNewSessionRequests(providerId: string, addedSessions: readonly AuthenticationSession[]): void;362}363364/**365* Options passed to the authentication provider when asking for sessions.366*/367export interface IAuthenticationProviderSessionOptions {368/**369* The account that is being asked about. If this is passed in, the provider should370* attempt to return the sessions that are only related to this account.371*/372account?: AuthenticationSessionAccount;373/**374* The authorization server that is being asked about. If this is passed in, the provider should375* attempt to return sessions that are only related to this authorization server.376*/377authorizationServer?: URI;378/**379* Allows the authentication provider to take in additional parameters.380* It is up to the provider to define what these parameters are and handle them.381* This is useful for passing in additional information that is specific to the provider382* and not part of the standard authentication flow.383*/384[key: string]: any;385}386387/**388* Represents an authentication provider.389*/390export interface IAuthenticationProvider {391/**392* The unique identifier of the authentication provider.393*/394readonly id: string;395396/**397* The display label of the authentication provider.398*/399readonly label: string;400401/**402* The resource server URI that this provider is responsible for, if any.403* TODO@TylerLeonhardt: Rather than this being added to the provider, it should be passed in to404* getSessions/createSession/etc... this way we can have providers that handle multiple resource servers.405*/406readonly resourceServer?: URI;407408/**409* The resolved authorization servers. These can still contain globs, but should be concrete URIs410*/411readonly authorizationServers?: ReadonlyArray<URI>;412413/**414* Indicates whether the authentication provider supports multiple accounts.415*/416readonly supportsMultipleAccounts: boolean;417418/**419* Optional function to provide a custom confirmation message for authentication prompts.420* If not implemented, the default confirmation messages will be used.421* @param extensionName - The name of the extension requesting authentication.422* @param recreatingSession - Whether this is recreating an existing session.423* @returns A custom confirmation message or undefined to use the default message.424*/425readonly confirmation?: (extensionName: string, recreatingSession: boolean) => string | undefined;426427/**428* An {@link Event} which fires when the array of sessions has changed, or data429* within a session has changed.430*/431readonly onDidChangeSessions: Event<AuthenticationSessionsChangeEvent>;432433/**434* Retrieves a list of authentication sessions.435* @param scopes - An optional list of scopes. If provided, the sessions returned should match these permissions, otherwise all sessions should be returned.436* @param options - Additional options for getting sessions.437* @returns A promise that resolves to an array of authentication sessions.438*/439getSessions(scopes: string[] | undefined, options: IAuthenticationProviderSessionOptions): Promise<readonly AuthenticationSession[]>;440441/**442* Prompts the user to log in.443* If login is successful, the `onDidChangeSessions` event should be fired.444* If login fails, a rejected promise should be returned.445* If the provider does not support multiple accounts, this method should not be called if there is already an existing session matching the provided scopes.446* @param scopes - A list of scopes that the new session should be created with.447* @param options - Additional options for creating the session.448* @returns A promise that resolves to an authentication session.449*/450createSession(scopes: string[], options: IAuthenticationProviderSessionOptions): Promise<AuthenticationSession>;451452/**453* Get existing sessions that match the given authentication constraints.454*455* @param constraint The authentication constraint containing challenges and optional scopes456* @param options Options for the session request457* @returns A thenable that resolves to an array of existing authentication sessions458*/459getSessionsFromChallenges?(constraint: IAuthenticationConstraint, options: IAuthenticationProviderSessionOptions): Promise<readonly AuthenticationSession[]>;460461/**462* Create a new session based on authentication constraints.463* This is called when no existing session matches the constraint requirements.464*465* @param constraint The authentication constraint containing challenges and optional scopes466* @param options Options for the session creation467* @returns A thenable that resolves to a new authentication session468*/469createSessionFromChallenges?(constraint: IAuthenticationConstraint, options: IAuthenticationProviderSessionOptions): Promise<AuthenticationSession>;470471/**472* Removes the session corresponding to the specified session ID.473* If the removal is successful, the `onDidChangeSessions` event should be fired.474* If a session cannot be removed, the provider should reject with an error message.475* @param sessionId - The ID of the session to remove.476*/477removeSession(sessionId: string): Promise<void>;478}479480481