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