From e3ecd7bd1ed3ef21c8177eab9f1be6446288493f Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Thu, 27 Jul 2023 00:21:59 -0700 Subject: [PATCH] [vscode] support AuthenticationForceNewSessionOptions with detail message (#12752) * Defines and supports this new type in the authentification service. * Updates prompt when new session is forced if detail message is present. Contributed on behalf of STMicroelectronics --- CHANGELOG.md | 1 + .../src/main/browser/authentication-main.ts | 29 +++++++++++++++---- .../src/plugin/authentication-ext.ts | 4 +-- packages/plugin/src/theia.d.ts | 13 ++++++++- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 909ae1c4dbfc8..710f39b8b9940 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - [workspace] Implement CanonicalUriProvider API [#12743](https://github.com/eclipse-theia/theia/pull/12743) - Contributed on behalf of STMicroelectronics - Show command shortcuts in toolbar item tooltips. [#12660](https://github.com/eclipse-theia/theia/pull/12660) - Contributed on behalf of STMicroelectronics - [cli] added `check:theia-extensions` which checks the uniqueness of Theia extension versions [#12596](https://github.com/eclipse-theia/theia/pull/12596) - Contributed on behalf of STMicroelectronics +- [vscode] Support AuthenticationForceNewSessionOptions and detail message [#12752](https://github.com/eclipse-theia/theia/pull/12752) - Contributed on behalf of STMicroelectronics - [vscode] Add support for the TaskPresentationOptions close property [#12749](https://github.com/eclipse-theia/theia/pull/12749) - Contributed on behalf of STMicroelectronics [Breaking Changes:](#breaking_changes_1.40.0) diff --git a/packages/plugin-ext/src/main/browser/authentication-main.ts b/packages/plugin-ext/src/main/browser/authentication-main.ts index d058ba2577b2e..046e5a7c81751 100644 --- a/packages/plugin-ext/src/main/browser/authentication-main.ts +++ b/packages/plugin-ext/src/main/browser/authentication-main.ts @@ -24,7 +24,7 @@ import { interfaces } from '@theia/core/shared/inversify'; import { AuthenticationExt, AuthenticationMain, MAIN_RPC_CONTEXT } from '../../common/plugin-api-rpc'; import { RPCProtocol } from '../../common/rpc-protocol'; import { MessageService } from '@theia/core/lib/common/message-service'; -import { Dialog, StorageService } from '@theia/core/lib/browser'; +import { ConfirmDialog, Dialog, StorageService } from '@theia/core/lib/browser'; import { AuthenticationProvider, AuthenticationService, @@ -34,6 +34,7 @@ import { QuickPickService } from '@theia/core/lib/common/quick-pick-service'; import * as theia from '@theia/plugin'; import { QuickPickValue } from '@theia/core/lib/browser/quick-input/quick-input-service'; import { nls } from '@theia/core/lib/common/nls'; +import { isObject } from '@theia/core'; export function getAuthenticationProviderActivationEvent(id: string): string { return `onAuthenticationRequest:${id}`; } @@ -117,7 +118,7 @@ export class AuthenticationMainImpl implements AuthenticationMain { // We may need to prompt because we don't have a valid session modal flows if (options.createIfNone || options.forceNewSession) { const providerName = this.authenticationService.getLabel(providerId); - const detail = (typeof options.forceNewSession === 'object') ? options.forceNewSession!.detail : undefined; + const detail = isAuthenticationForceNewSessionOptions(options.forceNewSession) ? options.forceNewSession!.detail : undefined; const isAllowed = await this.loginPrompt(providerName, extensionName, !!options.forceNewSession, detail); if (!isAllowed) { throw new Error('User did not consent to login.'); @@ -197,12 +198,24 @@ export class AuthenticationMainImpl implements AuthenticationMain { return allow; } - protected async loginPrompt(providerName: string, extensionName: string, recreatingSession: boolean, _detail?: string): Promise { - const message = recreatingSession + protected async loginPrompt(providerName: string, extensionName: string, recreatingSession: boolean, detail?: string): Promise { + const msg = document.createElement('span'); + msg.textContent = recreatingSession ? nls.localizeByDefault("The extension '{0}' wants you to sign in again using {1}.", extensionName, providerName) : nls.localizeByDefault("The extension '{0}' wants to sign in using {1}.", extensionName, providerName); - const choice = await this.messageService.info(message, 'Allow', 'Cancel'); - return choice === 'Allow'; + + if (detail) { + const detailElement = document.createElement('p'); + detailElement.textContent = detail; + msg.appendChild(detailElement); + } + + return !!await new ConfirmDialog({ + title: nls.localize('theia/plugin-ext/authentication-main/loginTitle', 'Login'), + msg, + ok: nls.localizeByDefault('Allow'), + cancel: Dialog.CANCEL + }).open(); } protected async isAccessAllowed(providerId: string, accountName: string, extensionId: string): Promise { @@ -224,6 +237,10 @@ export class AuthenticationMainImpl implements AuthenticationMain { } } +function isAuthenticationForceNewSessionOptions(arg: unknown): arg is theia.AuthenticationForceNewSessionOptions { + return isObject(arg) && typeof arg.detail === 'string'; +} + async function addAccountUsage(storageService: StorageService, providerId: string, accountName: string, extensionId: string, extensionName: string): Promise { const accountKey = `authentication-${providerId}-${accountName}-usages`; const usages = await readAccountUsages(storageService, providerId, accountName); diff --git a/packages/plugin-ext/src/plugin/authentication-ext.ts b/packages/plugin-ext/src/plugin/authentication-ext.ts index b8977bf33509c..25dba1325d0ab 100644 --- a/packages/plugin-ext/src/plugin/authentication-ext.ts +++ b/packages/plugin-ext/src/plugin/authentication-ext.ts @@ -42,12 +42,12 @@ export class AuthenticationExtImpl implements AuthenticationExt { } async getSession(requestingExtension: InternalPlugin, providerId: string, scopes: readonly string[], - options: theia.AuthenticationGetSessionOptions & ({ createIfNone: true } | { forceNewSession: true } | { forceNewSession: { detail: string } })): + options: theia.AuthenticationGetSessionOptions & ({ createIfNone: true } | { forceNewSession: true } | { forceNewSession: theia.AuthenticationForceNewSessionOptions })): Promise; async getSession(requestingExtension: InternalPlugin, providerId: string, scopes: readonly string[], options: theia.AuthenticationGetSessionOptions & { forceNewSession: true }): Promise; async getSession(requestingExtension: InternalPlugin, providerId: string, scopes: readonly string[], - options: theia.AuthenticationGetSessionOptions & { forceNewSession: { detail: string } }): Promise; + options: theia.AuthenticationGetSessionOptions & { forceNewSession: theia.AuthenticationForceNewSessionOptions }): Promise; async getSession(requestingExtension: InternalPlugin, providerId: string, scopes: readonly string[], options: theia.AuthenticationGetSessionOptions): Promise; async getSession(requestingExtension: InternalPlugin, providerId: string, scopes: readonly string[], diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 977221ce9ec97..46655c88d1b19 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -13671,6 +13671,17 @@ export module '@theia/plugin' { readonly label: string; } + /** + * Optional options to be used when calling {@link authentication.getSession} with the flag `forceNewSession`. + */ + export interface AuthenticationForceNewSessionOptions { + /** + * An optional message that will be displayed to the user when we ask to re-authenticate. Providing additional context + * as to why you are asking a user to re-authenticate can help increase the odds that they will accept. + */ + detail?: string; + } + /** * Options to be used when getting an {@link AuthenticationSession AuthenticationSession} from an {@link AuthenticationProvider AuthenticationProvider}. */ @@ -13705,7 +13716,7 @@ export module '@theia/plugin' { * * Defaults to false. */ - forceNewSession?: boolean | { detail: string }; + forceNewSession?: boolean | AuthenticationForceNewSessionOptions; /** * Whether we should show the indication to sign in in the Accounts menu.