@azure/msal-browser 3.28.0 → 4.0.0-alpha.0

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 (174) hide show
  1. package/dist/app/IPublicClientApplication.mjs +1 -1
  2. package/dist/app/PublicClientApplication.mjs +1 -1
  3. package/dist/app/PublicClientNext.mjs +1 -1
  4. package/dist/broker/nativeBroker/NativeMessageHandler.mjs +1 -1
  5. package/dist/broker/nativeBroker/NativeStatusCodes.mjs +1 -1
  6. package/dist/cache/AccountManager.mjs +1 -1
  7. package/dist/cache/AsyncMemoryStorage.mjs +1 -1
  8. package/dist/cache/BrowserCacheManager.d.ts +7 -47
  9. package/dist/cache/BrowserCacheManager.d.ts.map +1 -1
  10. package/dist/cache/BrowserCacheManager.mjs +36 -285
  11. package/dist/cache/BrowserCacheManager.mjs.map +1 -1
  12. package/dist/cache/CacheHelpers.d.ts +16 -0
  13. package/dist/cache/CacheHelpers.d.ts.map +1 -0
  14. package/dist/cache/CacheHelpers.mjs +46 -0
  15. package/dist/cache/CacheHelpers.mjs.map +1 -0
  16. package/dist/cache/CookieStorage.d.ts +3 -0
  17. package/dist/cache/CookieStorage.d.ts.map +1 -1
  18. package/dist/cache/CookieStorage.mjs +12 -1
  19. package/dist/cache/CookieStorage.mjs.map +1 -1
  20. package/dist/cache/DatabaseStorage.mjs +1 -1
  21. package/dist/cache/ITokenCache.d.ts +1 -1
  22. package/dist/cache/ITokenCache.d.ts.map +1 -1
  23. package/dist/cache/IWindowStorage.d.ts +12 -0
  24. package/dist/cache/IWindowStorage.d.ts.map +1 -1
  25. package/dist/cache/LocalStorage.d.ts +34 -1
  26. package/dist/cache/LocalStorage.d.ts.map +1 -1
  27. package/dist/cache/LocalStorage.mjs +169 -2
  28. package/dist/cache/LocalStorage.mjs.map +1 -1
  29. package/dist/cache/MemoryStorage.d.ts +3 -0
  30. package/dist/cache/MemoryStorage.d.ts.map +1 -1
  31. package/dist/cache/MemoryStorage.mjs +10 -1
  32. package/dist/cache/MemoryStorage.mjs.map +1 -1
  33. package/dist/cache/SessionStorage.d.ts +3 -0
  34. package/dist/cache/SessionStorage.d.ts.map +1 -1
  35. package/dist/cache/SessionStorage.mjs +10 -1
  36. package/dist/cache/SessionStorage.mjs.map +1 -1
  37. package/dist/cache/TokenCache.d.ts +1 -1
  38. package/dist/cache/TokenCache.d.ts.map +1 -1
  39. package/dist/cache/TokenCache.mjs +15 -15
  40. package/dist/cache/TokenCache.mjs.map +1 -1
  41. package/dist/config/Configuration.mjs +1 -1
  42. package/dist/controllers/ControllerFactory.mjs +1 -1
  43. package/dist/controllers/NestedAppAuthController.d.ts.map +1 -1
  44. package/dist/controllers/NestedAppAuthController.mjs +4 -4
  45. package/dist/controllers/NestedAppAuthController.mjs.map +1 -1
  46. package/dist/controllers/StandardController.d.ts.map +1 -1
  47. package/dist/controllers/StandardController.mjs +3 -2
  48. package/dist/controllers/StandardController.mjs.map +1 -1
  49. package/dist/controllers/UnknownOperatingContextController.mjs +1 -1
  50. package/dist/crypto/BrowserCrypto.d.ts +27 -0
  51. package/dist/crypto/BrowserCrypto.d.ts.map +1 -1
  52. package/dist/crypto/BrowserCrypto.mjs +83 -3
  53. package/dist/crypto/BrowserCrypto.mjs.map +1 -1
  54. package/dist/crypto/CryptoOps.mjs +1 -1
  55. package/dist/crypto/PkceGenerator.mjs +1 -1
  56. package/dist/crypto/SignedHttpRequest.mjs +1 -1
  57. package/dist/encode/Base64Decode.d.ts +5 -0
  58. package/dist/encode/Base64Decode.d.ts.map +1 -1
  59. package/dist/encode/Base64Decode.mjs +2 -2
  60. package/dist/encode/Base64Decode.mjs.map +1 -1
  61. package/dist/encode/Base64Encode.mjs +1 -1
  62. package/dist/error/BrowserAuthError.mjs +1 -1
  63. package/dist/error/BrowserAuthErrorCodes.mjs +1 -1
  64. package/dist/error/BrowserConfigurationAuthError.mjs +1 -1
  65. package/dist/error/BrowserConfigurationAuthErrorCodes.mjs +1 -1
  66. package/dist/error/NativeAuthError.mjs +1 -1
  67. package/dist/error/NativeAuthErrorCodes.mjs +1 -1
  68. package/dist/error/NestedAppAuthError.mjs +1 -1
  69. package/dist/event/EventHandler.mjs +1 -1
  70. package/dist/event/EventMessage.mjs +1 -1
  71. package/dist/event/EventType.mjs +1 -1
  72. package/dist/index.d.ts +0 -1
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.mjs +1 -2
  75. package/dist/index.mjs.map +1 -1
  76. package/dist/interaction_client/BaseInteractionClient.mjs +1 -1
  77. package/dist/interaction_client/HybridSpaAuthorizationCodeClient.mjs +1 -1
  78. package/dist/interaction_client/NativeInteractionClient.d.ts +1 -1
  79. package/dist/interaction_client/NativeInteractionClient.d.ts.map +1 -1
  80. package/dist/interaction_client/NativeInteractionClient.mjs +4 -4
  81. package/dist/interaction_client/NativeInteractionClient.mjs.map +1 -1
  82. package/dist/interaction_client/PopupClient.mjs +1 -1
  83. package/dist/interaction_client/RedirectClient.mjs +1 -1
  84. package/dist/interaction_client/SilentAuthCodeClient.mjs +1 -1
  85. package/dist/interaction_client/SilentCacheClient.mjs +1 -1
  86. package/dist/interaction_client/SilentIframeClient.mjs +1 -1
  87. package/dist/interaction_client/SilentRefreshClient.mjs +1 -1
  88. package/dist/interaction_client/StandardInteractionClient.d.ts.map +1 -1
  89. package/dist/interaction_client/StandardInteractionClient.mjs +1 -8
  90. package/dist/interaction_client/StandardInteractionClient.mjs.map +1 -1
  91. package/dist/interaction_handler/InteractionHandler.mjs +1 -1
  92. package/dist/interaction_handler/RedirectHandler.mjs +1 -1
  93. package/dist/interaction_handler/SilentHandler.mjs +1 -1
  94. package/dist/naa/BridgeError.mjs +1 -1
  95. package/dist/naa/BridgeProxy.mjs +1 -1
  96. package/dist/naa/BridgeStatusCode.mjs +1 -1
  97. package/dist/naa/mapping/NestedAppAuthAdapter.mjs +1 -1
  98. package/dist/navigation/NavigationClient.mjs +1 -1
  99. package/dist/network/FetchClient.mjs +1 -1
  100. package/dist/operatingcontext/BaseOperatingContext.mjs +1 -1
  101. package/dist/operatingcontext/NestedAppOperatingContext.mjs +1 -1
  102. package/dist/operatingcontext/StandardOperatingContext.mjs +1 -1
  103. package/dist/operatingcontext/UnknownOperatingContext.mjs +1 -1
  104. package/dist/packageMetadata.d.ts +1 -1
  105. package/dist/packageMetadata.d.ts.map +1 -1
  106. package/dist/packageMetadata.mjs +2 -2
  107. package/dist/request/RequestHelpers.mjs +1 -1
  108. package/dist/response/ResponseHandler.mjs +1 -1
  109. package/dist/telemetry/BrowserPerformanceClient.mjs +1 -1
  110. package/dist/telemetry/BrowserPerformanceMeasurement.mjs +1 -1
  111. package/dist/utils/BrowserConstants.mjs +1 -1
  112. package/dist/utils/BrowserProtocolUtils.mjs +1 -1
  113. package/dist/utils/BrowserUtils.mjs +1 -1
  114. package/lib/msal-browser.cjs +532 -591
  115. package/lib/msal-browser.cjs.map +1 -1
  116. package/lib/msal-browser.js +532 -591
  117. package/lib/msal-browser.js.map +1 -1
  118. package/lib/msal-browser.min.js +65 -66
  119. package/lib/types/cache/BrowserCacheManager.d.ts +7 -47
  120. package/lib/types/cache/BrowserCacheManager.d.ts.map +1 -1
  121. package/lib/types/cache/CacheHelpers.d.ts +16 -0
  122. package/lib/types/cache/CacheHelpers.d.ts.map +1 -0
  123. package/lib/types/cache/CookieStorage.d.ts +3 -0
  124. package/lib/types/cache/CookieStorage.d.ts.map +1 -1
  125. package/lib/types/cache/ITokenCache.d.ts +1 -1
  126. package/lib/types/cache/ITokenCache.d.ts.map +1 -1
  127. package/lib/types/cache/IWindowStorage.d.ts +12 -0
  128. package/lib/types/cache/IWindowStorage.d.ts.map +1 -1
  129. package/lib/types/cache/LocalStorage.d.ts +34 -1
  130. package/lib/types/cache/LocalStorage.d.ts.map +1 -1
  131. package/lib/types/cache/MemoryStorage.d.ts +3 -0
  132. package/lib/types/cache/MemoryStorage.d.ts.map +1 -1
  133. package/lib/types/cache/SessionStorage.d.ts +3 -0
  134. package/lib/types/cache/SessionStorage.d.ts.map +1 -1
  135. package/lib/types/cache/TokenCache.d.ts +1 -1
  136. package/lib/types/cache/TokenCache.d.ts.map +1 -1
  137. package/lib/types/controllers/NestedAppAuthController.d.ts.map +1 -1
  138. package/lib/types/controllers/StandardController.d.ts.map +1 -1
  139. package/lib/types/crypto/BrowserCrypto.d.ts +27 -0
  140. package/lib/types/crypto/BrowserCrypto.d.ts.map +1 -1
  141. package/lib/types/encode/Base64Decode.d.ts +5 -0
  142. package/lib/types/encode/Base64Decode.d.ts.map +1 -1
  143. package/lib/types/index.d.ts +0 -1
  144. package/lib/types/index.d.ts.map +1 -1
  145. package/lib/types/interaction_client/NativeInteractionClient.d.ts +1 -1
  146. package/lib/types/interaction_client/NativeInteractionClient.d.ts.map +1 -1
  147. package/lib/types/interaction_client/StandardInteractionClient.d.ts.map +1 -1
  148. package/lib/types/packageMetadata.d.ts +1 -1
  149. package/lib/types/packageMetadata.d.ts.map +1 -1
  150. package/package.json +2 -2
  151. package/src/cache/BrowserCacheManager.ts +59 -412
  152. package/src/cache/CacheHelpers.ts +52 -0
  153. package/src/cache/CookieStorage.ts +18 -0
  154. package/src/cache/ITokenCache.ts +1 -1
  155. package/src/cache/IWindowStorage.ts +14 -0
  156. package/src/cache/LocalStorage.ts +231 -1
  157. package/src/cache/MemoryStorage.ts +12 -0
  158. package/src/cache/SessionStorage.ts +12 -0
  159. package/src/cache/TokenCache.ts +19 -19
  160. package/src/controllers/NestedAppAuthController.ts +3 -3
  161. package/src/controllers/StandardController.ts +3 -1
  162. package/src/crypto/BrowserCrypto.ts +120 -1
  163. package/src/encode/Base64Decode.ts +1 -1
  164. package/src/index.ts +0 -1
  165. package/src/interaction_client/NativeInteractionClient.ts +3 -3
  166. package/src/interaction_client/StandardInteractionClient.ts +0 -8
  167. package/src/packageMetadata.ts +1 -1
  168. package/dist/cache/BrowserStorage.d.ts +0 -14
  169. package/dist/cache/BrowserStorage.d.ts.map +0 -1
  170. package/dist/cache/BrowserStorage.mjs +0 -46
  171. package/dist/cache/BrowserStorage.mjs.map +0 -1
  172. package/lib/types/cache/BrowserStorage.d.ts +0 -14
  173. package/lib/types/cache/BrowserStorage.d.ts.map +0 -1
  174. package/src/cache/BrowserStorage.ts +0 -52
