diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts index f966518e109..6222d09d045 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -16,6 +16,7 @@ import { toBase64UrlEncoding } from './utils'; import fetch, { Response } from 'node-fetch'; import { sha256 } from './env/node/sha256'; import * as nls from 'vscode-nls'; +import { MicrosoftAuthenticationSession } from './microsoft-authentication'; const localize = nls.loadMessageBundle(); @@ -26,6 +27,7 @@ const tenant = 'organizations'; interface IToken { accessToken?: string; // When unable to refresh due to network problems, the access token becomes undefined + idToken?: string; // depending on the scopes can be either supplied or empty expiresIn?: number; // How long access token is valid, in seconds expiresAt?: number; // UNIX epoch time at which token will expire @@ -71,6 +73,11 @@ export interface ITokenResponse { id_token?: string; } +export interface IMicrosoftTokens { + accessToken: string; + idToken?: string; +} + function parseQuery(uri: vscode.Uri) { return uri.query.split('&').reduce((prev: any, current) => { const queryString = current.split('='); @@ -228,29 +235,36 @@ export class AzureActiveDirectoryService { } } - private async convertToSession(token: IToken): Promise { - const resolvedToken = await this.resolveAccessToken(token); + private async convertToSession(token: IToken): Promise { + const resolvedTokens = await this.resolveAccessAndIdTokens(token); return { id: token.sessionId, - accessToken: resolvedToken, + accessToken: resolvedTokens.accessToken, + idToken: resolvedTokens.idToken, account: token.account, scopes: token.scope.split(' ') }; } - private async resolveAccessToken(token: IToken): Promise { + private async resolveAccessAndIdTokens(token: IToken): Promise { if (token.accessToken && (!token.expiresAt || token.expiresAt > Date.now())) { token.expiresAt ? Logger.info(`Token available from cache, expires in ${token.expiresAt - Date.now()} milliseconds`) : Logger.info('Token available from cache'); - return Promise.resolve(token.accessToken); + return Promise.resolve({ + accessToken: token.accessToken, + idToken: token.idToken + }); } try { Logger.info('Token expired or unavailable, trying refresh'); const refreshedToken = await this.refreshToken(token.refreshToken, token.scope, token.sessionId); if (refreshedToken.accessToken) { - return refreshedToken.accessToken; + return { + accessToken: refreshedToken.accessToken, + idToken: refreshedToken.idToken + }; } else { throw new Error(); } @@ -501,6 +515,7 @@ export class AzureActiveDirectoryService { expiresIn: json.expires_in, expiresAt: json.expires_in ? Date.now() + json.expires_in * 1000 : undefined, accessToken: json.access_token, + idToken: json.id_token, refreshToken: json.refresh_token, scope, sessionId: existingId || `${claims.tid}/${(claims.oid || (claims.altsecid || '' + claims.ipd || ''))}/${uuid()}`, diff --git a/extensions/microsoft-authentication/src/microsoft-authentication.d.ts b/extensions/microsoft-authentication/src/microsoft-authentication.d.ts new file mode 100644 index 00000000000..c04107ff5f4 --- /dev/null +++ b/extensions/microsoft-authentication/src/microsoft-authentication.d.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { AuthenticationSession } from 'vscode'; + +/** + * Represents a session of a currently logged in Microsoft user. + */ +export interface MicrosoftAuthenticationSession extends AuthenticationSession { + /** + * The id token. + */ + idToken?: string; +} \ No newline at end of file