@azure/msal-browser 3.0.2 → 3.2.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 (280) hide show
  1. package/dist/app/IPublicClientApplication.d.ts +2 -0
  2. package/dist/app/IPublicClientApplication.d.ts.map +1 -1
  3. package/dist/app/IPublicClientApplication.mjs +22 -18
  4. package/dist/app/IPublicClientApplication.mjs.map +1 -1
  5. package/dist/app/PublicClientApplication.d.ts +20 -6
  6. package/dist/app/PublicClientApplication.d.ts.map +1 -1
  7. package/dist/app/PublicClientApplication.mjs +24 -7
  8. package/dist/app/PublicClientApplication.mjs.map +1 -1
  9. package/dist/broker/nativeBroker/NativeMessageHandler.d.ts +3 -4
  10. package/dist/broker/nativeBroker/NativeMessageHandler.d.ts.map +1 -1
  11. package/dist/broker/nativeBroker/NativeMessageHandler.mjs +17 -16
  12. package/dist/broker/nativeBroker/NativeMessageHandler.mjs.map +1 -1
  13. package/dist/broker/nativeBroker/NativeStatusCodes.d.ts +8 -0
  14. package/dist/broker/nativeBroker/NativeStatusCodes.d.ts.map +1 -0
  15. package/dist/broker/nativeBroker/NativeStatusCodes.mjs +16 -0
  16. package/dist/broker/nativeBroker/NativeStatusCodes.mjs.map +1 -0
  17. package/dist/cache/AsyncMemoryStorage.mjs +4 -3
  18. package/dist/cache/AsyncMemoryStorage.mjs.map +1 -1
  19. package/dist/cache/BrowserCacheManager.d.ts +10 -3
  20. package/dist/cache/BrowserCacheManager.d.ts.map +1 -1
  21. package/dist/cache/BrowserCacheManager.mjs +58 -29
  22. package/dist/cache/BrowserCacheManager.mjs.map +1 -1
  23. package/dist/cache/BrowserStorage.d.ts.map +1 -1
  24. package/dist/cache/BrowserStorage.mjs +7 -9
  25. package/dist/cache/BrowserStorage.mjs.map +1 -1
  26. package/dist/cache/CryptoKeyStore.mjs +1 -1
  27. package/dist/cache/CryptoKeyStore.mjs.map +1 -1
  28. package/dist/cache/DatabaseStorage.d.ts.map +1 -1
  29. package/dist/cache/DatabaseStorage.mjs +9 -8
  30. package/dist/cache/DatabaseStorage.mjs.map +1 -1
  31. package/dist/cache/MemoryStorage.mjs +1 -1
  32. package/dist/cache/MemoryStorage.mjs.map +1 -1
  33. package/dist/cache/TokenCache.d.ts.map +1 -1
  34. package/dist/cache/TokenCache.mjs +30 -30
  35. package/dist/cache/TokenCache.mjs.map +1 -1
  36. package/dist/config/Configuration.d.ts +1 -0
  37. package/dist/config/Configuration.d.ts.map +1 -1
  38. package/dist/config/Configuration.mjs +12 -16
  39. package/dist/config/Configuration.mjs.map +1 -1
  40. package/dist/controllers/ControllerFactory.mjs +1 -1
  41. package/dist/controllers/ControllerFactory.mjs.map +1 -1
  42. package/dist/controllers/IController.d.ts +5 -28
  43. package/dist/controllers/IController.d.ts.map +1 -1
  44. package/dist/controllers/StandardController.d.ts +17 -31
  45. package/dist/controllers/StandardController.d.ts.map +1 -1
  46. package/dist/controllers/StandardController.mjs +78 -83
  47. package/dist/controllers/StandardController.mjs.map +1 -1
  48. package/dist/crypto/BrowserCrypto.d.ts +44 -49
  49. package/dist/crypto/BrowserCrypto.d.ts.map +1 -1
  50. package/dist/crypto/BrowserCrypto.mjs +78 -77
  51. package/dist/crypto/BrowserCrypto.mjs.map +1 -1
  52. package/dist/crypto/CryptoOps.d.ts +1 -10
  53. package/dist/crypto/CryptoOps.d.ts.map +1 -1
  54. package/dist/crypto/CryptoOps.mjs +23 -34
  55. package/dist/crypto/CryptoOps.mjs.map +1 -1
  56. package/dist/crypto/PkceGenerator.d.ts +6 -22
  57. package/dist/crypto/PkceGenerator.d.ts.map +1 -1
  58. package/dist/crypto/PkceGenerator.mjs +48 -49
  59. package/dist/crypto/PkceGenerator.mjs.map +1 -1
  60. package/dist/crypto/SignedHttpRequest.mjs +1 -1
  61. package/dist/crypto/SignedHttpRequest.mjs.map +1 -1
  62. package/dist/encode/Base64Decode.d.ts +6 -19
  63. package/dist/encode/Base64Decode.d.ts.map +1 -1
  64. package/dist/encode/Base64Decode.mjs +31 -68
  65. package/dist/encode/Base64Decode.mjs.map +1 -1
  66. package/dist/encode/Base64Encode.d.ts +15 -27
  67. package/dist/encode/Base64Encode.d.ts.map +1 -1
  68. package/dist/encode/Base64Encode.mjs +36 -75
  69. package/dist/encode/Base64Encode.mjs.map +1 -1
  70. package/dist/error/BrowserAuthError.d.ts +63 -195
  71. package/dist/error/BrowserAuthError.d.ts.map +1 -1
  72. package/dist/error/BrowserAuthError.mjs +159 -381
  73. package/dist/error/BrowserAuthError.mjs.map +1 -1
  74. package/dist/error/BrowserAuthErrorCodes.d.ts +46 -0
  75. package/dist/error/BrowserAuthErrorCodes.d.ts.map +1 -0
  76. package/dist/error/BrowserAuthErrorCodes.mjs +54 -0
  77. package/dist/error/BrowserAuthErrorCodes.mjs.map +1 -0
  78. package/dist/error/BrowserConfigurationAuthError.d.ts +9 -46
  79. package/dist/error/BrowserConfigurationAuthError.d.ts.map +1 -1
  80. package/dist/error/BrowserConfigurationAuthError.mjs +20 -73
  81. package/dist/error/BrowserConfigurationAuthError.mjs.map +1 -1
  82. package/dist/error/BrowserConfigurationAuthErrorCodes.d.ts +4 -0
  83. package/dist/error/BrowserConfigurationAuthErrorCodes.d.ts.map +1 -0
  84. package/dist/error/BrowserConfigurationAuthErrorCodes.mjs +12 -0
  85. package/dist/error/BrowserConfigurationAuthErrorCodes.mjs.map +1 -0
  86. package/dist/error/NativeAuthError.d.ts +17 -45
  87. package/dist/error/NativeAuthError.d.ts.map +1 -1
  88. package/dist/error/NativeAuthError.mjs +49 -79
  89. package/dist/error/NativeAuthError.mjs.map +1 -1
  90. package/dist/error/NativeAuthErrorCodes.d.ts +3 -0
  91. package/dist/error/NativeAuthErrorCodes.d.ts.map +1 -0
  92. package/dist/error/NativeAuthErrorCodes.mjs +11 -0
  93. package/dist/error/NativeAuthErrorCodes.mjs.map +1 -0
  94. package/dist/event/EventHandler.d.ts.map +1 -1
  95. package/dist/event/EventHandler.mjs +3 -2
  96. package/dist/event/EventHandler.mjs.map +1 -1
  97. package/dist/event/EventMessage.mjs +1 -1
  98. package/dist/event/EventMessage.mjs.map +1 -1
  99. package/dist/event/EventType.mjs +1 -1
  100. package/dist/event/EventType.mjs.map +1 -1
  101. package/dist/index.d.ts +6 -6
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.mjs +8 -4
  104. package/dist/index.mjs.map +1 -1
  105. package/dist/interaction_client/BaseInteractionClient.d.ts +2 -1
  106. package/dist/interaction_client/BaseInteractionClient.d.ts.map +1 -1
  107. package/dist/interaction_client/BaseInteractionClient.mjs +7 -8
  108. package/dist/interaction_client/BaseInteractionClient.mjs.map +1 -1
  109. package/dist/interaction_client/HybridSpaAuthorizationCodeClient.mjs +1 -1
  110. package/dist/interaction_client/HybridSpaAuthorizationCodeClient.mjs.map +1 -1
  111. package/dist/interaction_client/NativeInteractionClient.d.ts +4 -10
  112. package/dist/interaction_client/NativeInteractionClient.d.ts.map +1 -1
  113. package/dist/interaction_client/NativeInteractionClient.mjs +32 -38
  114. package/dist/interaction_client/NativeInteractionClient.mjs.map +1 -1
  115. package/dist/interaction_client/PopupClient.d.ts.map +1 -1
  116. package/dist/interaction_client/PopupClient.mjs +16 -15
  117. package/dist/interaction_client/PopupClient.mjs.map +1 -1
  118. package/dist/interaction_client/RedirectClient.d.ts.map +1 -1
  119. package/dist/interaction_client/RedirectClient.mjs +7 -6
  120. package/dist/interaction_client/RedirectClient.mjs.map +1 -1
  121. package/dist/interaction_client/SilentAuthCodeClient.d.ts.map +1 -1
  122. package/dist/interaction_client/SilentAuthCodeClient.mjs +9 -11
  123. package/dist/interaction_client/SilentAuthCodeClient.mjs.map +1 -1
  124. package/dist/interaction_client/SilentCacheClient.d.ts +4 -2
  125. package/dist/interaction_client/SilentCacheClient.d.ts.map +1 -1
  126. package/dist/interaction_client/SilentCacheClient.mjs +19 -25
  127. package/dist/interaction_client/SilentCacheClient.mjs.map +1 -1
  128. package/dist/interaction_client/SilentIframeClient.d.ts.map +1 -1
  129. package/dist/interaction_client/SilentIframeClient.mjs +20 -47
  130. package/dist/interaction_client/SilentIframeClient.mjs.map +1 -1
  131. package/dist/interaction_client/SilentRefreshClient.d.ts.map +1 -1
  132. package/dist/interaction_client/SilentRefreshClient.mjs +11 -20
  133. package/dist/interaction_client/SilentRefreshClient.mjs.map +1 -1
  134. package/dist/interaction_client/StandardInteractionClient.d.ts.map +1 -1
  135. package/dist/interaction_client/StandardInteractionClient.mjs +18 -38
  136. package/dist/interaction_client/StandardInteractionClient.mjs.map +1 -1
  137. package/dist/interaction_handler/InteractionHandler.d.ts.map +1 -1
  138. package/dist/interaction_handler/InteractionHandler.mjs +13 -16
  139. package/dist/interaction_handler/InteractionHandler.mjs.map +1 -1
  140. package/dist/interaction_handler/RedirectHandler.d.ts.map +1 -1
  141. package/dist/interaction_handler/RedirectHandler.mjs +12 -11
  142. package/dist/interaction_handler/RedirectHandler.mjs.map +1 -1
  143. package/dist/interaction_handler/SilentHandler.d.ts.map +1 -1
  144. package/dist/interaction_handler/SilentHandler.mjs +11 -11
  145. package/dist/interaction_handler/SilentHandler.mjs.map +1 -1
  146. package/dist/navigation/NavigationClient.mjs +1 -1
  147. package/dist/navigation/NavigationClient.mjs.map +1 -1
  148. package/dist/network/FetchClient.d.ts.map +1 -1
  149. package/dist/network/FetchClient.mjs +9 -8
  150. package/dist/network/FetchClient.mjs.map +1 -1
  151. package/dist/operatingcontext/BaseOperatingContext.mjs +1 -1
  152. package/dist/operatingcontext/BaseOperatingContext.mjs.map +1 -1
  153. package/dist/operatingcontext/StandardOperatingContext.mjs +1 -1
  154. package/dist/operatingcontext/StandardOperatingContext.mjs.map +1 -1
  155. package/dist/operatingcontext/TeamsAppOperatingContext.mjs +1 -1
  156. package/dist/operatingcontext/TeamsAppOperatingContext.mjs.map +1 -1
  157. package/dist/packageMetadata.d.ts +1 -1
  158. package/dist/packageMetadata.mjs +2 -2
  159. package/dist/packageMetadata.mjs.map +1 -1
  160. package/dist/request/ClearCacheRequest.d.ts +11 -0
  161. package/dist/request/ClearCacheRequest.d.ts.map +1 -0
  162. package/dist/telemetry/BrowserPerformanceClient.d.ts +2 -4
  163. package/dist/telemetry/BrowserPerformanceClient.d.ts.map +1 -1
  164. package/dist/telemetry/BrowserPerformanceClient.mjs +2 -6
  165. package/dist/telemetry/BrowserPerformanceClient.mjs.map +1 -1
  166. package/dist/telemetry/BrowserPerformanceMeasurement.mjs +1 -1
  167. package/dist/telemetry/BrowserPerformanceMeasurement.mjs.map +1 -1
  168. package/dist/utils/BrowserConstants.mjs +1 -1
  169. package/dist/utils/BrowserConstants.mjs.map +1 -1
  170. package/dist/utils/BrowserProtocolUtils.d.ts.map +1 -1
  171. package/dist/utils/BrowserProtocolUtils.mjs +4 -4
  172. package/dist/utils/BrowserProtocolUtils.mjs.map +1 -1
  173. package/dist/utils/BrowserStringUtils.mjs +1 -1
  174. package/dist/utils/BrowserStringUtils.mjs.map +1 -1
  175. package/dist/utils/BrowserUtils.d.ts +0 -9
  176. package/dist/utils/BrowserUtils.d.ts.map +1 -1
  177. package/dist/utils/BrowserUtils.mjs +8 -33
  178. package/dist/utils/BrowserUtils.mjs.map +1 -1
  179. package/lib/msal-browser.cjs +8729 -9566
  180. package/lib/msal-browser.cjs.map +1 -1
  181. package/lib/msal-browser.js +8729 -9566
  182. package/lib/msal-browser.js.map +1 -1
  183. package/lib/msal-browser.min.js +68 -62
  184. package/package.json +7 -5
  185. package/src/app/IPublicClientApplication.ts +236 -0
  186. package/src/app/PublicClientApplication.ts +417 -0
  187. package/src/broker/nativeBroker/NativeMessageHandler.ts +416 -0
  188. package/src/broker/nativeBroker/NativeRequest.ts +52 -0
  189. package/src/broker/nativeBroker/NativeResponse.ts +56 -0
  190. package/src/broker/nativeBroker/NativeStatusCodes.ts +13 -0
  191. package/src/cache/AsyncMemoryStorage.ts +158 -0
  192. package/src/cache/BrowserCacheManager.ts +2009 -0
  193. package/src/cache/BrowserStorage.ts +52 -0
  194. package/src/cache/CryptoKeyStore.ts +63 -0
  195. package/src/cache/DatabaseStorage.ts +287 -0
  196. package/src/cache/IAsyncMemoryStorage.ts +36 -0
  197. package/src/cache/ITokenCache.ts +21 -0
  198. package/src/cache/IWindowStorage.ts +36 -0
  199. package/src/cache/MemoryStorage.ts +42 -0
  200. package/src/cache/TokenCache.ts +484 -0
  201. package/src/config/Configuration.ts +383 -0
  202. package/src/controllers/ControllerFactory.ts +55 -0
  203. package/src/controllers/IController.ts +134 -0
  204. package/src/controllers/StandardController.ts +2170 -0
  205. package/src/crypto/BrowserCrypto.ts +151 -0
  206. package/src/crypto/CryptoOps.ts +235 -0
  207. package/src/crypto/PkceGenerator.ts +115 -0
  208. package/src/crypto/SignedHttpRequest.ts +76 -0
  209. package/src/encode/Base64Decode.ts +46 -0
  210. package/src/encode/Base64Encode.ts +52 -0
  211. package/src/error/BrowserAuthError.ts +352 -0
  212. package/src/error/BrowserAuthErrorCodes.ts +57 -0
  213. package/src/error/BrowserConfigurationAuthError.ts +64 -0
  214. package/src/error/BrowserConfigurationAuthErrorCodes.ts +9 -0
  215. package/src/error/NativeAuthError.ts +113 -0
  216. package/src/error/NativeAuthErrorCodes.ts +7 -0
  217. package/src/event/EventHandler.ts +177 -0
  218. package/src/event/EventMessage.ts +125 -0
  219. package/src/event/EventType.ts +33 -0
  220. package/src/index.ts +157 -0
  221. package/src/interaction_client/BaseInteractionClient.ts +307 -0
  222. package/src/interaction_client/HybridSpaAuthorizationCodeClient.ts +16 -0
  223. package/src/interaction_client/NativeInteractionClient.ts +925 -0
  224. package/src/interaction_client/PopupClient.ts +926 -0
  225. package/src/interaction_client/RedirectClient.ts +616 -0
  226. package/src/interaction_client/SilentAuthCodeClient.ts +165 -0
  227. package/src/interaction_client/SilentCacheClient.ts +125 -0
  228. package/src/interaction_client/SilentIframeClient.ts +301 -0
  229. package/src/interaction_client/SilentRefreshClient.ts +118 -0
  230. package/src/interaction_client/StandardInteractionClient.ts +450 -0
  231. package/src/interaction_handler/InteractionHandler.ts +251 -0
  232. package/src/interaction_handler/RedirectHandler.ts +229 -0
  233. package/src/interaction_handler/SilentHandler.ts +269 -0
  234. package/src/navigation/INavigationClient.ts +23 -0
  235. package/src/navigation/NavigationClient.ts +55 -0
  236. package/src/navigation/NavigationOptions.ts +18 -0
  237. package/src/network/FetchClient.ts +130 -0
  238. package/src/operatingcontext/BaseOperatingContext.ts +82 -0
  239. package/src/operatingcontext/StandardOperatingContext.ts +49 -0
  240. package/src/operatingcontext/TeamsAppOperatingContext.ts +48 -0
  241. package/src/packageMetadata.ts +3 -0
  242. package/src/request/AuthorizationCodeRequest.ts +19 -0
  243. package/src/request/AuthorizationUrlRequest.ts +17 -0
  244. package/src/request/ClearCacheRequest.ts +16 -0
  245. package/src/request/EndSessionPopupRequest.ts +26 -0
  246. package/src/request/EndSessionRequest.ts +23 -0
  247. package/src/request/PopupRequest.ts +51 -0
  248. package/src/request/PopupWindowAttributes.ts +22 -0
  249. package/src/request/RedirectRequest.ts +51 -0
  250. package/src/request/SilentRequest.ts +49 -0
  251. package/src/request/SsoSilentRequest.ts +44 -0
  252. package/src/response/AuthenticationResult.ts +13 -0
  253. package/src/telemetry/BrowserPerformanceClient.ts +226 -0
  254. package/src/telemetry/BrowserPerformanceMeasurement.ts +144 -0
  255. package/src/utils/BrowserConstants.ts +241 -0
  256. package/src/utils/BrowserProtocolUtils.ts +58 -0
  257. package/src/utils/BrowserStringUtils.ts +143 -0
  258. package/src/utils/BrowserUtils.ts +156 -0
  259. package/dist/crypto/GuidGenerator.d.ts +0 -13
  260. package/dist/crypto/GuidGenerator.d.ts.map +0 -1
  261. package/dist/crypto/GuidGenerator.mjs +0 -104
  262. package/dist/crypto/GuidGenerator.mjs.map +0 -1
  263. package/dist/crypto/ISubtleCrypto.d.ts +0 -10
  264. package/dist/crypto/ISubtleCrypto.d.ts.map +0 -1
  265. package/dist/crypto/ModernBrowserCrypto.d.ts +0 -10
  266. package/dist/crypto/ModernBrowserCrypto.d.ts.map +0 -1
  267. package/dist/crypto/ModernBrowserCrypto.mjs +0 -31
  268. package/dist/crypto/ModernBrowserCrypto.mjs.map +0 -1
  269. package/dist/internals.d.ts +0 -23
  270. package/dist/internals.d.ts.map +0 -1
  271. package/dist/internals.mjs +0 -28
  272. package/dist/internals.mjs.map +0 -1
  273. package/dist/network/XhrClient.d.ts +0 -41
  274. package/dist/network/XhrClient.d.ts.map +0 -1
  275. package/dist/network/XhrClient.mjs +0 -125
  276. package/dist/network/XhrClient.mjs.map +0 -1
  277. package/dist/utils/MathUtils.d.ts +0 -12
  278. package/dist/utils/MathUtils.d.ts.map +0 -1
  279. package/dist/utils/MathUtils.mjs +0 -26
  280. package/dist/utils/MathUtils.mjs.map +0 -1
