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

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.
Files changed (47) hide show
  1. package/README.md +156 -5
  2. package/binding.gyp +11 -1
  3. package/dist/{dpapi-addon/Dpapi.d.ts → Dpapi.d.ts} +0 -0
  4. package/dist/error/PersistenceError.d.ts +28 -0
  5. package/dist/index.d.ts +3 -0
  6. package/dist/msal-node-extensions.cjs.development.js +603 -859
  7. package/dist/msal-node-extensions.cjs.development.js.map +1 -1
  8. package/dist/msal-node-extensions.cjs.production.min.js +1 -1
  9. package/dist/msal-node-extensions.cjs.production.min.js.map +1 -1
  10. package/dist/msal-node-extensions.esm.js +613 -874
  11. package/dist/msal-node-extensions.esm.js.map +1 -1
  12. package/dist/packageMetadata.d.ts +2 -0
  13. package/dist/persistence/BasePersistence.d.ts +5 -0
  14. package/dist/persistence/FilePersistence.d.ts +4 -2
  15. package/dist/persistence/FilePersistenceWithDataProtection.d.ts +3 -1
  16. package/dist/persistence/IPersistence.d.ts +3 -1
  17. package/dist/persistence/IPersistenceConfiguration.d.ts +10 -0
  18. package/dist/persistence/KeychainPersistence.d.ts +3 -1
  19. package/dist/persistence/LibSecretPersistence.d.ts +3 -1
  20. package/dist/persistence/PersistenceCachePlugin.d.ts +11 -7
  21. package/dist/persistence/PersistenceCreator.d.ts +5 -0
  22. package/dist/utils/Constants.d.ts +26 -0
  23. package/dist/utils/Environment.d.ts +16 -0
  24. package/package.json +27 -15
  25. package/src/{dpapi-addon/Dpapi.ts → Dpapi.ts} +12 -12
  26. package/src/dpapi-addon/dpapi_addon.h +2 -1
  27. package/src/dpapi-addon/dpapi_not_supported.cpp +4 -10
  28. package/src/dpapi-addon/dpapi_win.cpp +25 -33
  29. package/src/dpapi-addon/main.cpp +14 -16
  30. package/src/error/PersistenceError.ts +101 -61
  31. package/src/index.ts +16 -8
  32. package/src/lock/CrossPlatformLock.ts +89 -89
  33. package/src/lock/CrossPlatformLockOptions.ts +15 -15
  34. package/src/packageMetadata.ts +3 -0
  35. package/src/persistence/BasePersistence.ts +43 -0
  36. package/src/persistence/FilePersistence.ts +140 -134
  37. package/src/persistence/FilePersistenceWithDataProtection.ts +92 -84
  38. package/src/persistence/IPersistence.ts +4 -2
  39. package/src/persistence/IPersistenceConfiguration.ts +17 -0
  40. package/src/persistence/KeychainPersistence.ts +89 -78
  41. package/src/persistence/LibSecretPersistence.ts +90 -79
  42. package/src/persistence/PersistenceCachePlugin.ts +40 -30
  43. package/src/persistence/PersistenceCreator.ts +78 -0
  44. package/src/utils/Constants.ts +62 -30
  45. package/src/utils/Environment.ts +101 -0
  46. package/CHANGELOG.json +0 -50
  47. package/changelog.md +0 -28
