Skip to content

Commit 719cdaa

Browse files
author
Benjamin Pasero
committed
web - implement callback auth flow
1 parent 284c044 commit 719cdaa

22 files changed

Lines changed: 303 additions & 65 deletions

src/vs/code/electron-main/app.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ import { startsWith } from 'vs/base/common/strings';
7070
import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
7171
import { IBackupMainService } from 'vs/platform/backup/common/backup';
7272
import { HistoryMainService } from 'vs/platform/history/electron-main/historyMainService';
73-
import { URLService } from 'vs/platform/url/common/urlService';
73+
import { URLService } from 'vs/platform/url/node/urlService';
7474
import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService';
7575
import { statSync } from 'fs';
7676
import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsIpc';

src/vs/platform/url/common/url.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { URI } from 'vs/base/common/uri';
7-
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
6+
import { URI, UriComponents } from 'vs/base/common/uri';
7+
import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
88
import { IDisposable } from 'vs/base/common/lifecycle';
99

1010
export const IURLService = createDecorator<IURLService>('urlService');
@@ -14,8 +14,17 @@ export interface IURLHandler {
1414
}
1515

1616
export interface IURLService {
17-
_serviceBrand: any;
17+
18+
_serviceBrand: ServiceIdentifier<any>;
19+
20+
/**
21+
* Create a URL that can be called to trigger IURLhandlers.
22+
* The URL that gets passed to the IURLHandlers carries over
23+
* any of the provided IURLCreateOption values.
24+
*/
25+
create(options?: Partial<UriComponents>): URI;
1826

1927
open(url: URI): Promise<boolean>;
28+
2029
registerHandler(handler: IURLHandler): IDisposable;
2130
}

src/vs/platform/url/common/urlService.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,20 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { IURLService, IURLHandler } from 'vs/platform/url/common/url';
7-
import { URI } from 'vs/base/common/uri';
8-
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
9-
import { first } from 'vs/base/common/async';
7+
import { URI, UriComponents } from 'vs/base/common/uri';
108
import { values } from 'vs/base/common/map';
9+
import { first } from 'vs/base/common/async';
10+
import { toDisposable, IDisposable, Disposable } from 'vs/base/common/lifecycle';
1111
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
1212

