Have getCurrentAuthenticationSessionInfo take in a secretStorageService (#187092)

To start the migration process, we have `getCurrentAuthenticationSessionInfo` take in a `secretStorageService` and also replaces the `LocalStorageCredentialProvider` with a `LocalStorageSecretStorageProvider`.

After this goes in we can then update all embedders (vscode.dev, github.dev, Codespaces, vscode-web-test?) and replace ICredentialProvider usages with ISecretStorageProviders and then once we do that, we can get rid of a bunch of code!
This commit is contained in:
Tyler James Leonhardt
2023-07-06 00:09:01 +02:00
committed by GitHub
parent c7d219b53f
commit 322bc2d7d8
8 changed files with 75 additions and 149 deletions
+40 -137
View File
@@ -4,38 +4,32 @@
*--------------------------------------------------------------------------------------------*/
import { isStandalone } from 'vs/base/browser/browser';
import { CancellationToken } from 'vs/base/common/cancellation';
import { parse } from 'vs/base/common/marshalling';
import { Emitter } from 'vs/base/common/event';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { isEqual } from 'vs/base/common/resources';
import { URI, UriComponents } from 'vs/base/common/uri';
import { request } from 'vs/base/parts/request/browser/request';
import product from 'vs/platform/product/common/product';
import { isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/window/common/window';
import { create } from 'vs/workbench/workbench.web.main';
import { posix } from 'vs/base/common/path';
import { ltrim } from 'vs/base/common/strings';
import type { ICredentialsProvider } from 'vs/platform/credentials/common/credentials';
import type { IURLCallbackProvider } from 'vs/workbench/services/url/browser/urlService';
import type { IWorkbenchConstructionOptions } from 'vs/workbench/browser/web.api';
import type { IWorkspace, IWorkspaceProvider } from 'vs/workbench/services/host/browser/browserHostService';
import { ISecretStorageProvider } from 'vs/platform/secrets/common/secrets';
import { AuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService';
interface ICredential {
service: string;
account: string;
password: string;
}
class LocalStorageSecretStorageProvider implements ISecretStorageProvider {
private static readonly STORAGE_KEY = 'secrets.provider';
class LocalStorageCredentialsProvider implements ICredentialsProvider {
private _secrets: Record<string, string> | undefined;
private static readonly CREDENTIALS_STORAGE_KEY = 'credentials.provider';
private readonly authService: string | undefined;
type: 'in-memory' | 'persisted' | 'unknown' = 'persisted';
constructor() {
let authSessionInfo: { readonly id: string; readonly accessToken: string; readonly providerId: string; readonly canSignOut?: boolean; readonly scopes: string[][] } | undefined;
let authSessionInfo: (AuthenticationSessionInfo & { scopes: string[][] }) | undefined;
const authSessionElement = document.getElementById('vscode-workbench-auth-session');
const authSessionElementAttribute = authSessionElement ? authSessionElement.getAttribute('data-settings') : undefined;
if (authSessionElementAttribute) {
@@ -46,11 +40,15 @@ class LocalStorageCredentialsProvider implements ICredentialsProvider {
if (authSessionInfo) {
// Settings Sync Entry
this.setPassword(`${product.urlProtocol}.login`, 'account', JSON.stringify(authSessionInfo));
this.set(`${product.urlProtocol}.loginAccount`, JSON.stringify(authSessionInfo));
// Auth extension Entry
this.authService = `${product.urlProtocol}-${authSessionInfo.providerId}.login`;
this.setPassword(this.authService, 'account', JSON.stringify(authSessionInfo.scopes.map(scopes => ({
if (authSessionInfo.providerId !== 'github') {
console.error(`Unexpected auth provider: ${authSessionInfo.providerId}. Expected 'github'.`);
return;
}
const authAccount = JSON.stringify({ extensionId: 'vscode.github-authentication', key: 'github.auth' });
this.set(authAccount, JSON.stringify(authSessionInfo.scopes.map(scopes => ({
id: authSessionInfo!.id,
scopes,
accessToken: authSessionInfo!.accessToken
@@ -58,121 +56,44 @@ class LocalStorageCredentialsProvider implements ICredentialsProvider {
}
}
private _credentials: ICredential[] | undefined;
private get credentials(): ICredential[] {
if (!this._credentials) {
get(key: string): Promise<string | undefined> {
return Promise.resolve(this.secrets[key]);
}
set(key: string, value: string): Promise<void> {
this.secrets[key] = value;
this.save();
return Promise.resolve();
}
async delete(key: string): Promise<void> {
delete this.secrets[key];
this.save();
return Promise.resolve();
}
private get secrets(): Record<string, string> {
if (!this._secrets) {
try {
const serializedCredentials = window.localStorage.getItem(LocalStorageCredentialsProvider.CREDENTIALS_STORAGE_KEY);
const serializedCredentials = window.localStorage.getItem(LocalStorageSecretStorageProvider.STORAGE_KEY);
if (serializedCredentials) {
this._credentials = JSON.parse(serializedCredentials);
this._secrets = JSON.parse(serializedCredentials);
}
} catch (error) {
// ignore
}
if (!Array.isArray(this._credentials)) {
this._credentials = [];
if (!(this._secrets instanceof Object)) {
this._secrets = {};
}
}
return this._credentials;
return this._secrets;
}
private save(): void {
window.localStorage.setItem(LocalStorageCredentialsProvider.CREDENTIALS_STORAGE_KEY, JSON.stringify(this.credentials));
}
async getPassword(service: string, account: string): Promise<string | null> {
return this.doGetPassword(service, account);
}
private async doGetPassword(service: string, account?: string): Promise<string | null> {
for (const credential of this.credentials) {
if (credential.service === service) {
if (typeof account !== 'string' || account === credential.account) {
return credential.password;
}
}
}
return null;
}
async setPassword(service: string, account: string, password: string): Promise<void> {
this.doDeletePassword(service, account);
this.credentials.push({ service, account, password });
this.save();
try {
if (password && service === this.authService) {
const value = JSON.parse(password);
if (Array.isArray(value) && value.length === 0) {
await this.logout(service);
}
}
} catch (error) {
console.log(error);
}
}
async deletePassword(service: string, account: string): Promise<boolean> {
const result = await this.doDeletePassword(service, account);
if (result && service === this.authService) {
try {
await this.logout(service);
} catch (error) {
console.log(error);
}
}
return result;
}
private async doDeletePassword(service: string, account: string): Promise<boolean> {
let found = false;
this._credentials = this.credentials.filter(credential => {
if (credential.service === service && credential.account === account) {
found = true;
return false;
}
return true;
});
if (found) {
this.save();
}
return found;
}
async findPassword(service: string): Promise<string | null> {
return this.doGetPassword(service);
}
async findCredentials(service: string): Promise<Array<{ account: string; password: string }>> {
return this.credentials
.filter(credential => credential.service === service)
.map(({ account, password }) => ({ account, password }));
}
private async logout(service: string): Promise<void> {
const queryValues: Map<string, string> = new Map();
queryValues.set('logout', String(true));
queryValues.set('service', service);
await request({
url: doCreateUri('/auth/logout', queryValues).toString(true)
}, CancellationToken.None);
}
async clear(): Promise<void> {
window.localStorage.removeItem(LocalStorageCredentialsProvider.CREDENTIALS_STORAGE_KEY);
window.localStorage.setItem(LocalStorageSecretStorageProvider.STORAGE_KEY, JSON.stringify(this.secrets));
}
}
@@ -469,24 +390,6 @@ class WorkspaceProvider implements IWorkspaceProvider {
}
}
function doCreateUri(path: string, queryValues: Map<string, string>): URI {
let query: string | undefined = undefined;
if (queryValues) {
let index = 0;
queryValues.forEach((value, key) => {
if (!query) {
query = '';
}
const prefix = (index++ === 0) ? '' : '&';
query += `${prefix}${key}=${encodeURIComponent(value)}`;
});
}
return URI.parse(window.location.href).with({ path, query });
}
(function () {
// Find config by checking for DOM
@@ -504,6 +407,6 @@ function doCreateUri(path: string, queryValues: Map<string, string>): URI {
settingsSyncOptions: config.settingsSyncOptions ? { enabled: config.settingsSyncOptions.enabled, } : undefined,
workspaceProvider: WorkspaceProvider.create(config),
urlCallbackProvider: new LocalStorageURLCallbackProvider(config.callbackRoute),
credentialsProvider: config.remoteAuthority ? undefined /* with a remote, we don't use a local credentials provider */ : new LocalStorageCredentialsProvider()
secretStorageProvider: config.remoteAuthority ? undefined /* with a remote, we don't use a local secret storage provider */ : new LocalStorageSecretStorageProvider()
});
})();