@azure/msal-node-extensions 1.0.0-alpha.30 → 1.0.0-alpha.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,9 @@
1
1
  import { promises } from 'fs';
2
2
  import { pid } from 'process';
3
3
  import path, { dirname } from 'path';
4
- import { Logger, LogLevel, StringUtils } from '@azure/msal-common';
4
+ import { Logger, LogLevel, StringUtils, AuthError, Constants as Constants$1, PromptValue, ClientAuthError, AuthenticationScheme, ClientConfigurationError, ServerError, InteractionRequiredAuthError } from '@azure/msal-common';
5
5
  import { setPassword, getPassword, deletePassword } from 'keytar';
6
+ import { msalNodeRuntime, ErrorStatus, LogLevel as LogLevel$1 } from '@azure/msal-node-runtime';
6
7
 
7
8
  /*
8
9
  * Copyright (c) Microsoft Corporation. All rights reserved.
@@ -13,35 +14,29 @@ const Constants = {
13
14
  * An existing file was the target of an operation that required that the target not exist
14
15
  */
15
16
  EEXIST_ERROR: "EEXIST",
16
-
17
17
  /**
18
18
  * No such file or directory: Commonly raised by fs operations to indicate that a component
19
19
  * of the specified pathname does not exist. No entity (file or directory) could be found
20
20
  * by the given path
21
21
  */
22
22
  ENOENT_ERROR: "ENOENT",
23
-
24
23
  /**
25
24
  * Operation not permitted. An attempt was made to perform an operation that requires
26
25
  * elevated privileges.
27
26
  */
28
27
  EPERM_ERROR: "EPERM",
29
-
30
28
  /**
31
29
  * Default service name for using MSAL Keytar
32
30
  */
33
31
  DEFAULT_SERVICE_NAME: "msal-node-extensions",
34
-
35
32
  /**
36
33
  * Test data used to verify underlying persistence mechanism
37
34
  */
38
35
  PERSISTENCE_TEST_DATA: "Dummy data to verify underlying persistence mechanism",
39
-
40
36
  /**
41
37
  * This is the value of a the guid if the process is being ran by the root user
42
38
  */
43
39
  LINUX_ROOT_USER_GUID: 0,
44
-
45
40
  /**
46
41
  * List of environment variables
47
42
  */
@@ -58,18 +53,21 @@ const Constants = {
58
53
  DEFAULT_CACHE_FILE_NAME: "cache.json"
59
54
  };
60
55
  var Platform;
61
-
62
56
  (function (Platform) {
63
57
  Platform["WINDOWS"] = "win32";
64
58
  Platform["LINUX"] = "linux";
65
59
  Platform["MACOS"] = "darwin";
66
60
  })(Platform || (Platform = {}));
61
+ var ErrorCodes;
62
+ (function (ErrorCodes) {
63
+ ErrorCodes["INTERATION_REQUIRED_ERROR_CODE"] = "interaction_required";
64
+ ErrorCodes["SERVER_UNAVAILABLE"] = "server_unavailable";
65
+ })(ErrorCodes || (ErrorCodes = {}));
67
66
 
68
67
  /*
69
68
  * Copyright (c) Microsoft Corporation. All rights reserved.
70
69
  * Licensed under the MIT License.
71
70
  */
72
-
73
71
  /**
74
72
  * Error thrown when trying to write MSAL cache to persistence.
75
73
  */
