mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-14 23:30:23 +01:00
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:
@@ -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()
|
||||
});
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user