13-
export class URLService implements IURLService {
13+
export abstract class AbstractURLService extends Disposable implements IURLService {
1414

1515
_serviceBrand!: ServiceIdentifier<any>;
1616

1717
private handlers = new Set<IURLHandler>();
1818

19+
abstract create(options?: Partial<UriComponents>): URI;
20+
1921
open(uri: URI): Promise<boolean> {
2022
const handlers = values(this.handlers);
2123
return first(handlers.map(h => () => h.handleURL(uri)), undefined, false).then(val => val || false);

src/vs/platform/url/node/urlIpc.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
7-
import { URI } from 'vs/base/common/uri';
7+
import { URI, UriComponents } from 'vs/base/common/uri';
88
import { IDisposable } from 'vs/base/common/lifecycle';
99
import { Event } from 'vs/base/common/event';
1010
import { IURLService, IURLHandler } from 'vs/platform/url/common/url';
@@ -39,6 +39,10 @@ export class URLServiceChannelClient implements IURLService {
3939
registerHandler(handler: IURLHandler): IDisposable {
4040
throw new Error('Not implemented.');
4141
}
42+
43+
create(_options?: Partial<UriComponents>): URI {
44+
throw new Error('Method not implemented.');
45+
}
4246
}
4347

4448
export class URLHandlerChannel implements IServerChannel {
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { URI, UriComponents } from 'vs/base/common/uri';
7+
import product from 'vs/platform/product/node/product';
8+
import { AbstractURLService } from 'vs/platform/url/common/urlService';
9+
10+
export class URLService extends AbstractURLService {
11+
12+
create(options?: Partial<UriComponents>): URI {
13+
const { authority, path, query, fragment } = options ? options : { authority: undefined, path: undefined, query: undefined, fragment: undefined };
14+
15+
return URI.from({ scheme: product.urlProtocol, authority, path, query, fragment });
16+
}
17+
}

src/vs/vscode.proposed.d.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,4 +1157,50 @@ declare module 'vscode' {
11571157
}
11581158

11591159
//#endregion
1160+
1161+
// #region Ben - extension auth flow (desktop+web)
1162+
1163+
export namespace env {
1164+
1165+
export interface AppUriOptions {
1166+
payload?: {
1167+
path?: string;
1168+
query?: string;
1169+
fragment?: string;
1170+
};
1171+
}
1172+
1173+
/**
1174+
* Creates a Uri that - if opened in a browser - will result in a
1175+
* registered [UriHandler](#UriHandler) to fire. The handler's
1176+
* Uri will be configured with the path, query and fragment of
1177+
* [AppUriOptions](#AppUriOptions) if provided, otherwise it will be empty.
1178+
*
1179+
* Extensions should not make any assumptions about the resulting
1180+
* Uri and should not alter it in anyway. Rather, extensions can e.g.
1181+
* use this Uri in an authentication flow, by adding the Uri as
1182+
* callback query argument to the server to authenticate to.
1183+
*
1184+
* Note: If the server decides to add additional query parameters to the Uri
1185+
* (e.g. a token or secret), it will appear in the Uri that is passed
1186+
* to the [UriHandler](#UriHandler).
1187+
*
1188+
* **Example** of an authentication flow:
1189+
* ```typescript
1190+
* vscode.window.registerUriHandler({
1191+
* handleUri(uri: vscode.Uri): vscode.ProviderResult<void> {
1192+
* if (uri.path === '/did-authenticate') {
1193+
* console.log(uri.toString());
1194+
* }
1195+
* }
1196+
* });
1197+
*
1198+
* const callableUri = await vscode.env.createAppUri({ payload: { path: '/did-authenticate' } });
1199+
* await vscode.env.openExternal(callableUri);
1200+
* ```
1201+
*/
1202+
export function createAppUri(options?: AppUriOptions): Thenable<Uri>;
1203+
}
1204+
1205+
//#endregion
11601206
}

src/vs/workbench/api/browser/mainThreadUrls.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import { ExtHostContext, IExtHostContext, MainContext, MainThreadUrlsShape, ExtHostUrlsShape } from 'vs/workbench/api/common/extHost.protocol';
77
import { extHostNamedCustomer } from '../common/extHostCustomers';
88
import { IURLService, IURLHandler } from 'vs/platform/url/common/url';
9-
import { URI } from 'vs/base/common/uri';
9+
import { URI, UriComponents } from 'vs/base/common/uri';
1010
import { IDisposable } from 'vs/base/common/lifecycle';
1111
import { IExtensionUrlHandler } from 'vs/workbench/services/extensions/common/inactiveExtensionUrlHandler';
1212
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
@@ -68,6 +68,16 @@ export class MainThreadUrls implements MainThreadUrlsShape {
6868
return Promise.resolve(undefined);
6969
}
7070

71+
async $createAppUri(extensionId: ExtensionIdentifier, options?: { payload?: Partial<UriComponents> }): Promise<URI> {
72+
const payload: Partial<UriComponents> = options && options.payload ? options.payload : Object.create(null);
73+
74+
// we define the authority to be the extension ID to ensure
75+
// that the Uri gets routed back to the extension properly.
76+
payload.authority = extensionId.value;
77+
78+
return this.urlService.create(payload);
79+
}
80+
7181
dispose(): void {
7282
this.handlers.forEach(({ disposable }) => disposable.dispose());
7383
this.handlers.clear();

src/vs/workbench/api/common/extHost.api.impl.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
234234
get appName() { return initData.environment.appName; },
235235
get appRoot() { return initData.environment.appRoot!.fsPath; },
236236
get uriScheme() { return initData.environment.appUriScheme; },
237+
createAppUri(options?) {
238+
checkProposedApiEnabled(extension);
239+
return extHostUrls.createAppUri(extension.identifier, options);
240+
},
237241
get logLevel() {
238242
checkProposedApiEnabled(extension);
239243
return typeConverters.LogLevel.to(extHostLogService.getLevel());

src/vs/workbench/api/common/extHost.protocol.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@ export interface ExtHostWebviewsShape {
567567
export interface MainThreadUrlsShape extends IDisposable {
568568
$registerUriHandler(handle: number, extensionId: ExtensionIdentifier): Promise<void>;
569569
$unregisterUriHandler(handle: number): Promise<void>;
570+
$createAppUri(extensionId: ExtensionIdentifier, options?: { payload?: Partial<UriComponents> }): Promise<UriComponents>;
570571
}
571572

572573
export interface ExtHostUrlsShape {

src/vs/workbench/api/common/extHostUrls.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,8 @@ export class ExtHostUrls implements ExtHostUrlsShape {
5555

5656
return Promise.resolve(undefined);
5757
}
58+
59+
async createAppUri(extensionId: ExtensionIdentifier, options?: vscode.env.AppUriOptions): Promise<vscode.Uri> {
60+
return URI.revive(await this._proxy.$createAppUri(extensionId, options));
61+
}
5862
}

0 commit comments

Comments
 (0)