Skip to content

Commit 56a42dd

Browse files
Add dynamic MSAL loading to azure-arm-rest package to fix compatibility issue with Node 10 execution handler (#344)
* Added msal v1 and msal v2 support using npm aliases.
1 parent 64b33d2 commit 56a42dd

File tree

3 files changed

+133
-154
lines changed

3 files changed

+133
-154
lines changed

common-npm-packages/azure-arm-rest/azure-arm-common.ts

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import constants = require('./constants');
77
import path = require('path');
88
import fs = require('fs');
99
import jwt = require('jsonwebtoken');
10-
import msal = require('@azure/msal-node');
1110
import crypto = require("crypto");
1211
import { Mutex } from 'async-mutex';
1312
import HttpsProxyAgent = require('https-proxy-agent');
@@ -16,6 +15,19 @@ import { getHandlerFromToken, WebApi } from "azure-devops-node-api";
1615
import { ITaskApi } from "azure-devops-node-api/TaskApi";
1716
import TaskAgentInterfaces = require("azure-devops-node-api/interfaces/TaskAgentInterfaces");
1817

18+
// Important note! Since the msal v2.** doesn't work with Node 10, and we still need to support Node 10 execution handler, a dynamic msal loading was implemented.
19+
// Dynamic loading imposes restrictions on type validation when compiling TypeScript and we can't use it in this case.
20+
// For this reason, all msal types were temporarily replaced with 'any' type.
21+
// When the support for Node 10 is dropped, the types should be restored and the dynamic loading should be removed.
22+
23+
/// Dynamic msal loading based on the node version
24+
const nodeVersion = parseInt(process.version.split('.')[0].replace('v', ''));
25+
const msalVer = nodeVersion < 16 ? "msalv1": "msalv2";
26+
27+
tl.debug('Using ' + msalVer);
28+
const msal = require(msalVer);
29+
///
30+
1931
tl.setResourcePath(path.join(__dirname, 'module.json'), true);
2032

2133
export class ApplicationTokenCredentials {
@@ -36,7 +48,7 @@ export class ApplicationTokenCredentials {
3648
private isADFSEnabled?: boolean;
3749
private token_deferred: Q.Promise<string>;
3850
private useMSAL: boolean;
39-
private msalInstance: msal.ConfidentialClientApplication;
51+
private msalInstance: any; //msal.ConfidentialClientApplication
4052

4153
private readonly tokenMutex: Mutex;
4254

@@ -232,7 +244,7 @@ export class ApplicationTokenCredentials {
232244
}
233245
}
234246

235-
private async getMSAL(): Promise<msal.ConfidentialClientApplication> {
247+
private async getMSAL(): Promise<any> /*Promise<msal.ConfidentialClientApplication>*/ {
236248
// use same instance if it already exists
237249
if (!this.msalInstance) {
238250
this.msalInstance = await this.buildMSAL();
@@ -241,7 +253,7 @@ export class ApplicationTokenCredentials {
241253
return this.msalInstance;
242254
}
243255

244-
private getProxyClient(agentProxyURL: URL): msal.INetworkModule {
256+
private getProxyClient(agentProxyURL: URL): any /*msal.INetworkModule*/ {
245257
let proxyURL = `${agentProxyURL.protocol}//${agentProxyURL.host}`;
246258

247259
const agentProxyUsername: string = tl.getVariable("agent.proxyusername");
@@ -262,7 +274,7 @@ export class ApplicationTokenCredentials {
262274
// direct usage of msalConfig.system.proxyUrl is not available at the moment due to the fact that Object.fromEntries requires >=Node12
263275
const proxyAgent = new HttpsProxyAgent(proxyURL);
264276

265-
const proxyNetworkClient: msal.INetworkModule = {
277+
const proxyNetworkClient: any /*msal.INetworkModule*/ = {
266278
async sendGetRequestAsync(url, options) {
267279
const customOptions = { ...options, ...{ method: "GET", agent: proxyAgent } }
268280
const response = await fetch(url, customOptions);
@@ -286,11 +298,11 @@ export class ApplicationTokenCredentials {
286298
return proxyNetworkClient;
287299
}
288300

289-
private async buildMSAL(): Promise<msal.ConfidentialClientApplication> {
301+
private async buildMSAL(): Promise<any> /*Promise<msal.ConfidentialClientApplication>*/ {
290302
// default configuration
291303
const authorityURL = (new URL(this.tenantId, this.authorityUrl)).toString();
292304

293-
const msalConfig: msal.Configuration = {
305+
const msalConfig: any /*msal.Configuration*/ = {
294306
auth: {
295307
clientId: this.clientId,
296308
authority: authorityURL
@@ -325,7 +337,7 @@ export class ApplicationTokenCredentials {
325337
}
326338
}
327339

328-
let msalInstance: msal.ConfidentialClientApplication;
340+
let msalInstance: any; //msal.ConfidentialClientApplication
329341

330342
// setup msal according to parameters
331343
switch (this.scheme) {
@@ -344,13 +356,13 @@ export class ApplicationTokenCredentials {
344356
return msalInstance;
345357
}
346358

347-
private configureMSALWithMSI(msalConfig: msal.Configuration): msal.ConfidentialClientApplication {
359+
private configureMSALWithMSI(msalConfig: any /*msal.Configuration*/): any /*msal.ConfidentialClientApplication*/ {
348360
let resourceId = this.activeDirectoryResourceId;
349-
let accessTokenProvider: msal.IAppTokenProvider = (appTokenProviderParameters: msal.AppTokenProviderParameters): Promise<msal.AppTokenProviderResult> => {
361+
let accessTokenProvider: any /*msal.IAppTokenProvider*/ = (appTokenProviderParameters: any /*msal.AppTokenProviderParameters*/): Promise<any> /*Promise<msal.AppTokenProviderResult>*/ => {
350362

351363
tl.debug("MSAL - ManagedIdentity is used.");
352364

353-
let providerResultPromise = new Promise<msal.AppTokenProviderResult>(function (resolve, reject) {
365+
let providerResultPromise = new Promise<any>/*Promise<msal.AppTokenProviderResult>*/(function (resolve, reject) {
354366
// same for MSAL
355367
let webRequest = new webClient.WebRequest();
356368
webRequest.method = "GET";
@@ -363,7 +375,7 @@ export class ApplicationTokenCredentials {
363375
webClient.sendRequest(webRequest).then(
364376
(response: webClient.WebResponse) => {
365377
if (response.statusCode == 200) {
366-
let providerResult: msal.AppTokenProviderResult = {
378+
let providerResult: any /*msal.AppTokenProviderResult*/ = {
367379
accessToken: response.body.access_token,
368380
expiresInSeconds: response.body.expires_in
369381
}
@@ -388,7 +400,7 @@ export class ApplicationTokenCredentials {
388400
return msalInstance;
389401
}
390402

391-
private configureMSALWithSP(msalConfig: msal.Configuration): msal.ConfidentialClientApplication {
403+
private configureMSALWithSP(msalConfig: any /*msal.Configuration*/): any /*msal.ConfidentialClientApplication*/ {
392404
switch (this.authType) {
393405
case constants.AzureServicePrinicipalAuthentications.servicePrincipalKey:
394406
tl.debug("MSAL - ServicePrincipal - clientSecret is used.");
@@ -459,7 +471,7 @@ export class ApplicationTokenCredentials {
459471
return oidc_token;
460472
}
461473

462-
private async configureMSALWithOIDC(msalConfig: msal.Configuration): Promise<msal.ConfidentialClientApplication> {
474+
private async configureMSALWithOIDC(msalConfig: any /*msal.Configuration*/): Promise<any> /*Promise<msal.ConfidentialClientApplication>*/ {
463475
tl.debug("MSAL - FederatedAccess - OIDC is used.");
464476

465477
msalConfig.auth.clientAssertion = await this.getFederatedToken();
@@ -470,13 +482,13 @@ export class ApplicationTokenCredentials {
470482

471483
private async getMSALToken(force?: boolean, retryCount: number = 3, retryWaitMS: number = 2000): Promise<string> {
472484
tl.debug(`MSAL - getMSALToken called. force=${force}`);
473-
const msalApp: msal.ConfidentialClientApplication = await this.getMSAL();
485+
const msalApp: any /*msal.ConfidentialClientApplication*/ = await this.getMSAL();
474486
if (force) {
475487
msalApp.clearCache();
476488
}
477489

478490
try {
479-
const request: msal.ClientCredentialRequest = {
491+
const request: any /*msal.ClientCredentialRequest*/ = {
480492
scopes: [this.activeDirectoryResourceId + "/.default"]
481493
};
482494
const response = await msalApp.acquireTokenByClientCredential(request);

0 commit comments

Comments
 (0)