Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/microsoft-authentication/src/node/flows.ts
5226 views
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 { AuthenticationResult } from '@azure/msal-node';
7
import { Uri, LogOutputChannel, env } from 'vscode';
8
import { ICachedPublicClientApplication } from '../common/publicClientCache';
9
import { UriHandlerLoopbackClient } from '../common/loopbackClientAndOpener';
10
import { UriEventHandler } from '../UriEventHandler';
11
import { loopbackTemplate } from './loopbackTemplate';
12
import { Config } from '../common/config';
13
14
const DEFAULT_REDIRECT_URI = 'https://vscode.dev/redirect';
15
16
export const enum ExtensionHost {
17
Remote,
18
Local
19
}
20
21
interface IMsalFlowOptions {
22
supportsRemoteExtensionHost: boolean;
23
supportsUnsupportedClient: boolean;
24
supportsBroker: boolean;
25
supportsPortableMode: boolean;
26
}
27
28
interface IMsalFlowTriggerOptions {
29
cachedPca: ICachedPublicClientApplication;
30
authority: string;
31
scopes: string[];
32
callbackUri: Uri;
33
loginHint?: string;
34
windowHandle?: Buffer;
35
logger: LogOutputChannel;
36
uriHandler: UriEventHandler;
37
claims?: string;
38
}
39
40
interface IMsalFlow {
41
readonly label: string;
42
readonly options: IMsalFlowOptions;
43
trigger(options: IMsalFlowTriggerOptions): Promise<AuthenticationResult>;
44
}
45
46
class DefaultLoopbackFlow implements IMsalFlow {
47
label = 'default';
48
options: IMsalFlowOptions = {
49
supportsRemoteExtensionHost: false,
50
supportsUnsupportedClient: true,
51
supportsBroker: true,
52
supportsPortableMode: true
53
};
54
55
async trigger({ cachedPca, authority, scopes, claims, loginHint, windowHandle, logger }: IMsalFlowTriggerOptions): Promise<AuthenticationResult> {
56
logger.info('Trying default msal flow...');
57
let redirectUri: string | undefined;
58
if (cachedPca.isBrokerAvailable && process.platform === 'darwin') {
59
redirectUri = Config.macOSBrokerRedirectUri;
60
}
61
return await cachedPca.acquireTokenInteractive({
62
openBrowser: async (url: string) => { await env.openExternal(Uri.parse(url)); },
63
scopes,
64
authority,
65
successTemplate: loopbackTemplate,
66
errorTemplate: loopbackTemplate,
67
loginHint,
68
prompt: loginHint ? undefined : 'select_account',
69
windowHandle,
70
claims,
71
redirectUri
72
});
73
}
74
}
75
76
class UrlHandlerFlow implements IMsalFlow {
77
label = 'protocol handler';
78
options: IMsalFlowOptions = {
79
supportsRemoteExtensionHost: true,
80
supportsUnsupportedClient: false,
81
supportsBroker: false,
82
supportsPortableMode: false
83
};
84
85
async trigger({ cachedPca, authority, scopes, claims, loginHint, windowHandle, logger, uriHandler, callbackUri }: IMsalFlowTriggerOptions): Promise<AuthenticationResult> {
86
logger.info('Trying protocol handler flow...');
87
const loopbackClient = new UriHandlerLoopbackClient(uriHandler, DEFAULT_REDIRECT_URI, callbackUri, logger);
88
let redirectUri: string | undefined;
89
if (cachedPca.isBrokerAvailable && process.platform === 'darwin') {
90
redirectUri = Config.macOSBrokerRedirectUri;
91
}
92
return await cachedPca.acquireTokenInteractive({
93
openBrowser: (url: string) => loopbackClient.openBrowser(url),
94
scopes,
95
authority,
96
loopbackClient,
97
loginHint,
98
prompt: loginHint ? undefined : 'select_account',
99
windowHandle,
100
claims,
101
redirectUri
102
});
103
}
104
}
105
106
class DeviceCodeFlow implements IMsalFlow {
107
label = 'device code';
108
options: IMsalFlowOptions = {
109
supportsRemoteExtensionHost: true,
110
supportsUnsupportedClient: true,
111
supportsBroker: false,
112
supportsPortableMode: true
113
};
114
115
async trigger({ cachedPca, authority, scopes, claims, logger }: IMsalFlowTriggerOptions): Promise<AuthenticationResult> {
116
logger.info('Trying device code flow...');
117
const result = await cachedPca.acquireTokenByDeviceCode({ scopes, authority, claims });
118
if (!result) {
119
throw new Error('Device code flow did not return a result');
120
}
121
return result;
122
}
123
}
124
125
const allFlows: IMsalFlow[] = [
126
new DefaultLoopbackFlow(),
127
new UrlHandlerFlow(),
128
new DeviceCodeFlow()
129
];
130
131
export interface IMsalFlowQuery {
132
extensionHost: ExtensionHost;
133
supportedClient: boolean;
134
isBrokerSupported: boolean;
135
isPortableMode: boolean;
136
}
137
138
export function getMsalFlows(query: IMsalFlowQuery): IMsalFlow[] {
139
const flows = [];
140
for (const flow of allFlows) {
141
let useFlow: boolean = true;
142
if (query.extensionHost === ExtensionHost.Remote) {
143
useFlow &&= flow.options.supportsRemoteExtensionHost;
144
}
145
useFlow &&= flow.options.supportsBroker || !query.isBrokerSupported;
146
useFlow &&= flow.options.supportsUnsupportedClient || query.supportedClient;
147
useFlow &&= flow.options.supportsPortableMode || !query.isPortableMode;
148
if (useFlow) {
149
flows.push(flow);
150
}
151
}
152
return flows;
153
}
154
155