@@ -0,0 +1,2009 @@
1
+ /*
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import {
7
+ Constants,
8
+ PersistentCacheKeys,
9
+ StringUtils,
10
+ CommonAuthorizationCodeRequest,
11
+ ICrypto,
12
+ AccountEntity,
13
+ IdTokenEntity,
14
+ AccessTokenEntity,
15
+ RefreshTokenEntity,
16
+ AppMetadataEntity,
17
+ CacheManager,
18
+ ServerTelemetryEntity,
19
+ ThrottlingEntity,
20
+ ProtocolUtils,
21
+ Logger,
22
+ AuthorityMetadataEntity,
23
+ DEFAULT_CRYPTO_IMPLEMENTATION,
24
+ AccountInfo,
25
+ ActiveAccountFilters,
26
+ CcsCredential,
27
+ CcsCredentialType,
28
+ AuthToken,
29
+ ValidCredentialType,
30
+ TokenKeys,
31
+ CredentialType,
32
+ CacheRecord,
33
+ AuthenticationScheme,
34
+ createClientAuthError,
35
+ ClientAuthErrorCodes,
36
+ PerformanceEvents,
37
+ IPerformanceClient,
38
+ } from "@azure/msal-common";
39
+ import { CacheOptions } from "../config/Configuration";
40
+ import {
41
+ createBrowserAuthError,
42
+ BrowserAuthErrorCodes,
43
+ } from "../error/BrowserAuthError";
44
+ import {
45
+ BrowserCacheLocation,
46
+ InteractionType,
47
+ TemporaryCacheKeys,
48
+ InMemoryCacheKeys,
49
+ StaticCacheKeys,
50
+ } from "../utils/BrowserConstants";
51
+ import { BrowserStorage } from "./BrowserStorage";
52
+ import { MemoryStorage } from "./MemoryStorage";
53
+ import { IWindowStorage } from "./IWindowStorage";
54
+ import { BrowserProtocolUtils } from "../utils/BrowserProtocolUtils";
55
+ import { NativeTokenRequest } from "../broker/nativeBroker/NativeRequest";
56
+ import { AuthenticationResult } from "../response/AuthenticationResult";
57
+ import { SilentRequest } from "../request/SilentRequest";
58
+ import { SsoSilentRequest } from "../request/SsoSilentRequest";
59
+ import { RedirectRequest } from "../request/RedirectRequest";
60
+ import { PopupRequest } from "../request/PopupRequest";
61
+ import { base64Decode } from "../encode/Base64Decode";
62
+ import { base64Encode } from "../encode/Base64Encode";
63
+
64
+ /**
65
+ * This class implements the cache storage interface for MSAL through browser local or session storage.
66
+ * Cookies are only used if storeAuthStateInCookie is true, and are only used for
67
+ * parameters such as state and nonce, generally.
68
+ */
69
+ export class BrowserCacheManager extends CacheManager {
70
+ // Cache configuration, either set by user or default values.
71
+ protected cacheConfig: Required<CacheOptions>;
72
+ // Window storage object (either local or sessionStorage)
73
+ protected browserStorage: IWindowStorage<string>;
74
+ // Internal in-memory storage object used for data used by msal that does not need to persist across page loads
75
+ protected internalStorage: MemoryStorage<string>;
76
+ // Temporary cache
77
+ protected temporaryCacheStorage: IWindowStorage<string>;
78
+ // Logger instance
79
+ protected logger: Logger;
80
+
81
+ // Cookie life calculation (hours * minutes * seconds * ms)
82
+ protected readonly COOKIE_LIFE_MULTIPLIER = 24 * 60 * 60 * 1000;
83
+
84
+ constructor(
85
+ clientId: string,
86
+ cacheConfig: Required<CacheOptions>,
87
+ cryptoImpl: ICrypto,
88
+ logger: Logger
89
+ ) {
90
+ super(clientId, cryptoImpl, logger);
91
+ this.cacheConfig = cacheConfig;
92
+ this.logger = logger;
93
+ this.internalStorage = new MemoryStorage();
94
+ this.browserStorage = this.setupBrowserStorage(
95
+ this.cacheConfig.cacheLocation
96
+ );
97
+ this.temporaryCacheStorage = this.setupTemporaryCacheStorage(
98
+ this.cacheConfig.temporaryCacheLocation,
99
+ this.cacheConfig.cacheLocation
100
+ );
101
+
102
+ // Migrate cache entries from older versions of MSAL.
103
+ if (cacheConfig.cacheMigrationEnabled) {
104
+ this.migrateCacheEntries();
105
+ this.createKeyMaps();
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Returns a window storage class implementing the IWindowStorage interface that corresponds to the configured cacheLocation.
111
+ * @param cacheLocation
112
+ */
113
+ protected setupBrowserStorage(
114
+ cacheLocation: BrowserCacheLocation | string
115
+ ): IWindowStorage<string> {
116
+ switch (cacheLocation) {
117
+ case BrowserCacheLocation.LocalStorage:
118
+ case BrowserCacheLocation.SessionStorage:
119
+ try {
120
+ return new BrowserStorage(cacheLocation);
121
+ } catch (e) {
122
+ this.logger.verbose(e as string);
123
+ break;
124
+ }
125
+ case BrowserCacheLocation.MemoryStorage:
126
+ default:
127
+ break;
128
+ }
129
+ this.cacheConfig.cacheLocation = BrowserCacheLocation.MemoryStorage;
130
+ return new MemoryStorage();
131
+ }
132
+
133
+ /**
134
+ * Returns a window storage class implementing the IWindowStorage interface that corresponds to the configured temporaryCacheLocation.
135
+ * @param temporaryCacheLocation
136
+ * @param cacheLocation
137
+ */
138
+ protected setupTemporaryCacheStorage(
139
+ temporaryCacheLocation: BrowserCacheLocation | string,
140
+ cacheLocation: BrowserCacheLocation | string
141
+ ): IWindowStorage<string> {
142
+ switch (cacheLocation) {
143
+ case BrowserCacheLocation.LocalStorage:
144
+ case BrowserCacheLocation.SessionStorage:
145
+ try {
146
+ // Temporary cache items will always be stored in session storage to mitigate problems caused by multiple tabs
147
+ return new BrowserStorage(
148
+ temporaryCacheLocation ||
149
+ BrowserCacheLocation.SessionStorage
150
+ );
151
+ } catch (e) {
152
+ this.logger.verbose(e as string);
153
+ return this.internalStorage;
154
+ }
155
+ case BrowserCacheLocation.MemoryStorage:
156
+ default:
157
+ return this.internalStorage;
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Migrate all old cache entries to new schema. No rollback supported.
163
+ * @param storeAuthStateInCookie
164
+ */
165
+ protected migrateCacheEntries(): void {
166
+ const idTokenKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.ID_TOKEN}`;
167
+ const clientInfoKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.CLIENT_INFO}`;
168
+ const errorKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.ERROR}`;
169
+ const errorDescKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.ERROR_DESC}`;
170
+
171
+ const idTokenValue = this.browserStorage.getItem(idTokenKey);
172
+ const clientInfoValue = this.browserStorage.getItem(clientInfoKey);
173
+ const errorValue = this.browserStorage.getItem(errorKey);
174
+ const errorDescValue = this.browserStorage.getItem(errorDescKey);
175
+
176
+ const values = [
177
+ idTokenValue,
178
+ clientInfoValue,
179
+ errorValue,
180
+ errorDescValue,
181
+ ];
182
+ const keysToMigrate = [
183
+ PersistentCacheKeys.ID_TOKEN,
184
+ PersistentCacheKeys.CLIENT_INFO,
185
+ PersistentCacheKeys.ERROR,
186
+ PersistentCacheKeys.ERROR_DESC,
187
+ ];
188
+
189
+ keysToMigrate.forEach((cacheKey: string, index: number) =>
190
+ this.migrateCacheEntry(cacheKey, values[index])
191
+ );
192
+ }
193
+
194
+ /**
195
+ * Utility function to help with migration.
196
+ * @param newKey
197
+ * @param value
198
+ * @param storeAuthStateInCookie
199
+ */
200
+ protected migrateCacheEntry(newKey: string, value: string | null): void {
201
+ if (value) {
202
+ this.setTemporaryCache(newKey, value, true);
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Searches all cache entries for MSAL accounts and creates the account key map
208
+ * This is used to migrate users from older versions of MSAL which did not create the map.
209
+ * @returns
210
+ */
211
+ private createKeyMaps(): void {
212
+ this.logger.trace("BrowserCacheManager - createKeyMaps called.");
213
+ const accountKeys = this.getItem(StaticCacheKeys.ACCOUNT_KEYS);
214
+ const tokenKeys = this.getItem(
215
+ `${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`
216
+ );
217
+ if (accountKeys && tokenKeys) {
218
+ this.logger.verbose(
219
+ "BrowserCacheManager:createKeyMaps - account and token key maps already exist, skipping migration."
220
+ );
221
+ // Key maps already exist, no need to iterate through cache
222
+ return;
223
+ }
224
+
225
+ const allKeys = this.browserStorage.getKeys();
226
+ allKeys.forEach((key) => {
227
+ if (this.isCredentialKey(key)) {
228
+ // Get item, parse, validate and write key to map
229
+ const value = this.getItem(key);
230
+ if (value) {
231
+ const credObj = this.validateAndParseJson(value);
232
+ if (credObj && credObj.hasOwnProperty("credentialType")) {
233
+ switch (credObj["credentialType"]) {
234
+ case CredentialType.ID_TOKEN:
235
+ if (IdTokenEntity.isIdTokenEntity(credObj)) {
236
+ this.logger.trace(
237
+ "BrowserCacheManager:createKeyMaps - idToken found, saving key to token key map"
238
+ );
239
+ this.logger.tracePii(
240
+ `BrowserCacheManager:createKeyMaps - idToken with key: ${key} found, saving key to token key map`
241
+ );
242
+ const idTokenEntity = CacheManager.toObject(
243
+ new IdTokenEntity(),
244
+ credObj
245
+ );
246
+ const newKey =
247
+ this.updateCredentialCacheKey(
248
+ key,
249
+ idTokenEntity
250
+ );
251
+ this.addTokenKey(
252
+ newKey,
253
+ CredentialType.ID_TOKEN
254
+ );
255
+ return;
256
+ } else {
257
+ this.logger.trace(
258
+ "BrowserCacheManager:createKeyMaps - key found matching idToken schema with value containing idToken credentialType field but value failed IdTokenEntity validation, skipping."
259
+ );
260
+ this.logger.tracePii(
261
+ `BrowserCacheManager:createKeyMaps - failed idToken validation on key: ${key}`
262
+ );
263
+ }
264
+ break;
265
+ case CredentialType.ACCESS_TOKEN:
266
+ case CredentialType.ACCESS_TOKEN_WITH_AUTH_SCHEME:
267
+ if (
268
+ AccessTokenEntity.isAccessTokenEntity(
269
+ credObj
270
+ )
271
+ ) {
272
+ this.logger.trace(
273
+ "BrowserCacheManager:createKeyMaps - accessToken found, saving key to token key map"
274
+ );
275
+ this.logger.tracePii(
276
+ `BrowserCacheManager:createKeyMaps - accessToken with key: ${key} found, saving key to token key map`
277
+ );
278
+ const accessTokenEntity =
279
+ CacheManager.toObject(
280
+ new AccessTokenEntity(),
281
+ credObj
282
+ );
283
+ const newKey =
284
+ this.updateCredentialCacheKey(
285
+ key,
286
+ accessTokenEntity
287
+ );
288
+ this.addTokenKey(
289
+ newKey,
290
+ CredentialType.ACCESS_TOKEN
291
+ );
292
+ return;
293
+ } else {
294
+ this.logger.trace(
295
+ "BrowserCacheManager:createKeyMaps - key found matching accessToken schema with value containing accessToken credentialType field but value failed AccessTokenEntity validation, skipping."
296
+ );
297
+ this.logger.tracePii(
298
+ `BrowserCacheManager:createKeyMaps - failed accessToken validation on key: ${key}`
299
+ );
300
+ }
301
+ break;
302
+ case CredentialType.REFRESH_TOKEN:
303
+ if (
304
+ RefreshTokenEntity.isRefreshTokenEntity(
305
+ credObj
306
+ )
307
+ ) {
308
+ this.logger.trace(
309
+ "BrowserCacheManager:createKeyMaps - refreshToken found, saving key to token key map"
310
+ );
311
+ this.logger.tracePii(
312
+ `BrowserCacheManager:createKeyMaps - refreshToken with key: ${key} found, saving key to token key map`
313
+ );
314
+ const refreshTokenEntity =
315
+ CacheManager.toObject(
316
+ new RefreshTokenEntity(),
317
+ credObj
318
+ );
319
+ const newKey =
320
+ this.updateCredentialCacheKey(
321
+ key,
322
+ refreshTokenEntity
323
+ );
324
+ this.addTokenKey(
325
+ newKey,
326
+ CredentialType.REFRESH_TOKEN
327
+ );
328
+ return;
329
+ } else {
330
+ this.logger.trace(
331
+ "BrowserCacheManager:createKeyMaps - key found matching refreshToken schema with value containing refreshToken credentialType field but value failed RefreshTokenEntity validation, skipping."
332
+ );
333
+ this.logger.tracePii(
334
+ `BrowserCacheManager:createKeyMaps - failed refreshToken validation on key: ${key}`
335
+ );
336
+ }
337
+ break;
338
+ default:
339
+ // If credentialType isn't one of our predefined ones, it may not be an MSAL cache value. Ignore.
340
+ }
341
+ }
342
+ }
343
+ }
344
+
345
+ if (this.isAccountKey(key)) {
346
+ const value = this.getItem(key);
347
+ if (value) {
348
+ const accountObj = this.validateAndParseJson(value);
349
+ if (
350
+ accountObj &&
351
+ AccountEntity.isAccountEntity(accountObj)
352
+ ) {
353
+ this.logger.trace(
354
+ "BrowserCacheManager:createKeyMaps - account found, saving key to account key map"
355
+ );
356
+ this.logger.tracePii(
357
+ `BrowserCacheManager:createKeyMaps - account with key: ${key} found, saving key to account key map`
358
+ );
359
+ this.addAccountKeyToMap(key);
360
+ }
361
+ }
362
+ }
363
+ });
364
+ }
365
+
366
+ /**
367
+ * Parses passed value as JSON object, JSON.parse() will throw an error.
368
+ * @param input
369
+ */
370
+ protected validateAndParseJson(jsonValue: string): object | null {
371
+ try {
372
+ const parsedJson = JSON.parse(jsonValue);
373
+ /**
374
+ * There are edge cases in which JSON.parse will successfully parse a non-valid JSON object
375
+ * (e.g. JSON.parse will parse an escaped string into an unescaped string), so adding a type check
376
+ * of the parsed value is necessary in order to be certain that the string represents a valid JSON object.
377
+ *
378
+ */
379
+ return parsedJson && typeof parsedJson === "object"
380
+ ? parsedJson
381
+ : null;
382
+ } catch (error) {
383
+ return null;
384
+ }
385
+ }
386
+
387
+ /**
388
+ * fetches the entry from the browser storage based off the key
389
+ * @param key
390
+ */
391
+ getItem(key: string): string | null {
392
+ return this.browserStorage.getItem(key);
393
+ }
394
+
395
+ /**
396
+ * sets the entry in the browser storage
397
+ * @param key
398
+ * @param value
399
+ */
400
+ setItem(key: string, value: string): void {
401
+ this.browserStorage.setItem(key, value);
402
+ }
403
+
404
+ /**
405
+ * fetch the account entity from the platform cache
406
+ * @param accountKey
407
+ */
408
+ getAccount(accountKey: string): AccountEntity | null {
409
+ this.logger.trace("BrowserCacheManager.getAccount called");
410
+ const account = this.getItem(accountKey);
411
+ if (!account) {
412
+ this.removeAccountKeyFromMap(accountKey);
413
+ return null;
414
+ }
415
+
416
+ const parsedAccount = this.validateAndParseJson(account);
417
+ if (!parsedAccount || !AccountEntity.isAccountEntity(parsedAccount)) {
418
+ this.removeAccountKeyFromMap(accountKey);
419
+ return null;
420
+ }
421
+
422
+ return CacheManager.toObject<AccountEntity>(
423
+ new AccountEntity(),
424
+ parsedAccount
425
+ );
426
+ }
427
+
428
+ /**
429
+ * set account entity in the platform cache
430
+ * @param account
431
+ */
432
+ setAccount(account: AccountEntity): void {
433
+ this.logger.trace("BrowserCacheManager.setAccount called");
434
+ const key = account.generateAccountKey();
435
+ this.setItem(key, JSON.stringify(account));
436
+ this.addAccountKeyToMap(key);
437
+ }
438
+
439
+ /**
440
+ * Returns the array of account keys currently cached
441
+ * @returns
442
+ */
443
+ getAccountKeys(): Array<string> {
444
+ this.logger.trace("BrowserCacheManager.getAccountKeys called");
445
+ const accountKeys = this.getItem(StaticCacheKeys.ACCOUNT_KEYS);
446
+ if (accountKeys) {
447
+ return JSON.parse(accountKeys);
448
+ }
449
+
450
+ this.logger.verbose(
451
+ "BrowserCacheManager.getAccountKeys - No account keys found"
452
+ );
453
+ return [];
454
+ }
455
+
456
+ /**
457
+ * Add a new account to the key map
458
+ * @param key
459
+ */
460
+ addAccountKeyToMap(key: string): void {
461
+ this.logger.trace("BrowserCacheManager.addAccountKeyToMap called");
462
+ this.logger.tracePii(
463
+ `BrowserCacheManager.addAccountKeyToMap called with key: ${key}`
464
+ );
465
+ const accountKeys = this.getAccountKeys();
466
+ if (accountKeys.indexOf(key) === -1) {
467
+ // Only add key if it does not already exist in the map
468
+ accountKeys.push(key);
469
+ this.setItem(
470
+ StaticCacheKeys.ACCOUNT_KEYS,
471
+ JSON.stringify(accountKeys)
472
+ );
473
+ this.logger.verbose(
474
+ "BrowserCacheManager.addAccountKeyToMap account key added"
475
+ );
476
+ } else {
477
+ this.logger.verbose(
478
+ "BrowserCacheManager.addAccountKeyToMap account key already exists in map"
479
+ );
480
+ }
481
+ }
482
+
483
+ /**
484
+ * Remove an account from the key map
485
+ * @param key
486
+ */
487
+ removeAccountKeyFromMap(key: string): void {
488
+ this.logger.trace("BrowserCacheManager.removeAccountKeyFromMap called");
489
+ this.logger.tracePii(
490
+ `BrowserCacheManager.removeAccountKeyFromMap called with key: ${key}`
491
+ );
492
+ const accountKeys = this.getAccountKeys();
493
+ const removalIndex = accountKeys.indexOf(key);
494
+ if (removalIndex > -1) {
495
+ accountKeys.splice(removalIndex, 1);
496
+ this.setItem(
497
+ StaticCacheKeys.ACCOUNT_KEYS,
498
+ JSON.stringify(accountKeys)
499
+ );
500
+ this.logger.trace(
501
+ "BrowserCacheManager.removeAccountKeyFromMap account key removed"
502
+ );
503
+ } else {
504
+ this.logger.trace(
505
+ "BrowserCacheManager.removeAccountKeyFromMap key not found in existing map"
506
+ );
507
+ }
508
+ }
509
+
510
+ /**
511
+ * Extends inherited removeAccount function to include removal of the account key from the map
512
+ * @param key
513
+ */
514
+ async removeAccount(key: string): Promise<void> {
515
+ void super.removeAccount(key);
516
+ this.removeAccountKeyFromMap(key);
517
+ }
518
+
519
+ /**
520
+ * Removes given idToken from the cache and from the key map
521
+ * @param key
522
+ */
523
+ removeIdToken(key: string): void {
524
+ super.removeIdToken(key);
525
+ this.removeTokenKey(key, CredentialType.ID_TOKEN);
526
+ }
527
+
528
+ /**
529
+ * Removes given accessToken from the cache and from the key map
530
+ * @param key
531
+ */
532
+ async removeAccessToken(key: string): Promise<void> {
533
+ void super.removeAccessToken(key);
534
+ this.removeTokenKey(key, CredentialType.ACCESS_TOKEN);
535
+ }
536
+
537
+ /**
538
+ * Removes given refreshToken from the cache and from the key map
539
+ * @param key
540
+ */
541
+ removeRefreshToken(key: string): void {
542
+ super.removeRefreshToken(key);
543
+ this.removeTokenKey(key, CredentialType.REFRESH_TOKEN);
544
+ }
545
+
546
+ /**
547
+ * Gets the keys for the cached tokens associated with this clientId
548
+ * @returns
549
+ */
550
+ getTokenKeys(): TokenKeys {
551
+ this.logger.trace("BrowserCacheManager.getTokenKeys called");
552
+ const item = this.getItem(
553
+ `${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`
554
+ );
555
+ if (item) {
556
+ const tokenKeys = this.validateAndParseJson(item);
557
+ if (
558
+ tokenKeys &&
559
+ tokenKeys.hasOwnProperty("idToken") &&
560
+ tokenKeys.hasOwnProperty("accessToken") &&
561
+ tokenKeys.hasOwnProperty("refreshToken")
562
+ ) {
563
+ return tokenKeys as TokenKeys;
564
+ } else {
565
+ this.logger.error(
566
+ "BrowserCacheManager.getTokenKeys - Token keys found but in an unknown format. Returning empty key map."
567
+ );
568
+ }
569
+ } else {
570
+ this.logger.verbose(
571
+ "BrowserCacheManager.getTokenKeys - No token keys found"
572
+ );
573
+ }
574
+
575
+ return {
576
+ idToken: [],
577
+ accessToken: [],
578
+ refreshToken: [],
579
+ };
580
+ }
581
+
582
+ /**
583
+ * Adds the given key to the token key map
584
+ * @param key
585
+ * @param type
586
+ */
587
+ addTokenKey(key: string, type: CredentialType): void {
588
+ this.logger.trace("BrowserCacheManager addTokenKey called");
589
+ const tokenKeys = this.getTokenKeys();
590
+
591
+ switch (type) {
592
+ case CredentialType.ID_TOKEN:
593
+ if (tokenKeys.idToken.indexOf(key) === -1) {
594
+ this.logger.info(
595
+ "BrowserCacheManager: addTokenKey - idToken added to map"
596
+ );
597
+ tokenKeys.idToken.push(key);
598
+ }
599
+ break;
600
+ case CredentialType.ACCESS_TOKEN:
601
+ if (tokenKeys.accessToken.indexOf(key) === -1) {
602
+ this.logger.info(
603
+ "BrowserCacheManager: addTokenKey - accessToken added to map"
604
+ );
605
+ tokenKeys.accessToken.push(key);
606
+ }
607
+ break;
608
+ case CredentialType.REFRESH_TOKEN:
609
+ if (tokenKeys.refreshToken.indexOf(key) === -1) {
610
+ this.logger.info(
611
+ "BrowserCacheManager: addTokenKey - refreshToken added to map"
612
+ );
613
+ tokenKeys.refreshToken.push(key);
614
+ }
615
+ break;
616
+ default:
617
+ this.logger.error(
618
+ `BrowserCacheManager:addTokenKey - CredentialType provided invalid. CredentialType: ${type}`
619
+ );
620
+ throw createClientAuthError(
621
+ ClientAuthErrorCodes.unexpectedCredentialType
622
+ );
623
+ }
624
+
625
+ this.setItem(
626
+ `${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`,
627
+ JSON.stringify(tokenKeys)
628
+ );
629
+ }
630
+
631
+ /**
632
+ * Removes the given key from the token key map
633
+ * @param key
634
+ * @param type
635
+ */
636
+ removeTokenKey(key: string, type: CredentialType): void {
637
+ this.logger.trace("BrowserCacheManager removeTokenKey called");
638
+ const tokenKeys = this.getTokenKeys();
639
+
640
+ switch (type) {
641
+ case CredentialType.ID_TOKEN:
642
+ this.logger.infoPii(
643
+ `BrowserCacheManager: removeTokenKey - attempting to remove idToken with key: ${key} from map`
644
+ );
645
+ const idRemoval = tokenKeys.idToken.indexOf(key);
646
+ if (idRemoval > -1) {
647
+ this.logger.info(
648
+ "BrowserCacheManager: removeTokenKey - idToken removed from map"
649
+ );
650
+ tokenKeys.idToken.splice(idRemoval, 1);
651
+ } else {
652
+ this.logger.info(
653
+ "BrowserCacheManager: removeTokenKey - idToken does not exist in map. Either it was previously removed or it was never added."
654
+ );
655
+ }
656
+ break;
657
+ case CredentialType.ACCESS_TOKEN:
658
+ this.logger.infoPii(
659
+ `BrowserCacheManager: removeTokenKey - attempting to remove accessToken with key: ${key} from map`
660
+ );
661
+ const accessRemoval = tokenKeys.accessToken.indexOf(key);
662
+ if (accessRemoval > -1) {
663
+ this.logger.info(
664
+ "BrowserCacheManager: removeTokenKey - accessToken removed from map"
665
+ );
666
+ tokenKeys.accessToken.splice(accessRemoval, 1);
667
+ } else {
668
+ this.logger.info(
669
+ "BrowserCacheManager: removeTokenKey - accessToken does not exist in map. Either it was previously removed or it was never added."
670
+ );
671
+ }
672
+ break;
673
+ case CredentialType.REFRESH_TOKEN:
674
+ this.logger.infoPii(
675
+ `BrowserCacheManager: removeTokenKey - attempting to remove refreshToken with key: ${key} from map`
676
+ );
677
+ const refreshRemoval = tokenKeys.refreshToken.indexOf(key);
678
+ if (refreshRemoval > -1) {
679
+ this.logger.info(
680
+ "BrowserCacheManager: removeTokenKey - refreshToken removed from map"
681
+ );
682
+ tokenKeys.refreshToken.splice(refreshRemoval, 1);
683
+ } else {
684
+ this.logger.info(
685
+ "BrowserCacheManager: removeTokenKey - refreshToken does not exist in map. Either it was previously removed or it was never added."
686
+ );
687
+ }
688
+ break;
689
+ default:
690
+ this.logger.error(
691
+ `BrowserCacheManager:removeTokenKey - CredentialType provided invalid. CredentialType: ${type}`
692
+ );
693
+ throw createClientAuthError(
694
+ ClientAuthErrorCodes.unexpectedCredentialType
695
+ );
696
+ }
697
+
698
+ this.setItem(
699
+ `${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`,
700
+ JSON.stringify(tokenKeys)
701
+ );
702
+ }
703
+
704
+ /**
705
+ * generates idToken entity from a string
706
+ * @param idTokenKey
707
+ */
708
+ getIdTokenCredential(idTokenKey: string): IdTokenEntity | null {
709
+ const value = this.getItem(idTokenKey);
710
+ if (!value) {
711
+ this.logger.trace(
712
+ "BrowserCacheManager.getIdTokenCredential: called, no cache hit"
713
+ );
714
+ this.removeTokenKey(idTokenKey, CredentialType.ID_TOKEN);
715
+ return null;
716
+ }
717
+
718
+ const parsedIdToken = this.validateAndParseJson(value);
719
+ if (!parsedIdToken || !IdTokenEntity.isIdTokenEntity(parsedIdToken)) {
720
+ this.logger.trace(
721
+ "BrowserCacheManager.getIdTokenCredential: called, no cache hit"
722
+ );
723
+ this.removeTokenKey(idTokenKey, CredentialType.ID_TOKEN);
724
+ return null;
725
+ }
726
+
727
+ this.logger.trace(
728
+ "BrowserCacheManager.getIdTokenCredential: cache hit"
729
+ );
730
+ return CacheManager.toObject(new IdTokenEntity(), parsedIdToken);
731
+ }
732
+
733
+ /**
734
+ * set IdToken credential to the platform cache
735
+ * @param idToken
736
+ */
737
+ setIdTokenCredential(idToken: IdTokenEntity): void {
738
+ this.logger.trace("BrowserCacheManager.setIdTokenCredential called");
739
+ const idTokenKey = idToken.generateCredentialKey();
740
+
741
+ this.setItem(idTokenKey, JSON.stringify(idToken));
742
+
743
+ this.addTokenKey(idTokenKey, CredentialType.ID_TOKEN);
744
+ }
745
+
746
+ /**
747
+ * generates accessToken entity from a string
748
+ * @param key
749
+ */
750
+ getAccessTokenCredential(accessTokenKey: string): AccessTokenEntity | null {
751
+ const value = this.getItem(accessTokenKey);
752
+ if (!value) {
753
+ this.logger.trace(
754
+ "BrowserCacheManager.getAccessTokenCredential: called, no cache hit"
755
+ );
756
+ this.removeTokenKey(accessTokenKey, CredentialType.ACCESS_TOKEN);
757
+ return null;
758
+ }
759
+ const parsedAccessToken = this.validateAndParseJson(value);
760
+ if (
761
+ !parsedAccessToken ||
762
+ !AccessTokenEntity.isAccessTokenEntity(parsedAccessToken)
763
+ ) {
764
+ this.logger.trace(
765
+ "BrowserCacheManager.getAccessTokenCredential: called, no cache hit"
766
+ );
767
+ this.removeTokenKey(accessTokenKey, CredentialType.ACCESS_TOKEN);
768
+ return null;
769
+ }
770
+
771
+ this.logger.trace(
772
+ "BrowserCacheManager.getAccessTokenCredential: cache hit"
773
+ );
774
+ return CacheManager.toObject(
775
+ new AccessTokenEntity(),
776
+ parsedAccessToken
777
+ );
778
+ }
779
+
780
+ /**
781
+ * set accessToken credential to the platform cache
782
+ * @param accessToken
783
+ */
784
+ setAccessTokenCredential(accessToken: AccessTokenEntity): void {
785
+ this.logger.trace(
786
+ "BrowserCacheManager.setAccessTokenCredential called"
787
+ );
788
+ const accessTokenKey = accessToken.generateCredentialKey();
789
+ this.setItem(accessTokenKey, JSON.stringify(accessToken));
790
+
791
+ this.addTokenKey(accessTokenKey, CredentialType.ACCESS_TOKEN);
792
+ }
793
+
794
+ /**
795
+ * generates refreshToken entity from a string
796
+ * @param refreshTokenKey
797
+ */
798
+ getRefreshTokenCredential(
799
+ refreshTokenKey: string
800
+ ): RefreshTokenEntity | null {
801
+ const value = this.getItem(refreshTokenKey);
802
+ if (!value) {
803
+ this.logger.trace(
804
+ "BrowserCacheManager.getRefreshTokenCredential: called, no cache hit"
805
+ );
806
+ this.removeTokenKey(refreshTokenKey, CredentialType.REFRESH_TOKEN);
807
+ return null;
808
+ }
809
+ const parsedRefreshToken = this.validateAndParseJson(value);
810
+ if (
811
+ !parsedRefreshToken ||
812
+ !RefreshTokenEntity.isRefreshTokenEntity(parsedRefreshToken)
813
+ ) {
814
+ this.logger.trace(
815
+ "BrowserCacheManager.getRefreshTokenCredential: called, no cache hit"
816
+ );
817
+ this.removeTokenKey(refreshTokenKey, CredentialType.REFRESH_TOKEN);
818
+ return null;
819
+ }
820
+
821
+ this.logger.trace(
822
+ "BrowserCacheManager.getRefreshTokenCredential: cache hit"
823
+ );
824
+ return CacheManager.toObject(
825
+ new RefreshTokenEntity(),
826
+ parsedRefreshToken
827
+ );
828
+ }
829
+
830
+ /**
831
+ * set refreshToken credential to the platform cache
832
+ * @param refreshToken
833
+ */
834
+ setRefreshTokenCredential(refreshToken: RefreshTokenEntity): void {
835
+ this.logger.trace(
836
+ "BrowserCacheManager.setRefreshTokenCredential called"
837
+ );
838
+ const refreshTokenKey = refreshToken.generateCredentialKey();
839
+ this.setItem(refreshTokenKey, JSON.stringify(refreshToken));
840
+
841
+ this.addTokenKey(refreshTokenKey, CredentialType.REFRESH_TOKEN);
842
+ }
843
+
844
+ /**
845
+ * fetch appMetadata entity from the platform cache
846
+ * @param appMetadataKey
847
+ */
848
+ getAppMetadata(appMetadataKey: string): AppMetadataEntity | null {
849
+ const value = this.getItem(appMetadataKey);
850
+ if (!value) {
851
+ this.logger.trace(
852
+ "BrowserCacheManager.getAppMetadata: called, no cache hit"
853
+ );
854
+ return null;
855
+ }
856
+
857
+ const parsedMetadata = this.validateAndParseJson(value);
858
+ if (
859
+ !parsedMetadata ||
860
+ !AppMetadataEntity.isAppMetadataEntity(
861
+ appMetadataKey,
862
+ parsedMetadata
863
+ )
864
+ ) {
865
+ this.logger.trace(
866
+ "BrowserCacheManager.getAppMetadata: called, no cache hit"
867
+ );
868
+ return null;
869
+ }
870
+
871
+ this.logger.trace("BrowserCacheManager.getAppMetadata: cache hit");
872
+ return CacheManager.toObject(new AppMetadataEntity(), parsedMetadata);
873
+ }
874
+
875
+ /**
876
+ * set appMetadata entity to the platform cache
877
+ * @param appMetadata
878
+ */
879
+ setAppMetadata(appMetadata: AppMetadataEntity): void {
880
+ this.logger.trace("BrowserCacheManager.setAppMetadata called");
881
+ const appMetadataKey = appMetadata.generateAppMetadataKey();
882
+ this.setItem(appMetadataKey, JSON.stringify(appMetadata));
883
+ }
884
+
885
+ /**
886
+ * fetch server telemetry entity from the platform cache
887
+ * @param serverTelemetryKey
888
+ */
889
+ getServerTelemetry(
890
+ serverTelemetryKey: string
891
+ ): ServerTelemetryEntity | null {
892
+ const value = this.getItem(serverTelemetryKey);
893
+ if (!value) {
894
+ this.logger.trace(
895
+ "BrowserCacheManager.getServerTelemetry: called, no cache hit"
896
+ );
897
+ return null;
898
+ }
899
+ const parsedMetadata = this.validateAndParseJson(value);
900
+ if (
901
+ !parsedMetadata ||
902
+ !ServerTelemetryEntity.isServerTelemetryEntity(
903
+ serverTelemetryKey,
904
+ parsedMetadata
905
+ )
906
+ ) {
907
+ this.logger.trace(
908
+ "BrowserCacheManager.getServerTelemetry: called, no cache hit"
909
+ );
910
+ return null;
911
+ }
912
+
913
+ this.logger.trace("BrowserCacheManager.getServerTelemetry: cache hit");
914
+ return CacheManager.toObject(
915
+ new ServerTelemetryEntity(),
916
+ parsedMetadata
917
+ );
918
+ }
919
+
920
+ /**
921
+ * set server telemetry entity to the platform cache
922
+ * @param serverTelemetryKey
923
+ * @param serverTelemetry
924
+ */
925
+ setServerTelemetry(
926
+ serverTelemetryKey: string,
927
+ serverTelemetry: ServerTelemetryEntity
928
+ ): void {
929
+ this.logger.trace("BrowserCacheManager.setServerTelemetry called");
930
+ this.setItem(serverTelemetryKey, JSON.stringify(serverTelemetry));
931
+ }
932
+
933
+ /**
934
+ *
935
+ */
936
+ getAuthorityMetadata(key: string): AuthorityMetadataEntity | null {
937
+ const value = this.internalStorage.getItem(key);
938
+ if (!value) {
939
+ this.logger.trace(
940
+ "BrowserCacheManager.getAuthorityMetadata: called, no cache hit"
941
+ );
942
+ return null;
943
+ }
944
+ const parsedMetadata = this.validateAndParseJson(value);
945
+ if (
946
+ parsedMetadata &&
947
+ AuthorityMetadataEntity.isAuthorityMetadataEntity(
948
+ key,
949
+ parsedMetadata
950
+ )
951
+ ) {
952
+ this.logger.trace(
953
+ "BrowserCacheManager.getAuthorityMetadata: cache hit"
954
+ );
955
+ return CacheManager.toObject(
956
+ new AuthorityMetadataEntity(),
957
+ parsedMetadata
958
+ );
959
+ }
960
+ return null;
961
+ }
962
+
963
+ /**
964
+ *
965
+ */
966
+ getAuthorityMetadataKeys(): Array<string> {
967
+ const allKeys = this.internalStorage.getKeys();
968
+ return allKeys.filter((key) => {
969
+ return this.isAuthorityMetadata(key);
970
+ });
971
+ }
972
+
973
+ /**
974
+ * Sets wrapper metadata in memory
975
+ * @param wrapperSKU
976
+ * @param wrapperVersion
977
+ */
978
+ setWrapperMetadata(wrapperSKU: string, wrapperVersion: string): void {
979
+ this.internalStorage.setItem(InMemoryCacheKeys.WRAPPER_SKU, wrapperSKU);
980
+ this.internalStorage.setItem(
981
+ InMemoryCacheKeys.WRAPPER_VER,
982
+ wrapperVersion
983
+ );
984
+ }
985
+
986
+ /**
987
+ * Returns wrapper metadata from in-memory storage
988
+ */
989
+ getWrapperMetadata(): [string, string] {
990
+ const sku =
991
+ this.internalStorage.getItem(InMemoryCacheKeys.WRAPPER_SKU) ||
992
+ Constants.EMPTY_STRING;
993
+ const version =
994
+ this.internalStorage.getItem(InMemoryCacheKeys.WRAPPER_VER) ||
995
+ Constants.EMPTY_STRING;
996
+ return [sku, version];
997
+ }
998
+
999
+ /**
1000
+ *
1001
+ * @param entity
1002
+ */
1003
+ setAuthorityMetadata(key: string, entity: AuthorityMetadataEntity): void {
1004
+ this.logger.trace("BrowserCacheManager.setAuthorityMetadata called");
1005
+ this.internalStorage.setItem(key, JSON.stringify(entity));
1006
+ }
1007
+
1008
+ /**
1009
+ * Gets the active account
1010
+ */
1011
+ getActiveAccount(): AccountInfo | null {
1012
+ const activeAccountKeyFilters = this.generateCacheKey(
1013
+ PersistentCacheKeys.ACTIVE_ACCOUNT_FILTERS
1014
+ );
1015
+ const activeAccountValueFilters = this.getItem(activeAccountKeyFilters);
1016
+ if (!activeAccountValueFilters) {
1017
+ // if new active account cache type isn't found, it's an old version, so look for that instead
1018
+ this.logger.trace(
1019
+ "BrowserCacheManager.getActiveAccount: No active account filters cache schema found, looking for legacy schema"
1020
+ );
1021
+ const activeAccountKeyLocal = this.generateCacheKey(
1022
+ PersistentCacheKeys.ACTIVE_ACCOUNT
1023
+ );
1024
+ const activeAccountValueLocal = this.getItem(activeAccountKeyLocal);
1025
+ if (!activeAccountValueLocal) {
1026
+ this.logger.trace(
1027
+ "BrowserCacheManager.getActiveAccount: No active account found"
1028
+ );
1029
+ return null;
1030
+ }
1031
+ const activeAccount =
1032
+ this.getAccountInfoByFilter({
1033
+ localAccountId: activeAccountValueLocal,
1034
+ })[0] || null;
1035
+ if (activeAccount) {
1036
+ this.logger.trace(
1037
+ "BrowserCacheManager.getActiveAccount: Legacy active account cache schema found"
1038
+ );
1039
+ this.logger.trace(
1040
+ "BrowserCacheManager.getActiveAccount: Adding active account filters cache schema"
1041
+ );
1042
+ this.setActiveAccount(activeAccount);
1043
+ return activeAccount;
1044
+ }
1045
+ return null;
1046
+ }
1047
+ const activeAccountValueObj = this.validateAndParseJson(
1048
+ activeAccountValueFilters
1049
+ ) as AccountInfo;
1050
+ if (activeAccountValueObj) {
1051
+ this.logger.trace(
1052
+ "BrowserCacheManager.getActiveAccount: Active account filters schema found"
1053
+ );
1054
+ return (
1055
+ this.getAccountInfoByFilter({
1056
+ homeAccountId: activeAccountValueObj.homeAccountId,
1057
+ localAccountId: activeAccountValueObj.localAccountId,
1058
+ })[0] || null
1059
+ );
1060
+ }
1061
+ this.logger.trace(
1062
+ "BrowserCacheManager.getActiveAccount: No active account found"
1063
+ );
1064
+ return null;
1065
+ }
1066
+
1067
+ /**
1068
+ * Sets the active account's localAccountId in cache
1069
+ * @param account
1070
+ */
1071
+ setActiveAccount(account: AccountInfo | null): void {
1072
+ const activeAccountKey = this.generateCacheKey(
1073
+ PersistentCacheKeys.ACTIVE_ACCOUNT_FILTERS
1074
+ );
1075
+ const activeAccountKeyLocal = this.generateCacheKey(
1076
+ PersistentCacheKeys.ACTIVE_ACCOUNT
1077
+ );
1078
+ if (account) {
1079
+ this.logger.verbose("setActiveAccount: Active account set");
1080
+ const activeAccountValue: ActiveAccountFilters = {
1081
+ homeAccountId: account.homeAccountId,
1082
+ localAccountId: account.localAccountId,
1083
+ };
1084
+ this.browserStorage.setItem(
1085
+ activeAccountKey,
1086
+ JSON.stringify(activeAccountValue)
1087
+ );
1088
+ this.browserStorage.setItem(
1089
+ activeAccountKeyLocal,
1090
+ account.localAccountId
1091
+ );
1092
+ } else {
1093
+ this.logger.verbose(
1094
+ "setActiveAccount: No account passed, active account not set"
1095
+ );
1096
+ this.browserStorage.removeItem(activeAccountKey);
1097
+ this.browserStorage.removeItem(activeAccountKeyLocal);
1098
+ }
1099
+ }
1100
+
1101
+ /**
1102
+ * Gets a list of accounts that match all of the filters provided
1103
+ * @param account
1104
+ */
1105
+ getAccountInfoByFilter(
1106
+ accountFilter: Partial<Omit<AccountInfo, "idTokenClaims" | "name">>
1107
+ ): AccountInfo[] {
1108
+ const allAccounts = this.getAllAccounts();
1109
+ this.logger.trace(
1110
+ `BrowserCacheManager.getAccountInfoByFilter: total ${allAccounts.length} accounts found`
1111
+ );
1112
+
1113
+ return allAccounts.filter((accountObj) => {
1114
+ if (
1115
+ accountFilter.username &&
1116
+ accountFilter.username.toLowerCase() !==
1117
+ accountObj.username.toLowerCase()
1118
+ ) {
1119
+ return false;
1120
+ }
1121
+
1122
+ if (
1123
+ accountFilter.homeAccountId &&
1124
+ accountFilter.homeAccountId !== accountObj.homeAccountId
1125
+ ) {
1126
+ return false;
1127
+ }
1128
+
1129
+ if (
1130
+ accountFilter.localAccountId &&
1131
+ accountFilter.localAccountId !== accountObj.localAccountId
1132
+ ) {
1133
+ return false;
1134
+ }
1135
+
1136
+ if (
1137
+ accountFilter.tenantId &&
1138
+ accountFilter.tenantId !== accountObj.tenantId
1139
+ ) {
1140
+ return false;
1141
+ }
1142
+
1143
+ if (
1144
+ accountFilter.environment &&
1145
+ accountFilter.environment !== accountObj.environment
1146
+ ) {
1147
+ return false;
1148
+ }
1149
+
1150
+ return true;
1151
+ });
1152
+ }
1153
+
1154
+ /**
1155
+ * Checks the cache for accounts matching loginHint or SID
1156
+ * @param loginHint
1157
+ * @param sid
1158
+ */
1159
+ getAccountInfoByHints(
1160
+ loginHint?: string,
1161
+ sid?: string
1162
+ ): AccountInfo | null {
1163
+ const matchingAccounts = this.getAllAccounts().filter((accountInfo) => {
1164
+ if (sid) {
1165
+ const accountSid =
1166
+ accountInfo.idTokenClaims &&
1167
+ accountInfo.idTokenClaims["sid"];
1168
+ return sid === accountSid;
1169
+ }
1170
+
1171
+ if (loginHint) {
1172
+ return loginHint === accountInfo.username;
1173
+ }
1174
+
1175
+ return false;
1176
+ });
1177
+
1178
+ if (matchingAccounts.length === 1) {
1179
+ return matchingAccounts[0];
1180
+ } else if (matchingAccounts.length > 1) {
1181
+ throw createClientAuthError(
1182
+ ClientAuthErrorCodes.multipleMatchingAccounts
1183
+ );
1184
+ }
1185
+
1186
+ return null;
1187
+ }
1188
+
1189
+ /**
1190
+ * fetch throttling entity from the platform cache
1191
+ * @param throttlingCacheKey
1192
+ */
1193
+ getThrottlingCache(throttlingCacheKey: string): ThrottlingEntity | null {
1194
+ const value = this.getItem(throttlingCacheKey);
1195
+ if (!value) {
1196
+ this.logger.trace(
1197
+ "BrowserCacheManager.getThrottlingCache: called, no cache hit"
1198
+ );
1199
+ return null;
1200
+ }
1201
+
1202
+ const parsedThrottlingCache = this.validateAndParseJson(value);
1203
+ if (
1204
+ !parsedThrottlingCache ||
1205
+ !ThrottlingEntity.isThrottlingEntity(
1206
+ throttlingCacheKey,
1207
+ parsedThrottlingCache
1208
+ )
1209
+ ) {
1210
+ this.logger.trace(
1211
+ "BrowserCacheManager.getThrottlingCache: called, no cache hit"
1212
+ );
1213
+ return null;
1214
+ }
1215
+
1216
+ this.logger.trace("BrowserCacheManager.getThrottlingCache: cache hit");
1217
+ return CacheManager.toObject(
1218
+ new ThrottlingEntity(),
1219
+ parsedThrottlingCache
1220
+ );
1221
+ }
1222
+
1223
+ /**
1224
+ * set throttling entity to the platform cache
1225
+ * @param throttlingCacheKey
1226
+ * @param throttlingCache
1227
+ */
1228
+ setThrottlingCache(
1229
+ throttlingCacheKey: string,
1230
+ throttlingCache: ThrottlingEntity
1231
+ ): void {
1232
+ this.logger.trace("BrowserCacheManager.setThrottlingCache called");
1233
+ this.setItem(throttlingCacheKey, JSON.stringify(throttlingCache));
1234
+ }
1235
+
1236
+ /**
1237
+ * Gets cache item with given key.
1238
+ * Will retrieve from cookies if storeAuthStateInCookie is set to true.
1239
+ * @param key
1240
+ */
1241
+ getTemporaryCache(cacheKey: string, generateKey?: boolean): string | null {
1242
+ const key = generateKey ? this.generateCacheKey(cacheKey) : cacheKey;
1243
+ if (this.cacheConfig.storeAuthStateInCookie) {
1244
+ const itemCookie = this.getItemCookie(key);
1245
+ if (itemCookie) {
1246
+ this.logger.trace(
1247
+ "BrowserCacheManager.getTemporaryCache: storeAuthStateInCookies set to true, retrieving from cookies"
1248
+ );
1249
+ return itemCookie;
1250
+ }
1251
+ }
1252
+
1253
+ const value = this.temporaryCacheStorage.getItem(key);
1254
+ if (!value) {
1255
+ // If temp cache item not found in session/memory, check local storage for items set by old versions
1256
+ if (
1257
+ this.cacheConfig.cacheLocation ===
1258
+ BrowserCacheLocation.LocalStorage
1259
+ ) {
1260
+ const item = this.browserStorage.getItem(key);
1261
+ if (item) {
1262
+ this.logger.trace(
1263
+ "BrowserCacheManager.getTemporaryCache: Temporary cache item found in local storage"
1264
+ );
1265
+ return item;
1266
+ }
1267
+ }
1268
+ this.logger.trace(
1269
+ "BrowserCacheManager.getTemporaryCache: No cache item found in local storage"
1270
+ );
1271
+ return null;
1272
+ }
1273
+ this.logger.trace(
1274
+ "BrowserCacheManager.getTemporaryCache: Temporary cache item returned"
1275
+ );
1276
+ return value;
1277
+ }
1278
+
1279
+ /**
1280
+ * Sets the cache item with the key and value given.
1281
+ * Stores in cookie if storeAuthStateInCookie is set to true.
1282
+ * This can cause cookie overflow if used incorrectly.
1283
+ * @param key
1284
+ * @param value
1285
+ */
1286
+ setTemporaryCache(
1287
+ cacheKey: string,
1288
+ value: string,
1289
+ generateKey?: boolean
1290
+ ): void {
1291
+ const key = generateKey ? this.generateCacheKey(cacheKey) : cacheKey;
1292
+
1293
+ this.temporaryCacheStorage.setItem(key, value);
1294
+ if (this.cacheConfig.storeAuthStateInCookie) {
1295
+ this.logger.trace(
1296
+ "BrowserCacheManager.setTemporaryCache: storeAuthStateInCookie set to true, setting item cookie"
1297
+ );
1298
+ this.setItemCookie(key, value);
1299
+ }
1300
+ }
1301
+
1302
+ /**
1303
+ * Removes the cache item with the given key.
1304
+ * Will also clear the cookie item if storeAuthStateInCookie is set to true.
1305
+ * @param key
1306
+ */
1307
+ removeItem(key: string): void {
1308
+ this.browserStorage.removeItem(key);
1309
+ this.temporaryCacheStorage.removeItem(key);
1310
+ if (this.cacheConfig.storeAuthStateInCookie) {
1311
+ this.logger.trace(
1312
+ "BrowserCacheManager.removeItem: storeAuthStateInCookie is true, clearing item cookie"
1313
+ );
1314
+ this.clearItemCookie(key);
1315
+ }
1316
+ }
1317
+
1318
+ /**
1319
+ * Checks whether key is in cache.
1320
+ * @param key
1321
+ */
1322
+ containsKey(key: string): boolean {
1323
+ return (
1324
+ this.browserStorage.containsKey(key) ||
1325
+ this.temporaryCacheStorage.containsKey(key)
1326
+ );
1327
+ }
1328
+
1329
+ /**
1330
+ * Gets all keys in window.
1331
+ */
1332
+ getKeys(): string[] {
1333
+ return [
1334
+ ...this.browserStorage.getKeys(),
1335
+ ...this.temporaryCacheStorage.getKeys(),
1336
+ ];
1337
+ }
1338
+
1339
+ /**
1340
+ * Clears all cache entries created by MSAL.
1341
+ */
1342
+ async clear(): Promise<void> {
1343
+ // Removes all accounts and their credentials
1344
+ await this.removeAllAccounts();
1345
+ this.removeAppMetadata();
1346
+
1347
+ // Removes all remaining MSAL cache items
1348
+ this.getKeys().forEach((cacheKey: string) => {
1349
+ // Check if key contains msal prefix; For now, we are clearing all the cache items created by MSAL.js
1350
+ if (
1351
+ (this.browserStorage.containsKey(cacheKey) ||
1352
+ this.temporaryCacheStorage.containsKey(cacheKey)) &&
1353
+ (cacheKey.indexOf(Constants.CACHE_PREFIX) !== -1 ||
1354
+ cacheKey.indexOf(this.clientId) !== -1)
1355
+ ) {
1356
+ this.removeItem(cacheKey);
1357
+ }
1358
+ });
1359
+
1360
+ this.internalStorage.clear();
1361
+ }
1362
+
1363
+ /**
1364
+ * Clears all access tokes that have claims prior to saving the current one
1365
+ * @param performanceClient {IPerformanceClient}
1366
+ * @returns
1367
+ */
1368
+ async clearTokensAndKeysWithClaims(
1369
+ performanceClient: IPerformanceClient
1370
+ ): Promise<void> {
1371
+ performanceClient.addQueueMeasurement(
1372
+ PerformanceEvents.ClearTokensAndKeysWithClaims
1373
+ );
1374
+
1375
+ const tokenKeys = this.getTokenKeys();
1376
+
1377
+ const removedAccessTokens: Array<Promise<void>> = [];
1378
+ tokenKeys.accessToken.forEach((key: string) => {
1379
+ // if the access token has claims in its key, remove the token key and the token
1380
+ const credential = this.getAccessTokenCredential(key);
1381
+ if (
1382
+ credential?.requestedClaimsHash &&
1383
+ key.includes(credential.requestedClaimsHash.toLowerCase())
1384
+ ) {
1385
+ removedAccessTokens.push(this.removeAccessToken(key));
1386
+ }
1387
+ });
1388
+ await Promise.all(removedAccessTokens);
1389
+
1390
+ // warn if any access tokens are removed
1391
+ if (removedAccessTokens.length > 0) {
1392
+ this.logger.warning(
1393
+ `${removedAccessTokens.length} access tokens with claims in the cache keys have been removed from the cache.`
1394
+ );
1395
+ }
1396
+ }
1397
+
1398
+ /**
1399
+ * Add value to cookies
1400
+ * @param cookieName
1401
+ * @param cookieValue
1402
+ * @param expires
1403
+ */
1404
+ setItemCookie(
1405
+ cookieName: string,
1406
+ cookieValue: string,
1407
+ expires?: number
1408
+ ): void {
1409
+ let cookieStr = `${encodeURIComponent(cookieName)}=${encodeURIComponent(
1410
+ cookieValue
1411
+ )};path=/;SameSite=Lax;`;
1412
+ if (expires) {
1413
+ const expireTime = this.getCookieExpirationTime(expires);
1414
+ cookieStr += `expires=${expireTime};`;
1415
+ }
1416
+
1417
+ if (this.cacheConfig.secureCookies) {
1418
+ cookieStr += "Secure;";
1419
+ }
1420
+
1421
+ document.cookie = cookieStr;
1422
+ }
1423
+
1424
+ /**
1425
+ * Get one item by key from cookies
1426
+ * @param cookieName
1427
+ */
1428
+ getItemCookie(cookieName: string): string {
1429
+ const name = `${encodeURIComponent(cookieName)}=`;
1430
+ const cookieList = document.cookie.split(";");
1431
+ for (let i: number = 0; i < cookieList.length; i++) {
1432
+ let cookie = cookieList[i];
1433
+ while (cookie.charAt(0) === " ") {
1434
+ cookie = cookie.substring(1);
1435
+ }
1436
+ if (cookie.indexOf(name) === 0) {
1437
+ return decodeURIComponent(
1438
+ cookie.substring(name.length, cookie.length)
1439
+ );
1440
+ }
1441
+ }
1442
+ return Constants.EMPTY_STRING;
1443
+ }
1444
+
1445
+ /**
1446
+ * Clear all msal-related cookies currently set in the browser. Should only be used to clear temporary cache items.
1447
+ */
1448
+ clearMsalCookies(): void {
1449
+ const cookiePrefix = `${Constants.CACHE_PREFIX}.${this.clientId}`;
1450
+ const cookieList = document.cookie.split(";");
1451
+ cookieList.forEach((cookie: string): void => {
1452
+ while (cookie.charAt(0) === " ") {
1453
+ // eslint-disable-next-line no-param-reassign
1454
+ cookie = cookie.substring(1);
1455
+ }
1456
+ if (cookie.indexOf(cookiePrefix) === 0) {
1457
+ const cookieKey = cookie.split("=")[0];
1458
+ this.clearItemCookie(cookieKey);
1459
+ }
1460
+ });
1461
+ }
1462
+
1463
+ /**
1464
+ * Clear an item in the cookies by key
1465
+ * @param cookieName
1466
+ */
1467
+ clearItemCookie(cookieName: string): void {
1468
+ this.setItemCookie(cookieName, Constants.EMPTY_STRING, -1);
1469
+ }
1470
+
1471
+ /**
1472
+ * Get cookie expiration time
1473
+ * @param cookieLifeDays
1474
+ */
1475
+ getCookieExpirationTime(cookieLifeDays: number): string {
1476
+ const today = new Date();
1477
+ const expr = new Date(
1478
+ today.getTime() + cookieLifeDays * this.COOKIE_LIFE_MULTIPLIER
1479
+ );
1480
+ return expr.toUTCString();
1481
+ }
1482
+
1483
+ /**
1484
+ * Gets the cache object referenced by the browser
1485
+ */
1486
+ getCache(): object {
1487
+ return this.browserStorage;
1488
+ }
1489
+
1490
+ /**
1491
+ * interface compat, we cannot overwrite browser cache; Functionality is supported by individual entities in browser
1492
+ */
1493
+ setCache(): void {
1494
+ // sets nothing
1495
+ }
1496
+
1497
+ /**
1498
+ * Prepend msal.<client-id> to each key; Skip for any JSON object as Key (defined schemas do not need the key appended: AccessToken Keys or the upcoming schema)
1499
+ * @param key
1500
+ * @param addInstanceId
1501
+ */
1502
+ generateCacheKey(key: string): string {
1503
+ const generatedKey = this.validateAndParseJson(key);
1504
+ if (!generatedKey) {
1505
+ if (
1506
+ StringUtils.startsWith(key, Constants.CACHE_PREFIX) ||
1507
+ StringUtils.startsWith(key, PersistentCacheKeys.ADAL_ID_TOKEN)
1508
+ ) {
1509
+ return key;
1510
+ }
1511
+ return `${Constants.CACHE_PREFIX}.${this.clientId}.${key}`;
1512
+ }
1513
+
1514
+ return JSON.stringify(key);
1515
+ }
1516
+
1517
+ /**
1518
+ * Create authorityKey to cache authority
1519
+ * @param state
1520
+ */
1521
+ generateAuthorityKey(stateString: string): string {
1522
+ const {
1523
+ libraryState: { id: stateId },
1524
+ } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
1525
+
1526
+ return this.generateCacheKey(
1527
+ `${TemporaryCacheKeys.AUTHORITY}.${stateId}`
1528
+ );
1529
+ }
1530
+
1531
+ /**
1532
+ * Create Nonce key to cache nonce
1533
+ * @param state
1534
+ */
1535
+ generateNonceKey(stateString: string): string {
1536
+ const {
1537
+ libraryState: { id: stateId },
1538
+ } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
1539
+
1540
+ return this.generateCacheKey(
1541
+ `${TemporaryCacheKeys.NONCE_IDTOKEN}.${stateId}`
1542
+ );
1543
+ }
1544
+
1545
+ /**
1546
+ * Creates full cache key for the request state
1547
+ * @param stateString State string for the request
1548
+ */
1549
+ generateStateKey(stateString: string): string {
1550
+ // Use the library state id to key temp storage for uniqueness for multiple concurrent requests
1551
+ const {
1552
+ libraryState: { id: stateId },
1553
+ } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
1554
+ return this.generateCacheKey(
1555
+ `${TemporaryCacheKeys.REQUEST_STATE}.${stateId}`
1556
+ );
1557
+ }
1558
+
1559
+ /**
1560
+ * Gets the cached authority based on the cached state. Returns empty if no cached state found.
1561
+ */
1562
+ getCachedAuthority(cachedState: string): string | null {
1563
+ const stateCacheKey = this.generateStateKey(cachedState);
1564
+ const state = this.getTemporaryCache(stateCacheKey);
1565
+ if (!state) {
1566
+ return null;
1567
+ }
1568
+
1569
+ const authorityCacheKey = this.generateAuthorityKey(state);
1570
+ return this.getTemporaryCache(authorityCacheKey);
1571
+ }
1572
+
1573
+ /**
1574
+ * Updates account, authority, and state in cache
1575
+ * @param serverAuthenticationRequest
1576
+ * @param account
1577
+ */
1578
+ updateCacheEntries(
1579
+ state: string,
1580
+ nonce: string,
1581
+ authorityInstance: string,
1582
+ loginHint: string,
1583
+ account: AccountInfo | null
1584
+ ): void {
1585
+ this.logger.trace("BrowserCacheManager.updateCacheEntries called");
1586
+ // Cache the request state
1587
+ const stateCacheKey = this.generateStateKey(state);
1588
+ this.setTemporaryCache(stateCacheKey, state, false);
1589
+
1590
+ // Cache the nonce
1591
+ const nonceCacheKey = this.generateNonceKey(state);
1592
+ this.setTemporaryCache(nonceCacheKey, nonce, false);
1593
+
1594
+ // Cache authorityKey
1595
+ const authorityCacheKey = this.generateAuthorityKey(state);
1596
+ this.setTemporaryCache(authorityCacheKey, authorityInstance, false);
1597
+
1598
+ if (account) {
1599
+ const ccsCredential: CcsCredential = {
1600
+ credential: account.homeAccountId,
1601
+ type: CcsCredentialType.HOME_ACCOUNT_ID,
1602
+ };
1603
+ this.setTemporaryCache(
1604
+ TemporaryCacheKeys.CCS_CREDENTIAL,
1605
+ JSON.stringify(ccsCredential),
1606
+ true
1607
+ );
1608
+ } else if (loginHint) {
1609
+ const ccsCredential: CcsCredential = {
1610
+ credential: loginHint,
1611
+ type: CcsCredentialType.UPN,
1612
+ };
1613
+ this.setTemporaryCache(
1614
+ TemporaryCacheKeys.CCS_CREDENTIAL,
1615
+ JSON.stringify(ccsCredential),
1616
+ true
1617
+ );
1618
+ }
1619
+ }
1620
+
1621
+ /**
1622
+ * Reset all temporary cache items
1623
+ * @param state
1624
+ */
1625
+ resetRequestCache(state: string): void {
1626
+ this.logger.trace("BrowserCacheManager.resetRequestCache called");
1627
+ // check state and remove associated cache items
1628
+ if (state) {
1629
+ this.getKeys().forEach((key) => {
1630
+ if (key.indexOf(state) !== -1) {
1631
+ this.removeItem(key);
1632
+ }
1633
+ });
1634
+
1635
+ // delete generic interactive request parameters
1636
+ this.removeItem(this.generateStateKey(state));
1637
+ this.removeItem(this.generateNonceKey(state));
1638
+ this.removeItem(this.generateAuthorityKey(state));
1639
+ }
1640
+ this.removeItem(
1641
+ this.generateCacheKey(TemporaryCacheKeys.REQUEST_PARAMS)
1642
+ );
1643
+ this.removeItem(this.generateCacheKey(TemporaryCacheKeys.ORIGIN_URI));
1644
+ this.removeItem(this.generateCacheKey(TemporaryCacheKeys.URL_HASH));
1645
+ this.removeItem(
1646
+ this.generateCacheKey(TemporaryCacheKeys.CORRELATION_ID)
1647
+ );
1648
+ this.removeItem(
1649
+ this.generateCacheKey(TemporaryCacheKeys.CCS_CREDENTIAL)
1650
+ );
1651
+ this.removeItem(
1652
+ this.generateCacheKey(TemporaryCacheKeys.NATIVE_REQUEST)
1653
+ );
1654
+ this.setInteractionInProgress(false);
1655
+ }
1656
+
1657
+ /**
1658
+ * Removes temporary cache for the provided state
1659
+ * @param stateString
1660
+ */
1661
+ cleanRequestByState(stateString: string): void {
1662
+ this.logger.trace("BrowserCacheManager.cleanRequestByState called");
1663
+ // Interaction is completed - remove interaction status.
1664
+ if (stateString) {
1665
+ const stateKey = this.generateStateKey(stateString);
1666
+ const cachedState = this.temporaryCacheStorage.getItem(stateKey);
1667
+ this.logger.infoPii(
1668
+ `BrowserCacheManager.cleanRequestByState: Removing temporary cache items for state: ${cachedState}`
1669
+ );
1670
+ this.resetRequestCache(cachedState || Constants.EMPTY_STRING);
1671
+ }
1672
+ this.clearMsalCookies();
1673
+ }
1674
+
1675
+ /**
1676
+ * Looks in temporary cache for any state values with the provided interactionType and removes all temporary cache items for that state
1677
+ * Used in scenarios where temp cache needs to be cleaned but state is not known, such as clicking browser back button.
1678
+ * @param interactionType
1679
+ */
1680
+ cleanRequestByInteractionType(interactionType: InteractionType): void {
1681
+ this.logger.trace(
1682
+ "BrowserCacheManager.cleanRequestByInteractionType called"
1683
+ );
1684
+ // Loop through all keys to find state key
1685
+ this.getKeys().forEach((key) => {
1686
+ // If this key is not the state key, move on
1687
+ if (key.indexOf(TemporaryCacheKeys.REQUEST_STATE) === -1) {
1688
+ return;
1689
+ }
1690
+
1691
+ // Retrieve state value, return if not a valid value
1692
+ const stateValue = this.temporaryCacheStorage.getItem(key);
1693
+ if (!stateValue) {
1694
+ return;
1695
+ }
1696
+ // Extract state and ensure it matches given InteractionType, then clean request cache
1697
+ const parsedState = BrowserProtocolUtils.extractBrowserRequestState(
1698
+ this.cryptoImpl,
1699
+ stateValue
1700
+ );
1701
+ if (
1702
+ parsedState &&
1703
+ parsedState.interactionType === interactionType
1704
+ ) {
1705
+ this.logger.infoPii(
1706
+ `BrowserCacheManager.cleanRequestByInteractionType: Removing temporary cache items for state: ${stateValue}`
1707
+ );
1708
+ this.resetRequestCache(stateValue);
1709
+ }
1710
+ });
1711
+ this.clearMsalCookies();
1712
+ this.setInteractionInProgress(false);
1713
+ }
1714
+
1715
+ cacheCodeRequest(authCodeRequest: CommonAuthorizationCodeRequest): void {
1716
+ this.logger.trace("BrowserCacheManager.cacheCodeRequest called");
1717
+
1718
+ const encodedValue = base64Encode(JSON.stringify(authCodeRequest));
1719
+ this.setTemporaryCache(
1720
+ TemporaryCacheKeys.REQUEST_PARAMS,
1721
+ encodedValue,
1722
+ true
1723
+ );
1724
+ }
1725
+
1726
+ /**
1727
+ * Gets the token exchange parameters from the cache. Throws an error if nothing is found.
1728
+ */
1729
+ getCachedRequest(state: string): CommonAuthorizationCodeRequest {
1730
+ this.logger.trace("BrowserCacheManager.getCachedRequest called");
1731
+ // Get token request from cache and parse as TokenExchangeParameters.
1732
+ const encodedTokenRequest = this.getTemporaryCache(
1733
+ TemporaryCacheKeys.REQUEST_PARAMS,
1734
+ true
1735
+ );
1736
+ if (!encodedTokenRequest) {
1737
+ throw createBrowserAuthError(
1738
+ BrowserAuthErrorCodes.noTokenRequestCacheError
1739
+ );
1740
+ }
1741
+
1742
+ let parsedRequest: CommonAuthorizationCodeRequest;
1743
+ try {
1744
+ parsedRequest = JSON.parse(base64Decode(encodedTokenRequest));
1745
+ } catch (e) {
1746
+ this.logger.errorPii(`Attempted to parse: ${encodedTokenRequest}`);
1747
+ this.logger.error(
1748
+ `Parsing cached token request threw with error: ${e}`
1749
+ );
1750
+ throw createBrowserAuthError(
1751
+ BrowserAuthErrorCodes.unableToParseTokenRequestCacheError
1752
+ );
1753
+ }
1754
+ this.removeItem(
1755
+ this.generateCacheKey(TemporaryCacheKeys.REQUEST_PARAMS)
1756
+ );
1757
+
1758
+ // Get cached authority and use if no authority is cached with request.
1759
+ if (!parsedRequest.authority) {
1760
+ const authorityCacheKey: string = this.generateAuthorityKey(state);
1761
+ const cachedAuthority = this.getTemporaryCache(authorityCacheKey);
1762
+ if (!cachedAuthority) {
1763
+ throw createBrowserAuthError(
1764
+ BrowserAuthErrorCodes.noCachedAuthorityError
1765
+ );
1766
+ }
1767
+ parsedRequest.authority = cachedAuthority;
1768
+ }
1769
+
1770
+ return parsedRequest;
1771
+ }
1772
+
1773
+ /**
1774
+ * Gets cached native request for redirect flows
1775
+ */
1776
+ getCachedNativeRequest(): NativeTokenRequest | null {
1777
+ this.logger.trace("BrowserCacheManager.getCachedNativeRequest called");
1778
+ const cachedRequest = this.getTemporaryCache(
1779
+ TemporaryCacheKeys.NATIVE_REQUEST,
1780
+ true
1781
+ );
1782
+ if (!cachedRequest) {
1783
+ this.logger.trace(
1784
+ "BrowserCacheManager.getCachedNativeRequest: No cached native request found"
1785
+ );
1786
+ return null;
1787
+ }
1788
+
1789
+ const parsedRequest = this.validateAndParseJson(
1790
+ cachedRequest
1791
+ ) as NativeTokenRequest;
1792
+ if (!parsedRequest) {
1793
+ this.logger.error(
1794
+ "BrowserCacheManager.getCachedNativeRequest: Unable to parse native request"
1795
+ );
1796
+ return null;
1797
+ }
1798
+
1799
+ return parsedRequest;
1800
+ }
1801
+
1802
+ isInteractionInProgress(matchClientId?: boolean): boolean {
1803
+ const clientId = this.getInteractionInProgress();
1804
+
1805
+ if (matchClientId) {
1806
+ return clientId === this.clientId;
1807
+ } else {
1808
+ return !!clientId;
1809
+ }
1810
+ }
1811
+
1812
+ getInteractionInProgress(): string | null {
1813
+ const key = `${Constants.CACHE_PREFIX}.${TemporaryCacheKeys.INTERACTION_STATUS_KEY}`;
1814
+ return this.getTemporaryCache(key, false);
1815
+ }
1816
+
1817
+ setInteractionInProgress(inProgress: boolean): void {
1818
+ // Ensure we don't overwrite interaction in progress for a different clientId
1819
+ const key = `${Constants.CACHE_PREFIX}.${TemporaryCacheKeys.INTERACTION_STATUS_KEY}`;
1820
+ if (inProgress) {
1821
+ if (this.getInteractionInProgress()) {
1822
+ throw createBrowserAuthError(
1823
+ BrowserAuthErrorCodes.interactionInProgress
1824
+ );
1825
+ } else {
1826
+ // No interaction is in progress
1827
+ this.setTemporaryCache(key, this.clientId, false);
1828
+ }
1829
+ } else if (
1830
+ !inProgress &&
1831
+ this.getInteractionInProgress() === this.clientId
1832
+ ) {
1833
+ this.removeItem(key);
1834
+ }
1835
+ }
1836
+
1837
+ /**
1838
+ * Returns username retrieved from ADAL or MSAL v1 idToken
1839
+ * @deprecated
1840
+ */
1841
+ getLegacyLoginHint(): string | null {
1842
+ // Only check for adal/msal token if no SSO params are being used
1843
+ const adalIdTokenString = this.getTemporaryCache(
1844
+ PersistentCacheKeys.ADAL_ID_TOKEN
1845
+ );
1846
+ if (adalIdTokenString) {
1847
+ this.browserStorage.removeItem(PersistentCacheKeys.ADAL_ID_TOKEN);
1848
+ this.logger.verbose("Cached ADAL id token retrieved.");
1849
+ }
1850
+
1851
+ // Check for cached MSAL v1 id token
1852
+ const msalIdTokenString = this.getTemporaryCache(
1853
+ PersistentCacheKeys.ID_TOKEN,
1854
+ true
1855
+ );
1856
+ if (msalIdTokenString) {
1857
+ this.removeItem(
1858
+ this.generateCacheKey(PersistentCacheKeys.ID_TOKEN)
1859
+ );
1860
+ this.logger.verbose("Cached MSAL.js v1 id token retrieved");
1861
+ }
1862
+
1863
+ const cachedIdTokenString = msalIdTokenString || adalIdTokenString;
1864
+ if (cachedIdTokenString) {
1865
+ const idTokenClaims = AuthToken.extractTokenClaims(
1866
+ cachedIdTokenString,
1867
+ base64Decode
1868
+ );
1869
+ if (idTokenClaims.preferred_username) {
1870
+ this.logger.verbose(
1871
+ "No SSO params used and ADAL/MSAL v1 token retrieved, setting ADAL/MSAL v1 preferred_username as loginHint"
1872
+ );
1873
+ return idTokenClaims.preferred_username;
1874
+ } else if (idTokenClaims.upn) {
1875
+ this.logger.verbose(
1876
+ "No SSO params used and ADAL/MSAL v1 token retrieved, setting ADAL/MSAL v1 upn as loginHint"
1877
+ );
1878
+ return idTokenClaims.upn;
1879
+ } else {
1880
+ this.logger.verbose(
1881
+ "No SSO params used and ADAL/MSAL v1 token retrieved, however, no account hint claim found. Enable preferred_username or upn id token claim to get SSO."
1882
+ );
1883
+ }
1884
+ }
1885
+
1886
+ return null;
1887
+ }
1888
+
1889
+ /**
1890
+ * Updates a credential's cache key if the current cache key is outdated
1891
+ */
1892
+ updateCredentialCacheKey(
1893
+ currentCacheKey: string,
1894
+ credential: ValidCredentialType
1895
+ ): string {
1896
+ const updatedCacheKey = credential.generateCredentialKey();
1897
+
1898
+ if (currentCacheKey !== updatedCacheKey) {
1899
+ const cacheItem = this.getItem(currentCacheKey);
1900
+ if (cacheItem) {
1901
+ this.removeItem(currentCacheKey);
1902
+ this.setItem(updatedCacheKey, cacheItem);
1903
+ this.logger.verbose(
1904
+ `Updated an outdated ${credential.credentialType} cache key`
1905
+ );
1906
+ return updatedCacheKey;
1907
+ } else {
1908
+ this.logger.error(
1909
+ `Attempted to update an outdated ${credential.credentialType} cache key but no item matching the outdated key was found in storage`
1910
+ );
1911
+ }
1912
+ }
1913
+
1914
+ return currentCacheKey;
1915
+ }
1916
+
1917
+ /**
1918
+ * Returns application id as redirect context during AcquireTokenRedirect flow.
1919
+ */
1920
+ getRedirectRequestContext(): string | null {
1921
+ return this.getTemporaryCache(
1922
+ TemporaryCacheKeys.REDIRECT_CONTEXT,
1923
+ true
1924
+ );
1925
+ }
1926
+
1927
+ /**
1928
+ * Sets application id as the redirect context during AcquireTokenRedirect flow.
1929
+ * @param value
1930
+ */
1931
+ setRedirectRequestContext(value: string): void {
1932
+ this.setTemporaryCache(
1933
+ TemporaryCacheKeys.REDIRECT_CONTEXT,
1934
+ value,
1935
+ true
1936
+ );
1937
+ }
1938
+
1939
+ /**
1940
+ * Builds credential entities from AuthenticationResult object and saves the resulting credentials to the cache
1941
+ * @param result
1942
+ * @param request
1943
+ */
1944
+ async hydrateCache(
1945
+ result: AuthenticationResult,
1946
+ request:
1947
+ | SilentRequest
1948
+ | SsoSilentRequest
1949
+ | RedirectRequest
1950
+ | PopupRequest
1951
+ ): Promise<void> {
1952
+ const idTokenEntity = IdTokenEntity.createIdTokenEntity(
1953
+ result.account?.homeAccountId,
1954
+ result.account?.environment,
1955
+ result.idToken,
1956
+ this.clientId,
1957
+ result.tenantId
1958
+ );
1959
+
1960
+ let claimsHash;
1961
+ if (request.claims) {
1962
+ claimsHash = await this.cryptoImpl.hashString(request.claims);
1963
+ }
1964
+ const accessTokenEntity = AccessTokenEntity.createAccessTokenEntity(
1965
+ result.account?.homeAccountId,
1966
+ result.account.environment,
1967
+ result.accessToken,
1968
+ this.clientId,
1969
+ result.tenantId,
1970
+ result.scopes.join(" "),
1971
+ result.expiresOn?.getTime() || 0,
1972
+ result.extExpiresOn?.getTime() || 0,
1973
+ this.cryptoImpl,
1974
+ undefined, // refreshOn
1975
+ result.tokenType as AuthenticationScheme,
1976
+ undefined, // userAssertionHash
1977
+ request.sshKid,
1978
+ request.claims,
1979
+ claimsHash
1980
+ );
1981
+
1982
+ const cacheRecord = new CacheRecord(
1983
+ undefined,
1984
+ idTokenEntity,
1985
+ accessTokenEntity
1986
+ );
1987
+ return this.saveCacheRecord(cacheRecord);
1988
+ }
1989
+ }
1990
+
1991
+ export const DEFAULT_BROWSER_CACHE_MANAGER = (
1992
+ clientId: string,
1993
+ logger: Logger
1994
+ ): BrowserCacheManager => {
1995
+ const cacheOptions: Required<CacheOptions> = {
1996
+ cacheLocation: BrowserCacheLocation.MemoryStorage,
1997
+ temporaryCacheLocation: BrowserCacheLocation.MemoryStorage,
1998
+ storeAuthStateInCookie: false,
1999
+ secureCookies: false,
2000
+ cacheMigrationEnabled: false,
2001
+ claimsBasedCachingEnabled: false,
2002
+ };
2003
+ return new BrowserCacheManager(
2004
+ clientId,
2005
+ cacheOptions,
2006
+ DEFAULT_CRYPTO_IMPLEMENTATION,
2007
+ logger
2008
+ );
2009
+ };