@@ -1,134 +1,140 @@
1
- /*
2
- * Copyright (c) Microsoft Corporation. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
-
6
- import { promises as fs } from "fs";
7
- import { dirname } from "path";
8
- import { IPersistence } from "./IPersistence";
9
- import { Constants } from "../utils/Constants";
10
- import { PersistenceError } from "../error/PersistenceError";
11
- import { Logger, LoggerOptions, LogLevel } from "@azure/msal-common";
12
-
13
- /**
14
- * Reads and writes data to file specified by file location. File contents are not
15
- * encrypted.
16
- *
17
- * If file or directory has not been created, it FilePersistence.create() will create
18
- * file and any directories in the path recursively.
19
- */
20
- export class FilePersistence implements IPersistence {
21
-
22
- private filePath: string;
23
- private logger: Logger;
24
-
25
- public static async create(fileLocation: string, loggerOptions?: LoggerOptions): Promise<FilePersistence> {
26
- const filePersistence = new FilePersistence();
27
- filePersistence.filePath = fileLocation;
28
- filePersistence.logger = new Logger(loggerOptions || FilePersistence.createDefaultLoggerOptions());
29
- await filePersistence.createCacheFile();
30
- return filePersistence;
31
- }
32
-
33
- public async save(contents: string): Promise<void> {
34
- try {
35
- await fs.writeFile(this.getFilePath(), contents, "utf-8");
36
- } catch (err) {
37
- throw PersistenceError.createFileSystemError(err.code, err.message);
38
- }
39
- }
40
-
41
- public async saveBuffer(contents: Uint8Array): Promise<void> {
42
- try {
43
- await fs.writeFile(this.getFilePath(), contents);
44
- } catch (err) {
45
- throw PersistenceError.createFileSystemError(err.code, err.message);
46
- }
47
- }
48
-
49
- public async load(): Promise<string> {
50
- try {
51
- return await fs.readFile(this.getFilePath(), "utf-8");
52
- } catch (err) {
53
- throw PersistenceError.createFileSystemError(err.code, err.message);
54
- }
55
- }
56
-
57
- public async loadBuffer(): Promise<Uint8Array> {
58
- try {
59
- return await fs.readFile(this.getFilePath());
60
- } catch (err) {
61
- throw PersistenceError.createFileSystemError(err.code, err.message);
62
- }
63
- }
64
-
65
- public async delete(): Promise<boolean> {
66
- try {
67
- await fs.unlink(this.getFilePath());
68
- return true;
69
- } catch (err) {
70
- if (err.code == Constants.ENOENT_ERROR) {
71
- // file does not exist, so it was not deleted
72
- this.logger.warning("Cache file does not exist, so it could not be deleted");
73
- return false;
74
- }
75
- throw PersistenceError.createFileSystemError(err.code, err.message);
76
- }
77
- }
78
-
79
- public getFilePath(): string {
80
- return this.filePath;
81
- }
82
-
83
- public async reloadNecessary(lastSync: number): Promise<boolean> {
84
- return lastSync < await this.timeLastModified();
85
- }
86
-
87
- public getLogger(): Logger {
88
- return this.logger;
89
- }
90
-
91
- private static createDefaultLoggerOptions(): LoggerOptions {
92
- return {
93
- loggerCallback: () => {
94
- // allow users to not set loggerCallback
95
- },
96
- piiLoggingEnabled: false,
97
- logLevel: LogLevel.Info
98
- };
99
- }
100
-
101
- private async timeLastModified(): Promise<number> {
102
- try {
103
- const stats = await fs.stat(this.filePath);
104
- return stats.mtime.getTime();
105
- } catch (err) {
106
- if (err.code == Constants.ENOENT_ERROR) {
107
- // file does not exist, so it's never been modified
108
- this.logger.verbose("Cache file does not exist");
109
- return 0;
110
- }
111
- throw PersistenceError.createFileSystemError(err.code, err.message);
112
- }
113
- }
114
-
115
- private async createCacheFile(): Promise<void> {
116
- await this.createFileDirectory();
117
- // File is created only if it does not exist
118
- const fileHandle = await fs.open(this.filePath, "a");
119
- await fileHandle.close();
120
- this.logger.info(`File created at ${this.filePath}`);
121
- }
122
-
123
- private async createFileDirectory(): Promise<void> {
124
- try {
125
- await fs.mkdir(dirname(this.filePath), {recursive: true});
126
- } catch (err) {
127
- if (err.code == Constants.EEXIST_ERROR) {
128
- this.logger.info(`Directory ${dirname(this.filePath)} already exists`);
129
- } else {
130
- throw PersistenceError.createFileSystemError(err.code, err.message);
131
- }
132
- }
133
- }
134
- }
1
+ /*
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { promises as fs } from "fs";
7
+ import { dirname } from "path";
8
+ import { IPersistence } from "./IPersistence";
9
+ import { Constants } from "../utils/Constants";
10
+ import { PersistenceError } from "../error/PersistenceError";
11
+ import { Logger, LoggerOptions, LogLevel } from "@azure/msal-common";
12
+ import { BasePersistence } from "./BasePersistence";
13
+
14
+ /**
15
+ * Reads and writes data to file specified by file location. File contents are not
16
+ * encrypted.
17
+ *
18
+ * If file or directory has not been created, it FilePersistence.create() will create
19
+ * file and any directories in the path recursively.
20
+ */
21
+ export class FilePersistence extends BasePersistence implements IPersistence {
22
+
23
+ private filePath: string;
24
+ private logger: Logger;
25
+
26
+ public static async create(fileLocation: string, loggerOptions?: LoggerOptions): Promise<FilePersistence> {
27
+ const filePersistence = new FilePersistence();
28
+ filePersistence.filePath = fileLocation;
29
+ filePersistence.logger = new Logger(loggerOptions || FilePersistence.createDefaultLoggerOptions());
30
+ await filePersistence.createCacheFile();
31
+ return filePersistence;
32
+ }
33
+
34
+ public async save(contents: string): Promise<void> {
35
+ try {
36
+ await fs.writeFile(this.getFilePath(), contents, "utf-8");
37
+ } catch (err) {
38
+ throw PersistenceError.createFileSystemError(err.code, err.message);
39
+ }
40
+ }
41
+
42
+ public async saveBuffer(contents: Uint8Array): Promise<void> {
43
+ try {
44
+ await fs.writeFile(this.getFilePath(), contents);
45
+ } catch (err) {
46
+ throw PersistenceError.createFileSystemError(err.code, err.message);
47
+ }
48
+ }
49
+
50
+ public async load(): Promise<string | null> {
51
+ try {
52
+ return await fs.readFile(this.getFilePath(), "utf-8");
53
+ } catch (err) {
54
+ throw PersistenceError.createFileSystemError(err.code, err.message);
55
+ }
56
+ }
57
+
58
+ public async loadBuffer(): Promise<Uint8Array> {
59
+ try {
60
+ return await fs.readFile(this.getFilePath());
61
+ } catch (err) {
62
+ throw PersistenceError.createFileSystemError(err.code, err.message);
63
+ }
64
+ }
65
+
66
+ public async delete(): Promise<boolean> {
67
+ try {
68
+ await fs.unlink(this.getFilePath());
69
+ return true;
70
+ } catch (err) {
71
+ if (err.code === Constants.ENOENT_ERROR) {
72
+ // file does not exist, so it was not deleted
73
+ this.logger.warning("Cache file does not exist, so it could not be deleted");
74
+ return false;
75
+ }
76
+ throw PersistenceError.createFileSystemError(err.code, err.message);
77
+ }
78
+ }
79
+
80
+ public getFilePath(): string {
81
+ return this.filePath;
82
+ }
83
+
84
+ public async reloadNecessary(lastSync: number): Promise<boolean> {
85
+ return lastSync < await this.timeLastModified();
86
+ }
87
+
88
+ public getLogger(): Logger {
89
+ return this.logger;
90
+ }
91
+
92
+ public createForPersistenceValidation(): Promise<FilePersistence> {
93
+ const testCacheFileLocation = `${dirname(this.filePath)}/test.cache`;
94
+ return FilePersistence.create(testCacheFileLocation);
95
+ }
96
+
97
+ private static createDefaultLoggerOptions(): LoggerOptions {
98
+ return {
99
+ loggerCallback: () => {
100
+ // allow users to not set loggerCallback
101
+ },
102
+ piiLoggingEnabled: false,
103
+ logLevel: LogLevel.Info
104
+ };
105
+ }
106
+
107
+ private async timeLastModified(): Promise<number> {
108
+ try {
109
+ const stats = await fs.stat(this.filePath);
110
+ return stats.mtime.getTime();
111
+ } catch (err) {
112
+ if (err.code === Constants.ENOENT_ERROR) {
113
+ // file does not exist, so it's never been modified
114
+ this.logger.verbose("Cache file does not exist");
115
+ return 0;
116
+ }
117
+ throw PersistenceError.createFileSystemError(err.code, err.message);
118
+ }
119
+ }
120
+
121
+ private async createCacheFile(): Promise<void> {
122
+ await this.createFileDirectory();
123
+ // File is created only if it does not exist
124
+ const fileHandle = await fs.open(this.filePath, "a");
125
+ await fileHandle.close();
126
+ this.logger.info(`File created at ${this.filePath}`);
127
+ }
128
+
129
+ private async createFileDirectory(): Promise<void> {
130
+ try {
131
+ await fs.mkdir(dirname(this.filePath), {recursive: true});
132
+ } catch (err) {
133
+ if (err.code === Constants.EEXIST_ERROR) {
134
+ this.logger.info(`Directory ${dirname(this.filePath)} already exists`);
135
+ } else {
136
+ throw PersistenceError.createFileSystemError(err.code, err.message);
137
+ }
138
+ }
139
+ }
140
+ }
@@ -1,84 +1,92 @@
1
- /*
2
- * Copyright (c) Microsoft Corporation. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
-
6
- import { IPersistence } from "./IPersistence";
7
- import { FilePersistence } from "./FilePersistence";
8
- import { PersistenceError } from "../error/PersistenceError";
9
- import { Dpapi } from "../dpapi-addon/Dpapi";
10
- import { DataProtectionScope } from "./DataProtectionScope";
11
- import { Logger, LoggerOptions } from "@azure/msal-common";
12
-
13
- /**
14
- * Uses CryptProtectData and CryptUnprotectData on Windows to encrypt and decrypt file contents.
15
- *
16
- * scope: Scope of the data protection. Either local user or the current machine
17
- * optionalEntropy: Password or other additional entropy used to encrypt the data
18
- */
19
- export class FilePersistenceWithDataProtection implements IPersistence {
20
-
21
- private filePersistence: FilePersistence;
22
- private scope: DataProtectionScope;
23
- private optionalEntropy: Uint8Array;
24
-
25
- private constructor(scope: DataProtectionScope, optionalEntropy?: string) {
26
- this.scope = scope;
27
- this.optionalEntropy = optionalEntropy ? Buffer.from(optionalEntropy, "utf-8") : null;
28
- }
29
-
30
- public static async create(
31
- fileLocation: string,
32
- scope: DataProtectionScope,
33
- optionalEntropy?: string,
34
- loggerOptions?: LoggerOptions): Promise<FilePersistenceWithDataProtection> {
35
-
36
- const persistence = new FilePersistenceWithDataProtection(scope, optionalEntropy);
37
- persistence.filePersistence = await FilePersistence.create(fileLocation, loggerOptions);
38
- return persistence;
39
- }
40
-
41
- public async save(contents: string): Promise<void> {
42
- try {
43
- const encryptedContents = Dpapi.protectData(
44
- Buffer.from(contents, "utf-8"),
45
- this.optionalEntropy,
46
- this.scope.toString());
47
- await this.filePersistence.saveBuffer(encryptedContents);
48
- } catch (err) {
49
- throw PersistenceError.createFilePersistenceWithDPAPIError(err.message);
50
- }
51
- }
52
-
53
- public async load(): Promise<string | null> {
54
- try {
55
- const encryptedContents = await this.filePersistence.loadBuffer();
56
- if (typeof encryptedContents === "undefined" || !encryptedContents || 0 === encryptedContents.length) {
57
- this.filePersistence.getLogger().info("Encrypted contents loaded from file were null or empty");
58
- return null;
59
- }
60
- return Dpapi.unprotectData(
61
- encryptedContents,
62
- this.optionalEntropy,
63
- this.scope.toString()).toString();
64
- } catch (err) {
65
- throw PersistenceError.createFilePersistenceWithDPAPIError(err.message);
66
- }
67
- }
68
-
69
- public async delete(): Promise<boolean> {
70
- return this.filePersistence.delete();
71
- }
72
-
73
- public async reloadNecessary(lastSync: number): Promise<boolean> {
74
- return this.filePersistence.reloadNecessary(lastSync);
75
- }
76
-
77
- public getFilePath(): string {
78
- return this.filePersistence.getFilePath();
79
- }
80
-
81
- public getLogger(): Logger {
82
- return this.filePersistence.getLogger();
83
- }
84
- }
1
+ /*
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { IPersistence } from "./IPersistence";
7
+ import { FilePersistence } from "./FilePersistence";
8
+ import { PersistenceError } from "../error/PersistenceError";
9
+ import { Dpapi } from "../Dpapi";
10
+ import { DataProtectionScope } from "./DataProtectionScope";
11
+ import { Logger, LoggerOptions } from "@azure/msal-common";
12
+ import { dirname } from "path";
13
+ import { BasePersistence } from "./BasePersistence";
14
+
15
+ /**
16
+ * Uses CryptProtectData and CryptUnprotectData on Windows to encrypt and decrypt file contents.
17
+ *
18
+ * scope: Scope of the data protection. Either local user or the current machine
19
+ * optionalEntropy: Password or other additional entropy used to encrypt the data
20
+ */
21
+ export class FilePersistenceWithDataProtection extends BasePersistence implements IPersistence {
22
+
23
+ private filePersistence: FilePersistence;
24
+ private scope: DataProtectionScope;
25
+ private optionalEntropy: Uint8Array;
26
+
27
+ private constructor(scope: DataProtectionScope, optionalEntropy?: string) {
28
+ super();
29
+ this.scope = scope;
30
+ this.optionalEntropy = optionalEntropy ? Buffer.from(optionalEntropy, "utf-8") : null;
31
+ }
32
+
33
+ public static async create(
34
+ fileLocation: string,
35
+ scope: DataProtectionScope,
36
+ optionalEntropy?: string,
37
+ loggerOptions?: LoggerOptions): Promise<FilePersistenceWithDataProtection> {
38
+
39
+ const persistence = new FilePersistenceWithDataProtection(scope, optionalEntropy);
40
+ persistence.filePersistence = await FilePersistence.create(fileLocation, loggerOptions);
41
+ return persistence;
42
+ }
43
+
44
+ public async save(contents: string): Promise<void> {
45
+ try {
46
+ const encryptedContents = Dpapi.protectData(
47
+ Buffer.from(contents, "utf-8"),
48
+ this.optionalEntropy,
49
+ this.scope.toString());
50
+ await this.filePersistence.saveBuffer(encryptedContents);
51
+ } catch (err) {
52
+ throw PersistenceError.createFilePersistenceWithDPAPIError(err.message);
53
+ }
54
+ }
55
+
56
+ public async load(): Promise<string | null> {
57
+ try {
58
+ const encryptedContents = await this.filePersistence.loadBuffer();
59
+ if (typeof encryptedContents === "undefined" || !encryptedContents || 0 === encryptedContents.length) {
60
+ this.filePersistence.getLogger().info("Encrypted contents loaded from file were null or empty");
61
+ return null;
62
+ }
63
+ return Dpapi.unprotectData(
64
+ encryptedContents,
65
+ this.optionalEntropy,
66
+ this.scope.toString()).toString();
67
+ } catch (err) {
68
+ throw PersistenceError.createFilePersistenceWithDPAPIError(err.message);
69
+ }
70
+ }
71
+
72
+ public async delete(): Promise<boolean> {
73
+ return this.filePersistence.delete();
74
+ }
75
+
76
+ public async reloadNecessary(lastSync: number): Promise<boolean> {
77
+ return this.filePersistence.reloadNecessary(lastSync);
78
+ }
79
+
80
+ public getFilePath(): string {
81
+ return this.filePersistence.getFilePath();
82
+ }
83
+
84
+ public getLogger(): Logger {
85
+ return this.filePersistence.getLogger();
86
+ }
87
+
88
+ public createForPersistenceValidation(): Promise<FilePersistenceWithDataProtection> {
89
+ const testCacheFileLocation = `${dirname(this.filePersistence.getFilePath())}/test.cache`;
90
+ return FilePersistenceWithDataProtection.create(testCacheFileLocation, DataProtectionScope.CurrentUser);
91
+ }
92
+ }
@@ -7,9 +7,11 @@ import { Logger } from "@azure/msal-common";
7
7
 
8
8
  export interface IPersistence {
9
9
  save(contents: string): Promise<void>;
10
- load(): Promise<string>;
10
+ load(): Promise<string | null>;
11
11
  delete(): Promise<boolean>;
12
12
  reloadNecessary(lastSync: number): Promise<boolean>;
13
13
  getFilePath(): string;
14
- getLogger(): Logger
14
+ getLogger(): Logger;
15
+ verifyPersistence(): Promise<boolean>;
16
+ createForPersistenceValidation(): Promise<IPersistence>;
15
17
  }
@@ -0,0 +1,17 @@
1
+ /*
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import type { LoggerOptions } from "@azure/msal-common";
7
+
8
+ import { DataProtectionScope } from "./DataProtectionScope";
9
+
10
+ export interface IPersistenceConfiguration {
11
+ cachePath?: string,
12
+ dataProtectionScope?: DataProtectionScope,
13
+ serviceName?: string,
14
+ accountName?: string,
15
+ usePlaintextFileOnLinux?: boolean,
16
+ loggerOptions?: LoggerOptions;
17
+ }