@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
|
@@ -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;
|
|
271
|
-
|
|
272
|
-
this.logger = persistence.getLogger();
|
|
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);
|
|
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: () => {
|
|
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();
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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;
|
|
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
|
-
}
|
|
744
|
+
}
|
|
745
|
+
// On Mac, uses keychain.
|
|
861
746
|
else if (Environment.isMacPlatform()) {
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
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
|