@@ -85,8 +83,6 @@ class PersistenceError extends Error {
85
83
  /**
86
84
  * Error thrown when trying to access the file system.
87
85
  */
88
-
89
-
90
86
  static createFileSystemError(errorCode, errorMessage) {
91
87
  return new PersistenceError(errorCode, errorMessage);
92
88
  }
@@ -94,32 +90,24 @@ class PersistenceError extends Error {
94
90
  * Error thrown when trying to write, load, or delete data from secret service on linux.
95
91
  * Libsecret is used to access secret service.
96
92
  */
97
-
98
-
99
93
  static createLibSecretError(errorMessage) {
100
94
  return new PersistenceError("GnomeKeyringError", errorMessage);
101
95
  }
102
96
  /**
103
97
  * Error thrown when trying to write, load, or delete data from keychain on macOs.
104
98
  */
105
-
106
-
107
99
  static createKeychainPersistenceError(errorMessage) {
108
100
  return new PersistenceError("KeychainError", errorMessage);
109
101
  }
110
102
  /**
111
103
  * Error thrown when trying to encrypt or decrypt data using DPAPI on Windows.
112
104
  */
113
-
114
-
115
105
  static createFilePersistenceWithDPAPIError(errorMessage) {
116
106
  return new PersistenceError("DPAPIEncryptedFileError", errorMessage);
117
107
  }
118
108
  /**
119
109
  * Error thrown when using the cross platform lock.
120
110
  */
121
-
122
-
123
111
  static createCrossPlatformLockError(errorMessage) {
124
112
  return new PersistenceError("CrossPlatformLockError", errorMessage);
125
113
  }
@@ -129,8 +117,6 @@ class PersistenceError extends Error {
129
117
  * @param errorMessage string
130
118
  * @returns PersistenceError
131
119
  */
132
-
133
-
134
120
  static createCachePersistenceError(errorMessage) {
135
121
  return new PersistenceError("CachePersistenceError", errorMessage);
136
122
  }
@@ -140,8 +126,6 @@ class PersistenceError extends Error {
140
126
  * @param errorMessage string
141
127
  * @returns PersistenceError
142
128
  */
143
-
144
-
145
129
  static createNotSupportedError(errorMessage) {
146
130
  return new PersistenceError("NotSupportedError", errorMessage);
147
131
  }
@@ -151,8 +135,6 @@ class PersistenceError extends Error {
151
135
  * @param errorMessage string
152
136
  * @returns PersistenceError
153
137
  */
154
-
155
-
156
138
  static createPersistenceNotVerifiedError(errorMessage) {
157
139
  return new PersistenceError("PersistenceNotVerifiedError", errorMessage);
158
140
  }
@@ -162,12 +144,9 @@ class PersistenceError extends Error {
162
144
  * @param errorMessage string
163
145
  * @returns PersistenceError
164
146
  */
165
-
166
-
167
147
  static createPersistenceNotValidatedError(errorMessage) {
168
148
  return new PersistenceError("PersistenceNotValidatedError", errorMessage);
169
149
  }
170
-
171
150
  }
172
151
 
173
152
  /*
@@ -177,7 +156,6 @@ class PersistenceError extends Error {
177
156
  /**
178
157
  * Cross-process lock that works on all platforms.
179
158
  */
180
-
181
159
  class CrossPlatformLock {
182
160
  constructor(lockFilePath, logger, lockOptions) {
183
161
  this.lockFilePath = lockFilePath;
@@ -190,8 +168,6 @@ class CrossPlatformLock {
190
168
  * cache file but with .lockfile extension. If another process has already created
191
169
  * the lockfile, will back off and retry based on configuration settings set by CrossPlatformLockOptions
192
170
  */
193
-
194
-
195
171
  async lock() {
196
172
  for (let tryCount = 0; tryCount < this.retryNumber; tryCount++) {
197
173
  try {
@@ -210,15 +186,12 @@ class CrossPlatformLock {
210
186
  }
211
187
  }
212
188
  }
213
-
214
189
  this.logger.error(`${pid} was not able to acquire lock. Exceeded amount of retries set in the options`);
215
190
  throw PersistenceError.createCrossPlatformLockError("Not able to acquire lock. Exceeded amount of retries set in options");
216
191
  }
217
192
  /**
218
193
  * unlocks cache file by deleting .lockfile.
219
194
  */
220
-
221
-
222
195
  async unlock() {
223
196
  try {
224
197
  if (this.lockFileHandle) {
@@ -238,13 +211,11 @@ class CrossPlatformLock {
238
211
  }
239
212
  }
240
213
  }
241
-
242
214
  sleep(ms) {
243
215
  return new Promise(resolve => {
244
216
  setTimeout(resolve, ms);
245
217
  });
246
218
  }
247
-
248
219
  }
249
220
 
250
221
  /*
@@ -264,16 +235,15 @@ class CrossPlatformLock {
264
235
  * - LibSecretPersistence: Used on linux, writes and reads from secret service API. Requires
265
236
  * libsecret be installed.
266
237
  */
267
-
268
238
  class PersistenceCachePlugin {
269
239
  constructor(persistence, lockOptions) {
270
- this.persistence = persistence; // initialize logger
271
-
272
- this.logger = persistence.getLogger(); // create file lock
273
-
240
+ this.persistence = persistence;
241
+ // initialize logger
242
+ this.logger = persistence.getLogger();
243
+ // create file lock
274
244
  this.lockFilePath = `${this.persistence.getFilePath()}.lockfile`;
275
- this.crossPlatformLock = new CrossPlatformLock(this.lockFilePath, this.logger, lockOptions); // initialize default values
276
-
245
+ this.crossPlatformLock = new CrossPlatformLock(this.lockFilePath, this.logger, lockOptions);
246
+ // initialize default values
277
247
  this.lastSync = 0;
278
248
  this.currentCache = null;
279
249
  }
@@ -285,21 +255,16 @@ class PersistenceCachePlugin {
285
255
  * afterCacheAccess() is called, to prevent the cache file from changing in between
286
256
  * beforeCacheAccess() and afterCacheAccess().
287
257
  */
288
-
289
-
290
258
  async beforeCacheAccess(cacheContext) {
291
259
  this.logger.info("Executing before cache access");
292
260
  const reloadNecessary = await this.persistence.reloadNecessary(this.lastSync);
293
-
294
261
  if (!reloadNecessary && this.currentCache !== null) {
295
262
  if (cacheContext.cacheHasChanged) {
296
263
  this.logger.verbose("Cache context has changed");
297
264
  await this.crossPlatformLock.lock();
298
265
  }
299
-
300
266
  return;
301
267
  }
302
-
303
268
  try {
304
269
  this.logger.info(`Reload necessary. Last sync time: ${this.lastSync}`);
305
270
  await this.crossPlatformLock.lock();
@@ -319,11 +284,8 @@ class PersistenceCachePlugin {
319
284
  /**
320
285
  * Writes to storage if MSAL in memory copy of cache has been changed.
321
286
  */
322
-
323
-
324
287
  async afterCacheAccess(cacheContext) {
325
288
  this.logger.info("Executing after cache access");
326
-
327
289
  try {
328
290
  if (cacheContext.cacheHasChanged) {
329
291
  this.logger.info("Msal in-memory cache has changed. Writing changes to persistence");
@@ -337,7 +299,6 @@ class PersistenceCachePlugin {
337
299
  this.logger.info(`Pid ${pid} afterCacheAccess released lock`);
338
300
  }
339
301
  }
340
-
341
302
  }
342
303
 
343
304
  /*
@@ -348,28 +309,23 @@ class BasePersistence {
348
309
  async verifyPersistence() {
349
310
  // We are using a different location for the test to avoid overriding the functional cache
350
311
  const persistenceValidator = await this.createForPersistenceValidation();
351
-
352
312
  try {
353
313
  await persistenceValidator.save(Constants.PERSISTENCE_TEST_DATA);
354
314
  const retrievedDummyData = await persistenceValidator.load();
355
-
356
315
  if (!retrievedDummyData) {
357
316
  throw PersistenceError.createCachePersistenceError("Persistence check failed. Data was written but it could not be read. " + "Possible cause: on Linux, LibSecret is installed but D-Bus isn't running \
358
317
  because it cannot be started over SSH.");
359
318
  }
360
-
361
319
  if (retrievedDummyData !== Constants.PERSISTENCE_TEST_DATA) {
362
320
  throw PersistenceError.createCachePersistenceError(`Persistence check failed. Data written ${Constants.PERSISTENCE_TEST_DATA} is different \
363
321
  from data read ${retrievedDummyData}`);
364
322
  }
365
-
366
323
  await persistenceValidator.delete();
367
324
  return true;
368
325
  } catch (e) {
369
326
  throw PersistenceError.createCachePersistenceError(`Verifing persistence failed with the error: ${e}`);
370
327
  }
371
328
  }
372
-
373
329
  }
374
330
 
375
331
  /*
@@ -383,7 +339,6 @@ class BasePersistence {
383
339
  * If file or directory has not been created, it FilePersistence.create() will create
384
340
  * file and any directories in the path recursively.
385
341
  */
386
-
387
342
  class FilePersistence extends BasePersistence {
388
343
  static async create(fileLocation, loggerOptions) {
389
344
  const filePersistence = new FilePersistence();
@@ -392,7 +347,6 @@ class FilePersistence extends BasePersistence {
392
347
  await filePersistence.createCacheFile();
393
348
  return filePersistence;
394
349
  }
395
-
396
350
  async save(contents) {
397
351
  try {
398
352
  await promises.writeFile(this.getFilePath(), contents, "utf-8");
@@ -400,7 +354,6 @@ class FilePersistence extends BasePersistence {
400
354
  throw PersistenceError.createFileSystemError(err.code, err.message);
401
355
  }
402
356
  }
403
-
404
357
  async saveBuffer(contents) {
405
358
  try {
406
359
  await promises.writeFile(this.getFilePath(), contents);
@@ -408,7 +361,6 @@ class FilePersistence extends BasePersistence {
408
361
  throw PersistenceError.createFileSystemError(err.code, err.message);
409
362
  }
410
363
  }
411
-
412
364
  async load() {
413
365
  try {
414
366
  return await promises.readFile(this.getFilePath(), "utf-8");
@@ -416,7 +368,6 @@ class FilePersistence extends BasePersistence {
416
368
  throw PersistenceError.createFileSystemError(err.code, err.message);
417
369
  }
418
370
  }
419
-
420
371
  async loadBuffer() {
421
372
  try {
422
373
  return await promises.readFile(this.getFilePath());
@@ -424,7 +375,6 @@ class FilePersistence extends BasePersistence {
424
375
  throw PersistenceError.createFileSystemError(err.code, err.message);
425
376
  }
426
377
  }
427
-
428
378
  async delete() {
429
379
  try {
430
380
  await promises.unlink(this.getFilePath());
@@ -435,37 +385,31 @@ class FilePersistence extends BasePersistence {
435
385
  this.logger.warning("Cache file does not exist, so it could not be deleted");
436
386
  return false;
437
387
  }
438
-
439
388
  throw PersistenceError.createFileSystemError(err.code, err.message);
440
389
  }
441
390
  }
442
-
443
391
  getFilePath() {
444
392
  return this.filePath;
445
393
  }
446
-
447
394
  async reloadNecessary(lastSync) {
448
395
  return lastSync < (await this.timeLastModified());
449
396
  }
450
-
451
397
  getLogger() {
452
398
  return this.logger;
453
399
  }
454
-
455
400
  createForPersistenceValidation() {
456
401
  const testCacheFileLocation = `${dirname(this.filePath)}/test.cache`;
457
402
  return FilePersistence.create(testCacheFileLocation);
458
403
  }
459
-
460
404
  static createDefaultLoggerOptions() {
461
405
  return {
462
- loggerCallback: () => {// allow users to not set loggerCallback
406
+ loggerCallback: () => {
407
+ // allow users to not set loggerCallback
463
408
  },
464
409
  piiLoggingEnabled: false,
465
410
  logLevel: LogLevel.Info
466
411
  };
467
412
  }
468
-
469
413
  async timeLastModified() {
470
414
  try {
471
415
  const stats = await promises.stat(this.filePath);
@@ -476,19 +420,16 @@ class FilePersistence extends BasePersistence {
476
420
  this.logger.verbose("Cache file does not exist");
477
421
  return 0;
478
422
  }
479
-
480
423
  throw PersistenceError.createFileSystemError(err.code, err.message);
481
424
  }
482
425
  }
483
-
484
426
  async createCacheFile() {
485
- await this.createFileDirectory(); // File is created only if it does not exist
486
-
427
+ await this.createFileDirectory();
428
+ // File is created only if it does not exist
487
429
  const fileHandle = await promises.open(this.filePath, "a");
488
430
  await fileHandle.close();
489
431
  this.logger.info(`File created at ${this.filePath}`);
490
432
  }
491
-
492
433
  async createFileDirectory() {
493
434
  try {
494
435
  await promises.mkdir(dirname(this.filePath), {
@@ -502,14 +443,12 @@ class FilePersistence extends BasePersistence {
502
443
  }
503
444
  }
504
445
  }
505
-
506
446
  }
507
447
 
508
448
  /*
509
449
  * Copyright (c) Microsoft Corporation. All rights reserved.
510
450
  * Licensed under the MIT License.
511
451
  */
512
-
513
452
  /* eslint-disable-next-line @typescript-eslint/no-var-requires, no-var, import/no-commonjs */
514
453
  var Dpapi = /*#__PURE__*/require("../build/Release/dpapi.node");
515
454
 
@@ -517,7 +456,6 @@ var Dpapi = /*#__PURE__*/require("../build/Release/dpapi.node");
517
456
  * Copyright (c) Microsoft Corporation. All rights reserved.
518
457
  * Licensed under the MIT License.
519
458
  */
520
-
521
459
  /**
522
460
  * Specifies the scope of the data protection - either the current user or the local
523
461
  * machine.
@@ -530,7 +468,6 @@ var Dpapi = /*#__PURE__*/require("../build/Release/dpapi.node");
530
468
  *
531
469
  */
532
470
  var DataProtectionScope;
533
-
534
471
  (function (DataProtectionScope) {
535
472
  DataProtectionScope["CurrentUser"] = "CurrentUser";
536
473
  DataProtectionScope["LocalMachine"] = "LocalMachine";
@@ -546,20 +483,17 @@ var DataProtectionScope;
546
483
  * scope: Scope of the data protection. Either local user or the current machine
547
484
  * optionalEntropy: Password or other additional entropy used to encrypt the data
548
485
  */
549
-
550
486
  class FilePersistenceWithDataProtection extends BasePersistence {
551
487
  constructor(scope, optionalEntropy) {
552
488
  super();
553
489
  this.scope = scope;
554
490
  this.optionalEntropy = optionalEntropy ? Buffer.from(optionalEntropy, "utf-8") : null;
555
491
  }
556
-
557
492
  static async create(fileLocation, scope, optionalEntropy, loggerOptions) {
558
493
  const persistence = new FilePersistenceWithDataProtection(scope, optionalEntropy);
559
494
  persistence.filePersistence = await FilePersistence.create(fileLocation, loggerOptions);
560
495
  return persistence;
561
496
  }
562
-
563
497
  async save(contents) {
564
498
  try {
565
499
  const encryptedContents = Dpapi.protectData(Buffer.from(contents, "utf-8"), this.optionalEntropy, this.scope.toString());
@@ -568,43 +502,34 @@ class FilePersistenceWithDataProtection extends BasePersistence {
568
502
  throw PersistenceError.createFilePersistenceWithDPAPIError(err.message);
569
503
  }
570
504
  }
571
-
572
505
  async load() {
573
506
  try {
574
507
  const encryptedContents = await this.filePersistence.loadBuffer();
575
-
576
508
  if (typeof encryptedContents === "undefined" || !encryptedContents || 0 === encryptedContents.length) {
577
509
  this.filePersistence.getLogger().info("Encrypted contents loaded from file were null or empty");
578
510
  return null;
579
511
  }
580
-
581
512
  return Dpapi.unprotectData(encryptedContents, this.optionalEntropy, this.scope.toString()).toString();
582
513
  } catch (err) {
583
514
  throw PersistenceError.createFilePersistenceWithDPAPIError(err.message);
584
515
  }
585
516
  }
586
-
587
517
  async delete() {
588
518
  return this.filePersistence.delete();
589
519
  }
590
-
591
520
  async reloadNecessary(lastSync) {
592
521
  return this.filePersistence.reloadNecessary(lastSync);
593
522
  }
594
-
595
523
  getFilePath() {
596
524
  return this.filePersistence.getFilePath();
597
525
  }
598
-
599
526
  getLogger() {
600
527
  return this.filePersistence.getLogger();
601
528
  }
602
-
603
529
  createForPersistenceValidation() {
604
530
  const testCacheFileLocation = `${dirname(this.filePersistence.getFilePath())}/test.cache`;
605
531
  return FilePersistenceWithDataProtection.create(testCacheFileLocation, DataProtectionScope.CurrentUser);
606
532
  }
607
-
608
533
  }
609
534
 
610
535
  /*
@@ -617,31 +542,26 @@ class FilePersistenceWithDataProtection extends BasePersistence {
617
542
  * serviceName: Identifier used as key for whatever value is stored
618
543
  * accountName: Account under which password should be stored
619
544
  */
620
-
621
545
  class KeychainPersistence extends BasePersistence {
622
546
  constructor(serviceName, accountName) {
623
547
  super();
624
548
  this.serviceName = serviceName;
625
549
  this.accountName = accountName;
626
550
  }
627
-
628
551
  static async create(fileLocation, serviceName, accountName, loggerOptions) {
629
552
  const persistence = new KeychainPersistence(serviceName, accountName);
630
553
  persistence.filePersistence = await FilePersistence.create(fileLocation, loggerOptions);
631
554
  return persistence;
632
555
  }
633
-
634
556
  async save(contents) {
635
557
  try {
636
558
  await setPassword(this.serviceName, this.accountName, contents);
637
559
  } catch (err) {
638
560
  throw PersistenceError.createKeychainPersistenceError(err.message);
639
- } // Write dummy data to update file mtime
640
-
641
-
561
+ }
562
+ // Write dummy data to update file mtime
642
563
  await this.filePersistence.save("{}");
643
564
  }
644
-
645
565
  async load() {
646
566
  try {
647
567
  return await getPassword(this.serviceName, this.accountName);
@@ -649,7 +569,6 @@ class KeychainPersistence extends BasePersistence {
649
569
  throw PersistenceError.createKeychainPersistenceError(err.message);
650
570
  }
651
571
  }
652
-
653
572
  async delete() {
654
573
  try {
655
574
  await this.filePersistence.delete();
@@ -658,24 +577,19 @@ class KeychainPersistence extends BasePersistence {
658
577
  throw PersistenceError.createKeychainPersistenceError(err.message);
659
578
  }
660
579
  }
661
-
662
580
  async reloadNecessary(lastSync) {
663
581
  return this.filePersistence.reloadNecessary(lastSync);
664
582
  }
665
-
666
583
  getFilePath() {
667
584
  return this.filePersistence.getFilePath();
668
585
  }
669
-
670
586
  getLogger() {
671
587
  return this.filePersistence.getLogger();
672
588
  }
673
-
674
589
  createForPersistenceValidation() {
675
590
  const testCacheFileLocation = `${dirname(this.filePersistence.getFilePath())}/test.cache`;
676
591
  return KeychainPersistence.create(testCacheFileLocation, "persistenceValidationServiceName", "persistencValidationAccountName");
677
592
  }
678
-
679
593
  }
680
594
 
681
595
  /*
@@ -689,31 +603,26 @@ class KeychainPersistence extends BasePersistence {
689
603
  * serviceName: Identifier used as key for whatever value is stored
690
604
  * accountName: Account under which password should be stored
691
605
  */
692
-
693
606
  class LibSecretPersistence extends BasePersistence {
694
607
  constructor(serviceName, accountName) {
695
608
  super();
696
609
  this.serviceName = serviceName;
697
610
  this.accountName = accountName;
698
611
  }
699
-
700
612
  static async create(fileLocation, serviceName, accountName, loggerOptions) {
701
613
  const persistence = new LibSecretPersistence(serviceName, accountName);
702
614
  persistence.filePersistence = await FilePersistence.create(fileLocation, loggerOptions);
703
615
  return persistence;
704
616
  }
705
-
706
617
  async save(contents) {
707
618
  try {
708
619
  await setPassword(this.serviceName, this.accountName, contents);
709
620
  } catch (err) {
710
621
  throw PersistenceError.createLibSecretError(err.message);
711
- } // Write dummy data to update file mtime
712
-
713
-
622
+ }
623
+ // Write dummy data to update file mtime
714
624
  await this.filePersistence.save("{}");
715
625
  }
716
-
717
626
  async load() {
718
627
  try {
719
628
  return await getPassword(this.serviceName, this.accountName);
@@ -721,7 +630,6 @@ class LibSecretPersistence extends BasePersistence {
721
630
  throw PersistenceError.createLibSecretError(err.message);
722
631
  }
723
632
  }
724
-
725
633
  async delete() {
726
634
  try {
727
635
  await this.filePersistence.delete();
@@ -730,24 +638,19 @@ class LibSecretPersistence extends BasePersistence {
730
638
  throw PersistenceError.createLibSecretError(err.message);
731
639
  }
732
640
  }
733
-
734
641
  async reloadNecessary(lastSync) {
735
642
  return this.filePersistence.reloadNecessary(lastSync);
736
643
  }
737
-
738
644
  getFilePath() {
739
645
  return this.filePersistence.getFilePath();
740
646
  }
741
-
742
647
  getLogger() {
743
648
  return this.filePersistence.getLogger();
744
649
  }
745
-
746
650
  createForPersistenceValidation() {
747
651
  const testCacheFileLocation = `${dirname(this.filePersistence.getFilePath())}/test.cache`;
748
652
  return LibSecretPersistence.create(testCacheFileLocation, "persistenceValidationServiceName", "persistencValidationAccountName");
749
653
  }
750
-
751
654
  }
752
655
 
753
656
  /*
@@ -758,66 +661,50 @@ class Environment {
758
661
  static get homeEnvVar() {
759
662
  return this.getEnvironmentVariable(Constants.ENVIRONMENT.HOME);
760
663
  }
761
-
762
664
  static get lognameEnvVar() {
763
665
  return this.getEnvironmentVariable(Constants.ENVIRONMENT.LOGNAME);
764
666
  }
765
-
766
667
  static get userEnvVar() {
767
668
  return this.getEnvironmentVariable(Constants.ENVIRONMENT.USER);
768
669
  }
769
-
770
670
  static get lnameEnvVar() {
771
671
  return this.getEnvironmentVariable(Constants.ENVIRONMENT.LNAME);
772
672
  }
773
-
774
673
  static get usernameEnvVar() {
775
674
  return this.getEnvironmentVariable(Constants.ENVIRONMENT.USERNAME);
776
675
  }
777
-
778
676
  static getEnvironmentVariable(name) {
779
677
  return process.env[name];
780
678
  }
781
-
782
679
  static getEnvironmentPlatform() {
783
680
  return process.platform;
784
681
  }
785
-
786
682
  static isWindowsPlatform() {
787
683
  return this.getEnvironmentPlatform() === Platform.WINDOWS;
788
684
  }
789
-
790
685
  static isLinuxPlatform() {
791
686
  return this.getEnvironmentPlatform() === Platform.LINUX;
792
687
  }
793
-
794
688
  static isMacPlatform() {
795
689
  return this.getEnvironmentPlatform() === Platform.MACOS;
796
690
  }
797
-
798
691
  static isLinuxRootUser() {
799
692
  return process.getuid() === Constants.LINUX_ROOT_USER_GUID;
800
693
  }
801
-
802
694
  static getUserRootDirectory() {
803
695
  return !this.isWindowsPlatform ? this.getUserHomeDirOnUnix() : this.getUserHomeDirOnWindows();
804
696
  }
805
-
806
697
  static getUserHomeDirOnWindows() {
807
698
  return this.getEnvironmentVariable(Constants.ENVIRONMENT.LOCAL_APPLICATION_DATA);
808
699
  }
809
-
810
700
  static getUserHomeDirOnUnix() {
811
701
  if (this.isWindowsPlatform()) {
812
702
  throw PersistenceError.createNotSupportedError("Getting the user home directory for unix is not supported in windows");
813
703
  }
814
-
815
704
  if (!StringUtils.isEmpty(this.homeEnvVar)) {
816
705
  return this.homeEnvVar;
817
706
  }
818
-
819
707
  let username = null;
820
-
821
708
  if (!StringUtils.isEmpty(this.lognameEnvVar)) {
822
709
  username = this.lognameEnvVar;
823
710
  } else if (!StringUtils.isEmpty(this.userEnvVar)) {
@@ -827,7 +714,6 @@ class Environment {
827
714
  } else if (!StringUtils.isEmpty(this.usernameEnvVar)) {
828
715
  username = this.usernameEnvVar;
829
716
  }
830
-
831
717
  if (this.isMacPlatform()) {
832
718
  return !StringUtils.isEmpty(username) ? path.join("/Users", username) : null;
833
719
  } else if (this.isLinuxPlatform()) {
@@ -840,7 +726,6 @@ class Environment {
840
726
  throw PersistenceError.createNotSupportedError("Getting the user home directory for unix is not supported in windows");
841
727
  }
842
728
  }
843
-
844
729
  }
845
730
 
846
731
  /*
@@ -849,56 +734,442 @@ class Environment {
849
734
  */
850
735
  class PersistenceCreator {
851
736
  static async createPersistence(config) {
852
- let peristence; // On Windows, uses a DPAPI encrypted file
853
-
737
+ let peristence;
738
+ // On Windows, uses a DPAPI encrypted file
854
739
  if (Environment.isWindowsPlatform()) {
855
740
  if (!config.cachePath || !config.dataProtectionScope) {
856
741
  throw PersistenceError.createPersistenceNotValidatedError("Cache path and/or data protection scope not provided for the FilePersistenceWithDataProtection cache plugin");
857
742
  }
858
-
859
743
  peristence = await FilePersistenceWithDataProtection.create(config.cachePath, DataProtectionScope.CurrentUser, undefined, config.loggerOptions);
860
- } // On Mac, uses keychain.
744
+ }
745
+ // On Mac, uses keychain.
861
746
  else if (Environment.isMacPlatform()) {
862
- if (!config.cachePath || !config.serviceName || !config.accountName) {
863
- throw PersistenceError.createPersistenceNotValidatedError("Cache path, service name and/or account name not provided for the KeychainPersistence cache plugin");
864
- }
865
-
866
- peristence = await KeychainPersistence.create(config.cachePath, config.serviceName, config.accountName, config.loggerOptions);
867
- } // On Linux, uses libsecret to store to secret service. Libsecret has to be installed.
868
- else if (Environment.isLinuxPlatform()) {
869
- if (!config.cachePath || !config.serviceName || !config.accountName) {
870
- throw PersistenceError.createPersistenceNotValidatedError("Cache path, service name and/or account name not provided for the LibSecretPersistence cache plugin");
871
- }
872
-
873
- peristence = await LibSecretPersistence.create(config.cachePath, config.serviceName, config.accountName, config.loggerOptions);
874
- } else {
875
- throw PersistenceError.createNotSupportedError("The current environment is not supported by msal-node-extensions yet.");
876
- } // Initially suppress the error thrown during persistence verification to allow us to fallback to plain text
877
-
878
-
747
+ if (!config.cachePath || !config.serviceName || !config.accountName) {
748
+ throw PersistenceError.createPersistenceNotValidatedError("Cache path, service name and/or account name not provided for the KeychainPersistence cache plugin");
749
+ }
750
+ peristence = await KeychainPersistence.create(config.cachePath, config.serviceName, config.accountName, config.loggerOptions);
751
+ }
752
+ // On Linux, uses libsecret to store to secret service. Libsecret has to be installed.
753
+ else if (Environment.isLinuxPlatform()) {
754
+ if (!config.cachePath || !config.serviceName || !config.accountName) {
755
+ throw PersistenceError.createPersistenceNotValidatedError("Cache path, service name and/or account name not provided for the LibSecretPersistence cache plugin");
756
+ }
757
+ peristence = await LibSecretPersistence.create(config.cachePath, config.serviceName, config.accountName, config.loggerOptions);
758
+ } else {
759
+ throw PersistenceError.createNotSupportedError("The current environment is not supported by msal-node-extensions yet.");
760
+ }
761
+ // Initially suppress the error thrown during persistence verification to allow us to fallback to plain text
879
762
  const isPersistenceVerified = await peristence.verifyPersistence().catch(() => false);
880
-
881
763
  if (!isPersistenceVerified) {
882
764
  if (Environment.isLinuxPlatform() && config.usePlaintextFileOnLinux) {
883
765
  if (!config.cachePath) {
884
766
  throw PersistenceError.createPersistenceNotValidatedError("Cache path not provided for the FilePersistence cache plugin");
885
767
  }
886
-
887
768
  peristence = await FilePersistence.create(config.cachePath, config.loggerOptions);
888
769
  const isFilePersistenceVerified = await peristence.verifyPersistence();
889
-
890
770
  if (isFilePersistenceVerified) {
891
771
  return peristence;
892
772
  }
893
773
  }
894
-
895
774
  throw PersistenceError.createPersistenceNotVerifiedError("Persistence could not be verified");
896
775
  }
897
-
898
776
  return peristence;
899
777
  }
778
+ }
779
+
780
+ /*
781
+ * Copyright (c) Microsoft Corporation. All rights reserved.
782
+ * Licensed under the MIT License.
783
+ */
784
+ class NativeAuthError extends AuthError {
785
+ constructor(errorStatus, errorContext, errorCode, errorTag) {
786
+ super(errorStatus, errorContext);
787
+ this.name = "NativeAuthError";
788
+ this.statusCode = errorCode;
789
+ this.tag = errorTag;
790
+ Object.setPrototypeOf(this, NativeAuthError.prototype);
791
+ }
792
+ }
900
793
 
794
+ /* eslint-disable header/header */
795
+ const name = "@azure/msal-node-extensions";
796
+ const version = "1.0.0-alpha.32";
797
+
798
+ /*
799
+ * Copyright (c) Microsoft Corporation. All rights reserved.
800
+ * Licensed under the MIT License.
801
+ */
802
+ class NativeBrokerPlugin {
803
+ constructor() {
804
+ const defaultLoggerOptions = {
805
+ loggerCallback: () => {
806
+ // Empty logger callback
807
+ },
808
+ piiLoggingEnabled: false
809
+ };
810
+ this.logger = new Logger(defaultLoggerOptions, name, version); // Default logger
811
+ this.isBrokerAvailable = msalNodeRuntime.StartupError ? false : true;
812
+ }
813
+ setLogger(loggerOptions) {
814
+ this.logger = new Logger(loggerOptions, name, version);
815
+ const logCallback = (message, logLevel, containsPii) => {
816
+ switch (logLevel) {
817
+ case LogLevel$1.Trace:
818
+ if (containsPii) {
819
+ this.logger.tracePii(message);
820
+ } else {
821
+ this.logger.trace(message);
822
+ }
823
+ break;
824
+ case LogLevel$1.Debug:
825
+ if (containsPii) {
826
+ this.logger.tracePii(message);
827
+ } else {
828
+ this.logger.trace(message);
829
+ }
830
+ break;
831
+ case LogLevel$1.Info:
832
+ if (containsPii) {
833
+ this.logger.infoPii(message);
834
+ } else {
835
+ this.logger.info(message);
836
+ }
837
+ break;
838
+ case LogLevel$1.Warning:
839
+ if (containsPii) {
840
+ this.logger.warningPii(message);
841
+ } else {
842
+ this.logger.warning(message);
843
+ }
844
+ break;
845
+ case LogLevel$1.Error:
846
+ if (containsPii) {
847
+ this.logger.errorPii(message);
848
+ } else {
849
+ this.logger.error(message);
850
+ }
851
+ break;
852
+ case LogLevel$1.Fatal:
853
+ if (containsPii) {
854
+ this.logger.errorPii(message);
855
+ } else {
856
+ this.logger.error(message);
857
+ }
858
+ break;
859
+ default:
860
+ if (containsPii) {
861
+ this.logger.infoPii(message);
862
+ } else {
863
+ this.logger.info(message);
864
+ }
865
+ break;
866
+ }
867
+ };
868
+ try {
869
+ msalNodeRuntime.RegisterLogger(logCallback, loggerOptions.piiLoggingEnabled);
870
+ } catch (e) {
871
+ const wrappedError = this.wrapError(e);
872
+ if (wrappedError) {
873
+ throw wrappedError;
874
+ }
875
+ }
876
+ }
877
+ async getAccountById(accountId, correlationId) {
878
+ this.logger.trace("NativeBrokerPlugin - getAccountById called", correlationId);
879
+ const readAccountResult = await this.readAccountById(accountId, correlationId);
880
+ return this.generateAccountInfo(readAccountResult.account);
881
+ }
882
+ async getAllAccounts(clientId, correlationId) {
883
+ this.logger.trace("NativeBrokerPlugin - getAllAccounts called", correlationId);
884
+ return new Promise((resolve, reject) => {
885
+ const resultCallback = result => {
886
+ try {
887
+ result.CheckError();
888
+ } catch (e) {
889
+ const wrappedError = this.wrapError(e);
890
+ if (wrappedError) {
891
+ reject(wrappedError);
892
+ return;
893
+ }
894
+ }
895
+ const accountInfoResult = [];
896
+ result.accounts.forEach(account => {
897
+ accountInfoResult.push(this.generateAccountInfo(account));
898
+ });
899
+ resolve(accountInfoResult);
900
+ };
901
+ try {
902
+ msalNodeRuntime.DiscoverAccountsAsync(clientId, correlationId, resultCallback);
903
+ } catch (e) {
904
+ const wrappedError = this.wrapError(e);
905
+ if (wrappedError) {
906
+ reject(wrappedError);
907
+ }
908
+ }
909
+ });
910
+ }
911
+ async acquireTokenSilent(request) {
912
+ this.logger.trace("NativeBrokerPlugin - acquireTokenSilent called", request.correlationId);
913
+ const authParams = this.generateRequestParameters(request);
914
+ const account = await this.getAccount(request);
915
+ return new Promise((resolve, reject) => {
916
+ const resultCallback = result => {
917
+ try {
918
+ result.CheckError();
919
+ } catch (e) {
920
+ const wrappedError = this.wrapError(e);
921
+ if (wrappedError) {
922
+ reject(wrappedError);
923
+ return;
924
+ }
925
+ }
926
+ const authenticationResult = this.getAuthenticationResult(request, result);
927
+ resolve(authenticationResult);
928
+ };
929
+ try {
930
+ if (account) {
931
+ msalNodeRuntime.AcquireTokenSilentlyAsync(authParams, request.correlationId, account, resultCallback);
932
+ } else {
933
+ msalNodeRuntime.SignInSilentlyAsync(authParams, request.correlationId, resultCallback);
934
+ }
935
+ } catch (e) {
936
+ const wrappedError = this.wrapError(e);
937
+ if (wrappedError) {
938
+ reject(wrappedError);
939
+ }
940
+ }
941
+ });
942
+ }
943
+ async acquireTokenInteractive(request, providedWindowHandle) {
944
+ this.logger.trace("NativeBrokerPlugin - acquireTokenInteractive called", request.correlationId);
945
+ const authParams = this.generateRequestParameters(request);
946
+ const account = await this.getAccount(request);
947
+ const windowHandle = providedWindowHandle || Buffer.from([0]);
948
+ return new Promise((resolve, reject) => {
949
+ const resultCallback = result => {
950
+ try {
951
+ result.CheckError();
952
+ } catch (e) {
953
+ const wrappedError = this.wrapError(e);
954
+ if (wrappedError) {
955
+ reject(wrappedError);
956
+ return;
957
+ }
958
+ }
959
+ const authenticationResult = this.getAuthenticationResult(request, result);
960
+ resolve(authenticationResult);
961
+ };
962
+ try {
963
+ switch (request.prompt) {
964
+ case PromptValue.LOGIN:
965
+ case PromptValue.SELECT_ACCOUNT:
966
+ case PromptValue.CREATE:
967
+ this.logger.info("Calling native interop SignInInteractively API", request.correlationId);
968
+ const loginHint = request.loginHint || Constants$1.EMPTY_STRING;
969
+ msalNodeRuntime.SignInInteractivelyAsync(windowHandle, authParams, request.correlationId, loginHint, resultCallback);
970
+ break;
971
+ case PromptValue.NONE:
972
+ if (account) {
973
+ this.logger.info("Calling native interop AcquireTokenSilently API", request.correlationId);
974
+ msalNodeRuntime.AcquireTokenSilentlyAsync(authParams, request.correlationId, account, resultCallback);
975
+ } else {
976
+ this.logger.info("Calling native interop SignInSilently API", request.correlationId);
977
+ msalNodeRuntime.SignInSilentlyAsync(authParams, request.correlationId, resultCallback);
978
+ }
979
+ break;
980
+ default:
981
+ if (account) {
982
+ this.logger.info("Calling native interop AcquireTokenInteractively API", request.correlationId);
983
+ msalNodeRuntime.AcquireTokenInteractivelyAsync(windowHandle, authParams, request.correlationId, account, resultCallback);
984
+ } else {
985
+ this.logger.info("Calling native interop SignIn API", request.correlationId);
986
+ const loginHint = request.loginHint || Constants$1.EMPTY_STRING;
987
+ msalNodeRuntime.SignInAsync(windowHandle, authParams, request.correlationId, loginHint, resultCallback);
988
+ }
989
+ break;
990
+ }
991
+ } catch (e) {
992
+ const wrappedError = this.wrapError(e);
993
+ if (wrappedError) {
994
+ reject(wrappedError);
995
+ }
996
+ }
997
+ });
998
+ }
999
+ async signOut(request) {
1000
+ this.logger.trace("NativeBrokerPlugin - signOut called", request.correlationId);
1001
+ const account = await this.getAccount(request);
1002
+ if (!account) {
1003
+ throw ClientAuthError.createNoAccountFoundError();
1004
+ }
1005
+ return new Promise((resolve, reject) => {
1006
+ const resultCallback = result => {
1007
+ try {
1008
+ result.CheckError();
1009
+ } catch (e) {
1010
+ const wrappedError = this.wrapError(e);
1011
+ if (wrappedError) {
1012
+ reject(wrappedError);
1013
+ return;
1014
+ }
1015
+ }
1016
+ resolve();
1017
+ };
1018
+ try {
1019
+ msalNodeRuntime.SignOutSilentlyAsync(request.clientId, request.correlationId, account, resultCallback);
1020
+ } catch (e) {
1021
+ const wrappedError = this.wrapError(e);
1022
+ if (wrappedError) {
1023
+ reject(wrappedError);
1024
+ }
1025
+ }
1026
+ });
1027
+ }
1028
+ async getAccount(request) {
1029
+ if (request.accountId) {
1030
+ const readAccountResult = await this.readAccountById(request.accountId, request.correlationId);
1031
+ return readAccountResult.account;
1032
+ }
1033
+ return null;
1034
+ }
1035
+ async readAccountById(accountId, correlationId) {
1036
+ this.logger.trace("NativeBrokerPlugin - readAccountById called", correlationId);
1037
+ return new Promise((resolve, reject) => {
1038
+ const resultCallback = result => {
1039
+ try {
1040
+ result.CheckError();
1041
+ } catch (e) {
1042
+ const wrappedError = this.wrapError(e);
1043
+ if (wrappedError) {
1044
+ reject(wrappedError);
1045
+ return;
1046
+ }
1047
+ }
1048
+ resolve(result);
1049
+ };
1050
+ try {
1051
+ msalNodeRuntime.ReadAccountByIdAsync(accountId, correlationId, resultCallback);
1052
+ } catch (e) {
1053
+ const wrappedError = this.wrapError(e);
1054
+ if (wrappedError) {
1055
+ reject(wrappedError);
1056
+ }
1057
+ }
1058
+ });
1059
+ }
1060
+ generateRequestParameters(request) {
1061
+ this.logger.trace("NativeBrokerPlugin - generateRequestParameters called", request.correlationId);
1062
+ const authParams = new msalNodeRuntime.AuthParameters();
1063
+ try {
1064
+ authParams.CreateAuthParameters(request.clientId, request.authority);
1065
+ authParams.SetRedirectUri(request.redirectUri);
1066
+ authParams.SetRequestedScopes(request.scopes.join(" "));
1067
+ if (request.claims) {
1068
+ authParams.SetDecodedClaims(request.claims);
1069
+ }
1070
+ if (request.authenticationScheme === AuthenticationScheme.POP) {
1071
+ if (!request.resourceRequestMethod || !request.resourceRequestUri || !request.shrNonce) {
1072
+ throw new Error("Authentication Scheme set to POP but one or more of the following parameters are missing: resourceRequestMethod, resourceRequestUri, shrNonce");
1073
+ }
1074
+ const resourceUrl = new URL(request.resourceRequestUri);
1075
+ authParams.SetPopParams(request.resourceRequestMethod, resourceUrl.host, resourceUrl.pathname, request.shrNonce);
1076
+ }
1077
+ if (request.extraParameters) {
1078
+ Object.keys(request.extraParameters).forEach(key => {
1079
+ authParams.SetAdditionalParameter(key, request.extraParameters[key]);
1080
+ });
1081
+ }
1082
+ } catch (e) {
1083
+ const wrappedError = this.wrapError(e);
1084
+ if (wrappedError) {
1085
+ throw wrappedError;
1086
+ }
1087
+ }
1088
+ return authParams;
1089
+ }
1090
+ getAuthenticationResult(request, authResult) {
1091
+ this.logger.trace("NativeBrokerPlugin - getAuthenticationResult called", request.correlationId);
1092
+ let fromCache;
1093
+ try {
1094
+ const telemetryJSON = JSON.parse(authResult.telemetryData);
1095
+ fromCache = !!telemetryJSON["is_cache"];
1096
+ } catch (e) {
1097
+ this.logger.error("NativeBrokerPlugin: getAuthenticationResult - Error parsing telemetry data. Could not determine if response came from cache.", request.correlationId);
1098
+ }
1099
+ let idTokenClaims;
1100
+ try {
1101
+ idTokenClaims = JSON.parse(authResult.idToken);
1102
+ } catch (e) {
1103
+ throw new Error("Unable to parse idToken claims");
1104
+ }
1105
+ const accountInfo = this.generateAccountInfo(authResult.account, idTokenClaims);
1106
+ const result = {
1107
+ authority: request.authority,
1108
+ uniqueId: idTokenClaims.oid || idTokenClaims.sub || "",
1109
+ tenantId: idTokenClaims.tid || "",
1110
+ scopes: authResult.grantedScopes.split(" "),
1111
+ account: accountInfo,
1112
+ idToken: authResult.rawIdToken,
1113
+ idTokenClaims: idTokenClaims,
1114
+ accessToken: authResult.accessToken,
1115
+ fromCache: fromCache,
1116
+ expiresOn: new Date(authResult.expiresOn * 1000),
1117
+ tokenType: authResult.isPopAuthorization ? AuthenticationScheme.POP : AuthenticationScheme.BEARER,
1118
+ correlationId: request.correlationId,
1119
+ fromNativeBroker: true
1120
+ };
1121
+ return result;
1122
+ }
1123
+ generateAccountInfo(account, idTokenClaims) {
1124
+ this.logger.trace("NativeBrokerPlugin - generateAccountInfo called");
1125
+ const accountInfo = {
1126
+ homeAccountId: account.homeAccountId,
1127
+ environment: account.environment,
1128
+ tenantId: account.realm,
1129
+ username: account.username,
1130
+ localAccountId: account.localAccountId,
1131
+ name: account.displayName,
1132
+ idTokenClaims: idTokenClaims,
1133
+ nativeAccountId: account.accountId
1134
+ };
1135
+ return accountInfo;
1136
+ }
1137
+ isMsalRuntimeError(result) {
1138
+ return result.hasOwnProperty("errorCode") || result.hasOwnProperty("errorStatus") || result.hasOwnProperty("errorContext") || result.hasOwnProperty("errorTag");
1139
+ }
1140
+ wrapError(error) {
1141
+ if (this.isMsalRuntimeError(error)) {
1142
+ const {
1143
+ errorCode,
1144
+ errorStatus,
1145
+ errorContext,
1146
+ errorTag
1147
+ } = error;
1148
+ switch (errorStatus) {
1149
+ case ErrorStatus.InteractionRequired:
1150
+ case ErrorStatus.AccountUnusable:
1151
+ return new InteractionRequiredAuthError(ErrorCodes.INTERATION_REQUIRED_ERROR_CODE, errorContext);
1152
+ case ErrorStatus.NoNetwork:
1153
+ case ErrorStatus.NetworkTemporarilyUnavailable:
1154
+ return ClientAuthError.createNoNetworkConnectivityError();
1155
+ case ErrorStatus.ServerTemporarilyUnavailable:
1156
+ return new ServerError(ErrorCodes.SERVER_UNAVAILABLE, errorContext);
1157
+ case ErrorStatus.UserCanceled:
1158
+ return ClientAuthError.createUserCanceledError();
1159
+ case ErrorStatus.AuthorityUntrusted:
1160
+ return ClientConfigurationError.createUntrustedAuthorityError();
1161
+ case ErrorStatus.UserSwitched:
1162
+ // Not an error case, if there's customer demand we can surface this as a response property
1163
+ return null;
1164
+ case ErrorStatus.AccountNotFound:
1165
+ return ClientAuthError.createNoAccountFoundError();
1166
+ default:
1167
+ return new NativeAuthError(ErrorStatus[errorStatus], errorContext, errorCode, errorTag);
1168
+ }
1169
+ }
1170
+ return error;
1171
+ }
901
1172
  }
902
1173
 
903
- export { DataProtectionScope, Environment, FilePersistence, FilePersistenceWithDataProtection, KeychainPersistence, LibSecretPersistence, PersistenceCachePlugin, PersistenceCreator };
1174
+ export { DataProtectionScope, Environment, FilePersistence, FilePersistenceWithDataProtection, KeychainPersistence, LibSecretPersistence, NativeBrokerPlugin, PersistenceCachePlugin, PersistenceCreator };
904
1175
  //# sourceMappingURL=msal-node-extensions.esm.js.map