@@ -0,0 +1,52 @@
1
+ /*
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { TokenKeys } from "@azure/msal-common/browser";
7
+ import { StaticCacheKeys } from "../utils/BrowserConstants.js";
8
+ import { IWindowStorage } from "./IWindowStorage.js";
9
+
10
+ /**
11
+ * Returns a list of cache keys for all known accounts
12
+ * @param storage
13
+ * @returns
14
+ */
15
+ export function getAccountKeys(storage: IWindowStorage<string>): Array<string> {
16
+ const accountKeys = storage.getItem(StaticCacheKeys.ACCOUNT_KEYS);
17
+ if (accountKeys) {
18
+ return JSON.parse(accountKeys);
19
+ }
20
+
21
+ return [];
22
+ }
23
+
24
+ /**
25
+ * Returns a list of cache keys for all known tokens
26
+ * @param clientId
27
+ * @param storage
28
+ * @returns
29
+ */
30
+ export function getTokenKeys(
31
+ clientId: string,
32
+ storage: IWindowStorage<string>
33
+ ): TokenKeys {
34
+ const item = storage.getItem(`${StaticCacheKeys.TOKEN_KEYS}.${clientId}`);
35
+ if (item) {
36
+ const tokenKeys = JSON.parse(item);
37
+ if (
38
+ tokenKeys &&
39
+ tokenKeys.hasOwnProperty("idToken") &&
40
+ tokenKeys.hasOwnProperty("accessToken") &&
41
+ tokenKeys.hasOwnProperty("refreshToken")
42
+ ) {
43
+ return tokenKeys as TokenKeys;
44
+ }
45
+ }
46
+
47
+ return {
48
+ idToken: [],
49
+ accessToken: [],
50
+ refreshToken: [],
51
+ };
52
+ }
@@ -3,12 +3,20 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
+ import {
7
+ ClientAuthErrorCodes,
8
+ createClientAuthError,
9
+ } from "@azure/msal-common/browser";
6
10
  import { IWindowStorage } from "./IWindowStorage.js";
