mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode a234f13c45b40a0929777cb440ee011b7549eed2 (#8911)
* Merge from vscode a234f13c45b40a0929777cb440ee011b7549eed2 * update distro * fix layering * update distro * fix tests
This commit is contained in:
265
extensions/vscode-account/src/AADHelper.ts
Normal file
265
extensions/vscode-account/src/AADHelper.ts
Normal file
@@ -0,0 +1,265 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as crypto from 'crypto';
|
||||
import * as vscode from 'vscode';
|
||||
import * as https from 'https';
|
||||
import * as querystring from 'querystring';
|
||||
import { keychain } from './keychain';
|
||||
import { toBase64UrlEncoding } from './utils';
|
||||
import { createServer, startServer } from './authServer';
|
||||
|
||||
const redirectUrl = 'https://vscode-redirect.azurewebsites.net/';
|
||||
const loginEndpointUrl = 'https://login.microsoftonline.com/';
|
||||
const clientId = 'aebc6443-996d-45c2-90f0-388ff96faa56';
|
||||
const scope = 'https://management.core.windows.net/.default offline_access';
|
||||
const tenant = 'common';
|
||||
|
||||
interface IToken {
|
||||
expiresIn: string; // How long access token is valid, in seconds
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
}
|
||||
|
||||
export const onDidChangeSessions = new vscode.EventEmitter<void>();
|
||||
|
||||
export class AzureActiveDirectoryService {
|
||||
private _token: IToken | undefined;
|
||||
private _refreshTimeout: NodeJS.Timeout | undefined;
|
||||
|
||||
public async initialize(): Promise<void> {
|
||||
const existingRefreshToken = await keychain.getToken();
|
||||
if (existingRefreshToken) {
|
||||
await this.refreshToken(existingRefreshToken);
|
||||
}
|
||||
|
||||
this.pollForChange();
|
||||
}
|
||||
|
||||
private pollForChange() {
|
||||
setTimeout(async () => {
|
||||
const refreshToken = await keychain.getToken();
|
||||
// Another window has logged in, generate access token for this instance.
|
||||
if (refreshToken && !this._token) {
|
||||
await this.refreshToken(refreshToken);
|
||||
onDidChangeSessions.fire();
|
||||
}
|
||||
|
||||
// Another window has logged out
|
||||
if (!refreshToken && this._token) {
|
||||
await this.logout();
|
||||
onDidChangeSessions.fire();
|
||||
}
|
||||
|
||||
this.pollForChange();
|
||||
}, 1000 * 30);
|
||||
}
|
||||
|
||||
private tokenToAccount(token: IToken): vscode.Session {
|
||||
return {
|
||||
id: '',
|
||||
accessToken: token.accessToken,
|
||||
displayName: this.getDisplayNameFromToken(token.accessToken)
|
||||
};
|
||||
}
|
||||
|
||||
private getDisplayNameFromToken(accessToken: string): string {
|
||||
let displayName = 'user@example.com';
|
||||
try {
|
||||
// TODO fixme
|
||||
displayName = JSON.parse(atob(accessToken.split('.')[1]));
|
||||
} catch (e) {
|
||||
// Fall back to example display name
|
||||
}
|
||||
|
||||
return displayName;
|
||||
}
|
||||
|
||||
get sessions(): vscode.Session[] {
|
||||
return this._token ? [this.tokenToAccount(this._token)] : [];
|
||||
}
|
||||
|
||||
public async login(): Promise<void> {
|
||||
const nonce = crypto.randomBytes(16).toString('base64');
|
||||
const { server, redirectPromise, codePromise } = createServer(nonce);
|
||||
|
||||
let token: IToken | undefined;
|
||||
try {
|
||||
const port = await startServer(server);
|
||||
vscode.env.openExternal(vscode.Uri.parse(`http://localhost:${port}/signin?nonce=${encodeURIComponent(nonce)}`));
|
||||
|
||||
const redirectReq = await redirectPromise;
|
||||
if ('err' in redirectReq) {
|
||||
const { err, res } = redirectReq;
|
||||
res.writeHead(302, { Location: `/?error=${encodeURIComponent(err && err.message || 'Unknown error')}` });
|
||||
res.end();
|
||||
throw err;
|
||||
}
|
||||
|
||||
const host = redirectReq.req.headers.host || '';
|
||||
const updatedPortStr = (/^[^:]+:(\d+)$/.exec(Array.isArray(host) ? host[0] : host) || [])[1];
|
||||
const updatedPort = updatedPortStr ? parseInt(updatedPortStr, 10) : port;
|
||||
|
||||
const state = `${updatedPort},${encodeURIComponent(nonce)}`;
|
||||
|
||||
const codeVerifier = toBase64UrlEncoding(crypto.randomBytes(32).toString('base64'));
|
||||
const codeChallenge = toBase64UrlEncoding(crypto.createHash('sha256').update(codeVerifier).digest('base64'));
|
||||
const loginUrl = `${loginEndpointUrl}${tenant}/oauth2/v2.0/authorize?response_type=code&response_mode=query&client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUrl)}&state=${state}&scope=${encodeURIComponent(scope)}&prompt=select_account&code_challenge_method=S256&code_challenge=${codeChallenge}`;
|
||||
|
||||
await redirectReq.res.writeHead(302, { Location: loginUrl });
|
||||
redirectReq.res.end();
|
||||
|
||||
const codeRes = await codePromise;
|
||||
const res = codeRes.res;
|
||||
|
||||
try {
|
||||
if ('err' in codeRes) {
|
||||
throw codeRes.err;
|
||||
}
|
||||
token = await this.exchangeCodeForToken(codeRes.code, codeVerifier);
|
||||
this.setToken(token);
|
||||
res.writeHead(302, { Location: '/' });
|
||||
res.end();
|
||||
} catch (err) {
|
||||
res.writeHead(302, { Location: `/?error=${encodeURIComponent(err && err.message || 'Unknown error')}` });
|
||||
res.end();
|
||||
}
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
server.close();
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
private async setToken(token: IToken): Promise<void> {
|
||||
this._token = token;
|
||||
|
||||
if (this._refreshTimeout) {
|
||||
clearTimeout(this._refreshTimeout);
|
||||
}
|
||||
|
||||
this._refreshTimeout = setTimeout(async () => {
|
||||
try {
|
||||
await this.refreshToken(token.refreshToken);
|
||||
} catch (e) {
|
||||
await this.logout();
|
||||
} finally {
|
||||
onDidChangeSessions.fire();
|
||||
}
|
||||
}, 1000 * (parseInt(token.expiresIn) - 10));
|
||||
|
||||
await keychain.setToken(token.refreshToken);
|
||||
}
|
||||
|
||||
private async exchangeCodeForToken(code: string, codeVerifier: string): Promise<IToken> {
|
||||
return new Promise((resolve: (value: IToken) => void, reject) => {
|
||||
try {
|
||||
const postData = querystring.stringify({
|
||||
grant_type: 'authorization_code',
|
||||
code: code,
|
||||
client_id: clientId,
|
||||
scope: scope,
|
||||
code_verifier: codeVerifier,
|
||||
redirect_uri: redirectUrl
|
||||
});
|
||||
|
||||
const tokenUrl = vscode.Uri.parse(`${loginEndpointUrl}${tenant}/oauth2/v2.0/token`);
|
||||
|
||||
const post = https.request({
|
||||
host: tokenUrl.authority,
|
||||
path: tokenUrl.path,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Content-Length': postData.length
|
||||
}
|
||||
}, result => {
|
||||
const buffer: Buffer[] = [];
|
||||
result.on('data', (chunk: Buffer) => {
|
||||
buffer.push(chunk);
|
||||
});
|
||||
result.on('end', () => {
|
||||
if (result.statusCode === 200) {
|
||||
const json = JSON.parse(Buffer.concat(buffer).toString());
|
||||
resolve({
|
||||
expiresIn: json.expires_in,
|
||||
accessToken: json.access_token,
|
||||
refreshToken: json.refresh_token
|
||||
});
|
||||
} else {
|
||||
reject(new Error('Unable to login.'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
post.write(postData);
|
||||
|
||||
post.end();
|
||||
post.on('error', err => {
|
||||
reject(err);
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async refreshToken(refreshToken: string): Promise<IToken> {
|
||||
return new Promise((resolve: (value: IToken) => void, reject) => {
|
||||
const postData = querystring.stringify({
|
||||
refresh_token: refreshToken,
|
||||
client_id: clientId,
|
||||
grant_type: 'refresh_token',
|
||||
scope: scope
|
||||
});
|
||||
|
||||
const post = https.request({
|
||||
host: 'login.microsoftonline.com',
|
||||
path: `/${tenant}/oauth2/v2.0/token`,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Content-Length': postData.length
|
||||
}
|
||||
}, result => {
|
||||
const buffer: Buffer[] = [];
|
||||
result.on('data', (chunk: Buffer) => {
|
||||
buffer.push(chunk);
|
||||
});
|
||||
result.on('end', async () => {
|
||||
if (result.statusCode === 200) {
|
||||
const json = JSON.parse(Buffer.concat(buffer).toString());
|
||||
const token = {
|
||||
expiresIn: json.expires_in,
|
||||
accessToken: json.access_token,
|
||||
refreshToken: json.refresh_token
|
||||
};
|
||||
this.setToken(token);
|
||||
resolve(token);
|
||||
} else {
|
||||
await this.logout();
|
||||
reject(new Error('Refreshing token failed.'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
post.write(postData);
|
||||
|
||||
post.end();
|
||||
post.on('error', err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public async logout() {
|
||||
delete this._token;
|
||||
await keychain.deleteToken();
|
||||
if (this._refreshTimeout) {
|
||||
clearTimeout(this._refreshTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
197
extensions/vscode-account/src/authServer.ts
Normal file
197
extensions/vscode-account/src/authServer.ts
Normal file
@@ -0,0 +1,197 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as http from 'http';
|
||||
import * as url from 'url';
|
||||
import * as fs from 'fs';
|
||||
import * as net from 'net';
|
||||
import * as path from 'path';
|
||||
|
||||
interface Deferred<T> {
|
||||
resolve: (result: T | Promise<T>) => void;
|
||||
reject: (reason: any) => void;
|
||||
}
|
||||
|
||||
const _typeof = {
|
||||
number: 'number',
|
||||
string: 'string',
|
||||
undefined: 'undefined',
|
||||
object: 'object',
|
||||
function: 'function'
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is undefined.
|
||||
*/
|
||||
export function isUndefined(obj: any): obj is undefined {
|
||||
return typeof (obj) === _typeof.undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is undefined or null.
|
||||
*/
|
||||
export function isUndefinedOrNull(obj: any): obj is undefined | null {
|
||||
return isUndefined(obj) || obj === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the argument passed in is neither undefined nor null.
|
||||
*/
|
||||
export function assertIsDefined<T>(arg: T | null | undefined): T {
|
||||
if (isUndefinedOrNull(arg)) {
|
||||
throw new Error('Assertion Failed: argument is undefined or null');
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
export function createTerminateServer(server: http.Server) {
|
||||
const sockets: Record<number, net.Socket> = {};
|
||||
let socketCount = 0;
|
||||
server.on('connection', socket => {
|
||||
const id = socketCount++;
|
||||
sockets[id] = socket;
|
||||
socket.on('close', () => {
|
||||
delete sockets[id];
|
||||
});
|
||||
});
|
||||
return async () => {
|
||||
const result = new Promise<Error | undefined>(resolve => server.close(resolve));
|
||||
for (const id in sockets) {
|
||||
sockets[id].destroy();
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export async function startServer(server: http.Server): Promise<string> {
|
||||
let portTimer: NodeJS.Timer;
|
||||
|
||||
function cancelPortTimer() {
|
||||
clearTimeout(portTimer);
|
||||
}
|
||||
|
||||
const port = new Promise<string>((resolve, reject) => {
|
||||
portTimer = setTimeout(() => {
|
||||
reject(new Error('Timeout waiting for port'));
|
||||
}, 5000);
|
||||
|
||||
server.on('listening', () => {
|
||||
const address = server.address();
|
||||
if (typeof address === 'string') {
|
||||
resolve(address);
|
||||
} else {
|
||||
resolve(assertIsDefined(address).port.toString());
|
||||
}
|
||||
});
|
||||
|
||||
server.on('error', err => {
|
||||
reject(err);
|
||||
});
|
||||
|
||||
server.on('close', () => {
|
||||
reject(new Error('Closed'));
|
||||
});
|
||||
|
||||
server.listen(0);
|
||||
});
|
||||
|
||||
port.then(cancelPortTimer, cancelPortTimer);
|
||||
return port;
|
||||
}
|
||||
|
||||
function sendFile(res: http.ServerResponse, filepath: string, contentType: string) {
|
||||
fs.readFile(filepath, (err, body) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
res.writeHead(404);
|
||||
res.end();
|
||||
} else {
|
||||
res.writeHead(200, {
|
||||
'Content-Length': body.length,
|
||||
'Content-Type': contentType
|
||||
});
|
||||
res.end(body);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function callback(nonce: string, reqUrl: url.Url): Promise<string> {
|
||||
const query = reqUrl.query;
|
||||
if (!query || typeof query === 'string') {
|
||||
throw new Error('No query received.');
|
||||
}
|
||||
|
||||
let error = query.error_description || query.error;
|
||||
|
||||
if (!error) {
|
||||
const state = (query.state as string) || '';
|
||||
const receivedNonce = (state.split(',')[1] || '').replace(/ /g, '+');
|
||||
if (receivedNonce !== nonce) {
|
||||
error = 'Nonce does not match.';
|
||||
}
|
||||
}
|
||||
|
||||
const code = query.code as string;
|
||||
if (!error && code) {
|
||||
return code;
|
||||
}
|
||||
|
||||
throw new Error((error as string) || 'No code received.');
|
||||
}
|
||||
|
||||
export function createServer(nonce: string) {
|
||||
type RedirectResult = { req: http.IncomingMessage; res: http.ServerResponse; } | { err: any; res: http.ServerResponse; };
|
||||
let deferredRedirect: Deferred<RedirectResult>;
|
||||
const redirectPromise = new Promise<RedirectResult>((resolve, reject) => deferredRedirect = { resolve, reject });
|
||||
|
||||
type CodeResult = { code: string; res: http.ServerResponse; } | { err: any; res: http.ServerResponse; };
|
||||
let deferredCode: Deferred<CodeResult>;
|
||||
const codePromise = new Promise<CodeResult>((resolve, reject) => deferredCode = { resolve, reject });
|
||||
|
||||
const codeTimer = setTimeout(() => {
|
||||
deferredCode.reject(new Error('Timeout waiting for code'));
|
||||
}, 5 * 60 * 1000);
|
||||
|
||||
function cancelCodeTimer() {
|
||||
clearTimeout(codeTimer);
|
||||
}
|
||||
|
||||
const server = http.createServer(function (req, res) {
|
||||
const reqUrl = url.parse(req.url!, /* parseQueryString */ true);
|
||||
switch (reqUrl.pathname) {
|
||||
case '/signin':
|
||||
const receivedNonce = ((reqUrl.query.nonce as string) || '').replace(/ /g, '+');
|
||||
if (receivedNonce === nonce) {
|
||||
deferredRedirect.resolve({ req, res });
|
||||
} else {
|
||||
const err = new Error('Nonce does not match.');
|
||||
deferredRedirect.resolve({ err, res });
|
||||
}
|
||||
break;
|
||||
case '/':
|
||||
sendFile(res, path.join(__dirname, '../media/auth.html'), 'text/html; charset=utf-8');
|
||||
break;
|
||||
case '/auth.css':
|
||||
sendFile(res, path.join(__dirname, '../media/auth.css'), 'text/css; charset=utf-8');
|
||||
break;
|
||||
case '/callback':
|
||||
deferredCode.resolve(callback(nonce, reqUrl)
|
||||
.then(code => ({ code, res }), err => ({ err, res })));
|
||||
break;
|
||||
default:
|
||||
res.writeHead(404);
|
||||
res.end();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
codePromise.then(cancelCodeTimer, cancelCodeTimer);
|
||||
return {
|
||||
server,
|
||||
redirectPromise,
|
||||
codePromise
|
||||
};
|
||||
}
|
||||
38
extensions/vscode-account/src/extension.ts
Normal file
38
extensions/vscode-account/src/extension.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { AzureActiveDirectoryService, onDidChangeSessions } from './AADHelper';
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
const loginService = new AzureActiveDirectoryService();
|
||||
|
||||
await loginService.initialize();
|
||||
|
||||
vscode.authentication.registerAuthenticationProvider({
|
||||
id: 'MSA',
|
||||
displayName: 'Microsoft Account', // TODO localize
|
||||
onDidChangeSessions: onDidChangeSessions.event,
|
||||
getSessions: () => Promise.resolve(loginService.sessions),
|
||||
login: async () => {
|
||||
try {
|
||||
await loginService.login();
|
||||
return loginService.sessions[0]!;
|
||||
} catch (e) {
|
||||
vscode.window.showErrorMessage(`Logging in failed: ${e}`);
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
logout: async (id: string) => {
|
||||
return loginService.logout();
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// this method is called when your extension is deactivated
|
||||
export function deactivate() { }
|
||||
67
extensions/vscode-account/src/keychain.ts
Normal file
67
extensions/vscode-account/src/keychain.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// keytar depends on a native module shipped in vscode, so this is
|
||||
// how we load it
|
||||
import * as keytarType from 'keytar';
|
||||
import { env } from 'vscode';
|
||||
|
||||
function getKeytar(): Keytar | undefined {
|
||||
try {
|
||||
return require('keytar');
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export type Keytar = {
|
||||
getPassword: typeof keytarType['getPassword'];
|
||||
setPassword: typeof keytarType['setPassword'];
|
||||
deletePassword: typeof keytarType['deletePassword'];
|
||||
};
|
||||
|
||||
const SERVICE_ID = `${env.uriScheme}-vscode.login`;
|
||||
const ACCOUNT_ID = 'account';
|
||||
|
||||
export class Keychain {
|
||||
private keytar: Keytar;
|
||||
|
||||
constructor() {
|
||||
const keytar = getKeytar();
|
||||
if (!keytar) {
|
||||
throw new Error('System keychain unavailable');
|
||||
}
|
||||
|
||||
this.keytar = keytar;
|
||||
}
|
||||
|
||||
async setToken(token: string): Promise<void> {
|
||||
try {
|
||||
return await this.keytar.setPassword(SERVICE_ID, ACCOUNT_ID, token);
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
async getToken() {
|
||||
try {
|
||||
return await this.keytar.getPassword(SERVICE_ID, ACCOUNT_ID);
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
async deleteToken() {
|
||||
try {
|
||||
return await this.keytar.deletePassword(SERVICE_ID, ACCOUNT_ID);
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const keychain = new Keychain();
|
||||
8
extensions/vscode-account/src/utils.ts
Normal file
8
extensions/vscode-account/src/utils.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export function toBase64UrlEncoding(base64string: string) {
|
||||
return base64string.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); // Need to use base64url encoding
|
||||
}
|
||||
66
extensions/vscode-account/src/vscode.proposed.d.ts
vendored
Normal file
66
extensions/vscode-account/src/vscode.proposed.d.ts
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* This is the place for API experiments and proposals.
|
||||
* These API are NOT stable and subject to change. They are only available in the Insiders
|
||||
* distribution and CANNOT be used in published extensions.
|
||||
*
|
||||
* To test these API in local environment:
|
||||
* - Use Insiders release of VS Code.
|
||||
* - Add `"enableProposedApi": true` to your package.json.
|
||||
* - Copy this file to your project.
|
||||
*/
|
||||
|
||||
declare module 'vscode' {
|
||||
|
||||
export interface Session {
|
||||
id: string;
|
||||
accessToken: string;
|
||||
displayName: string;
|
||||
}
|
||||
|
||||
export interface AuthenticationProvider {
|
||||
readonly id: string;
|
||||
readonly displayName: string;
|
||||
readonly onDidChangeSessions: Event<void>;
|
||||
|
||||
/**
|
||||
* Returns an array of current sessions.
|
||||
*/
|
||||
getSessions(): Promise<ReadonlyArray<Session>>;
|
||||
|
||||
/**
|
||||
* Prompts a user to login.
|
||||
*/
|
||||
login(): Promise<Session>;
|
||||
logout(sessionId: string): Promise<void>;
|
||||
}
|
||||
|
||||
export namespace authentication {
|
||||
export function registerAuthenticationProvider(provider: AuthenticationProvider): Disposable;
|
||||
|
||||
/**
|
||||
* Fires with the provider id that was registered or unregistered.
|
||||
*/
|
||||
export const onDidRegisterAuthenticationProvider: Event<string>;
|
||||
export const onDidUnregisterAuthenticationProvider: Event<string>;
|
||||
|
||||
/**
|
||||
* Fires with the provider id that changed sessions.
|
||||
*/
|
||||
export const onDidChangeSessions: Event<string>;
|
||||
export function login(providerId: string): Promise<Session>;
|
||||
export function logout(providerId: string, accountId: string): Promise<void>;
|
||||
export function getSessions(providerId: string): Promise<ReadonlyArray<Session> | undefined>;
|
||||
}
|
||||
|
||||
// #region Ben - extension auth flow (desktop+web)
|
||||
|
||||
export namespace env {
|
||||
|
||||
export function asExternalUri(target: Uri): Thenable<Uri>
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user