Skip to content

Commit ef41557

Browse files
Take advantage of platform features in Microsoft Authentication extension (microsoft#166066)
1 parent 74d29f0 commit ef41557

File tree

14 files changed

+105
-104
lines changed

14 files changed

+105
-104
lines changed

extensions/microsoft-authentication/extension-browser.webpack.config.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ module.exports = withBrowserDefaults({
2525
},
2626
resolve: {
2727
alias: {
28-
'./env/node': path.resolve(__dirname, 'src/env/browser'),
29-
'./authServer': path.resolve(__dirname, 'src/env/browser/authServer'),
28+
'./node/crypto': path.resolve(__dirname, 'src/browser/crypto'),
29+
'./node/authServer': path.resolve(__dirname, 'src/browser/authServer'),
30+
'./node/buffer': path.resolve(__dirname, 'src/browser/buffer'),
31+
'./node/fetch': path.resolve(__dirname, 'src/browser/fetch'),
3032
}
3133
}
3234
});

extensions/microsoft-authentication/package.json

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,7 @@
5555
"@types/uuid": "8.0.0"
5656
},
5757
"dependencies": {
58-
"buffer": "^5.6.0",
5958
"node-fetch": "2.6.7",
60-
"randombytes": "~2.1.0",
61-
"sha.js": "2.4.11",
62-
"stream": "0.0.2",
63-
"uuid": "^8.2.0",
6459
"@vscode/extension-telemetry": "0.7.0-preview"
6560
},
6661
"repository": {

extensions/microsoft-authentication/src/AADHelper.ts

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

6-
import * as randomBytes from 'randombytes';
7-
import * as querystring from 'querystring';
8-
import { Buffer } from 'buffer';
96
import * as vscode from 'vscode';
10-
import { v4 as uuid } from 'uuid';
11-
import fetch, { Response } from 'node-fetch';
7+
import * as querystring from 'querystring';
8+
import path = require('path');
129
import Logger from './logger';
13-
import { isSupportedEnvironment, toBase64UrlEncoding } from './utils';
14-
import { sha256 } from './env/node/sha256';
10+
import { isSupportedEnvironment } from './utils';
11+
import { generateCodeChallenge, generateCodeVerifier, randomUUID } from './cryptoUtils';
1512
import { BetterTokenStorage, IDidChangeInOtherWindowEvent } from './betterSecretStorage';
16-
import { LoopbackAuthServer } from './authServer';
17-
import path = require('path');
13+
import { LoopbackAuthServer } from './node/authServer';
14+
import { base64Decode } from './node/buffer';
15+
import { fetching } from './node/fetch';
1816

1917
const redirectUrl = 'https://vscode.dev/redirect';
2018
const loginEndpointUrl = 'https://login.microsoftonline.com/';
@@ -295,8 +293,8 @@ export class AzureActiveDirectoryService {
295293
}
296294

297295
private async createSessionWithLocalServer(scopeData: IScopeData) {
298-
const codeVerifier = toBase64UrlEncoding(randomBytes(32).toString('base64'));
299-
const codeChallenge = toBase64UrlEncoding(await sha256(codeVerifier));
296+
const codeVerifier = generateCodeVerifier();
297+
const codeChallenge = await generateCodeChallenge(codeVerifier);
300298
const qs = new URLSearchParams({
301299
response_type: 'code',
302300
response_mode: 'query',
@@ -328,15 +326,15 @@ export class AzureActiveDirectoryService {
328326

329327
private async createSessionWithoutLocalServer(scopeData: IScopeData): Promise<vscode.AuthenticationSession> {
330328
let callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.microsoft-authentication`));
331-
const nonce = randomBytes(16).toString('base64');
329+
const nonce = generateCodeVerifier();
332330
const callbackQuery = new URLSearchParams(callbackUri.query);
333331
callbackQuery.set('nonce', encodeURIComponent(nonce));
334332
callbackUri = callbackUri.with({
335333
query: callbackQuery.toString()
336334
});
337335
const state = encodeURIComponent(callbackUri.toString(true));
338-
const codeVerifier = toBase64UrlEncoding(randomBytes(32).toString('base64'));
339-
const codeChallenge = toBase64UrlEncoding(await sha256(codeVerifier));
336+
const codeVerifier = generateCodeVerifier();
337+
const codeChallenge = await generateCodeChallenge(codeVerifier);
340338
const signInUrl = `${loginEndpointUrl}${scopeData.tenant}/oauth2/v2.0/authorize`;
341339
const oauthStartQuery = new URLSearchParams({
342340
response_type: 'code',
@@ -467,10 +465,10 @@ export class AzureActiveDirectoryService {
467465

468466
try {
469467
if (json.id_token) {
470-
claims = JSON.parse(Buffer.from(json.id_token.split('.')[1], 'base64').toString());
468+
claims = JSON.parse(base64Decode(json.id_token.split('.')[1]));
471469
} else {
472470
Logger.info('Attempting to parse access_token instead since no id_token was included in the response.');
473-
claims = JSON.parse(Buffer.from(json.access_token.split('.')[1], 'base64').toString());
471+
claims = JSON.parse(base64Decode(json.access_token.split('.')[1]));
474472
}
475473
} catch (e) {
476474
throw e;
@@ -491,7 +489,7 @@ export class AzureActiveDirectoryService {
491489
idToken: json.id_token,
492490
refreshToken: json.refresh_token,
493491
scope: scopeData.scopeStr,
494-
sessionId: existingId || `${id}/${uuid()}`,
492+
sessionId: existingId || `${id}/${randomUUID()}`,
495493
account: {
496494
label,
497495
id
@@ -739,10 +737,10 @@ export class AzureActiveDirectoryService {
739737
let attempts = 0;
740738
while (attempts <= 3) {
741739
attempts++;
742-
let result: Response | undefined;
740+
let result;
743741
let errorMessage: string | undefined;
744742
try {
745-
result = await fetch(endpoint, {
743+
result = await fetching(endpoint, {
746744
method: 'POST',
747745
headers: {
748746
'Content-Type': 'application/x-www-form-urlencoded',

extensions/microsoft-authentication/src/env/browser/sha256.ts renamed to extensions/microsoft-authentication/src/browser/buffer.ts

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

6-
'use strict';
6+
export function base64Encode(text: string): string {
7+
return btoa(text);
8+
}
79

8-
export async function sha256(s: string | Uint8Array): Promise<string> {
9-
const createHash = require('sha.js');
10-
return createHash('sha256').update(s).digest('base64');
10+
export function base64Decode(text: string): string {
11+
const data = atob(text);
12+
return data;
1113
}

extensions/microsoft-authentication/src/env/node/sha256.ts renamed to extensions/microsoft-authentication/src/browser/crypto.ts

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

6-
'use strict';
7-
8-
export async function sha256(s: string | Uint8Array): Promise<string> {
9-
return (require('crypto')).createHash('sha256').update(s).digest('base64');
10-
}
6+
export const crypto = globalThis.crypto;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
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+
export const fetching = fetch;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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+
import { base64Encode } from './node/buffer';
6+
import { crypto } from './node/crypto';
7+
8+
export function randomUUID() {
9+
return crypto.randomUUID();
10+
}
11+
12+
function dec2hex(dec: number): string {
13+
return ('0' + dec.toString(16)).slice(-2);
14+
}
15+
16+
export function generateCodeVerifier(): string {
17+
const array = new Uint32Array(56 / 2);
18+
crypto.getRandomValues(array);
19+
return Array.from(array, dec2hex).join('');
20+
}
21+
22+
function sha256(plain: string | undefined) {
23+
const encoder = new TextEncoder();
24+
const data = encoder.encode(plain);
25+
return crypto.subtle.digest('SHA-256', data);
26+
}
27+
28+
function base64urlencode(a: ArrayBuffer) {
29+
let str = '';
30+
const bytes = new Uint8Array(a);
31+
const len = bytes.byteLength;
32+
for (let i = 0; i < len; i++) {
33+
str += String.fromCharCode(bytes[i]);
34+
}
35+
return base64Encode(str)
36+
.replace(/\+/g, '-')
37+
.replace(/\//g, '_')
38+
.replace(/=+$/, '');
39+
}
40+
41+
export async function generateCodeChallenge(v: string) {
42+
const hashed = await sha256(v);
43+
const base64encoded = base64urlencode(hashed);
44+
return base64encoded;
45+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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+
export function base64Encode(text: string): string {
7+
return Buffer.from(text, 'binary').toString('base64');
8+
}
9+
10+
export function base64Decode(text: string): string {
11+
return Buffer.from(text, 'base64').toString('utf8');
12+
}

0 commit comments

Comments
 (0)