-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathBasicOAuthProvider.ts
More file actions
111 lines (92 loc) · 4.62 KB
/
BasicOAuthProvider.ts
File metadata and controls
111 lines (92 loc) · 4.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { AuthenticationProvider } from '../contracts/AuthenticationProvider';
import { RequestLike, httpRequest } from '../utils/httpRequest';
import { BasicAuthProvider } from './BasicAuthProvider';
import { LoginInformation } from '../contracts/BasicCredentials';
export class BasicOAuthProvider extends BasicAuthProvider implements AuthenticationProvider {
private oAuthEndpoint: string | undefined;
private oAuthService: string | undefined;
private defaultScopes: string[] | undefined;
private _didFallback: boolean = false;
public constructor(storageMemento: vscode.Memento, secretStorage: vscode.SecretStorage, private readonly registryUri: vscode.Uri) {
super(storageMemento, secretStorage, registryUri.toString());
}
public async getSession(scopes: string[], options?: vscode.AuthenticationGetSessionOptions): Promise<vscode.AuthenticationSession & { type: string }> {
const { username, secret } = await this.getBasicCredentials();
if (this.oAuthEndpoint === undefined || this.oAuthService === undefined) {
return {
id: 'basic',
type: 'Basic',
account: {
label: username,
id: username,
},
accessToken: this.getBasicAuthToken(username, secret),
scopes: scopes,
};
} else {
const scope = [...this.defaultScopes ?? [], ...scopes].join(' ');
const request: RequestLike = {
method: 'GET',
headers: {
'Authorization': `Basic ${this.getBasicAuthToken(username, secret)}`,
// eslint-disable-next-line @typescript-eslint/naming-convention
'grant_type': 'password',
'service': this.oAuthService,
'scope': scope,
},
};
const oAuthRequestQuery = new URLSearchParams({
service: this.oAuthService,
scope: scope
}).toString();
const requestUri = vscode.Uri.parse(this.oAuthEndpoint.toString()).with({ query: oAuthRequestQuery });
const oAuthResponse = await httpRequest<{ token: string }>(requestUri.toString(true), request);
return {
id: 'oauth',
type: 'Bearer',
account: {
label: username,
id: username,
},
accessToken: (await oAuthResponse.json()).token,
scopes: scopes,
};
}
}
public fallback(wwwAuthenticateHeader: string): void {
const wwwAuthenticateHeaderRegex = /Bearer\s+realm="(?<realm>[^"]+)",\s*service="(?<service>[^"]+)",\s*scope="(?<scope>[^"]+)"/i;
const match = wwwAuthenticateHeaderRegex.exec(wwwAuthenticateHeader);
if (match?.groups?.realm && match?.groups?.service && match?.groups?.scope) {
this.oAuthEndpoint = match.groups.realm;
this.oAuthService = match.groups.service;
this.defaultScopes = match.groups.scope.split(' ');
} else if (!/Basic\s+/i.test(wwwAuthenticateHeader)) {
throw new Error(vscode.l10n.t('Unable to parse WWW-Authenticate header: "{0}"', wwwAuthenticateHeader));
}
// For Basic challenges, oAuthEndpoint/oAuthService remain undefined,
// so getSession will use Basic auth directly.
this._didFallback = true;
}
public get didFallback(): boolean {
return this._didFallback;
}
public async getLoginInformation(): Promise<LoginInformation> {
const credentials = await this.getBasicCredentials();
return {
server: this.registryUri.toString(),
username: credentials.username,
secret: credentials.secret,
};
}
private getBasicAuthToken(username: string, secret: string): string {
return Buffer.from(`${username}:${secret}`).toString('base64');
}
}
export function isBasicOAuthProvider(maybeProvider: unknown): maybeProvider is BasicOAuthProvider {
return maybeProvider instanceof BasicOAuthProvider;
}