7
11
 
8
12
  // Cookie life calculation (hours * minutes * seconds * ms)
9
13
  const COOKIE_LIFE_MULTIPLIER = 24 * 60 * 60 * 1000;
10
14
 
11
15
  export class CookieStorage implements IWindowStorage<string> {
16
+ initialize(): Promise<void> {
17
+ return Promise.resolve();
18
+ }
19
+
12
20
  getItem(key: string): string | null {
13
21
  const name = `${encodeURIComponent(key)}`;
14
22
  const cookieList = document.cookie.split(";");
@@ -24,6 +32,10 @@ export class CookieStorage implements IWindowStorage<string> {
24
32
  return "";
25
33
  }
26
34
 
35
+ getUserData(): string | null {
36
+ throw createClientAuthError(ClientAuthErrorCodes.methodNotImplemented);
37
+ }
38
+
27
39
  setItem(
28
40
  key: string,
29
41
  value: string,
@@ -46,6 +58,12 @@ export class CookieStorage implements IWindowStorage<string> {
46
58
  document.cookie = cookieStr;
47
59
  }
48
60
 
61
+ async setUserData(): Promise<void> {
62
+ return Promise.reject(
63
+ createClientAuthError(ClientAuthErrorCodes.methodNotImplemented)
64
+ );
65
+ }
66
+
49
67
  removeItem(key: string): void {
50
68
  // Setting expiration to -1 removes it
51
69
  this.setItem(key, "", -1);
@@ -17,5 +17,5 @@ export interface ITokenCache {
17
17
  request: SilentRequest,
18
18
  response: ExternalTokenResponse,
19
19
  options: LoadTokenOptions
20
- ): AuthenticationResult;
20
+ ): Promise<AuthenticationResult>;
21
21
  }
@@ -4,12 +4,21 @@
4
4
  */
5
5
 
6
6
  export interface IWindowStorage<T> {
7
+ /**
8
+ * Async initializer
9
+ */
10
+ initialize(): Promise<void>;
7
11
  /**
8
12
  * Get the item from the window storage object matching the given key.
9
13
  * @param key
10
14
  */
11
15
  getItem(key: string): T | null;
12
16
 
17
+ /**
18
+ * Getter for sensitive data that may contain PII.
19
+ */
20
+ getUserData(key: string): T | null;
21
+
13
22
  /**
14
23
  * Sets the item in the window storage object with the given key.
15
24
  * @param key
@@ -17,6 +26,11 @@ export interface IWindowStorage<T> {
17
26
  */
18
27
  setItem(key: string, value: T): void;
19
28
 
29
+ /**
30
+ * Setter for sensitive data that may contain PII.
31
+ */
32
+ setUserData(key: string, value: T): Promise<void>;
33
+
20
34
  /**
21
35
  * Removes the item in the window storage object matching the given key.
22
36
  * @param key
@@ -3,30 +3,139 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
+ import { Constants, TokenKeys } from "@azure/msal-common/browser";
7
+ import {
8
+ createNewGuid,
9
+ decrypt,
10
+ encrypt,
11
+ generateBaseKey,
12
+ generateHKDF,
13
+ } from "../crypto/BrowserCrypto.js";
14
+ import { base64DecToArr } from "../encode/Base64Decode.js";
15
+ import { urlEncodeArr } from "../encode/Base64Encode.js";
16
+ import {
17
+ BrowserAuthErrorCodes,
18
+ createBrowserAuthError,
19
+ } from "../error/BrowserAuthError.js";
6
20
  import {
7
21
  BrowserConfigurationAuthErrorCodes,
8
22
  createBrowserConfigurationAuthError,
9
23
  } from "../error/BrowserConfigurationAuthError.js";
24
+ import { CookieStorage } from "./CookieStorage.js";
10
25
  import { IWindowStorage } from "./IWindowStorage.js";
26
+ import { MemoryStorage } from "./MemoryStorage.js";
27
+ import { getAccountKeys, getTokenKeys } from "./CacheHelpers.js";
28
+
29
+ const ENCRYPTION_KEY = "msal.cache.encryption";
30
+
31
+ type EncryptionCookie = {
32
+ id: string;
33
+ key: CryptoKey;
34
+ };
35
+
36
+ type EncryptedData = {
37
+ id: string;
38
+ nonce: string;
39
+ data: string;
40
+ };
11
41
 
12
42
  export class LocalStorage implements IWindowStorage<string> {
13
- constructor() {
43
+ private clientId: string;
44
+ private initialized: boolean;
45
+ private memoryStorage: MemoryStorage<string>;
46
+ private encryptionCookie?: EncryptionCookie;
47
+
48
+ constructor(clientId: string) {
14
49
  if (!window.localStorage) {
15
50
  throw createBrowserConfigurationAuthError(
16
51
  BrowserConfigurationAuthErrorCodes.storageNotSupported
17
52
  );
18
53
  }
54
+ this.memoryStorage = new MemoryStorage<string>();
55
+ this.initialized = false;
56
+ this.clientId = clientId;
57
+ }
58
+
59
+ async initialize(): Promise<void> {
60
+ this.initialized = true;
61
+
62
+ const cookies = new CookieStorage();
63
+ const cookieString = cookies.getItem(ENCRYPTION_KEY);
64
+ let parsedCookie = { key: "", id: "" };
65
+ if (cookieString) {
66
+ try {
67
+ parsedCookie = JSON.parse(cookieString);
68
+ } catch (e) {
69
+ // TODO: Log telemetry but don't throw
70
+ }
71
+ }
72
+ if (parsedCookie.key && parsedCookie.id) {
73
+ // Encryption key already exists, import
74
+ this.encryptionCookie = {
75
+ id: parsedCookie.id,
76
+ key: await generateHKDF(base64DecToArr(parsedCookie.key)),
77
+ };
78
+ await this.importExistingCache();
79
+ } else {
80
+ // Encryption key doesn't exist or is invalid, generate a new one and clear existing cache
81
+ this.clear();
82
+ const id = createNewGuid();
83
+ const baseKey = await generateBaseKey();
84
+ const keyStr = urlEncodeArr(new Uint8Array(baseKey));
85
+ this.encryptionCookie = {
86
+ id: id,
87
+ key: await generateHKDF(baseKey),
88
+ };
89
+
90
+ const cookieData = {
91
+ id: id,
92
+ key: keyStr,
93
+ };
94
+ cookies.setItem(ENCRYPTION_KEY, JSON.stringify(cookieData));
95
+ }
19
96
  }
20
97
 
21
98
  getItem(key: string): string | null {
22
99
  return window.localStorage.getItem(key);
23
100
  }
24
101
 
102
+ getUserData(key: string): string | null {
103
+ if (!this.initialized) {
104
+ throw createBrowserAuthError(
105
+ BrowserAuthErrorCodes.uninitializedPublicClientApplication
106
+ );
107
+ }
108
+ return this.memoryStorage.getItem(key);
109
+ }
110
+
25
111
  setItem(key: string, value: string): void {
26
112
  window.localStorage.setItem(key, value);
27
113
  }
28
114
 
115
+ async setUserData(key: string, value: string): Promise<void> {
116
+ if (!this.initialized || !this.encryptionCookie) {
117
+ throw createBrowserAuthError(
118
+ BrowserAuthErrorCodes.uninitializedPublicClientApplication
119
+ );
120
+ }
121
+
122
+ const { data, nonce } = await encrypt(
123
+ this.encryptionCookie.key,
124
+ value,
125
+ this.getContext(key)
126
+ );
127
+ const encryptedData: EncryptedData = {
128
+ id: this.encryptionCookie.id,
129
+ nonce: nonce,
130
+ data: data,
131
+ };
132
+
133
+ this.memoryStorage.setItem(key, value);
134
+ this.setItem(key, JSON.stringify(encryptedData));
135
+ }
136
+
29
137
  removeItem(key: string): void {
138
+ this.memoryStorage.removeItem(key);
30
139
  window.localStorage.removeItem(key);
31
140
  }
32
141
 
@@ -37,4 +146,125 @@ export class LocalStorage implements IWindowStorage<string> {
37
146
  containsKey(key: string): boolean {
38
147
  return window.localStorage.hasOwnProperty(key);
39
148
  }
149
+
150
+ /**
151
+ * Removes all known MSAL keys from the cache
152
+ */
153
+ clear(): void {
154
+ // Removes all remaining MSAL cache items
155
+ this.memoryStorage.clear();
156
+
157
+ const accountKeys = getAccountKeys(this);
158
+ accountKeys.forEach((key) => this.removeItem(key));
159
+ const tokenKeys = getTokenKeys(this.clientId, this);
160
+ tokenKeys.idToken.forEach((key) => this.removeItem(key));
161
+ tokenKeys.accessToken.forEach((key) => this.removeItem(key));
162
+ tokenKeys.refreshToken.forEach((key) => this.removeItem(key));
163
+
164
+ // Clean up anything left
165
+ this.getKeys().forEach((cacheKey: string) => {
166
+ if (
167
+ cacheKey.startsWith(Constants.CACHE_PREFIX) ||
168
+ cacheKey.indexOf(this.clientId) !== -1
169
+ ) {
170
+ this.removeItem(cacheKey);
171
+ }
172
+ });
173
+ }
174
+
175
+ /**
176
+ * Helper to decrypt all known MSAL keys in localStorage and save them to inMemory storage
177
+ * @returns
178
+ */
179
+ private async importExistingCache(): Promise<void> {
180
+ if (!this.encryptionCookie) {
181
+ return;
182
+ }
183
+
184
+ const accountKeys = getAccountKeys(this);
185
+ await this.importArray(accountKeys);
186
+
187
+ const tokenKeys: TokenKeys = getTokenKeys(this.clientId, this);
188
+ await Promise.all([
189
+ this.importArray(tokenKeys.idToken),
190
+ this.importArray(tokenKeys.accessToken),
191
+ this.importArray(tokenKeys.refreshToken),
192
+ ]);
193
+ }
194
+
195
+ /**
196
+ * Helper to decrypt and save cache entries
197
+ * @param key
198
+ * @returns
199
+ */
200
+ private async getItemFromEncryptedCache(
201
+ key: string
202
+ ): Promise<string | null> {
203
+ if (!this.encryptionCookie) {
204
+ return null;
205
+ }
206
+
207
+ const rawCache = this.getItem(key);
208
+ if (!rawCache) {
209
+ return null;
210
+ }
211
+
212
+ let encObj: EncryptedData;
213
+ try {
214
+ encObj = JSON.parse(rawCache);
215
+ if (!encObj.id || !encObj.nonce || !encObj.data) {
216
+ throw "Not encrypted!"; // TODO: Typed error
217
+ }
218
+
219
+ if (encObj.id !== this.encryptionCookie.id) {
220
+ throw "Old item!"; // TODO: Typed error
221
+ }
222
+ } catch (e) {
223
+ // Not a valid encrypted object, remove
224
+ this.removeItem(key);
225
+ // TODO: Log to telemetry
226
+ return null;
227
+ }
228
+
229
+ return decrypt(
230
+ this.encryptionCookie.key,
231
+ encObj.nonce,
232
+ this.getContext(key),
233
+ encObj.data
234
+ );
235
+ }
236
+
237
+ /**
238
+ * Helper to decrypt and save an array of cache keys
239
+ * @param arr
240
+ */
241
+ private async importArray(arr: Array<string>): Promise<void> {
242
+ const promiseArr: Array<Promise<void>> = [];
243
+ arr.forEach((key) => {
244
+ const promise = this.getItemFromEncryptedCache(key).then(
245
+ (value) => {
246
+ if (value) {
247
+ this.memoryStorage.setItem(key, value);
248
+ }
249
+ }
250
+ );
251
+ promiseArr.push(promise);
252
+ });
253
+
254
+ await Promise.all(promiseArr);
255
+ }
256
+
257
+ /**
258
+ * Gets encryption context for a given cache entry. This is clientId for app specific entries, empty string for shared entries
259
+ * @param key
260
+ * @returns
261
+ */
262
+ private getContext(key: string): string {
263
+ let context = "";
264
+ if (key.includes(this.clientId)) {
265
+ context = this.clientId; // Used to bind encryption key to this appId
266
+ }
267
+
268
+ return context;
269
+ }
40
270
  }
@@ -12,14 +12,26 @@ export class MemoryStorage<T> implements IWindowStorage<T> {
12
12
  this.cache = new Map<string, T>();
13
13
  }
14
14
 
15
+ async initialize(): Promise<void> {
16
+ // Memory storage does not require initialization
17
+ }
18
+
15
19
  getItem(key: string): T | null {
16
20
  return this.cache.get(key) || null;
17
21
  }
18
22
 
23
+ getUserData(key: string): T | null {
24
+ return this.getItem(key);
25
+ }
26
+
19
27
  setItem(key: string, value: T): void {
20
28
  this.cache.set(key, value);
21
29
  }
22
30
 
31
+ async setUserData(key: string, value: T): Promise<void> {
32
+ this.setItem(key, value);
33
+ }
34
+
23
35
  removeItem(key: string): void {
24
36
  this.cache.delete(key);
25
37
  }
@@ -18,14 +18,26 @@ export class SessionStorage implements IWindowStorage<string> {
18
18
  }
19
19
  }
20
20
 
21
+ async initialize(): Promise<void> {
22
+ // Session storage does not require initialization
23
+ }
24
+
21
25
  getItem(key: string): string | null {
22
26
  return window.sessionStorage.getItem(key);
23
27
  }
24
28
 
29
+ getUserData(key: string): string | null {
30
+ return this.getItem(key);
31
+ }
32
+
25
33
  setItem(key: string, value: string): void {
26
34
  window.sessionStorage.setItem(key, value);
27
35
  }
28
36
 
37
+ async setUserData(key: string, value: string): Promise<void> {
38
+ this.setItem(key, value);
39
+ }
40
+
29
41
  removeItem(key: string): void {
30
42
  window.sessionStorage.removeItem(key);
31
43
  }
@@ -75,11 +75,11 @@ export class TokenCache implements ITokenCache {
75
75
  * @param options
76
76
  * @returns `AuthenticationResult` for the response that was loaded.
77
77
  */
78
- loadExternalTokens(
78
+ async loadExternalTokens(
79
79
  request: SilentRequest,
80
80
  response: ExternalTokenResponse,
81
81
  options: LoadTokenOptions
82
- ): AuthenticationResult {
82
+ ): Promise<AuthenticationResult> {
83
83
  if (!this.isBrowserEnvironment) {
84
84
  throw createBrowserAuthError(
85
85
  BrowserAuthErrorCodes.nonBrowserEnvironment
@@ -112,21 +112,21 @@ export class TokenCache implements ITokenCache {
112
112
  )
113
113
  : undefined;
114
114
 
115
- const cacheRecordAccount: AccountEntity = this.loadAccount(
115
+ const cacheRecordAccount: AccountEntity = await this.loadAccount(
116
116
  request,
117
117
  options.clientInfo || response.client_info || "",
118
118
  idTokenClaims,
119
119
  authority
120
120
  );
121
121
 
122
- const idToken = this.loadIdToken(
122
+ const idToken = await this.loadIdToken(
123
123
  response,
124
124
  cacheRecordAccount.homeAccountId,
125
125
  cacheRecordAccount.environment,
126
126
  cacheRecordAccount.realm
127
127
  );
128
128
 
129
- const accessToken = this.loadAccessToken(
129
+ const accessToken = await this.loadAccessToken(
130
130
  request,
131
131
  response,
132
132
  cacheRecordAccount.homeAccountId,
@@ -135,7 +135,7 @@ export class TokenCache implements ITokenCache {
135
135
  options
136
136
  );
137
137
 
138
- const refreshToken = this.loadRefreshToken(
138
+ const refreshToken = await this.loadRefreshToken(
139
139
  response,
140
140
  cacheRecordAccount.homeAccountId,
141
141
  cacheRecordAccount.environment
@@ -163,19 +163,19 @@ export class TokenCache implements ITokenCache {
163
163
  * @param requestHomeAccountId
164
164
  * @returns `AccountEntity`
165
165
  */
166
- private loadAccount(
166
+ private async loadAccount(
167
167
  request: SilentRequest,
168
168
  clientInfo: string,
169
169
  idTokenClaims?: TokenClaims,
170
170
  authority?: Authority
171
- ): AccountEntity {
171
+ ): Promise<AccountEntity> {
172
172
  this.logger.verbose("TokenCache - loading account");
173
173
 
174
174
  if (request.account) {
175
175
  const accountEntity = AccountEntity.createFromAccountInfo(
176
176
  request.account
177
177
  );
178
- this.storage.setAccount(accountEntity);
178
+ await this.storage.setAccount(accountEntity);
179
179
  return accountEntity;
180
180
  } else if (!authority || (!clientInfo && !idTokenClaims)) {
181
181
  this.logger.error(
@@ -210,7 +210,7 @@ export class TokenCache implements ITokenCache {
210
210
  this.logger
211
211
  );
212
212
 
213
- this.storage.setAccount(cachedAccount);
213
+ await this.storage.setAccount(cachedAccount);
214
214
  return cachedAccount;
215
215
  }
216
216
 
@@ -222,12 +222,12 @@ export class TokenCache implements ITokenCache {
222
222
  * @param tenantId
223
223
  * @returns `IdTokenEntity`
224
224
  */
225
- private loadIdToken(
225
+ private async loadIdToken(
226
226
  response: ExternalTokenResponse,
227
227
  homeAccountId: string,
228
228
  environment: string,
229
229
  tenantId: string
230
- ): IdTokenEntity | null {
230
+ ): Promise<IdTokenEntity | null> {
231
231
  if (!response.id_token) {
232
232
  this.logger.verbose("TokenCache - no id token found in response");
233
233
  return null;
@@ -242,7 +242,7 @@ export class TokenCache implements ITokenCache {
242
242
  tenantId
243
243
  );
244
244
 
245
- this.storage.setIdTokenCredential(idTokenEntity);
245
+ await this.storage.setIdTokenCredential(idTokenEntity);
246
246
  return idTokenEntity;
247
247
  }
248
248
 
@@ -255,14 +255,14 @@ export class TokenCache implements ITokenCache {
255
255
  * @param tenantId
256
256
  * @returns `AccessTokenEntity`
257
257
  */
258
- private loadAccessToken(
258
+ private async loadAccessToken(
259
259
  request: SilentRequest,
260
260
  response: ExternalTokenResponse,
261
261
  homeAccountId: string,
262
262
  environment: string,
263
263
  tenantId: string,
264
264
  options: LoadTokenOptions
265
- ): AccessTokenEntity | null {
265
+ ): Promise<AccessTokenEntity | null> {
266
266
  if (!response.access_token) {
267
267
  this.logger.verbose(
268
268
  "TokenCache - no access token found in response"
@@ -309,7 +309,7 @@ export class TokenCache implements ITokenCache {
309
309
  base64Decode
310
310
  );
311
311
 
312
- this.storage.setAccessTokenCredential(accessTokenEntity);
312
+ await this.storage.setAccessTokenCredential(accessTokenEntity);
313
313
  return accessTokenEntity;
314
314
  }
315
315
 
@@ -321,11 +321,11 @@ export class TokenCache implements ITokenCache {
321
321
  * @param environment
322
322
  * @returns `RefreshTokenEntity`
323
323
  */
324
- private loadRefreshToken(
324
+ private async loadRefreshToken(
325
325
  response: ExternalTokenResponse,
326
326
  homeAccountId: string,
327
327
  environment: string
328
- ): RefreshTokenEntity | null {
328
+ ): Promise<RefreshTokenEntity | null> {
329
329
  if (!response.refresh_token) {
330
330
  this.logger.verbose(
331
331
  "TokenCache - no refresh token found in response"
@@ -344,7 +344,7 @@ export class TokenCache implements ITokenCache {
344
344
  response.refresh_token_expires_in
345
345
  );
346
346
 
347
- this.storage.setRefreshTokenCredential(refreshTokenEntity);
347
+ await this.storage.setRefreshTokenCredential(refreshTokenEntity);
348
348
  return refreshTokenEntity;
349
349
  }
350
350
 
@@ -157,8 +157,8 @@ export class NestedAppAuthController implements IController {
157
157
  * Specific implementation of initialize function for NestedAppAuthController
158
158
  * @returns
159
159
  */
160
- initialize(): Promise<void> {
161
- // do nothing not required by this controller
160
+ async initialize(): Promise<void> {
161
+ await this.browserStorage.initialize();
162
162
  return Promise.resolve();
163
163
  }
164
164
 
@@ -842,7 +842,7 @@ export class NestedAppAuthController implements IController {
842
842
  result.cloudGraphHostName,
843
843
  result.msGraphHost
844
844
  );
845
- this.browserStorage.setAccount(accountEntity);
845
+ await this.browserStorage.setAccount(accountEntity);
846
846
  return this.browserStorage.hydrateCache(result, request);
847
847
  }
848
848
  }
@@ -331,6 +331,8 @@ export class StandardController implements IController {
331
331
  return;
332
332
  }
333
333
 
334
+ await this.browserStorage.initialize();
335
+
334
336
  const initCorrelationId =
335
337
  request?.correlationId || this.getRequestCorrelationId();
336
338
  const allowNativeBroker = this.config.system.allowNativeBroker;
@@ -1472,7 +1474,7 @@ export class StandardController implements IController {
1472
1474
  result.cloudGraphHostName,
1473
1475
  result.msGraphHost
1474
1476
  );
1475
- this.browserStorage.setAccount(accountEntity);
1477
+ await this.browserStorage.setAccount(accountEntity);
1476
1478
 
1477
1479
  if (result.fromNativeBroker) {
1478
1480
  this.logger.verbose(