@azure/msal-browser 3.20.0 → 3.22.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.
- package/dist/app/IPublicClientApplication.d.ts +53 -53
- package/dist/app/IPublicClientApplication.mjs +105 -105
- package/dist/app/PublicClientApplication.d.ts +293 -293
- package/dist/app/PublicClientApplication.mjs +364 -364
- package/dist/app/PublicClientNext.d.ts +276 -276
- package/dist/app/PublicClientNext.mjs +354 -354
- package/dist/broker/nativeBroker/NativeMessageHandler.d.ts +62 -62
- package/dist/broker/nativeBroker/NativeMessageHandler.mjs +254 -254
- package/dist/broker/nativeBroker/NativeRequest.d.ts +45 -45
- package/dist/broker/nativeBroker/NativeResponse.d.ts +48 -48
- package/dist/broker/nativeBroker/NativeStatusCodes.d.ts +7 -7
- package/dist/broker/nativeBroker/NativeStatusCodes.mjs +11 -11
- package/dist/cache/AccountManager.d.ts +48 -48
- package/dist/cache/AccountManager.mjs +126 -126
- package/dist/cache/AsyncMemoryStorage.d.ts +50 -50
- package/dist/cache/AsyncMemoryStorage.mjs +131 -131
- package/dist/cache/BrowserCacheManager.d.ts +385 -385
- package/dist/cache/BrowserCacheManager.mjs +1294 -1294
- package/dist/cache/BrowserStorage.d.ts +11 -11
- package/dist/cache/BrowserStorage.mjs +32 -32
- package/dist/cache/DatabaseStorage.d.ts +56 -56
- package/dist/cache/DatabaseStorage.mjs +200 -200
- package/dist/cache/IAsyncStorage.d.ts +27 -27
- package/dist/cache/ITokenCache.d.ts +11 -11
- package/dist/cache/IWindowStorage.d.ts +27 -27
- package/dist/cache/MemoryStorage.d.ts +11 -11
- package/dist/cache/MemoryStorage.mjs +31 -31
- package/dist/cache/TokenCache.d.ts +77 -77
- package/dist/cache/TokenCache.mjs +195 -195
- package/dist/config/Configuration.d.ts +213 -208
- package/dist/config/Configuration.d.ts.map +1 -1
- package/dist/config/Configuration.mjs +128 -128
- package/dist/config/Configuration.mjs.map +1 -1
- package/dist/controllers/ControllerFactory.d.ts +5 -5
- package/dist/controllers/ControllerFactory.mjs +25 -25
- package/dist/controllers/IController.d.ts +60 -60
- package/dist/controllers/NestedAppAuthController.d.ts +202 -202
- package/dist/controllers/NestedAppAuthController.mjs +473 -473
- package/dist/controllers/StandardController.d.ts +405 -405
- package/dist/controllers/StandardController.d.ts.map +1 -1
- package/dist/controllers/StandardController.mjs +1334 -1316
- package/dist/controllers/StandardController.mjs.map +1 -1
- package/dist/controllers/UnknownOperatingContextController.d.ts +86 -86
- package/dist/controllers/UnknownOperatingContextController.mjs +256 -256
- package/dist/crypto/BrowserCrypto.d.ts +52 -52
- package/dist/crypto/BrowserCrypto.mjs +151 -151
- package/dist/crypto/CryptoOps.d.ts +74 -74
- package/dist/crypto/CryptoOps.mjs +176 -176
- package/dist/crypto/PkceGenerator.d.ts +8 -8
- package/dist/crypto/PkceGenerator.mjs +54 -54
- package/dist/crypto/SignedHttpRequest.d.ts +30 -30
- package/dist/crypto/SignedHttpRequest.mjs +39 -39
- package/dist/encode/Base64Decode.d.ts +9 -9
- package/dist/encode/Base64Decode.mjs +36 -36
- package/dist/encode/Base64Encode.d.ts +19 -19
- package/dist/encode/Base64Encode.mjs +43 -43
- package/dist/error/BrowserAuthError.d.ts +255 -255
- package/dist/error/BrowserAuthError.mjs +263 -263
- package/dist/error/BrowserAuthErrorCodes.d.ts +46 -46
- package/dist/error/BrowserAuthErrorCodes.mjs +50 -50
- package/dist/error/BrowserConfigurationAuthError.d.ts +33 -33
- package/dist/error/BrowserConfigurationAuthError.mjs +40 -40
- package/dist/error/BrowserConfigurationAuthError.mjs.map +1 -1
- package/dist/error/BrowserConfigurationAuthErrorCodes.d.ts +3 -3
- package/dist/error/BrowserConfigurationAuthErrorCodes.mjs +7 -7
- package/dist/error/NativeAuthError.d.ts +29 -29
- package/dist/error/NativeAuthError.mjs +60 -60
- package/dist/error/NativeAuthErrorCodes.d.ts +2 -2
- package/dist/error/NativeAuthErrorCodes.mjs +6 -6
- package/dist/error/NestedAppAuthError.d.ts +14 -14
- package/dist/error/NestedAppAuthError.mjs +23 -23
- package/dist/event/EventHandler.d.ts +41 -41
- package/dist/event/EventHandler.mjs +125 -125
- package/dist/event/EventMessage.d.ts +25 -25
- package/dist/event/EventMessage.mjs +68 -68
- package/dist/event/EventType.d.ts +29 -29
- package/dist/event/EventType.mjs +32 -32
- package/dist/index.d.ts +40 -40
- package/dist/index.mjs +1 -1
- package/dist/interaction_client/BaseInteractionClient.d.ts +50 -50
- package/dist/interaction_client/BaseInteractionClient.mjs +106 -106
- package/dist/interaction_client/HybridSpaAuthorizationCodeClient.d.ts +4 -4
- package/dist/interaction_client/HybridSpaAuthorizationCodeClient.mjs +10 -10
- package/dist/interaction_client/NativeInteractionClient.d.ts +151 -151
- package/dist/interaction_client/NativeInteractionClient.mjs +621 -621
- package/dist/interaction_client/PopupClient.d.ts +105 -105
- package/dist/interaction_client/PopupClient.d.ts.map +1 -1
- package/dist/interaction_client/PopupClient.mjs +433 -433
- package/dist/interaction_client/PopupClient.mjs.map +1 -1
- package/dist/interaction_client/RedirectClient.d.ts +50 -50
- package/dist/interaction_client/RedirectClient.d.ts.map +1 -1
- package/dist/interaction_client/RedirectClient.mjs +315 -314
- package/dist/interaction_client/RedirectClient.mjs.map +1 -1
- package/dist/interaction_client/SilentAuthCodeClient.d.ts +23 -23
- package/dist/interaction_client/SilentAuthCodeClient.mjs +57 -57
- package/dist/interaction_client/SilentCacheClient.d.ts +16 -16
- package/dist/interaction_client/SilentCacheClient.mjs +42 -42
- package/dist/interaction_client/SilentIframeClient.d.ts +32 -32
- package/dist/interaction_client/SilentIframeClient.d.ts.map +1 -1
- package/dist/interaction_client/SilentIframeClient.mjs +110 -100
- package/dist/interaction_client/SilentIframeClient.mjs.map +1 -1
- package/dist/interaction_client/SilentRefreshClient.d.ts +20 -20
- package/dist/interaction_client/SilentRefreshClient.mjs +47 -47
- package/dist/interaction_client/StandardInteractionClient.d.ts +48 -48
- package/dist/interaction_client/StandardInteractionClient.mjs +199 -199
- package/dist/interaction_handler/InteractionHandler.d.ts +33 -33
- package/dist/interaction_handler/InteractionHandler.mjs +93 -93
- package/dist/interaction_handler/RedirectHandler.d.ts +32 -32
- package/dist/interaction_handler/RedirectHandler.mjs +134 -134
- package/dist/interaction_handler/SilentHandler.d.ts +13 -13
- package/dist/interaction_handler/SilentHandler.mjs +134 -134
- package/dist/naa/AccountInfo.d.ts +11 -11
- package/dist/naa/AuthBridge.d.ts +8 -8
- package/dist/naa/AuthResult.d.ts +6 -6
- package/dist/naa/BridgeAccountContext.d.ts +12 -12
- package/dist/naa/BridgeCapabilities.d.ts +3 -3
- package/dist/naa/BridgeError.d.ts +9 -9
- package/dist/naa/BridgeError.mjs +7 -7
- package/dist/naa/BridgeProxy.d.ts +67 -67
- package/dist/naa/BridgeProxy.mjs +148 -148
- package/dist/naa/BridgeRequest.d.ts +7 -7
- package/dist/naa/BridgeRequestEnvelope.d.ts +12 -12
- package/dist/naa/BridgeResponseEnvelope.d.ts +13 -13
- package/dist/naa/BridgeStatusCode.d.ts +11 -11
- package/dist/naa/BridgeStatusCode.mjs +14 -14
- package/dist/naa/IBridgeProxy.d.ts +10 -10
- package/dist/naa/InitContext.d.ts +8 -8
- package/dist/naa/TokenRequest.d.ts +18 -18
- package/dist/naa/TokenResponse.d.ts +13 -13
- package/dist/naa/mapping/NestedAppAuthAdapter.d.ts +35 -35
- package/dist/naa/mapping/NestedAppAuthAdapter.mjs +179 -179
- package/dist/navigation/INavigationClient.d.ts +16 -16
- package/dist/navigation/NavigationClient.d.ts +22 -22
- package/dist/navigation/NavigationClient.mjs +40 -40
- package/dist/navigation/NavigationOptions.d.ts +12 -12
- package/dist/network/FetchClient.d.ts +26 -26
- package/dist/network/FetchClient.mjs +99 -99
- package/dist/operatingcontext/BaseOperatingContext.d.ts +41 -41
- package/dist/operatingcontext/BaseOperatingContext.mjs +94 -94
- package/dist/operatingcontext/NestedAppOperatingContext.d.ts +34 -34
- package/dist/operatingcontext/NestedAppOperatingContext.mjs +71 -71
- package/dist/operatingcontext/StandardOperatingContext.d.ts +25 -25
- package/dist/operatingcontext/StandardOperatingContext.mjs +43 -43
- package/dist/operatingcontext/UnknownOperatingContext.d.ts +25 -25
- package/dist/operatingcontext/UnknownOperatingContext.mjs +42 -42
- package/dist/packageMetadata.d.ts +2 -2
- package/dist/packageMetadata.mjs +4 -4
- package/dist/request/AuthorizationCodeRequest.d.ts +8 -8
- package/dist/request/AuthorizationUrlRequest.d.ts +8 -8
- package/dist/request/ClearCacheRequest.d.ts +10 -10
- package/dist/request/EndSessionPopupRequest.d.ts +18 -18
- package/dist/request/EndSessionRequest.d.ts +15 -15
- package/dist/request/InitializeApplicationRequest.d.ts +8 -8
- package/dist/request/PopupRequest.d.ts +34 -34
- package/dist/request/PopupWindowAttributes.d.ts +15 -15
- package/dist/request/RedirectRequest.d.ts +40 -35
- package/dist/request/RedirectRequest.d.ts.map +1 -1
- package/dist/request/RequestHelpers.d.ts +13 -13
- package/dist/request/RequestHelpers.mjs +53 -53
- package/dist/request/SilentRequest.d.ts +33 -33
- package/dist/request/SsoSilentRequest.d.ts +29 -29
- package/dist/response/AuthenticationResult.d.ts +4 -4
- package/dist/response/ResponseHandler.d.ts +7 -7
- package/dist/response/ResponseHandler.mjs +36 -36
- package/dist/telemetry/BrowserPerformanceClient.d.ts +34 -34
- package/dist/telemetry/BrowserPerformanceClient.mjs +162 -162
- package/dist/telemetry/BrowserPerformanceMeasurement.d.ts +21 -21
- package/dist/telemetry/BrowserPerformanceMeasurement.mjs +92 -92
- package/dist/utils/BrowserConstants.d.ts +180 -180
- package/dist/utils/BrowserConstants.mjs +212 -212
- package/dist/utils/BrowserProtocolUtils.d.ts +11 -11
- package/dist/utils/BrowserProtocolUtils.mjs +21 -21
- package/dist/utils/BrowserUtils.d.ts +75 -75
- package/dist/utils/BrowserUtils.mjs +157 -157
- package/lib/msal-browser.cjs +18884 -18855
- package/lib/msal-browser.cjs.map +1 -1
- package/lib/msal-browser.js +18884 -18855
- package/lib/msal-browser.js.map +1 -1
- package/lib/msal-browser.min.js +59 -59
- package/package.json +2 -2
- package/src/config/Configuration.ts +9 -1
- package/src/controllers/StandardController.ts +30 -12
- package/src/interaction_client/PopupClient.ts +1 -0
- package/src/interaction_client/RedirectClient.ts +4 -1
- package/src/interaction_client/SilentIframeClient.ts +40 -3
- package/src/packageMetadata.ts +1 -1
- package/src/request/RedirectRequest.ts +5 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @azure/msal-browser v3.
|
|
1
|
+
/*! @azure/msal-browser v3.22.0 2024-08-28 */
|
|
2
2
|
'use strict';
|
|
3
3
|
import { CacheManager, CredentialType, CacheHelpers, AccountEntity, createClientAuthError, ClientAuthErrorCodes, Constants, PersistentCacheKeys, PerformanceEvents, StringUtils, ProtocolUtils, AuthToken, CacheError, DEFAULT_CRYPTO_IMPLEMENTATION, CcsCredentialType } from '@azure/msal-common';
|
|
4
4
|
import { createBrowserAuthError } from '../error/BrowserAuthError.mjs';
|
|
@@ -10,1299 +10,1299 @@ import { base64Decode } from '../encode/Base64Decode.mjs';
|
|
|
10
10
|
import { base64Encode } from '../encode/Base64Encode.mjs';
|
|
11
11
|
import { noTokenRequestCacheError, unableToParseTokenRequestCacheError, noCachedAuthorityError, interactionInProgress } from '../error/BrowserAuthErrorCodes.mjs';
|
|
12
12
|
|
|
13
|
-
/*
|
|
14
|
-
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
15
|
-
* Licensed under the MIT License.
|
|
16
|
-
*/
|
|
17
|
-
/**
|
|
18
|
-
* This class implements the cache storage interface for MSAL through browser local or session storage.
|
|
19
|
-
* Cookies are only used if storeAuthStateInCookie is true, and are only used for
|
|
20
|
-
* parameters such as state and nonce, generally.
|
|
21
|
-
*/
|
|
22
|
-
class BrowserCacheManager extends CacheManager {
|
|
23
|
-
constructor(clientId, cacheConfig, cryptoImpl, logger, staticAuthorityOptions, performanceClient) {
|
|
24
|
-
super(clientId, cryptoImpl, logger, staticAuthorityOptions);
|
|
25
|
-
// Cookie life calculation (hours * minutes * seconds * ms)
|
|
26
|
-
this.COOKIE_LIFE_MULTIPLIER = 24 * 60 * 60 * 1000;
|
|
27
|
-
this.cacheConfig = cacheConfig;
|
|
28
|
-
this.logger = logger;
|
|
29
|
-
this.internalStorage = new MemoryStorage();
|
|
30
|
-
this.browserStorage = this.setupBrowserStorage(this.cacheConfig.cacheLocation);
|
|
31
|
-
this.temporaryCacheStorage = this.setupTemporaryCacheStorage(this.cacheConfig.temporaryCacheLocation, this.cacheConfig.cacheLocation);
|
|
32
|
-
// Migrate cache entries from older versions of MSAL.
|
|
33
|
-
if (cacheConfig.cacheMigrationEnabled) {
|
|
34
|
-
this.migrateCacheEntries();
|
|
35
|
-
this.createKeyMaps();
|
|
36
|
-
}
|
|
37
|
-
this.performanceClient = performanceClient;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Returns a window storage class implementing the IWindowStorage interface that corresponds to the configured cacheLocation.
|
|
41
|
-
* @param cacheLocation
|
|
42
|
-
*/
|
|
43
|
-
setupBrowserStorage(cacheLocation) {
|
|
44
|
-
switch (cacheLocation) {
|
|
45
|
-
case BrowserCacheLocation.LocalStorage:
|
|
46
|
-
case BrowserCacheLocation.SessionStorage:
|
|
47
|
-
try {
|
|
48
|
-
return new BrowserStorage(cacheLocation);
|
|
49
|
-
}
|
|
50
|
-
catch (e) {
|
|
51
|
-
this.logger.verbose(e);
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
this.cacheConfig.cacheLocation = BrowserCacheLocation.MemoryStorage;
|
|
56
|
-
return new MemoryStorage();
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Returns a window storage class implementing the IWindowStorage interface that corresponds to the configured temporaryCacheLocation.
|
|
60
|
-
* @param temporaryCacheLocation
|
|
61
|
-
* @param cacheLocation
|
|
62
|
-
*/
|
|
63
|
-
setupTemporaryCacheStorage(temporaryCacheLocation, cacheLocation) {
|
|
64
|
-
switch (cacheLocation) {
|
|
65
|
-
case BrowserCacheLocation.LocalStorage:
|
|
66
|
-
case BrowserCacheLocation.SessionStorage:
|
|
67
|
-
try {
|
|
68
|
-
// Temporary cache items will always be stored in session storage to mitigate problems caused by multiple tabs
|
|
69
|
-
return new BrowserStorage(temporaryCacheLocation ||
|
|
70
|
-
BrowserCacheLocation.SessionStorage);
|
|
71
|
-
}
|
|
72
|
-
catch (e) {
|
|
73
|
-
this.logger.verbose(e);
|
|
74
|
-
return this.internalStorage;
|
|
75
|
-
}
|
|
76
|
-
case BrowserCacheLocation.MemoryStorage:
|
|
77
|
-
default:
|
|
78
|
-
return this.internalStorage;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Migrate all old cache entries to new schema. No rollback supported.
|
|
83
|
-
* @param storeAuthStateInCookie
|
|
84
|
-
*/
|
|
85
|
-
migrateCacheEntries() {
|
|
86
|
-
const idTokenKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.ID_TOKEN}`;
|
|
87
|
-
const clientInfoKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.CLIENT_INFO}`;
|
|
88
|
-
const errorKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.ERROR}`;
|
|
89
|
-
const errorDescKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.ERROR_DESC}`;
|
|
90
|
-
const idTokenValue = this.browserStorage.getItem(idTokenKey);
|
|
91
|
-
const clientInfoValue = this.browserStorage.getItem(clientInfoKey);
|
|
92
|
-
const errorValue = this.browserStorage.getItem(errorKey);
|
|
93
|
-
const errorDescValue = this.browserStorage.getItem(errorDescKey);
|
|
94
|
-
const values = [
|
|
95
|
-
idTokenValue,
|
|
96
|
-
clientInfoValue,
|
|
97
|
-
errorValue,
|
|
98
|
-
errorDescValue,
|
|
99
|
-
];
|
|
100
|
-
const keysToMigrate = [
|
|
101
|
-
PersistentCacheKeys.ID_TOKEN,
|
|
102
|
-
PersistentCacheKeys.CLIENT_INFO,
|
|
103
|
-
PersistentCacheKeys.ERROR,
|
|
104
|
-
PersistentCacheKeys.ERROR_DESC,
|
|
105
|
-
];
|
|
106
|
-
keysToMigrate.forEach((cacheKey, index) => {
|
|
107
|
-
const value = values[index];
|
|
108
|
-
if (value) {
|
|
109
|
-
this.setTemporaryCache(cacheKey, value, true);
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Searches all cache entries for MSAL accounts and creates the account key map
|
|
115
|
-
* This is used to migrate users from older versions of MSAL which did not create the map.
|
|
116
|
-
* @returns
|
|
117
|
-
*/
|
|
118
|
-
createKeyMaps() {
|
|
119
|
-
this.logger.trace("BrowserCacheManager - createKeyMaps called.");
|
|
120
|
-
const accountKeys = this.getItem(StaticCacheKeys.ACCOUNT_KEYS);
|
|
121
|
-
const tokenKeys = this.getItem(`${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`);
|
|
122
|
-
if (accountKeys && tokenKeys) {
|
|
123
|
-
this.logger.verbose("BrowserCacheManager:createKeyMaps - account and token key maps already exist, skipping migration.");
|
|
124
|
-
// Key maps already exist, no need to iterate through cache
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
const allKeys = this.browserStorage.getKeys();
|
|
128
|
-
allKeys.forEach((key) => {
|
|
129
|
-
if (this.isCredentialKey(key)) {
|
|
130
|
-
// Get item, parse, validate and write key to map
|
|
131
|
-
const value = this.getItem(key);
|
|
132
|
-
if (value) {
|
|
133
|
-
const credObj = this.validateAndParseJson(value);
|
|
134
|
-
if (credObj && credObj.hasOwnProperty("credentialType")) {
|
|
135
|
-
switch (credObj["credentialType"]) {
|
|
136
|
-
case CredentialType.ID_TOKEN:
|
|
137
|
-
if (CacheHelpers.isIdTokenEntity(credObj)) {
|
|
138
|
-
this.logger.trace("BrowserCacheManager:createKeyMaps - idToken found, saving key to token key map");
|
|
139
|
-
this.logger.tracePii(`BrowserCacheManager:createKeyMaps - idToken with key: ${key} found, saving key to token key map`);
|
|
140
|
-
const idTokenEntity = credObj;
|
|
141
|
-
const newKey = this.updateCredentialCacheKey(key, idTokenEntity);
|
|
142
|
-
this.addTokenKey(newKey, CredentialType.ID_TOKEN);
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
146
|
-
this.logger.trace("BrowserCacheManager:createKeyMaps - key found matching idToken schema with value containing idToken credentialType field but value failed IdTokenEntity validation, skipping.");
|
|
147
|
-
this.logger.tracePii(`BrowserCacheManager:createKeyMaps - failed idToken validation on key: ${key}`);
|
|
148
|
-
}
|
|
149
|
-
break;
|
|
150
|
-
case CredentialType.ACCESS_TOKEN:
|
|
151
|
-
case CredentialType.ACCESS_TOKEN_WITH_AUTH_SCHEME:
|
|
152
|
-
if (CacheHelpers.isAccessTokenEntity(credObj)) {
|
|
153
|
-
this.logger.trace("BrowserCacheManager:createKeyMaps - accessToken found, saving key to token key map");
|
|
154
|
-
this.logger.tracePii(`BrowserCacheManager:createKeyMaps - accessToken with key: ${key} found, saving key to token key map`);
|
|
155
|
-
const accessTokenEntity = credObj;
|
|
156
|
-
const newKey = this.updateCredentialCacheKey(key, accessTokenEntity);
|
|
157
|
-
this.addTokenKey(newKey, CredentialType.ACCESS_TOKEN);
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
this.logger.trace("BrowserCacheManager:createKeyMaps - key found matching accessToken schema with value containing accessToken credentialType field but value failed AccessTokenEntity validation, skipping.");
|
|
162
|
-
this.logger.tracePii(`BrowserCacheManager:createKeyMaps - failed accessToken validation on key: ${key}`);
|
|
163
|
-
}
|
|
164
|
-
break;
|
|
165
|
-
case CredentialType.REFRESH_TOKEN:
|
|
166
|
-
if (CacheHelpers.isRefreshTokenEntity(credObj)) {
|
|
167
|
-
this.logger.trace("BrowserCacheManager:createKeyMaps - refreshToken found, saving key to token key map");
|
|
168
|
-
this.logger.tracePii(`BrowserCacheManager:createKeyMaps - refreshToken with key: ${key} found, saving key to token key map`);
|
|
169
|
-
const refreshTokenEntity = credObj;
|
|
170
|
-
const newKey = this.updateCredentialCacheKey(key, refreshTokenEntity);
|
|
171
|
-
this.addTokenKey(newKey, CredentialType.REFRESH_TOKEN);
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
this.logger.trace("BrowserCacheManager:createKeyMaps - key found matching refreshToken schema with value containing refreshToken credentialType field but value failed RefreshTokenEntity validation, skipping.");
|
|
176
|
-
this.logger.tracePii(`BrowserCacheManager:createKeyMaps - failed refreshToken validation on key: ${key}`);
|
|
177
|
-
}
|
|
178
|
-
break;
|
|
179
|
-
// If credentialType isn't one of our predefined ones, it may not be an MSAL cache value. Ignore.
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
if (this.isAccountKey(key)) {
|
|
185
|
-
const value = this.getItem(key);
|
|
186
|
-
if (value) {
|
|
187
|
-
const accountObj = this.validateAndParseJson(value);
|
|
188
|
-
if (accountObj &&
|
|
189
|
-
AccountEntity.isAccountEntity(accountObj)) {
|
|
190
|
-
this.logger.trace("BrowserCacheManager:createKeyMaps - account found, saving key to account key map");
|
|
191
|
-
this.logger.tracePii(`BrowserCacheManager:createKeyMaps - account with key: ${key} found, saving key to account key map`);
|
|
192
|
-
this.addAccountKeyToMap(key);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* Parses passed value as JSON object, JSON.parse() will throw an error.
|
|
200
|
-
* @param input
|
|
201
|
-
*/
|
|
202
|
-
validateAndParseJson(jsonValue) {
|
|
203
|
-
try {
|
|
204
|
-
const parsedJson = JSON.parse(jsonValue);
|
|
205
|
-
/**
|
|
206
|
-
* There are edge cases in which JSON.parse will successfully parse a non-valid JSON object
|
|
207
|
-
* (e.g. JSON.parse will parse an escaped string into an unescaped string), so adding a type check
|
|
208
|
-
* of the parsed value is necessary in order to be certain that the string represents a valid JSON object.
|
|
209
|
-
*
|
|
210
|
-
*/
|
|
211
|
-
return parsedJson && typeof parsedJson === "object"
|
|
212
|
-
? parsedJson
|
|
213
|
-
: null;
|
|
214
|
-
}
|
|
215
|
-
catch (error) {
|
|
216
|
-
return null;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* fetches the entry from the browser storage based off the key
|
|
221
|
-
* @param key
|
|
222
|
-
*/
|
|
223
|
-
getItem(key) {
|
|
224
|
-
return this.browserStorage.getItem(key);
|
|
225
|
-
}
|
|
226
|
-
/**
|
|
227
|
-
* sets the entry in the browser storage
|
|
228
|
-
* @param key
|
|
229
|
-
* @param value
|
|
230
|
-
*/
|
|
231
|
-
setItem(key, value) {
|
|
232
|
-
this.browserStorage.setItem(key, value);
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* fetch the account entity from the platform cache
|
|
236
|
-
* @param accountKey
|
|
237
|
-
*/
|
|
238
|
-
getAccount(accountKey, logger) {
|
|
239
|
-
this.logger.trace("BrowserCacheManager.getAccount called");
|
|
240
|
-
const accountEntity = this.getCachedAccountEntity(accountKey);
|
|
241
|
-
return this.updateOutdatedCachedAccount(accountKey, accountEntity, logger);
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Reads account from cache, deserializes it into an account entity and returns it.
|
|
245
|
-
* If account is not found from the key, returns null and removes key from map.
|
|
246
|
-
* @param accountKey
|
|
247
|
-
* @returns
|
|
248
|
-
*/
|
|
249
|
-
getCachedAccountEntity(accountKey) {
|
|
250
|
-
const serializedAccount = this.getItem(accountKey);
|
|
251
|
-
if (!serializedAccount) {
|
|
252
|
-
this.removeAccountKeyFromMap(accountKey);
|
|
253
|
-
return null;
|
|
254
|
-
}
|
|
255
|
-
const parsedAccount = this.validateAndParseJson(serializedAccount);
|
|
256
|
-
if (!parsedAccount || !AccountEntity.isAccountEntity(parsedAccount)) {
|
|
257
|
-
this.removeAccountKeyFromMap(accountKey);
|
|
258
|
-
return null;
|
|
259
|
-
}
|
|
260
|
-
return CacheManager.toObject(new AccountEntity(), parsedAccount);
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* set account entity in the platform cache
|
|
264
|
-
* @param account
|
|
265
|
-
*/
|
|
266
|
-
setAccount(account) {
|
|
267
|
-
this.logger.trace("BrowserCacheManager.setAccount called");
|
|
268
|
-
const key = account.generateAccountKey();
|
|
269
|
-
this.setItem(key, JSON.stringify(account));
|
|
270
|
-
this.addAccountKeyToMap(key);
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* Returns the array of account keys currently cached
|
|
274
|
-
* @returns
|
|
275
|
-
*/
|
|
276
|
-
getAccountKeys() {
|
|
277
|
-
this.logger.trace("BrowserCacheManager.getAccountKeys called");
|
|
278
|
-
const accountKeys = this.getItem(StaticCacheKeys.ACCOUNT_KEYS);
|
|
279
|
-
if (accountKeys) {
|
|
280
|
-
return JSON.parse(accountKeys);
|
|
281
|
-
}
|
|
282
|
-
this.logger.verbose("BrowserCacheManager.getAccountKeys - No account keys found");
|
|
283
|
-
return [];
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Add a new account to the key map
|
|
287
|
-
* @param key
|
|
288
|
-
*/
|
|
289
|
-
addAccountKeyToMap(key) {
|
|
290
|
-
this.logger.trace("BrowserCacheManager.addAccountKeyToMap called");
|
|
291
|
-
this.logger.tracePii(`BrowserCacheManager.addAccountKeyToMap called with key: ${key}`);
|
|
292
|
-
const accountKeys = this.getAccountKeys();
|
|
293
|
-
if (accountKeys.indexOf(key) === -1) {
|
|
294
|
-
// Only add key if it does not already exist in the map
|
|
295
|
-
accountKeys.push(key);
|
|
296
|
-
this.setItem(StaticCacheKeys.ACCOUNT_KEYS, JSON.stringify(accountKeys));
|
|
297
|
-
this.logger.verbose("BrowserCacheManager.addAccountKeyToMap account key added");
|
|
298
|
-
}
|
|
299
|
-
else {
|
|
300
|
-
this.logger.verbose("BrowserCacheManager.addAccountKeyToMap account key already exists in map");
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Remove an account from the key map
|
|
305
|
-
* @param key
|
|
306
|
-
*/
|
|
307
|
-
removeAccountKeyFromMap(key) {
|
|
308
|
-
this.logger.trace("BrowserCacheManager.removeAccountKeyFromMap called");
|
|
309
|
-
this.logger.tracePii(`BrowserCacheManager.removeAccountKeyFromMap called with key: ${key}`);
|
|
310
|
-
const accountKeys = this.getAccountKeys();
|
|
311
|
-
const removalIndex = accountKeys.indexOf(key);
|
|
312
|
-
if (removalIndex > -1) {
|
|
313
|
-
accountKeys.splice(removalIndex, 1);
|
|
314
|
-
this.setItem(StaticCacheKeys.ACCOUNT_KEYS, JSON.stringify(accountKeys));
|
|
315
|
-
this.logger.trace("BrowserCacheManager.removeAccountKeyFromMap account key removed");
|
|
316
|
-
}
|
|
317
|
-
else {
|
|
318
|
-
this.logger.trace("BrowserCacheManager.removeAccountKeyFromMap key not found in existing map");
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* Extends inherited removeAccount function to include removal of the account key from the map
|
|
323
|
-
* @param key
|
|
324
|
-
*/
|
|
325
|
-
async removeAccount(key) {
|
|
326
|
-
void super.removeAccount(key);
|
|
327
|
-
this.removeAccountKeyFromMap(key);
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* Remove account entity from the platform cache if it's outdated
|
|
331
|
-
* @param accountKey
|
|
332
|
-
*/
|
|
333
|
-
removeOutdatedAccount(accountKey) {
|
|
334
|
-
this.removeItem(accountKey);
|
|
335
|
-
this.removeAccountKeyFromMap(accountKey);
|
|
336
|
-
}
|
|
337
|
-
/**
|
|
338
|
-
* Removes given idToken from the cache and from the key map
|
|
339
|
-
* @param key
|
|
340
|
-
*/
|
|
341
|
-
removeIdToken(key) {
|
|
342
|
-
super.removeIdToken(key);
|
|
343
|
-
this.removeTokenKey(key, CredentialType.ID_TOKEN);
|
|
344
|
-
}
|
|
345
|
-
/**
|
|
346
|
-
* Removes given accessToken from the cache and from the key map
|
|
347
|
-
* @param key
|
|
348
|
-
*/
|
|
349
|
-
async removeAccessToken(key) {
|
|
350
|
-
void super.removeAccessToken(key);
|
|
351
|
-
this.removeTokenKey(key, CredentialType.ACCESS_TOKEN);
|
|
352
|
-
}
|
|
353
|
-
/**
|
|
354
|
-
* Removes given refreshToken from the cache and from the key map
|
|
355
|
-
* @param key
|
|
356
|
-
*/
|
|
357
|
-
removeRefreshToken(key) {
|
|
358
|
-
super.removeRefreshToken(key);
|
|
359
|
-
this.removeTokenKey(key, CredentialType.REFRESH_TOKEN);
|
|
360
|
-
}
|
|
361
|
-
/**
|
|
362
|
-
* Gets the keys for the cached tokens associated with this clientId
|
|
363
|
-
* @returns
|
|
364
|
-
*/
|
|
365
|
-
getTokenKeys() {
|
|
366
|
-
this.logger.trace("BrowserCacheManager.getTokenKeys called");
|
|
367
|
-
const item = this.getItem(`${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`);
|
|
368
|
-
if (item) {
|
|
369
|
-
const tokenKeys = this.validateAndParseJson(item);
|
|
370
|
-
if (tokenKeys &&
|
|
371
|
-
tokenKeys.hasOwnProperty("idToken") &&
|
|
372
|
-
tokenKeys.hasOwnProperty("accessToken") &&
|
|
373
|
-
tokenKeys.hasOwnProperty("refreshToken")) {
|
|
374
|
-
return tokenKeys;
|
|
375
|
-
}
|
|
376
|
-
else {
|
|
377
|
-
this.logger.error("BrowserCacheManager.getTokenKeys - Token keys found but in an unknown format. Returning empty key map.");
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
else {
|
|
381
|
-
this.logger.verbose("BrowserCacheManager.getTokenKeys - No token keys found");
|
|
382
|
-
}
|
|
383
|
-
return {
|
|
384
|
-
idToken: [],
|
|
385
|
-
accessToken: [],
|
|
386
|
-
refreshToken: [],
|
|
387
|
-
};
|
|
388
|
-
}
|
|
389
|
-
/**
|
|
390
|
-
* Adds the given key to the token key map
|
|
391
|
-
* @param key
|
|
392
|
-
* @param type
|
|
393
|
-
*/
|
|
394
|
-
addTokenKey(key, type) {
|
|
395
|
-
this.logger.trace("BrowserCacheManager addTokenKey called");
|
|
396
|
-
const tokenKeys = this.getTokenKeys();
|
|
397
|
-
switch (type) {
|
|
398
|
-
case CredentialType.ID_TOKEN:
|
|
399
|
-
if (tokenKeys.idToken.indexOf(key) === -1) {
|
|
400
|
-
this.logger.info("BrowserCacheManager: addTokenKey - idToken added to map");
|
|
401
|
-
tokenKeys.idToken.push(key);
|
|
402
|
-
}
|
|
403
|
-
break;
|
|
404
|
-
case CredentialType.ACCESS_TOKEN:
|
|
405
|
-
if (tokenKeys.accessToken.indexOf(key) === -1) {
|
|
406
|
-
this.logger.info("BrowserCacheManager: addTokenKey - accessToken added to map");
|
|
407
|
-
tokenKeys.accessToken.push(key);
|
|
408
|
-
}
|
|
409
|
-
break;
|
|
410
|
-
case CredentialType.REFRESH_TOKEN:
|
|
411
|
-
if (tokenKeys.refreshToken.indexOf(key) === -1) {
|
|
412
|
-
this.logger.info("BrowserCacheManager: addTokenKey - refreshToken added to map");
|
|
413
|
-
tokenKeys.refreshToken.push(key);
|
|
414
|
-
}
|
|
415
|
-
break;
|
|
416
|
-
default:
|
|
417
|
-
this.logger.error(`BrowserCacheManager:addTokenKey - CredentialType provided invalid. CredentialType: ${type}`);
|
|
418
|
-
throw createClientAuthError(ClientAuthErrorCodes.unexpectedCredentialType);
|
|
419
|
-
}
|
|
420
|
-
this.setItem(`${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`, JSON.stringify(tokenKeys));
|
|
421
|
-
}
|
|
422
|
-
/**
|
|
423
|
-
* Removes the given key from the token key map
|
|
424
|
-
* @param key
|
|
425
|
-
* @param type
|
|
426
|
-
*/
|
|
427
|
-
removeTokenKey(key, type) {
|
|
428
|
-
this.logger.trace("BrowserCacheManager removeTokenKey called");
|
|
429
|
-
const tokenKeys = this.getTokenKeys();
|
|
430
|
-
switch (type) {
|
|
431
|
-
case CredentialType.ID_TOKEN:
|
|
432
|
-
this.logger.infoPii(`BrowserCacheManager: removeTokenKey - attempting to remove idToken with key: ${key} from map`);
|
|
433
|
-
const idRemoval = tokenKeys.idToken.indexOf(key);
|
|
434
|
-
if (idRemoval > -1) {
|
|
435
|
-
this.logger.info("BrowserCacheManager: removeTokenKey - idToken removed from map");
|
|
436
|
-
tokenKeys.idToken.splice(idRemoval, 1);
|
|
437
|
-
}
|
|
438
|
-
else {
|
|
439
|
-
this.logger.info("BrowserCacheManager: removeTokenKey - idToken does not exist in map. Either it was previously removed or it was never added.");
|
|
440
|
-
}
|
|
441
|
-
break;
|
|
442
|
-
case CredentialType.ACCESS_TOKEN:
|
|
443
|
-
this.logger.infoPii(`BrowserCacheManager: removeTokenKey - attempting to remove accessToken with key: ${key} from map`);
|
|
444
|
-
const accessRemoval = tokenKeys.accessToken.indexOf(key);
|
|
445
|
-
if (accessRemoval > -1) {
|
|
446
|
-
this.logger.info("BrowserCacheManager: removeTokenKey - accessToken removed from map");
|
|
447
|
-
tokenKeys.accessToken.splice(accessRemoval, 1);
|
|
448
|
-
}
|
|
449
|
-
else {
|
|
450
|
-
this.logger.info("BrowserCacheManager: removeTokenKey - accessToken does not exist in map. Either it was previously removed or it was never added.");
|
|
451
|
-
}
|
|
452
|
-
break;
|
|
453
|
-
case CredentialType.REFRESH_TOKEN:
|
|
454
|
-
this.logger.infoPii(`BrowserCacheManager: removeTokenKey - attempting to remove refreshToken with key: ${key} from map`);
|
|
455
|
-
const refreshRemoval = tokenKeys.refreshToken.indexOf(key);
|
|
456
|
-
if (refreshRemoval > -1) {
|
|
457
|
-
this.logger.info("BrowserCacheManager: removeTokenKey - refreshToken removed from map");
|
|
458
|
-
tokenKeys.refreshToken.splice(refreshRemoval, 1);
|
|
459
|
-
}
|
|
460
|
-
else {
|
|
461
|
-
this.logger.info("BrowserCacheManager: removeTokenKey - refreshToken does not exist in map. Either it was previously removed or it was never added.");
|
|
462
|
-
}
|
|
463
|
-
break;
|
|
464
|
-
default:
|
|
465
|
-
this.logger.error(`BrowserCacheManager:removeTokenKey - CredentialType provided invalid. CredentialType: ${type}`);
|
|
466
|
-
throw createClientAuthError(ClientAuthErrorCodes.unexpectedCredentialType);
|
|
467
|
-
}
|
|
468
|
-
this.setItem(`${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`, JSON.stringify(tokenKeys));
|
|
469
|
-
}
|
|
470
|
-
/**
|
|
471
|
-
* generates idToken entity from a string
|
|
472
|
-
* @param idTokenKey
|
|
473
|
-
*/
|
|
474
|
-
getIdTokenCredential(idTokenKey) {
|
|
475
|
-
const value = this.getItem(idTokenKey);
|
|
476
|
-
if (!value) {
|
|
477
|
-
this.logger.trace("BrowserCacheManager.getIdTokenCredential: called, no cache hit");
|
|
478
|
-
this.removeTokenKey(idTokenKey, CredentialType.ID_TOKEN);
|
|
479
|
-
return null;
|
|
480
|
-
}
|
|
481
|
-
const parsedIdToken = this.validateAndParseJson(value);
|
|
482
|
-
if (!parsedIdToken || !CacheHelpers.isIdTokenEntity(parsedIdToken)) {
|
|
483
|
-
this.logger.trace("BrowserCacheManager.getIdTokenCredential: called, no cache hit");
|
|
484
|
-
this.removeTokenKey(idTokenKey, CredentialType.ID_TOKEN);
|
|
485
|
-
return null;
|
|
486
|
-
}
|
|
487
|
-
this.logger.trace("BrowserCacheManager.getIdTokenCredential: cache hit");
|
|
488
|
-
return parsedIdToken;
|
|
489
|
-
}
|
|
490
|
-
/**
|
|
491
|
-
* set IdToken credential to the platform cache
|
|
492
|
-
* @param idToken
|
|
493
|
-
*/
|
|
494
|
-
setIdTokenCredential(idToken) {
|
|
495
|
-
this.logger.trace("BrowserCacheManager.setIdTokenCredential called");
|
|
496
|
-
const idTokenKey = CacheHelpers.generateCredentialKey(idToken);
|
|
497
|
-
this.setItem(idTokenKey, JSON.stringify(idToken));
|
|
498
|
-
this.addTokenKey(idTokenKey, CredentialType.ID_TOKEN);
|
|
499
|
-
}
|
|
500
|
-
/**
|
|
501
|
-
* generates accessToken entity from a string
|
|
502
|
-
* @param key
|
|
503
|
-
*/
|
|
504
|
-
getAccessTokenCredential(accessTokenKey) {
|
|
505
|
-
const value = this.getItem(accessTokenKey);
|
|
506
|
-
if (!value) {
|
|
507
|
-
this.logger.trace("BrowserCacheManager.getAccessTokenCredential: called, no cache hit");
|
|
508
|
-
this.removeTokenKey(accessTokenKey, CredentialType.ACCESS_TOKEN);
|
|
509
|
-
return null;
|
|
510
|
-
}
|
|
511
|
-
const parsedAccessToken = this.validateAndParseJson(value);
|
|
512
|
-
if (!parsedAccessToken ||
|
|
513
|
-
!CacheHelpers.isAccessTokenEntity(parsedAccessToken)) {
|
|
514
|
-
this.logger.trace("BrowserCacheManager.getAccessTokenCredential: called, no cache hit");
|
|
515
|
-
this.removeTokenKey(accessTokenKey, CredentialType.ACCESS_TOKEN);
|
|
516
|
-
return null;
|
|
517
|
-
}
|
|
518
|
-
this.logger.trace("BrowserCacheManager.getAccessTokenCredential: cache hit");
|
|
519
|
-
return parsedAccessToken;
|
|
520
|
-
}
|
|
521
|
-
/**
|
|
522
|
-
* set accessToken credential to the platform cache
|
|
523
|
-
* @param accessToken
|
|
524
|
-
*/
|
|
525
|
-
setAccessTokenCredential(accessToken) {
|
|
526
|
-
this.logger.trace("BrowserCacheManager.setAccessTokenCredential called");
|
|
527
|
-
const accessTokenKey = CacheHelpers.generateCredentialKey(accessToken);
|
|
528
|
-
this.setItem(accessTokenKey, JSON.stringify(accessToken));
|
|
529
|
-
this.addTokenKey(accessTokenKey, CredentialType.ACCESS_TOKEN);
|
|
530
|
-
}
|
|
531
|
-
/**
|
|
532
|
-
* generates refreshToken entity from a string
|
|
533
|
-
* @param refreshTokenKey
|
|
534
|
-
*/
|
|
535
|
-
getRefreshTokenCredential(refreshTokenKey) {
|
|
536
|
-
const value = this.getItem(refreshTokenKey);
|
|
537
|
-
if (!value) {
|
|
538
|
-
this.logger.trace("BrowserCacheManager.getRefreshTokenCredential: called, no cache hit");
|
|
539
|
-
this.removeTokenKey(refreshTokenKey, CredentialType.REFRESH_TOKEN);
|
|
540
|
-
return null;
|
|
541
|
-
}
|
|
542
|
-
const parsedRefreshToken = this.validateAndParseJson(value);
|
|
543
|
-
if (!parsedRefreshToken ||
|
|
544
|
-
!CacheHelpers.isRefreshTokenEntity(parsedRefreshToken)) {
|
|
545
|
-
this.logger.trace("BrowserCacheManager.getRefreshTokenCredential: called, no cache hit");
|
|
546
|
-
this.removeTokenKey(refreshTokenKey, CredentialType.REFRESH_TOKEN);
|
|
547
|
-
return null;
|
|
548
|
-
}
|
|
549
|
-
this.logger.trace("BrowserCacheManager.getRefreshTokenCredential: cache hit");
|
|
550
|
-
return parsedRefreshToken;
|
|
551
|
-
}
|
|
552
|
-
/**
|
|
553
|
-
* set refreshToken credential to the platform cache
|
|
554
|
-
* @param refreshToken
|
|
555
|
-
*/
|
|
556
|
-
setRefreshTokenCredential(refreshToken) {
|
|
557
|
-
this.logger.trace("BrowserCacheManager.setRefreshTokenCredential called");
|
|
558
|
-
const refreshTokenKey = CacheHelpers.generateCredentialKey(refreshToken);
|
|
559
|
-
this.setItem(refreshTokenKey, JSON.stringify(refreshToken));
|
|
560
|
-
this.addTokenKey(refreshTokenKey, CredentialType.REFRESH_TOKEN);
|
|
561
|
-
}
|
|
562
|
-
/**
|
|
563
|
-
* fetch appMetadata entity from the platform cache
|
|
564
|
-
* @param appMetadataKey
|
|
565
|
-
*/
|
|
566
|
-
getAppMetadata(appMetadataKey) {
|
|
567
|
-
const value = this.getItem(appMetadataKey);
|
|
568
|
-
if (!value) {
|
|
569
|
-
this.logger.trace("BrowserCacheManager.getAppMetadata: called, no cache hit");
|
|
570
|
-
return null;
|
|
571
|
-
}
|
|
572
|
-
const parsedMetadata = this.validateAndParseJson(value);
|
|
573
|
-
if (!parsedMetadata ||
|
|
574
|
-
!CacheHelpers.isAppMetadataEntity(appMetadataKey, parsedMetadata)) {
|
|
575
|
-
this.logger.trace("BrowserCacheManager.getAppMetadata: called, no cache hit");
|
|
576
|
-
return null;
|
|
577
|
-
}
|
|
578
|
-
this.logger.trace("BrowserCacheManager.getAppMetadata: cache hit");
|
|
579
|
-
return parsedMetadata;
|
|
580
|
-
}
|
|
581
|
-
/**
|
|
582
|
-
* set appMetadata entity to the platform cache
|
|
583
|
-
* @param appMetadata
|
|
584
|
-
*/
|
|
585
|
-
setAppMetadata(appMetadata) {
|
|
586
|
-
this.logger.trace("BrowserCacheManager.setAppMetadata called");
|
|
587
|
-
const appMetadataKey = CacheHelpers.generateAppMetadataKey(appMetadata);
|
|
588
|
-
this.setItem(appMetadataKey, JSON.stringify(appMetadata));
|
|
589
|
-
}
|
|
590
|
-
/**
|
|
591
|
-
* fetch server telemetry entity from the platform cache
|
|
592
|
-
* @param serverTelemetryKey
|
|
593
|
-
*/
|
|
594
|
-
getServerTelemetry(serverTelemetryKey) {
|
|
595
|
-
const value = this.getItem(serverTelemetryKey);
|
|
596
|
-
if (!value) {
|
|
597
|
-
this.logger.trace("BrowserCacheManager.getServerTelemetry: called, no cache hit");
|
|
598
|
-
return null;
|
|
599
|
-
}
|
|
600
|
-
const parsedEntity = this.validateAndParseJson(value);
|
|
601
|
-
if (!parsedEntity ||
|
|
602
|
-
!CacheHelpers.isServerTelemetryEntity(serverTelemetryKey, parsedEntity)) {
|
|
603
|
-
this.logger.trace("BrowserCacheManager.getServerTelemetry: called, no cache hit");
|
|
604
|
-
return null;
|
|
605
|
-
}
|
|
606
|
-
this.logger.trace("BrowserCacheManager.getServerTelemetry: cache hit");
|
|
607
|
-
return parsedEntity;
|
|
608
|
-
}
|
|
609
|
-
/**
|
|
610
|
-
* set server telemetry entity to the platform cache
|
|
611
|
-
* @param serverTelemetryKey
|
|
612
|
-
* @param serverTelemetry
|
|
613
|
-
*/
|
|
614
|
-
setServerTelemetry(serverTelemetryKey, serverTelemetry) {
|
|
615
|
-
this.logger.trace("BrowserCacheManager.setServerTelemetry called");
|
|
616
|
-
this.setItem(serverTelemetryKey, JSON.stringify(serverTelemetry));
|
|
617
|
-
}
|
|
618
|
-
/**
|
|
619
|
-
*
|
|
620
|
-
*/
|
|
621
|
-
getAuthorityMetadata(key) {
|
|
622
|
-
const value = this.internalStorage.getItem(key);
|
|
623
|
-
if (!value) {
|
|
624
|
-
this.logger.trace("BrowserCacheManager.getAuthorityMetadata: called, no cache hit");
|
|
625
|
-
return null;
|
|
626
|
-
}
|
|
627
|
-
const parsedMetadata = this.validateAndParseJson(value);
|
|
628
|
-
if (parsedMetadata &&
|
|
629
|
-
CacheHelpers.isAuthorityMetadataEntity(key, parsedMetadata)) {
|
|
630
|
-
this.logger.trace("BrowserCacheManager.getAuthorityMetadata: cache hit");
|
|
631
|
-
return parsedMetadata;
|
|
632
|
-
}
|
|
633
|
-
return null;
|
|
634
|
-
}
|
|
635
|
-
/**
|
|
636
|
-
*
|
|
637
|
-
*/
|
|
638
|
-
getAuthorityMetadataKeys() {
|
|
639
|
-
const allKeys = this.internalStorage.getKeys();
|
|
640
|
-
return allKeys.filter((key) => {
|
|
641
|
-
return this.isAuthorityMetadata(key);
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
/**
|
|
645
|
-
* Sets wrapper metadata in memory
|
|
646
|
-
* @param wrapperSKU
|
|
647
|
-
* @param wrapperVersion
|
|
648
|
-
*/
|
|
649
|
-
setWrapperMetadata(wrapperSKU, wrapperVersion) {
|
|
650
|
-
this.internalStorage.setItem(InMemoryCacheKeys.WRAPPER_SKU, wrapperSKU);
|
|
651
|
-
this.internalStorage.setItem(InMemoryCacheKeys.WRAPPER_VER, wrapperVersion);
|
|
652
|
-
}
|
|
653
|
-
/**
|
|
654
|
-
* Returns wrapper metadata from in-memory storage
|
|
655
|
-
*/
|
|
656
|
-
getWrapperMetadata() {
|
|
657
|
-
const sku = this.internalStorage.getItem(InMemoryCacheKeys.WRAPPER_SKU) ||
|
|
658
|
-
Constants.EMPTY_STRING;
|
|
659
|
-
const version = this.internalStorage.getItem(InMemoryCacheKeys.WRAPPER_VER) ||
|
|
660
|
-
Constants.EMPTY_STRING;
|
|
661
|
-
return [sku, version];
|
|
662
|
-
}
|
|
663
|
-
/**
|
|
664
|
-
*
|
|
665
|
-
* @param entity
|
|
666
|
-
*/
|
|
667
|
-
setAuthorityMetadata(key, entity) {
|
|
668
|
-
this.logger.trace("BrowserCacheManager.setAuthorityMetadata called");
|
|
669
|
-
this.internalStorage.setItem(key, JSON.stringify(entity));
|
|
670
|
-
}
|
|
671
|
-
/**
|
|
672
|
-
* Gets the active account
|
|
673
|
-
*/
|
|
674
|
-
getActiveAccount() {
|
|
675
|
-
const activeAccountKeyFilters = this.generateCacheKey(PersistentCacheKeys.ACTIVE_ACCOUNT_FILTERS);
|
|
676
|
-
const activeAccountValueFilters = this.getItem(activeAccountKeyFilters);
|
|
677
|
-
if (!activeAccountValueFilters) {
|
|
678
|
-
// if new active account cache type isn't found, it's an old version, so look for that instead
|
|
679
|
-
this.logger.trace("BrowserCacheManager.getActiveAccount: No active account filters cache schema found, looking for legacy schema");
|
|
680
|
-
const activeAccountKeyLocal = this.generateCacheKey(PersistentCacheKeys.ACTIVE_ACCOUNT);
|
|
681
|
-
const activeAccountValueLocal = this.getItem(activeAccountKeyLocal);
|
|
682
|
-
if (!activeAccountValueLocal) {
|
|
683
|
-
this.logger.trace("BrowserCacheManager.getActiveAccount: No active account found");
|
|
684
|
-
return null;
|
|
685
|
-
}
|
|
686
|
-
const activeAccount = this.getAccountInfoFilteredBy({
|
|
687
|
-
localAccountId: activeAccountValueLocal,
|
|
688
|
-
});
|
|
689
|
-
if (activeAccount) {
|
|
690
|
-
this.logger.trace("BrowserCacheManager.getActiveAccount: Legacy active account cache schema found");
|
|
691
|
-
this.logger.trace("BrowserCacheManager.getActiveAccount: Adding active account filters cache schema");
|
|
692
|
-
this.setActiveAccount(activeAccount);
|
|
693
|
-
return activeAccount;
|
|
694
|
-
}
|
|
695
|
-
return null;
|
|
696
|
-
}
|
|
697
|
-
const activeAccountValueObj = this.validateAndParseJson(activeAccountValueFilters);
|
|
698
|
-
if (activeAccountValueObj) {
|
|
699
|
-
this.logger.trace("BrowserCacheManager.getActiveAccount: Active account filters schema found");
|
|
700
|
-
return this.getAccountInfoFilteredBy({
|
|
701
|
-
homeAccountId: activeAccountValueObj.homeAccountId,
|
|
702
|
-
localAccountId: activeAccountValueObj.localAccountId,
|
|
703
|
-
tenantId: activeAccountValueObj.tenantId,
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
this.logger.trace("BrowserCacheManager.getActiveAccount: No active account found");
|
|
707
|
-
return null;
|
|
708
|
-
}
|
|
709
|
-
/**
|
|
710
|
-
* Sets the active account's localAccountId in cache
|
|
711
|
-
* @param account
|
|
712
|
-
*/
|
|
713
|
-
setActiveAccount(account) {
|
|
714
|
-
const activeAccountKey = this.generateCacheKey(PersistentCacheKeys.ACTIVE_ACCOUNT_FILTERS);
|
|
715
|
-
const activeAccountKeyLocal = this.generateCacheKey(PersistentCacheKeys.ACTIVE_ACCOUNT);
|
|
716
|
-
if (account) {
|
|
717
|
-
this.logger.verbose("setActiveAccount: Active account set");
|
|
718
|
-
const activeAccountValue = {
|
|
719
|
-
homeAccountId: account.homeAccountId,
|
|
720
|
-
localAccountId: account.localAccountId,
|
|
721
|
-
tenantId: account.tenantId,
|
|
722
|
-
};
|
|
723
|
-
this.browserStorage.setItem(activeAccountKey, JSON.stringify(activeAccountValue));
|
|
724
|
-
this.browserStorage.setItem(activeAccountKeyLocal, account.localAccountId);
|
|
725
|
-
}
|
|
726
|
-
else {
|
|
727
|
-
this.logger.verbose("setActiveAccount: No account passed, active account not set");
|
|
728
|
-
this.browserStorage.removeItem(activeAccountKey);
|
|
729
|
-
this.browserStorage.removeItem(activeAccountKeyLocal);
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
/**
|
|
733
|
-
* fetch throttling entity from the platform cache
|
|
734
|
-
* @param throttlingCacheKey
|
|
735
|
-
*/
|
|
736
|
-
getThrottlingCache(throttlingCacheKey) {
|
|
737
|
-
const value = this.getItem(throttlingCacheKey);
|
|
738
|
-
if (!value) {
|
|
739
|
-
this.logger.trace("BrowserCacheManager.getThrottlingCache: called, no cache hit");
|
|
740
|
-
return null;
|
|
741
|
-
}
|
|
742
|
-
const parsedThrottlingCache = this.validateAndParseJson(value);
|
|
743
|
-
if (!parsedThrottlingCache ||
|
|
744
|
-
!CacheHelpers.isThrottlingEntity(throttlingCacheKey, parsedThrottlingCache)) {
|
|
745
|
-
this.logger.trace("BrowserCacheManager.getThrottlingCache: called, no cache hit");
|
|
746
|
-
return null;
|
|
747
|
-
}
|
|
748
|
-
this.logger.trace("BrowserCacheManager.getThrottlingCache: cache hit");
|
|
749
|
-
return parsedThrottlingCache;
|
|
750
|
-
}
|
|
751
|
-
/**
|
|
752
|
-
* set throttling entity to the platform cache
|
|
753
|
-
* @param throttlingCacheKey
|
|
754
|
-
* @param throttlingCache
|
|
755
|
-
*/
|
|
756
|
-
setThrottlingCache(throttlingCacheKey, throttlingCache) {
|
|
757
|
-
this.logger.trace("BrowserCacheManager.setThrottlingCache called");
|
|
758
|
-
this.setItem(throttlingCacheKey, JSON.stringify(throttlingCache));
|
|
759
|
-
}
|
|
760
|
-
/**
|
|
761
|
-
* Gets cache item with given key.
|
|
762
|
-
* Will retrieve from cookies if storeAuthStateInCookie is set to true.
|
|
763
|
-
* @param key
|
|
764
|
-
*/
|
|
765
|
-
getTemporaryCache(cacheKey, generateKey) {
|
|
766
|
-
const key = generateKey ? this.generateCacheKey(cacheKey) : cacheKey;
|
|
767
|
-
if (this.cacheConfig.storeAuthStateInCookie) {
|
|
768
|
-
const itemCookie = this.getItemCookie(key);
|
|
769
|
-
if (itemCookie) {
|
|
770
|
-
this.logger.trace("BrowserCacheManager.getTemporaryCache: storeAuthStateInCookies set to true, retrieving from cookies");
|
|
771
|
-
return itemCookie;
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
const value = this.temporaryCacheStorage.getItem(key);
|
|
775
|
-
if (!value) {
|
|
776
|
-
// If temp cache item not found in session/memory, check local storage for items set by old versions
|
|
777
|
-
if (this.cacheConfig.cacheLocation ===
|
|
778
|
-
BrowserCacheLocation.LocalStorage) {
|
|
779
|
-
const item = this.browserStorage.getItem(key);
|
|
780
|
-
if (item) {
|
|
781
|
-
this.logger.trace("BrowserCacheManager.getTemporaryCache: Temporary cache item found in local storage");
|
|
782
|
-
return item;
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
this.logger.trace("BrowserCacheManager.getTemporaryCache: No cache item found in local storage");
|
|
786
|
-
return null;
|
|
787
|
-
}
|
|
788
|
-
this.logger.trace("BrowserCacheManager.getTemporaryCache: Temporary cache item returned");
|
|
789
|
-
return value;
|
|
790
|
-
}
|
|
791
|
-
/**
|
|
792
|
-
* Sets the cache item with the key and value given.
|
|
793
|
-
* Stores in cookie if storeAuthStateInCookie is set to true.
|
|
794
|
-
* This can cause cookie overflow if used incorrectly.
|
|
795
|
-
* @param key
|
|
796
|
-
* @param value
|
|
797
|
-
*/
|
|
798
|
-
setTemporaryCache(cacheKey, value, generateKey) {
|
|
799
|
-
const key = generateKey ? this.generateCacheKey(cacheKey) : cacheKey;
|
|
800
|
-
this.temporaryCacheStorage.setItem(key, value);
|
|
801
|
-
if (this.cacheConfig.storeAuthStateInCookie) {
|
|
802
|
-
this.logger.trace("BrowserCacheManager.setTemporaryCache: storeAuthStateInCookie set to true, setting item cookie");
|
|
803
|
-
this.setItemCookie(key, value);
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
/**
|
|
807
|
-
* Removes the cache item with the given key.
|
|
808
|
-
* @param key
|
|
809
|
-
*/
|
|
810
|
-
removeItem(key) {
|
|
811
|
-
this.browserStorage.removeItem(key);
|
|
812
|
-
}
|
|
813
|
-
/**
|
|
814
|
-
* Removes the temporary cache item with the given key.
|
|
815
|
-
* Will also clear the cookie item if storeAuthStateInCookie is set to true.
|
|
816
|
-
* @param key
|
|
817
|
-
*/
|
|
818
|
-
removeTemporaryItem(key) {
|
|
819
|
-
this.temporaryCacheStorage.removeItem(key);
|
|
820
|
-
if (this.cacheConfig.storeAuthStateInCookie) {
|
|
821
|
-
this.logger.trace("BrowserCacheManager.removeItem: storeAuthStateInCookie is true, clearing item cookie");
|
|
822
|
-
this.clearItemCookie(key);
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
/**
|
|
826
|
-
* Gets all keys in window.
|
|
827
|
-
*/
|
|
828
|
-
getKeys() {
|
|
829
|
-
return this.browserStorage.getKeys();
|
|
830
|
-
}
|
|
831
|
-
/**
|
|
832
|
-
* Clears all cache entries created by MSAL.
|
|
833
|
-
*/
|
|
834
|
-
async clear() {
|
|
835
|
-
// Removes all accounts and their credentials
|
|
836
|
-
await this.removeAllAccounts();
|
|
837
|
-
this.removeAppMetadata();
|
|
838
|
-
// Remove temp storage first to make sure any cookies are cleared
|
|
839
|
-
this.temporaryCacheStorage.getKeys().forEach((cacheKey) => {
|
|
840
|
-
if (cacheKey.indexOf(Constants.CACHE_PREFIX) !== -1 ||
|
|
841
|
-
cacheKey.indexOf(this.clientId) !== -1) {
|
|
842
|
-
this.removeTemporaryItem(cacheKey);
|
|
843
|
-
}
|
|
844
|
-
});
|
|
845
|
-
// Removes all remaining MSAL cache items
|
|
846
|
-
this.browserStorage.getKeys().forEach((cacheKey) => {
|
|
847
|
-
if (cacheKey.indexOf(Constants.CACHE_PREFIX) !== -1 ||
|
|
848
|
-
cacheKey.indexOf(this.clientId) !== -1) {
|
|
849
|
-
this.browserStorage.removeItem(cacheKey);
|
|
850
|
-
}
|
|
851
|
-
});
|
|
852
|
-
this.internalStorage.clear();
|
|
853
|
-
}
|
|
854
|
-
/**
|
|
855
|
-
* Clears all access tokes that have claims prior to saving the current one
|
|
856
|
-
* @param performanceClient {IPerformanceClient}
|
|
857
|
-
* @param correlationId {string} correlation id
|
|
858
|
-
* @returns
|
|
859
|
-
*/
|
|
860
|
-
async clearTokensAndKeysWithClaims(performanceClient, correlationId) {
|
|
861
|
-
performanceClient.addQueueMeasurement(PerformanceEvents.ClearTokensAndKeysWithClaims, correlationId);
|
|
862
|
-
const tokenKeys = this.getTokenKeys();
|
|
863
|
-
const removedAccessTokens = [];
|
|
864
|
-
tokenKeys.accessToken.forEach((key) => {
|
|
865
|
-
// if the access token has claims in its key, remove the token key and the token
|
|
866
|
-
const credential = this.getAccessTokenCredential(key);
|
|
867
|
-
if (credential?.requestedClaimsHash &&
|
|
868
|
-
key.includes(credential.requestedClaimsHash.toLowerCase())) {
|
|
869
|
-
removedAccessTokens.push(this.removeAccessToken(key));
|
|
870
|
-
}
|
|
871
|
-
});
|
|
872
|
-
await Promise.all(removedAccessTokens);
|
|
873
|
-
// warn if any access tokens are removed
|
|
874
|
-
if (removedAccessTokens.length > 0) {
|
|
875
|
-
this.logger.warning(`${removedAccessTokens.length} access tokens with claims in the cache keys have been removed from the cache.`);
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
/**
|
|
879
|
-
* Add value to cookies
|
|
880
|
-
* @param cookieName
|
|
881
|
-
* @param cookieValue
|
|
882
|
-
* @param expires
|
|
883
|
-
* @deprecated
|
|
884
|
-
*/
|
|
885
|
-
setItemCookie(cookieName, cookieValue, expires) {
|
|
886
|
-
let cookieStr = `${encodeURIComponent(cookieName)}=${encodeURIComponent(cookieValue)};path=/;SameSite=Lax;`;
|
|
887
|
-
if (expires) {
|
|
888
|
-
const expireTime = this.getCookieExpirationTime(expires);
|
|
889
|
-
cookieStr += `expires=${expireTime};`;
|
|
890
|
-
}
|
|
891
|
-
if (this.cacheConfig.secureCookies) {
|
|
892
|
-
cookieStr += "Secure;";
|
|
893
|
-
}
|
|
894
|
-
document.cookie = cookieStr;
|
|
895
|
-
}
|
|
896
|
-
/**
|
|
897
|
-
* Get one item by key from cookies
|
|
898
|
-
* @param cookieName
|
|
899
|
-
* @deprecated
|
|
900
|
-
*/
|
|
901
|
-
getItemCookie(cookieName) {
|
|
902
|
-
const name = `${encodeURIComponent(cookieName)}=`;
|
|
903
|
-
const cookieList = document.cookie.split(";");
|
|
904
|
-
for (let i = 0; i < cookieList.length; i++) {
|
|
905
|
-
let cookie = cookieList[i];
|
|
906
|
-
while (cookie.charAt(0) === " ") {
|
|
907
|
-
cookie = cookie.substring(1);
|
|
908
|
-
}
|
|
909
|
-
if (cookie.indexOf(name) === 0) {
|
|
910
|
-
return decodeURIComponent(cookie.substring(name.length, cookie.length));
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
return Constants.EMPTY_STRING;
|
|
914
|
-
}
|
|
915
|
-
/**
|
|
916
|
-
* Clear all msal-related cookies currently set in the browser. Should only be used to clear temporary cache items.
|
|
917
|
-
* @deprecated
|
|
918
|
-
*/
|
|
919
|
-
clearMsalCookies() {
|
|
920
|
-
const cookiePrefix = `${Constants.CACHE_PREFIX}.${this.clientId}`;
|
|
921
|
-
const cookieList = document.cookie.split(";");
|
|
922
|
-
cookieList.forEach((cookie) => {
|
|
923
|
-
while (cookie.charAt(0) === " ") {
|
|
924
|
-
// eslint-disable-next-line no-param-reassign
|
|
925
|
-
cookie = cookie.substring(1);
|
|
926
|
-
}
|
|
927
|
-
if (cookie.indexOf(cookiePrefix) === 0) {
|
|
928
|
-
const cookieKey = cookie.split("=")[0];
|
|
929
|
-
this.clearItemCookie(cookieKey);
|
|
930
|
-
}
|
|
931
|
-
});
|
|
932
|
-
}
|
|
933
|
-
/**
|
|
934
|
-
* Clear an item in the cookies by key
|
|
935
|
-
* @param cookieName
|
|
936
|
-
* @deprecated
|
|
937
|
-
*/
|
|
938
|
-
clearItemCookie(cookieName) {
|
|
939
|
-
this.setItemCookie(cookieName, Constants.EMPTY_STRING, -1);
|
|
940
|
-
}
|
|
941
|
-
/**
|
|
942
|
-
* Get cookie expiration time
|
|
943
|
-
* @param cookieLifeDays
|
|
944
|
-
* @deprecated
|
|
945
|
-
*/
|
|
946
|
-
getCookieExpirationTime(cookieLifeDays) {
|
|
947
|
-
const today = new Date();
|
|
948
|
-
const expr = new Date(today.getTime() + cookieLifeDays * this.COOKIE_LIFE_MULTIPLIER);
|
|
949
|
-
return expr.toUTCString();
|
|
950
|
-
}
|
|
951
|
-
/**
|
|
952
|
-
* 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)
|
|
953
|
-
* @param key
|
|
954
|
-
* @param addInstanceId
|
|
955
|
-
*/
|
|
956
|
-
generateCacheKey(key) {
|
|
957
|
-
const generatedKey = this.validateAndParseJson(key);
|
|
958
|
-
if (!generatedKey) {
|
|
959
|
-
if (StringUtils.startsWith(key, Constants.CACHE_PREFIX) ||
|
|
960
|
-
StringUtils.startsWith(key, PersistentCacheKeys.ADAL_ID_TOKEN)) {
|
|
961
|
-
return key;
|
|
962
|
-
}
|
|
963
|
-
return `${Constants.CACHE_PREFIX}.${this.clientId}.${key}`;
|
|
964
|
-
}
|
|
965
|
-
return JSON.stringify(key);
|
|
966
|
-
}
|
|
967
|
-
/**
|
|
968
|
-
* Create authorityKey to cache authority
|
|
969
|
-
* @param state
|
|
970
|
-
*/
|
|
971
|
-
generateAuthorityKey(stateString) {
|
|
972
|
-
const { libraryState: { id: stateId }, } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
|
|
973
|
-
return this.generateCacheKey(`${TemporaryCacheKeys.AUTHORITY}.${stateId}`);
|
|
974
|
-
}
|
|
975
|
-
/**
|
|
976
|
-
* Create Nonce key to cache nonce
|
|
977
|
-
* @param state
|
|
978
|
-
*/
|
|
979
|
-
generateNonceKey(stateString) {
|
|
980
|
-
const { libraryState: { id: stateId }, } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
|
|
981
|
-
return this.generateCacheKey(`${TemporaryCacheKeys.NONCE_IDTOKEN}.${stateId}`);
|
|
982
|
-
}
|
|
983
|
-
/**
|
|
984
|
-
* Creates full cache key for the request state
|
|
985
|
-
* @param stateString State string for the request
|
|
986
|
-
*/
|
|
987
|
-
generateStateKey(stateString) {
|
|
988
|
-
// Use the library state id to key temp storage for uniqueness for multiple concurrent requests
|
|
989
|
-
const { libraryState: { id: stateId }, } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
|
|
990
|
-
return this.generateCacheKey(`${TemporaryCacheKeys.REQUEST_STATE}.${stateId}`);
|
|
991
|
-
}
|
|
992
|
-
/**
|
|
993
|
-
* Gets the cached authority based on the cached state. Returns empty if no cached state found.
|
|
994
|
-
*/
|
|
995
|
-
getCachedAuthority(cachedState) {
|
|
996
|
-
const stateCacheKey = this.generateStateKey(cachedState);
|
|
997
|
-
const state = this.getTemporaryCache(stateCacheKey);
|
|
998
|
-
if (!state) {
|
|
999
|
-
return null;
|
|
1000
|
-
}
|
|
1001
|
-
const authorityCacheKey = this.generateAuthorityKey(state);
|
|
1002
|
-
return this.getTemporaryCache(authorityCacheKey);
|
|
1003
|
-
}
|
|
1004
|
-
/**
|
|
1005
|
-
* Updates account, authority, and state in cache
|
|
1006
|
-
* @param serverAuthenticationRequest
|
|
1007
|
-
* @param account
|
|
1008
|
-
*/
|
|
1009
|
-
updateCacheEntries(state, nonce, authorityInstance, loginHint, account) {
|
|
1010
|
-
this.logger.trace("BrowserCacheManager.updateCacheEntries called");
|
|
1011
|
-
// Cache the request state
|
|
1012
|
-
const stateCacheKey = this.generateStateKey(state);
|
|
1013
|
-
this.setTemporaryCache(stateCacheKey, state, false);
|
|
1014
|
-
// Cache the nonce
|
|
1015
|
-
const nonceCacheKey = this.generateNonceKey(state);
|
|
1016
|
-
this.setTemporaryCache(nonceCacheKey, nonce, false);
|
|
1017
|
-
// Cache authorityKey
|
|
1018
|
-
const authorityCacheKey = this.generateAuthorityKey(state);
|
|
1019
|
-
this.setTemporaryCache(authorityCacheKey, authorityInstance, false);
|
|
1020
|
-
if (account) {
|
|
1021
|
-
const ccsCredential = {
|
|
1022
|
-
credential: account.homeAccountId,
|
|
1023
|
-
type: CcsCredentialType.HOME_ACCOUNT_ID,
|
|
1024
|
-
};
|
|
1025
|
-
this.setTemporaryCache(TemporaryCacheKeys.CCS_CREDENTIAL, JSON.stringify(ccsCredential), true);
|
|
1026
|
-
}
|
|
1027
|
-
else if (loginHint) {
|
|
1028
|
-
const ccsCredential = {
|
|
1029
|
-
credential: loginHint,
|
|
1030
|
-
type: CcsCredentialType.UPN,
|
|
1031
|
-
};
|
|
1032
|
-
this.setTemporaryCache(TemporaryCacheKeys.CCS_CREDENTIAL, JSON.stringify(ccsCredential), true);
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
/**
|
|
1036
|
-
* Reset all temporary cache items
|
|
1037
|
-
* @param state
|
|
1038
|
-
*/
|
|
1039
|
-
resetRequestCache(state) {
|
|
1040
|
-
this.logger.trace("BrowserCacheManager.resetRequestCache called");
|
|
1041
|
-
// check state and remove associated cache items
|
|
1042
|
-
if (state) {
|
|
1043
|
-
this.temporaryCacheStorage.getKeys().forEach((key) => {
|
|
1044
|
-
if (key.indexOf(state) !== -1) {
|
|
1045
|
-
this.removeTemporaryItem(key);
|
|
1046
|
-
}
|
|
1047
|
-
});
|
|
1048
|
-
// delete generic interactive request parameters
|
|
1049
|
-
this.removeTemporaryItem(this.generateStateKey(state));
|
|
1050
|
-
this.removeTemporaryItem(this.generateNonceKey(state));
|
|
1051
|
-
this.removeTemporaryItem(this.generateAuthorityKey(state));
|
|
1052
|
-
}
|
|
1053
|
-
this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.REQUEST_PARAMS));
|
|
1054
|
-
this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.ORIGIN_URI));
|
|
1055
|
-
this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.URL_HASH));
|
|
1056
|
-
this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.CORRELATION_ID));
|
|
1057
|
-
this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.CCS_CREDENTIAL));
|
|
1058
|
-
this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.NATIVE_REQUEST));
|
|
1059
|
-
this.setInteractionInProgress(false);
|
|
1060
|
-
}
|
|
1061
|
-
/**
|
|
1062
|
-
* Removes temporary cache for the provided state
|
|
1063
|
-
* @param stateString
|
|
1064
|
-
*/
|
|
1065
|
-
cleanRequestByState(stateString) {
|
|
1066
|
-
this.logger.trace("BrowserCacheManager.cleanRequestByState called");
|
|
1067
|
-
// Interaction is completed - remove interaction status.
|
|
1068
|
-
if (stateString) {
|
|
1069
|
-
const stateKey = this.generateStateKey(stateString);
|
|
1070
|
-
const cachedState = this.temporaryCacheStorage.getItem(stateKey);
|
|
1071
|
-
this.logger.infoPii(`BrowserCacheManager.cleanRequestByState: Removing temporary cache items for state: ${cachedState}`);
|
|
1072
|
-
this.resetRequestCache(cachedState || Constants.EMPTY_STRING);
|
|
1073
|
-
}
|
|
1074
|
-
this.clearMsalCookies();
|
|
1075
|
-
}
|
|
1076
|
-
/**
|
|
1077
|
-
* Looks in temporary cache for any state values with the provided interactionType and removes all temporary cache items for that state
|
|
1078
|
-
* Used in scenarios where temp cache needs to be cleaned but state is not known, such as clicking browser back button.
|
|
1079
|
-
* @param interactionType
|
|
1080
|
-
*/
|
|
1081
|
-
cleanRequestByInteractionType(interactionType) {
|
|
1082
|
-
this.logger.trace("BrowserCacheManager.cleanRequestByInteractionType called");
|
|
1083
|
-
// Loop through all keys to find state key
|
|
1084
|
-
this.temporaryCacheStorage.getKeys().forEach((key) => {
|
|
1085
|
-
// If this key is not the state key, move on
|
|
1086
|
-
if (key.indexOf(TemporaryCacheKeys.REQUEST_STATE) === -1) {
|
|
1087
|
-
return;
|
|
1088
|
-
}
|
|
1089
|
-
// Retrieve state value, return if not a valid value
|
|
1090
|
-
const stateValue = this.temporaryCacheStorage.getItem(key);
|
|
1091
|
-
if (!stateValue) {
|
|
1092
|
-
return;
|
|
1093
|
-
}
|
|
1094
|
-
// Extract state and ensure it matches given InteractionType, then clean request cache
|
|
1095
|
-
const parsedState = extractBrowserRequestState(this.cryptoImpl, stateValue);
|
|
1096
|
-
if (parsedState &&
|
|
1097
|
-
parsedState.interactionType === interactionType) {
|
|
1098
|
-
this.logger.infoPii(`BrowserCacheManager.cleanRequestByInteractionType: Removing temporary cache items for state: ${stateValue}`);
|
|
1099
|
-
this.resetRequestCache(stateValue);
|
|
1100
|
-
}
|
|
1101
|
-
});
|
|
1102
|
-
this.clearMsalCookies();
|
|
1103
|
-
this.setInteractionInProgress(false);
|
|
1104
|
-
}
|
|
1105
|
-
cacheCodeRequest(authCodeRequest) {
|
|
1106
|
-
this.logger.trace("BrowserCacheManager.cacheCodeRequest called");
|
|
1107
|
-
const encodedValue = base64Encode(JSON.stringify(authCodeRequest));
|
|
1108
|
-
this.setTemporaryCache(TemporaryCacheKeys.REQUEST_PARAMS, encodedValue, true);
|
|
1109
|
-
}
|
|
1110
|
-
/**
|
|
1111
|
-
* Gets the token exchange parameters from the cache. Throws an error if nothing is found.
|
|
1112
|
-
*/
|
|
1113
|
-
getCachedRequest(state) {
|
|
1114
|
-
this.logger.trace("BrowserCacheManager.getCachedRequest called");
|
|
1115
|
-
// Get token request from cache and parse as TokenExchangeParameters.
|
|
1116
|
-
const encodedTokenRequest = this.getTemporaryCache(TemporaryCacheKeys.REQUEST_PARAMS, true);
|
|
1117
|
-
if (!encodedTokenRequest) {
|
|
1118
|
-
throw createBrowserAuthError(noTokenRequestCacheError);
|
|
1119
|
-
}
|
|
1120
|
-
let parsedRequest;
|
|
1121
|
-
try {
|
|
1122
|
-
parsedRequest = JSON.parse(base64Decode(encodedTokenRequest));
|
|
1123
|
-
}
|
|
1124
|
-
catch (e) {
|
|
1125
|
-
this.logger.errorPii(`Attempted to parse: ${encodedTokenRequest}`);
|
|
1126
|
-
this.logger.error(`Parsing cached token request threw with error: ${e}`);
|
|
1127
|
-
throw createBrowserAuthError(unableToParseTokenRequestCacheError);
|
|
1128
|
-
}
|
|
1129
|
-
this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.REQUEST_PARAMS));
|
|
1130
|
-
// Get cached authority and use if no authority is cached with request.
|
|
1131
|
-
if (!parsedRequest.authority) {
|
|
1132
|
-
const authorityCacheKey = this.generateAuthorityKey(state);
|
|
1133
|
-
const cachedAuthority = this.getTemporaryCache(authorityCacheKey);
|
|
1134
|
-
if (!cachedAuthority) {
|
|
1135
|
-
throw createBrowserAuthError(noCachedAuthorityError);
|
|
1136
|
-
}
|
|
1137
|
-
parsedRequest.authority = cachedAuthority;
|
|
1138
|
-
}
|
|
1139
|
-
return parsedRequest;
|
|
1140
|
-
}
|
|
1141
|
-
/**
|
|
1142
|
-
* Gets cached native request for redirect flows
|
|
1143
|
-
*/
|
|
1144
|
-
getCachedNativeRequest() {
|
|
1145
|
-
this.logger.trace("BrowserCacheManager.getCachedNativeRequest called");
|
|
1146
|
-
const cachedRequest = this.getTemporaryCache(TemporaryCacheKeys.NATIVE_REQUEST, true);
|
|
1147
|
-
if (!cachedRequest) {
|
|
1148
|
-
this.logger.trace("BrowserCacheManager.getCachedNativeRequest: No cached native request found");
|
|
1149
|
-
return null;
|
|
1150
|
-
}
|
|
1151
|
-
const parsedRequest = this.validateAndParseJson(cachedRequest);
|
|
1152
|
-
if (!parsedRequest) {
|
|
1153
|
-
this.logger.error("BrowserCacheManager.getCachedNativeRequest: Unable to parse native request");
|
|
1154
|
-
return null;
|
|
1155
|
-
}
|
|
1156
|
-
return parsedRequest;
|
|
1157
|
-
}
|
|
1158
|
-
isInteractionInProgress(matchClientId) {
|
|
1159
|
-
const clientId = this.getInteractionInProgress();
|
|
1160
|
-
if (matchClientId) {
|
|
1161
|
-
return clientId === this.clientId;
|
|
1162
|
-
}
|
|
1163
|
-
else {
|
|
1164
|
-
return !!clientId;
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
getInteractionInProgress() {
|
|
1168
|
-
const key = `${Constants.CACHE_PREFIX}.${TemporaryCacheKeys.INTERACTION_STATUS_KEY}`;
|
|
1169
|
-
return this.getTemporaryCache(key, false);
|
|
1170
|
-
}
|
|
1171
|
-
setInteractionInProgress(inProgress) {
|
|
1172
|
-
// Ensure we don't overwrite interaction in progress for a different clientId
|
|
1173
|
-
const key = `${Constants.CACHE_PREFIX}.${TemporaryCacheKeys.INTERACTION_STATUS_KEY}`;
|
|
1174
|
-
if (inProgress) {
|
|
1175
|
-
if (this.getInteractionInProgress()) {
|
|
1176
|
-
throw createBrowserAuthError(interactionInProgress);
|
|
1177
|
-
}
|
|
1178
|
-
else {
|
|
1179
|
-
// No interaction is in progress
|
|
1180
|
-
this.setTemporaryCache(key, this.clientId, false);
|
|
1181
|
-
}
|
|
1182
|
-
}
|
|
1183
|
-
else if (!inProgress &&
|
|
1184
|
-
this.getInteractionInProgress() === this.clientId) {
|
|
1185
|
-
this.removeTemporaryItem(key);
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
/**
|
|
1189
|
-
* Returns username retrieved from ADAL or MSAL v1 idToken
|
|
1190
|
-
* @deprecated
|
|
1191
|
-
*/
|
|
1192
|
-
getLegacyLoginHint() {
|
|
1193
|
-
// Only check for adal/msal token if no SSO params are being used
|
|
1194
|
-
const adalIdTokenString = this.getTemporaryCache(PersistentCacheKeys.ADAL_ID_TOKEN);
|
|
1195
|
-
if (adalIdTokenString) {
|
|
1196
|
-
this.browserStorage.removeItem(PersistentCacheKeys.ADAL_ID_TOKEN);
|
|
1197
|
-
this.logger.verbose("Cached ADAL id token retrieved.");
|
|
1198
|
-
}
|
|
1199
|
-
// Check for cached MSAL v1 id token
|
|
1200
|
-
const msalIdTokenString = this.getTemporaryCache(PersistentCacheKeys.ID_TOKEN, true);
|
|
1201
|
-
if (msalIdTokenString) {
|
|
1202
|
-
this.browserStorage.removeItem(this.generateCacheKey(PersistentCacheKeys.ID_TOKEN));
|
|
1203
|
-
this.logger.verbose("Cached MSAL.js v1 id token retrieved");
|
|
1204
|
-
}
|
|
1205
|
-
const cachedIdTokenString = msalIdTokenString || adalIdTokenString;
|
|
1206
|
-
if (cachedIdTokenString) {
|
|
1207
|
-
const idTokenClaims = AuthToken.extractTokenClaims(cachedIdTokenString, base64Decode);
|
|
1208
|
-
if (idTokenClaims.preferred_username) {
|
|
1209
|
-
this.logger.verbose("No SSO params used and ADAL/MSAL v1 token retrieved, setting ADAL/MSAL v1 preferred_username as loginHint");
|
|
1210
|
-
return idTokenClaims.preferred_username;
|
|
1211
|
-
}
|
|
1212
|
-
else if (idTokenClaims.upn) {
|
|
1213
|
-
this.logger.verbose("No SSO params used and ADAL/MSAL v1 token retrieved, setting ADAL/MSAL v1 upn as loginHint");
|
|
1214
|
-
return idTokenClaims.upn;
|
|
1215
|
-
}
|
|
1216
|
-
else {
|
|
1217
|
-
this.logger.verbose("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.");
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
return null;
|
|
1221
|
-
}
|
|
1222
|
-
/**
|
|
1223
|
-
* Updates a credential's cache key if the current cache key is outdated
|
|
1224
|
-
*/
|
|
1225
|
-
updateCredentialCacheKey(currentCacheKey, credential) {
|
|
1226
|
-
const updatedCacheKey = CacheHelpers.generateCredentialKey(credential);
|
|
1227
|
-
if (currentCacheKey !== updatedCacheKey) {
|
|
1228
|
-
const cacheItem = this.getItem(currentCacheKey);
|
|
1229
|
-
if (cacheItem) {
|
|
1230
|
-
this.browserStorage.removeItem(currentCacheKey);
|
|
1231
|
-
this.setItem(updatedCacheKey, cacheItem);
|
|
1232
|
-
this.logger.verbose(`Updated an outdated ${credential.credentialType} cache key`);
|
|
1233
|
-
return updatedCacheKey;
|
|
1234
|
-
}
|
|
1235
|
-
else {
|
|
1236
|
-
this.logger.error(`Attempted to update an outdated ${credential.credentialType} cache key but no item matching the outdated key was found in storage`);
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
return currentCacheKey;
|
|
1240
|
-
}
|
|
1241
|
-
/**
|
|
1242
|
-
* Builds credential entities from AuthenticationResult object and saves the resulting credentials to the cache
|
|
1243
|
-
* @param result
|
|
1244
|
-
* @param request
|
|
1245
|
-
*/
|
|
1246
|
-
async hydrateCache(result, request) {
|
|
1247
|
-
const idTokenEntity = CacheHelpers.createIdTokenEntity(result.account?.homeAccountId, result.account?.environment, result.idToken, this.clientId, result.tenantId);
|
|
1248
|
-
let claimsHash;
|
|
1249
|
-
if (request.claims) {
|
|
1250
|
-
claimsHash = await this.cryptoImpl.hashString(request.claims);
|
|
1251
|
-
}
|
|
1252
|
-
/**
|
|
1253
|
-
* meta data for cache stores time in seconds from epoch
|
|
1254
|
-
* AuthenticationResult returns expiresOn and extExpiresOn in milliseconds (as a Date object which is in ms)
|
|
1255
|
-
* We need to map these for the cache when building tokens from AuthenticationResult
|
|
1256
|
-
*
|
|
1257
|
-
* The next MSAL VFuture should map these both to same value if possible
|
|
1258
|
-
*/
|
|
1259
|
-
const accessTokenEntity = CacheHelpers.createAccessTokenEntity(result.account?.homeAccountId, result.account.environment, result.accessToken, this.clientId, result.tenantId, result.scopes.join(" "), result.expiresOn ? result.expiresOn.getTime() / 1000 : 0, result.extExpiresOn ? result.extExpiresOn.getTime() / 1000 : 0, base64Decode, undefined, // refreshOn
|
|
1260
|
-
result.tokenType, undefined, // userAssertionHash
|
|
1261
|
-
request.sshKid, request.claims, claimsHash);
|
|
1262
|
-
const cacheRecord = {
|
|
1263
|
-
idToken: idTokenEntity,
|
|
1264
|
-
accessToken: accessTokenEntity,
|
|
1265
|
-
};
|
|
1266
|
-
return this.saveCacheRecord(cacheRecord);
|
|
1267
|
-
}
|
|
1268
|
-
/**
|
|
1269
|
-
* saves a cache record
|
|
1270
|
-
* @param cacheRecord {CacheRecord}
|
|
1271
|
-
* @param storeInCache {?StoreInCache}
|
|
1272
|
-
* @param correlationId {?string} correlation id
|
|
1273
|
-
*/
|
|
1274
|
-
async saveCacheRecord(cacheRecord, storeInCache, correlationId) {
|
|
1275
|
-
try {
|
|
1276
|
-
await super.saveCacheRecord(cacheRecord, storeInCache, correlationId);
|
|
1277
|
-
}
|
|
1278
|
-
catch (e) {
|
|
1279
|
-
if (e instanceof CacheError &&
|
|
1280
|
-
this.performanceClient &&
|
|
1281
|
-
correlationId) {
|
|
1282
|
-
try {
|
|
1283
|
-
const tokenKeys = this.getTokenKeys();
|
|
1284
|
-
this.performanceClient.addFields({
|
|
1285
|
-
cacheRtCount: tokenKeys.refreshToken.length,
|
|
1286
|
-
cacheIdCount: tokenKeys.idToken.length,
|
|
1287
|
-
cacheAtCount: tokenKeys.accessToken.length,
|
|
1288
|
-
}, correlationId);
|
|
1289
|
-
}
|
|
1290
|
-
catch (e) { }
|
|
1291
|
-
}
|
|
1292
|
-
throw e;
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
const DEFAULT_BROWSER_CACHE_MANAGER = (clientId, logger) => {
|
|
1297
|
-
const cacheOptions = {
|
|
1298
|
-
cacheLocation: BrowserCacheLocation.MemoryStorage,
|
|
1299
|
-
temporaryCacheLocation: BrowserCacheLocation.MemoryStorage,
|
|
1300
|
-
storeAuthStateInCookie: false,
|
|
1301
|
-
secureCookies: false,
|
|
1302
|
-
cacheMigrationEnabled: false,
|
|
1303
|
-
claimsBasedCachingEnabled: false,
|
|
1304
|
-
};
|
|
1305
|
-
return new BrowserCacheManager(clientId, cacheOptions, DEFAULT_CRYPTO_IMPLEMENTATION, logger);
|
|
13
|
+
/*
|
|
14
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
15
|
+
* Licensed under the MIT License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* This class implements the cache storage interface for MSAL through browser local or session storage.
|
|
19
|
+
* Cookies are only used if storeAuthStateInCookie is true, and are only used for
|
|
20
|
+
* parameters such as state and nonce, generally.
|
|
21
|
+
*/
|
|
22
|
+
class BrowserCacheManager extends CacheManager {
|
|
23
|
+
constructor(clientId, cacheConfig, cryptoImpl, logger, staticAuthorityOptions, performanceClient) {
|
|
24
|
+
super(clientId, cryptoImpl, logger, staticAuthorityOptions);
|
|
25
|
+
// Cookie life calculation (hours * minutes * seconds * ms)
|
|
26
|
+
this.COOKIE_LIFE_MULTIPLIER = 24 * 60 * 60 * 1000;
|
|
27
|
+
this.cacheConfig = cacheConfig;
|
|
28
|
+
this.logger = logger;
|
|
29
|
+
this.internalStorage = new MemoryStorage();
|
|
30
|
+
this.browserStorage = this.setupBrowserStorage(this.cacheConfig.cacheLocation);
|
|
31
|
+
this.temporaryCacheStorage = this.setupTemporaryCacheStorage(this.cacheConfig.temporaryCacheLocation, this.cacheConfig.cacheLocation);
|
|
32
|
+
// Migrate cache entries from older versions of MSAL.
|
|
33
|
+
if (cacheConfig.cacheMigrationEnabled) {
|
|
34
|
+
this.migrateCacheEntries();
|
|
35
|
+
this.createKeyMaps();
|
|
36
|
+
}
|
|
37
|
+
this.performanceClient = performanceClient;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Returns a window storage class implementing the IWindowStorage interface that corresponds to the configured cacheLocation.
|
|
41
|
+
* @param cacheLocation
|
|
42
|
+
*/
|
|
43
|
+
setupBrowserStorage(cacheLocation) {
|
|
44
|
+
switch (cacheLocation) {
|
|
45
|
+
case BrowserCacheLocation.LocalStorage:
|
|
46
|
+
case BrowserCacheLocation.SessionStorage:
|
|
47
|
+
try {
|
|
48
|
+
return new BrowserStorage(cacheLocation);
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
this.logger.verbose(e);
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
this.cacheConfig.cacheLocation = BrowserCacheLocation.MemoryStorage;
|
|
56
|
+
return new MemoryStorage();
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Returns a window storage class implementing the IWindowStorage interface that corresponds to the configured temporaryCacheLocation.
|
|
60
|
+
* @param temporaryCacheLocation
|
|
61
|
+
* @param cacheLocation
|
|
62
|
+
*/
|
|
63
|
+
setupTemporaryCacheStorage(temporaryCacheLocation, cacheLocation) {
|
|
64
|
+
switch (cacheLocation) {
|
|
65
|
+
case BrowserCacheLocation.LocalStorage:
|
|
66
|
+
case BrowserCacheLocation.SessionStorage:
|
|
67
|
+
try {
|
|
68
|
+
// Temporary cache items will always be stored in session storage to mitigate problems caused by multiple tabs
|
|
69
|
+
return new BrowserStorage(temporaryCacheLocation ||
|
|
70
|
+
BrowserCacheLocation.SessionStorage);
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
this.logger.verbose(e);
|
|
74
|
+
return this.internalStorage;
|
|
75
|
+
}
|
|
76
|
+
case BrowserCacheLocation.MemoryStorage:
|
|
77
|
+
default:
|
|
78
|
+
return this.internalStorage;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Migrate all old cache entries to new schema. No rollback supported.
|
|
83
|
+
* @param storeAuthStateInCookie
|
|
84
|
+
*/
|
|
85
|
+
migrateCacheEntries() {
|
|
86
|
+
const idTokenKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.ID_TOKEN}`;
|
|
87
|
+
const clientInfoKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.CLIENT_INFO}`;
|
|
88
|
+
const errorKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.ERROR}`;
|
|
89
|
+
const errorDescKey = `${Constants.CACHE_PREFIX}.${PersistentCacheKeys.ERROR_DESC}`;
|
|
90
|
+
const idTokenValue = this.browserStorage.getItem(idTokenKey);
|
|
91
|
+
const clientInfoValue = this.browserStorage.getItem(clientInfoKey);
|
|
92
|
+
const errorValue = this.browserStorage.getItem(errorKey);
|
|
93
|
+
const errorDescValue = this.browserStorage.getItem(errorDescKey);
|
|
94
|
+
const values = [
|
|
95
|
+
idTokenValue,
|
|
96
|
+
clientInfoValue,
|
|
97
|
+
errorValue,
|
|
98
|
+
errorDescValue,
|
|
99
|
+
];
|
|
100
|
+
const keysToMigrate = [
|
|
101
|
+
PersistentCacheKeys.ID_TOKEN,
|
|
102
|
+
PersistentCacheKeys.CLIENT_INFO,
|
|
103
|
+
PersistentCacheKeys.ERROR,
|
|
104
|
+
PersistentCacheKeys.ERROR_DESC,
|
|
105
|
+
];
|
|
106
|
+
keysToMigrate.forEach((cacheKey, index) => {
|
|
107
|
+
const value = values[index];
|
|
108
|
+
if (value) {
|
|
109
|
+
this.setTemporaryCache(cacheKey, value, true);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Searches all cache entries for MSAL accounts and creates the account key map
|
|
115
|
+
* This is used to migrate users from older versions of MSAL which did not create the map.
|
|
116
|
+
* @returns
|
|
117
|
+
*/
|
|
118
|
+
createKeyMaps() {
|
|
119
|
+
this.logger.trace("BrowserCacheManager - createKeyMaps called.");
|
|
120
|
+
const accountKeys = this.getItem(StaticCacheKeys.ACCOUNT_KEYS);
|
|
121
|
+
const tokenKeys = this.getItem(`${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`);
|
|
122
|
+
if (accountKeys && tokenKeys) {
|
|
123
|
+
this.logger.verbose("BrowserCacheManager:createKeyMaps - account and token key maps already exist, skipping migration.");
|
|
124
|
+
// Key maps already exist, no need to iterate through cache
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const allKeys = this.browserStorage.getKeys();
|
|
128
|
+
allKeys.forEach((key) => {
|
|
129
|
+
if (this.isCredentialKey(key)) {
|
|
130
|
+
// Get item, parse, validate and write key to map
|
|
131
|
+
const value = this.getItem(key);
|
|
132
|
+
if (value) {
|
|
133
|
+
const credObj = this.validateAndParseJson(value);
|
|
134
|
+
if (credObj && credObj.hasOwnProperty("credentialType")) {
|
|
135
|
+
switch (credObj["credentialType"]) {
|
|
136
|
+
case CredentialType.ID_TOKEN:
|
|
137
|
+
if (CacheHelpers.isIdTokenEntity(credObj)) {
|
|
138
|
+
this.logger.trace("BrowserCacheManager:createKeyMaps - idToken found, saving key to token key map");
|
|
139
|
+
this.logger.tracePii(`BrowserCacheManager:createKeyMaps - idToken with key: ${key} found, saving key to token key map`);
|
|
140
|
+
const idTokenEntity = credObj;
|
|
141
|
+
const newKey = this.updateCredentialCacheKey(key, idTokenEntity);
|
|
142
|
+
this.addTokenKey(newKey, CredentialType.ID_TOKEN);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
this.logger.trace("BrowserCacheManager:createKeyMaps - key found matching idToken schema with value containing idToken credentialType field but value failed IdTokenEntity validation, skipping.");
|
|
147
|
+
this.logger.tracePii(`BrowserCacheManager:createKeyMaps - failed idToken validation on key: ${key}`);
|
|
148
|
+
}
|
|
149
|
+
break;
|
|
150
|
+
case CredentialType.ACCESS_TOKEN:
|
|
151
|
+
case CredentialType.ACCESS_TOKEN_WITH_AUTH_SCHEME:
|
|
152
|
+
if (CacheHelpers.isAccessTokenEntity(credObj)) {
|
|
153
|
+
this.logger.trace("BrowserCacheManager:createKeyMaps - accessToken found, saving key to token key map");
|
|
154
|
+
this.logger.tracePii(`BrowserCacheManager:createKeyMaps - accessToken with key: ${key} found, saving key to token key map`);
|
|
155
|
+
const accessTokenEntity = credObj;
|
|
156
|
+
const newKey = this.updateCredentialCacheKey(key, accessTokenEntity);
|
|
157
|
+
this.addTokenKey(newKey, CredentialType.ACCESS_TOKEN);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
this.logger.trace("BrowserCacheManager:createKeyMaps - key found matching accessToken schema with value containing accessToken credentialType field but value failed AccessTokenEntity validation, skipping.");
|
|
162
|
+
this.logger.tracePii(`BrowserCacheManager:createKeyMaps - failed accessToken validation on key: ${key}`);
|
|
163
|
+
}
|
|
164
|
+
break;
|
|
165
|
+
case CredentialType.REFRESH_TOKEN:
|
|
166
|
+
if (CacheHelpers.isRefreshTokenEntity(credObj)) {
|
|
167
|
+
this.logger.trace("BrowserCacheManager:createKeyMaps - refreshToken found, saving key to token key map");
|
|
168
|
+
this.logger.tracePii(`BrowserCacheManager:createKeyMaps - refreshToken with key: ${key} found, saving key to token key map`);
|
|
169
|
+
const refreshTokenEntity = credObj;
|
|
170
|
+
const newKey = this.updateCredentialCacheKey(key, refreshTokenEntity);
|
|
171
|
+
this.addTokenKey(newKey, CredentialType.REFRESH_TOKEN);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
this.logger.trace("BrowserCacheManager:createKeyMaps - key found matching refreshToken schema with value containing refreshToken credentialType field but value failed RefreshTokenEntity validation, skipping.");
|
|
176
|
+
this.logger.tracePii(`BrowserCacheManager:createKeyMaps - failed refreshToken validation on key: ${key}`);
|
|
177
|
+
}
|
|
178
|
+
break;
|
|
179
|
+
// If credentialType isn't one of our predefined ones, it may not be an MSAL cache value. Ignore.
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (this.isAccountKey(key)) {
|
|
185
|
+
const value = this.getItem(key);
|
|
186
|
+
if (value) {
|
|
187
|
+
const accountObj = this.validateAndParseJson(value);
|
|
188
|
+
if (accountObj &&
|
|
189
|
+
AccountEntity.isAccountEntity(accountObj)) {
|
|
190
|
+
this.logger.trace("BrowserCacheManager:createKeyMaps - account found, saving key to account key map");
|
|
191
|
+
this.logger.tracePii(`BrowserCacheManager:createKeyMaps - account with key: ${key} found, saving key to account key map`);
|
|
192
|
+
this.addAccountKeyToMap(key);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Parses passed value as JSON object, JSON.parse() will throw an error.
|
|
200
|
+
* @param input
|
|
201
|
+
*/
|
|
202
|
+
validateAndParseJson(jsonValue) {
|
|
203
|
+
try {
|
|
204
|
+
const parsedJson = JSON.parse(jsonValue);
|
|
205
|
+
/**
|
|
206
|
+
* There are edge cases in which JSON.parse will successfully parse a non-valid JSON object
|
|
207
|
+
* (e.g. JSON.parse will parse an escaped string into an unescaped string), so adding a type check
|
|
208
|
+
* of the parsed value is necessary in order to be certain that the string represents a valid JSON object.
|
|
209
|
+
*
|
|
210
|
+
*/
|
|
211
|
+
return parsedJson && typeof parsedJson === "object"
|
|
212
|
+
? parsedJson
|
|
213
|
+
: null;
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* fetches the entry from the browser storage based off the key
|
|
221
|
+
* @param key
|
|
222
|
+
*/
|
|
223
|
+
getItem(key) {
|
|
224
|
+
return this.browserStorage.getItem(key);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* sets the entry in the browser storage
|
|
228
|
+
* @param key
|
|
229
|
+
* @param value
|
|
230
|
+
*/
|
|
231
|
+
setItem(key, value) {
|
|
232
|
+
this.browserStorage.setItem(key, value);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* fetch the account entity from the platform cache
|
|
236
|
+
* @param accountKey
|
|
237
|
+
*/
|
|
238
|
+
getAccount(accountKey, logger) {
|
|
239
|
+
this.logger.trace("BrowserCacheManager.getAccount called");
|
|
240
|
+
const accountEntity = this.getCachedAccountEntity(accountKey);
|
|
241
|
+
return this.updateOutdatedCachedAccount(accountKey, accountEntity, logger);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Reads account from cache, deserializes it into an account entity and returns it.
|
|
245
|
+
* If account is not found from the key, returns null and removes key from map.
|
|
246
|
+
* @param accountKey
|
|
247
|
+
* @returns
|
|
248
|
+
*/
|
|
249
|
+
getCachedAccountEntity(accountKey) {
|
|
250
|
+
const serializedAccount = this.getItem(accountKey);
|
|
251
|
+
if (!serializedAccount) {
|
|
252
|
+
this.removeAccountKeyFromMap(accountKey);
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
const parsedAccount = this.validateAndParseJson(serializedAccount);
|
|
256
|
+
if (!parsedAccount || !AccountEntity.isAccountEntity(parsedAccount)) {
|
|
257
|
+
this.removeAccountKeyFromMap(accountKey);
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
return CacheManager.toObject(new AccountEntity(), parsedAccount);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* set account entity in the platform cache
|
|
264
|
+
* @param account
|
|
265
|
+
*/
|
|
266
|
+
setAccount(account) {
|
|
267
|
+
this.logger.trace("BrowserCacheManager.setAccount called");
|
|
268
|
+
const key = account.generateAccountKey();
|
|
269
|
+
this.setItem(key, JSON.stringify(account));
|
|
270
|
+
this.addAccountKeyToMap(key);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Returns the array of account keys currently cached
|
|
274
|
+
* @returns
|
|
275
|
+
*/
|
|
276
|
+
getAccountKeys() {
|
|
277
|
+
this.logger.trace("BrowserCacheManager.getAccountKeys called");
|
|
278
|
+
const accountKeys = this.getItem(StaticCacheKeys.ACCOUNT_KEYS);
|
|
279
|
+
if (accountKeys) {
|
|
280
|
+
return JSON.parse(accountKeys);
|
|
281
|
+
}
|
|
282
|
+
this.logger.verbose("BrowserCacheManager.getAccountKeys - No account keys found");
|
|
283
|
+
return [];
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Add a new account to the key map
|
|
287
|
+
* @param key
|
|
288
|
+
*/
|
|
289
|
+
addAccountKeyToMap(key) {
|
|
290
|
+
this.logger.trace("BrowserCacheManager.addAccountKeyToMap called");
|
|
291
|
+
this.logger.tracePii(`BrowserCacheManager.addAccountKeyToMap called with key: ${key}`);
|
|
292
|
+
const accountKeys = this.getAccountKeys();
|
|
293
|
+
if (accountKeys.indexOf(key) === -1) {
|
|
294
|
+
// Only add key if it does not already exist in the map
|
|
295
|
+
accountKeys.push(key);
|
|
296
|
+
this.setItem(StaticCacheKeys.ACCOUNT_KEYS, JSON.stringify(accountKeys));
|
|
297
|
+
this.logger.verbose("BrowserCacheManager.addAccountKeyToMap account key added");
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
this.logger.verbose("BrowserCacheManager.addAccountKeyToMap account key already exists in map");
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Remove an account from the key map
|
|
305
|
+
* @param key
|
|
306
|
+
*/
|
|
307
|
+
removeAccountKeyFromMap(key) {
|
|
308
|
+
this.logger.trace("BrowserCacheManager.removeAccountKeyFromMap called");
|
|
309
|
+
this.logger.tracePii(`BrowserCacheManager.removeAccountKeyFromMap called with key: ${key}`);
|
|
310
|
+
const accountKeys = this.getAccountKeys();
|
|
311
|
+
const removalIndex = accountKeys.indexOf(key);
|
|
312
|
+
if (removalIndex > -1) {
|
|
313
|
+
accountKeys.splice(removalIndex, 1);
|
|
314
|
+
this.setItem(StaticCacheKeys.ACCOUNT_KEYS, JSON.stringify(accountKeys));
|
|
315
|
+
this.logger.trace("BrowserCacheManager.removeAccountKeyFromMap account key removed");
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
this.logger.trace("BrowserCacheManager.removeAccountKeyFromMap key not found in existing map");
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Extends inherited removeAccount function to include removal of the account key from the map
|
|
323
|
+
* @param key
|
|
324
|
+
*/
|
|
325
|
+
async removeAccount(key) {
|
|
326
|
+
void super.removeAccount(key);
|
|
327
|
+
this.removeAccountKeyFromMap(key);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Remove account entity from the platform cache if it's outdated
|
|
331
|
+
* @param accountKey
|
|
332
|
+
*/
|
|
333
|
+
removeOutdatedAccount(accountKey) {
|
|
334
|
+
this.removeItem(accountKey);
|
|
335
|
+
this.removeAccountKeyFromMap(accountKey);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Removes given idToken from the cache and from the key map
|
|
339
|
+
* @param key
|
|
340
|
+
*/
|
|
341
|
+
removeIdToken(key) {
|
|
342
|
+
super.removeIdToken(key);
|
|
343
|
+
this.removeTokenKey(key, CredentialType.ID_TOKEN);
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Removes given accessToken from the cache and from the key map
|
|
347
|
+
* @param key
|
|
348
|
+
*/
|
|
349
|
+
async removeAccessToken(key) {
|
|
350
|
+
void super.removeAccessToken(key);
|
|
351
|
+
this.removeTokenKey(key, CredentialType.ACCESS_TOKEN);
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Removes given refreshToken from the cache and from the key map
|
|
355
|
+
* @param key
|
|
356
|
+
*/
|
|
357
|
+
removeRefreshToken(key) {
|
|
358
|
+
super.removeRefreshToken(key);
|
|
359
|
+
this.removeTokenKey(key, CredentialType.REFRESH_TOKEN);
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Gets the keys for the cached tokens associated with this clientId
|
|
363
|
+
* @returns
|
|
364
|
+
*/
|
|
365
|
+
getTokenKeys() {
|
|
366
|
+
this.logger.trace("BrowserCacheManager.getTokenKeys called");
|
|
367
|
+
const item = this.getItem(`${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`);
|
|
368
|
+
if (item) {
|
|
369
|
+
const tokenKeys = this.validateAndParseJson(item);
|
|
370
|
+
if (tokenKeys &&
|
|
371
|
+
tokenKeys.hasOwnProperty("idToken") &&
|
|
372
|
+
tokenKeys.hasOwnProperty("accessToken") &&
|
|
373
|
+
tokenKeys.hasOwnProperty("refreshToken")) {
|
|
374
|
+
return tokenKeys;
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
this.logger.error("BrowserCacheManager.getTokenKeys - Token keys found but in an unknown format. Returning empty key map.");
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
this.logger.verbose("BrowserCacheManager.getTokenKeys - No token keys found");
|
|
382
|
+
}
|
|
383
|
+
return {
|
|
384
|
+
idToken: [],
|
|
385
|
+
accessToken: [],
|
|
386
|
+
refreshToken: [],
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Adds the given key to the token key map
|
|
391
|
+
* @param key
|
|
392
|
+
* @param type
|
|
393
|
+
*/
|
|
394
|
+
addTokenKey(key, type) {
|
|
395
|
+
this.logger.trace("BrowserCacheManager addTokenKey called");
|
|
396
|
+
const tokenKeys = this.getTokenKeys();
|
|
397
|
+
switch (type) {
|
|
398
|
+
case CredentialType.ID_TOKEN:
|
|
399
|
+
if (tokenKeys.idToken.indexOf(key) === -1) {
|
|
400
|
+
this.logger.info("BrowserCacheManager: addTokenKey - idToken added to map");
|
|
401
|
+
tokenKeys.idToken.push(key);
|
|
402
|
+
}
|
|
403
|
+
break;
|
|
404
|
+
case CredentialType.ACCESS_TOKEN:
|
|
405
|
+
if (tokenKeys.accessToken.indexOf(key) === -1) {
|
|
406
|
+
this.logger.info("BrowserCacheManager: addTokenKey - accessToken added to map");
|
|
407
|
+
tokenKeys.accessToken.push(key);
|
|
408
|
+
}
|
|
409
|
+
break;
|
|
410
|
+
case CredentialType.REFRESH_TOKEN:
|
|
411
|
+
if (tokenKeys.refreshToken.indexOf(key) === -1) {
|
|
412
|
+
this.logger.info("BrowserCacheManager: addTokenKey - refreshToken added to map");
|
|
413
|
+
tokenKeys.refreshToken.push(key);
|
|
414
|
+
}
|
|
415
|
+
break;
|
|
416
|
+
default:
|
|
417
|
+
this.logger.error(`BrowserCacheManager:addTokenKey - CredentialType provided invalid. CredentialType: ${type}`);
|
|
418
|
+
throw createClientAuthError(ClientAuthErrorCodes.unexpectedCredentialType);
|
|
419
|
+
}
|
|
420
|
+
this.setItem(`${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`, JSON.stringify(tokenKeys));
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Removes the given key from the token key map
|
|
424
|
+
* @param key
|
|
425
|
+
* @param type
|
|
426
|
+
*/
|
|
427
|
+
removeTokenKey(key, type) {
|
|
428
|
+
this.logger.trace("BrowserCacheManager removeTokenKey called");
|
|
429
|
+
const tokenKeys = this.getTokenKeys();
|
|
430
|
+
switch (type) {
|
|
431
|
+
case CredentialType.ID_TOKEN:
|
|
432
|
+
this.logger.infoPii(`BrowserCacheManager: removeTokenKey - attempting to remove idToken with key: ${key} from map`);
|
|
433
|
+
const idRemoval = tokenKeys.idToken.indexOf(key);
|
|
434
|
+
if (idRemoval > -1) {
|
|
435
|
+
this.logger.info("BrowserCacheManager: removeTokenKey - idToken removed from map");
|
|
436
|
+
tokenKeys.idToken.splice(idRemoval, 1);
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
this.logger.info("BrowserCacheManager: removeTokenKey - idToken does not exist in map. Either it was previously removed or it was never added.");
|
|
440
|
+
}
|
|
441
|
+
break;
|
|
442
|
+
case CredentialType.ACCESS_TOKEN:
|
|
443
|
+
this.logger.infoPii(`BrowserCacheManager: removeTokenKey - attempting to remove accessToken with key: ${key} from map`);
|
|
444
|
+
const accessRemoval = tokenKeys.accessToken.indexOf(key);
|
|
445
|
+
if (accessRemoval > -1) {
|
|
446
|
+
this.logger.info("BrowserCacheManager: removeTokenKey - accessToken removed from map");
|
|
447
|
+
tokenKeys.accessToken.splice(accessRemoval, 1);
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
this.logger.info("BrowserCacheManager: removeTokenKey - accessToken does not exist in map. Either it was previously removed or it was never added.");
|
|
451
|
+
}
|
|
452
|
+
break;
|
|
453
|
+
case CredentialType.REFRESH_TOKEN:
|
|
454
|
+
this.logger.infoPii(`BrowserCacheManager: removeTokenKey - attempting to remove refreshToken with key: ${key} from map`);
|
|
455
|
+
const refreshRemoval = tokenKeys.refreshToken.indexOf(key);
|
|
456
|
+
if (refreshRemoval > -1) {
|
|
457
|
+
this.logger.info("BrowserCacheManager: removeTokenKey - refreshToken removed from map");
|
|
458
|
+
tokenKeys.refreshToken.splice(refreshRemoval, 1);
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
this.logger.info("BrowserCacheManager: removeTokenKey - refreshToken does not exist in map. Either it was previously removed or it was never added.");
|
|
462
|
+
}
|
|
463
|
+
break;
|
|
464
|
+
default:
|
|
465
|
+
this.logger.error(`BrowserCacheManager:removeTokenKey - CredentialType provided invalid. CredentialType: ${type}`);
|
|
466
|
+
throw createClientAuthError(ClientAuthErrorCodes.unexpectedCredentialType);
|
|
467
|
+
}
|
|
468
|
+
this.setItem(`${StaticCacheKeys.TOKEN_KEYS}.${this.clientId}`, JSON.stringify(tokenKeys));
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* generates idToken entity from a string
|
|
472
|
+
* @param idTokenKey
|
|
473
|
+
*/
|
|
474
|
+
getIdTokenCredential(idTokenKey) {
|
|
475
|
+
const value = this.getItem(idTokenKey);
|
|
476
|
+
if (!value) {
|
|
477
|
+
this.logger.trace("BrowserCacheManager.getIdTokenCredential: called, no cache hit");
|
|
478
|
+
this.removeTokenKey(idTokenKey, CredentialType.ID_TOKEN);
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
const parsedIdToken = this.validateAndParseJson(value);
|
|
482
|
+
if (!parsedIdToken || !CacheHelpers.isIdTokenEntity(parsedIdToken)) {
|
|
483
|
+
this.logger.trace("BrowserCacheManager.getIdTokenCredential: called, no cache hit");
|
|
484
|
+
this.removeTokenKey(idTokenKey, CredentialType.ID_TOKEN);
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
this.logger.trace("BrowserCacheManager.getIdTokenCredential: cache hit");
|
|
488
|
+
return parsedIdToken;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* set IdToken credential to the platform cache
|
|
492
|
+
* @param idToken
|
|
493
|
+
*/
|
|
494
|
+
setIdTokenCredential(idToken) {
|
|
495
|
+
this.logger.trace("BrowserCacheManager.setIdTokenCredential called");
|
|
496
|
+
const idTokenKey = CacheHelpers.generateCredentialKey(idToken);
|
|
497
|
+
this.setItem(idTokenKey, JSON.stringify(idToken));
|
|
498
|
+
this.addTokenKey(idTokenKey, CredentialType.ID_TOKEN);
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* generates accessToken entity from a string
|
|
502
|
+
* @param key
|
|
503
|
+
*/
|
|
504
|
+
getAccessTokenCredential(accessTokenKey) {
|
|
505
|
+
const value = this.getItem(accessTokenKey);
|
|
506
|
+
if (!value) {
|
|
507
|
+
this.logger.trace("BrowserCacheManager.getAccessTokenCredential: called, no cache hit");
|
|
508
|
+
this.removeTokenKey(accessTokenKey, CredentialType.ACCESS_TOKEN);
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
const parsedAccessToken = this.validateAndParseJson(value);
|
|
512
|
+
if (!parsedAccessToken ||
|
|
513
|
+
!CacheHelpers.isAccessTokenEntity(parsedAccessToken)) {
|
|
514
|
+
this.logger.trace("BrowserCacheManager.getAccessTokenCredential: called, no cache hit");
|
|
515
|
+
this.removeTokenKey(accessTokenKey, CredentialType.ACCESS_TOKEN);
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
this.logger.trace("BrowserCacheManager.getAccessTokenCredential: cache hit");
|
|
519
|
+
return parsedAccessToken;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* set accessToken credential to the platform cache
|
|
523
|
+
* @param accessToken
|
|
524
|
+
*/
|
|
525
|
+
setAccessTokenCredential(accessToken) {
|
|
526
|
+
this.logger.trace("BrowserCacheManager.setAccessTokenCredential called");
|
|
527
|
+
const accessTokenKey = CacheHelpers.generateCredentialKey(accessToken);
|
|
528
|
+
this.setItem(accessTokenKey, JSON.stringify(accessToken));
|
|
529
|
+
this.addTokenKey(accessTokenKey, CredentialType.ACCESS_TOKEN);
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* generates refreshToken entity from a string
|
|
533
|
+
* @param refreshTokenKey
|
|
534
|
+
*/
|
|
535
|
+
getRefreshTokenCredential(refreshTokenKey) {
|
|
536
|
+
const value = this.getItem(refreshTokenKey);
|
|
537
|
+
if (!value) {
|
|
538
|
+
this.logger.trace("BrowserCacheManager.getRefreshTokenCredential: called, no cache hit");
|
|
539
|
+
this.removeTokenKey(refreshTokenKey, CredentialType.REFRESH_TOKEN);
|
|
540
|
+
return null;
|
|
541
|
+
}
|
|
542
|
+
const parsedRefreshToken = this.validateAndParseJson(value);
|
|
543
|
+
if (!parsedRefreshToken ||
|
|
544
|
+
!CacheHelpers.isRefreshTokenEntity(parsedRefreshToken)) {
|
|
545
|
+
this.logger.trace("BrowserCacheManager.getRefreshTokenCredential: called, no cache hit");
|
|
546
|
+
this.removeTokenKey(refreshTokenKey, CredentialType.REFRESH_TOKEN);
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
this.logger.trace("BrowserCacheManager.getRefreshTokenCredential: cache hit");
|
|
550
|
+
return parsedRefreshToken;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* set refreshToken credential to the platform cache
|
|
554
|
+
* @param refreshToken
|
|
555
|
+
*/
|
|
556
|
+
setRefreshTokenCredential(refreshToken) {
|
|
557
|
+
this.logger.trace("BrowserCacheManager.setRefreshTokenCredential called");
|
|
558
|
+
const refreshTokenKey = CacheHelpers.generateCredentialKey(refreshToken);
|
|
559
|
+
this.setItem(refreshTokenKey, JSON.stringify(refreshToken));
|
|
560
|
+
this.addTokenKey(refreshTokenKey, CredentialType.REFRESH_TOKEN);
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* fetch appMetadata entity from the platform cache
|
|
564
|
+
* @param appMetadataKey
|
|
565
|
+
*/
|
|
566
|
+
getAppMetadata(appMetadataKey) {
|
|
567
|
+
const value = this.getItem(appMetadataKey);
|
|
568
|
+
if (!value) {
|
|
569
|
+
this.logger.trace("BrowserCacheManager.getAppMetadata: called, no cache hit");
|
|
570
|
+
return null;
|
|
571
|
+
}
|
|
572
|
+
const parsedMetadata = this.validateAndParseJson(value);
|
|
573
|
+
if (!parsedMetadata ||
|
|
574
|
+
!CacheHelpers.isAppMetadataEntity(appMetadataKey, parsedMetadata)) {
|
|
575
|
+
this.logger.trace("BrowserCacheManager.getAppMetadata: called, no cache hit");
|
|
576
|
+
return null;
|
|
577
|
+
}
|
|
578
|
+
this.logger.trace("BrowserCacheManager.getAppMetadata: cache hit");
|
|
579
|
+
return parsedMetadata;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* set appMetadata entity to the platform cache
|
|
583
|
+
* @param appMetadata
|
|
584
|
+
*/
|
|
585
|
+
setAppMetadata(appMetadata) {
|
|
586
|
+
this.logger.trace("BrowserCacheManager.setAppMetadata called");
|
|
587
|
+
const appMetadataKey = CacheHelpers.generateAppMetadataKey(appMetadata);
|
|
588
|
+
this.setItem(appMetadataKey, JSON.stringify(appMetadata));
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* fetch server telemetry entity from the platform cache
|
|
592
|
+
* @param serverTelemetryKey
|
|
593
|
+
*/
|
|
594
|
+
getServerTelemetry(serverTelemetryKey) {
|
|
595
|
+
const value = this.getItem(serverTelemetryKey);
|
|
596
|
+
if (!value) {
|
|
597
|
+
this.logger.trace("BrowserCacheManager.getServerTelemetry: called, no cache hit");
|
|
598
|
+
return null;
|
|
599
|
+
}
|
|
600
|
+
const parsedEntity = this.validateAndParseJson(value);
|
|
601
|
+
if (!parsedEntity ||
|
|
602
|
+
!CacheHelpers.isServerTelemetryEntity(serverTelemetryKey, parsedEntity)) {
|
|
603
|
+
this.logger.trace("BrowserCacheManager.getServerTelemetry: called, no cache hit");
|
|
604
|
+
return null;
|
|
605
|
+
}
|
|
606
|
+
this.logger.trace("BrowserCacheManager.getServerTelemetry: cache hit");
|
|
607
|
+
return parsedEntity;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* set server telemetry entity to the platform cache
|
|
611
|
+
* @param serverTelemetryKey
|
|
612
|
+
* @param serverTelemetry
|
|
613
|
+
*/
|
|
614
|
+
setServerTelemetry(serverTelemetryKey, serverTelemetry) {
|
|
615
|
+
this.logger.trace("BrowserCacheManager.setServerTelemetry called");
|
|
616
|
+
this.setItem(serverTelemetryKey, JSON.stringify(serverTelemetry));
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
*
|
|
620
|
+
*/
|
|
621
|
+
getAuthorityMetadata(key) {
|
|
622
|
+
const value = this.internalStorage.getItem(key);
|
|
623
|
+
if (!value) {
|
|
624
|
+
this.logger.trace("BrowserCacheManager.getAuthorityMetadata: called, no cache hit");
|
|
625
|
+
return null;
|
|
626
|
+
}
|
|
627
|
+
const parsedMetadata = this.validateAndParseJson(value);
|
|
628
|
+
if (parsedMetadata &&
|
|
629
|
+
CacheHelpers.isAuthorityMetadataEntity(key, parsedMetadata)) {
|
|
630
|
+
this.logger.trace("BrowserCacheManager.getAuthorityMetadata: cache hit");
|
|
631
|
+
return parsedMetadata;
|
|
632
|
+
}
|
|
633
|
+
return null;
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
*
|
|
637
|
+
*/
|
|
638
|
+
getAuthorityMetadataKeys() {
|
|
639
|
+
const allKeys = this.internalStorage.getKeys();
|
|
640
|
+
return allKeys.filter((key) => {
|
|
641
|
+
return this.isAuthorityMetadata(key);
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Sets wrapper metadata in memory
|
|
646
|
+
* @param wrapperSKU
|
|
647
|
+
* @param wrapperVersion
|
|
648
|
+
*/
|
|
649
|
+
setWrapperMetadata(wrapperSKU, wrapperVersion) {
|
|
650
|
+
this.internalStorage.setItem(InMemoryCacheKeys.WRAPPER_SKU, wrapperSKU);
|
|
651
|
+
this.internalStorage.setItem(InMemoryCacheKeys.WRAPPER_VER, wrapperVersion);
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Returns wrapper metadata from in-memory storage
|
|
655
|
+
*/
|
|
656
|
+
getWrapperMetadata() {
|
|
657
|
+
const sku = this.internalStorage.getItem(InMemoryCacheKeys.WRAPPER_SKU) ||
|
|
658
|
+
Constants.EMPTY_STRING;
|
|
659
|
+
const version = this.internalStorage.getItem(InMemoryCacheKeys.WRAPPER_VER) ||
|
|
660
|
+
Constants.EMPTY_STRING;
|
|
661
|
+
return [sku, version];
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
*
|
|
665
|
+
* @param entity
|
|
666
|
+
*/
|
|
667
|
+
setAuthorityMetadata(key, entity) {
|
|
668
|
+
this.logger.trace("BrowserCacheManager.setAuthorityMetadata called");
|
|
669
|
+
this.internalStorage.setItem(key, JSON.stringify(entity));
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Gets the active account
|
|
673
|
+
*/
|
|
674
|
+
getActiveAccount() {
|
|
675
|
+
const activeAccountKeyFilters = this.generateCacheKey(PersistentCacheKeys.ACTIVE_ACCOUNT_FILTERS);
|
|
676
|
+
const activeAccountValueFilters = this.getItem(activeAccountKeyFilters);
|
|
677
|
+
if (!activeAccountValueFilters) {
|
|
678
|
+
// if new active account cache type isn't found, it's an old version, so look for that instead
|
|
679
|
+
this.logger.trace("BrowserCacheManager.getActiveAccount: No active account filters cache schema found, looking for legacy schema");
|
|
680
|
+
const activeAccountKeyLocal = this.generateCacheKey(PersistentCacheKeys.ACTIVE_ACCOUNT);
|
|
681
|
+
const activeAccountValueLocal = this.getItem(activeAccountKeyLocal);
|
|
682
|
+
if (!activeAccountValueLocal) {
|
|
683
|
+
this.logger.trace("BrowserCacheManager.getActiveAccount: No active account found");
|
|
684
|
+
return null;
|
|
685
|
+
}
|
|
686
|
+
const activeAccount = this.getAccountInfoFilteredBy({
|
|
687
|
+
localAccountId: activeAccountValueLocal,
|
|
688
|
+
});
|
|
689
|
+
if (activeAccount) {
|
|
690
|
+
this.logger.trace("BrowserCacheManager.getActiveAccount: Legacy active account cache schema found");
|
|
691
|
+
this.logger.trace("BrowserCacheManager.getActiveAccount: Adding active account filters cache schema");
|
|
692
|
+
this.setActiveAccount(activeAccount);
|
|
693
|
+
return activeAccount;
|
|
694
|
+
}
|
|
695
|
+
return null;
|
|
696
|
+
}
|
|
697
|
+
const activeAccountValueObj = this.validateAndParseJson(activeAccountValueFilters);
|
|
698
|
+
if (activeAccountValueObj) {
|
|
699
|
+
this.logger.trace("BrowserCacheManager.getActiveAccount: Active account filters schema found");
|
|
700
|
+
return this.getAccountInfoFilteredBy({
|
|
701
|
+
homeAccountId: activeAccountValueObj.homeAccountId,
|
|
702
|
+
localAccountId: activeAccountValueObj.localAccountId,
|
|
703
|
+
tenantId: activeAccountValueObj.tenantId,
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
this.logger.trace("BrowserCacheManager.getActiveAccount: No active account found");
|
|
707
|
+
return null;
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Sets the active account's localAccountId in cache
|
|
711
|
+
* @param account
|
|
712
|
+
*/
|
|
713
|
+
setActiveAccount(account) {
|
|
714
|
+
const activeAccountKey = this.generateCacheKey(PersistentCacheKeys.ACTIVE_ACCOUNT_FILTERS);
|
|
715
|
+
const activeAccountKeyLocal = this.generateCacheKey(PersistentCacheKeys.ACTIVE_ACCOUNT);
|
|
716
|
+
if (account) {
|
|
717
|
+
this.logger.verbose("setActiveAccount: Active account set");
|
|
718
|
+
const activeAccountValue = {
|
|
719
|
+
homeAccountId: account.homeAccountId,
|
|
720
|
+
localAccountId: account.localAccountId,
|
|
721
|
+
tenantId: account.tenantId,
|
|
722
|
+
};
|
|
723
|
+
this.browserStorage.setItem(activeAccountKey, JSON.stringify(activeAccountValue));
|
|
724
|
+
this.browserStorage.setItem(activeAccountKeyLocal, account.localAccountId);
|
|
725
|
+
}
|
|
726
|
+
else {
|
|
727
|
+
this.logger.verbose("setActiveAccount: No account passed, active account not set");
|
|
728
|
+
this.browserStorage.removeItem(activeAccountKey);
|
|
729
|
+
this.browserStorage.removeItem(activeAccountKeyLocal);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* fetch throttling entity from the platform cache
|
|
734
|
+
* @param throttlingCacheKey
|
|
735
|
+
*/
|
|
736
|
+
getThrottlingCache(throttlingCacheKey) {
|
|
737
|
+
const value = this.getItem(throttlingCacheKey);
|
|
738
|
+
if (!value) {
|
|
739
|
+
this.logger.trace("BrowserCacheManager.getThrottlingCache: called, no cache hit");
|
|
740
|
+
return null;
|
|
741
|
+
}
|
|
742
|
+
const parsedThrottlingCache = this.validateAndParseJson(value);
|
|
743
|
+
if (!parsedThrottlingCache ||
|
|
744
|
+
!CacheHelpers.isThrottlingEntity(throttlingCacheKey, parsedThrottlingCache)) {
|
|
745
|
+
this.logger.trace("BrowserCacheManager.getThrottlingCache: called, no cache hit");
|
|
746
|
+
return null;
|
|
747
|
+
}
|
|
748
|
+
this.logger.trace("BrowserCacheManager.getThrottlingCache: cache hit");
|
|
749
|
+
return parsedThrottlingCache;
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* set throttling entity to the platform cache
|
|
753
|
+
* @param throttlingCacheKey
|
|
754
|
+
* @param throttlingCache
|
|
755
|
+
*/
|
|
756
|
+
setThrottlingCache(throttlingCacheKey, throttlingCache) {
|
|
757
|
+
this.logger.trace("BrowserCacheManager.setThrottlingCache called");
|
|
758
|
+
this.setItem(throttlingCacheKey, JSON.stringify(throttlingCache));
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Gets cache item with given key.
|
|
762
|
+
* Will retrieve from cookies if storeAuthStateInCookie is set to true.
|
|
763
|
+
* @param key
|
|
764
|
+
*/
|
|
765
|
+
getTemporaryCache(cacheKey, generateKey) {
|
|
766
|
+
const key = generateKey ? this.generateCacheKey(cacheKey) : cacheKey;
|
|
767
|
+
if (this.cacheConfig.storeAuthStateInCookie) {
|
|
768
|
+
const itemCookie = this.getItemCookie(key);
|
|
769
|
+
if (itemCookie) {
|
|
770
|
+
this.logger.trace("BrowserCacheManager.getTemporaryCache: storeAuthStateInCookies set to true, retrieving from cookies");
|
|
771
|
+
return itemCookie;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
const value = this.temporaryCacheStorage.getItem(key);
|
|
775
|
+
if (!value) {
|
|
776
|
+
// If temp cache item not found in session/memory, check local storage for items set by old versions
|
|
777
|
+
if (this.cacheConfig.cacheLocation ===
|
|
778
|
+
BrowserCacheLocation.LocalStorage) {
|
|
779
|
+
const item = this.browserStorage.getItem(key);
|
|
780
|
+
if (item) {
|
|
781
|
+
this.logger.trace("BrowserCacheManager.getTemporaryCache: Temporary cache item found in local storage");
|
|
782
|
+
return item;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
this.logger.trace("BrowserCacheManager.getTemporaryCache: No cache item found in local storage");
|
|
786
|
+
return null;
|
|
787
|
+
}
|
|
788
|
+
this.logger.trace("BrowserCacheManager.getTemporaryCache: Temporary cache item returned");
|
|
789
|
+
return value;
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Sets the cache item with the key and value given.
|
|
793
|
+
* Stores in cookie if storeAuthStateInCookie is set to true.
|
|
794
|
+
* This can cause cookie overflow if used incorrectly.
|
|
795
|
+
* @param key
|
|
796
|
+
* @param value
|
|
797
|
+
*/
|
|
798
|
+
setTemporaryCache(cacheKey, value, generateKey) {
|
|
799
|
+
const key = generateKey ? this.generateCacheKey(cacheKey) : cacheKey;
|
|
800
|
+
this.temporaryCacheStorage.setItem(key, value);
|
|
801
|
+
if (this.cacheConfig.storeAuthStateInCookie) {
|
|
802
|
+
this.logger.trace("BrowserCacheManager.setTemporaryCache: storeAuthStateInCookie set to true, setting item cookie");
|
|
803
|
+
this.setItemCookie(key, value);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Removes the cache item with the given key.
|
|
808
|
+
* @param key
|
|
809
|
+
*/
|
|
810
|
+
removeItem(key) {
|
|
811
|
+
this.browserStorage.removeItem(key);
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Removes the temporary cache item with the given key.
|
|
815
|
+
* Will also clear the cookie item if storeAuthStateInCookie is set to true.
|
|
816
|
+
* @param key
|
|
817
|
+
*/
|
|
818
|
+
removeTemporaryItem(key) {
|
|
819
|
+
this.temporaryCacheStorage.removeItem(key);
|
|
820
|
+
if (this.cacheConfig.storeAuthStateInCookie) {
|
|
821
|
+
this.logger.trace("BrowserCacheManager.removeItem: storeAuthStateInCookie is true, clearing item cookie");
|
|
822
|
+
this.clearItemCookie(key);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* Gets all keys in window.
|
|
827
|
+
*/
|
|
828
|
+
getKeys() {
|
|
829
|
+
return this.browserStorage.getKeys();
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* Clears all cache entries created by MSAL.
|
|
833
|
+
*/
|
|
834
|
+
async clear() {
|
|
835
|
+
// Removes all accounts and their credentials
|
|
836
|
+
await this.removeAllAccounts();
|
|
837
|
+
this.removeAppMetadata();
|
|
838
|
+
// Remove temp storage first to make sure any cookies are cleared
|
|
839
|
+
this.temporaryCacheStorage.getKeys().forEach((cacheKey) => {
|
|
840
|
+
if (cacheKey.indexOf(Constants.CACHE_PREFIX) !== -1 ||
|
|
841
|
+
cacheKey.indexOf(this.clientId) !== -1) {
|
|
842
|
+
this.removeTemporaryItem(cacheKey);
|
|
843
|
+
}
|
|
844
|
+
});
|
|
845
|
+
// Removes all remaining MSAL cache items
|
|
846
|
+
this.browserStorage.getKeys().forEach((cacheKey) => {
|
|
847
|
+
if (cacheKey.indexOf(Constants.CACHE_PREFIX) !== -1 ||
|
|
848
|
+
cacheKey.indexOf(this.clientId) !== -1) {
|
|
849
|
+
this.browserStorage.removeItem(cacheKey);
|
|
850
|
+
}
|
|
851
|
+
});
|
|
852
|
+
this.internalStorage.clear();
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Clears all access tokes that have claims prior to saving the current one
|
|
856
|
+
* @param performanceClient {IPerformanceClient}
|
|
857
|
+
* @param correlationId {string} correlation id
|
|
858
|
+
* @returns
|
|
859
|
+
*/
|
|
860
|
+
async clearTokensAndKeysWithClaims(performanceClient, correlationId) {
|
|
861
|
+
performanceClient.addQueueMeasurement(PerformanceEvents.ClearTokensAndKeysWithClaims, correlationId);
|
|
862
|
+
const tokenKeys = this.getTokenKeys();
|
|
863
|
+
const removedAccessTokens = [];
|
|
864
|
+
tokenKeys.accessToken.forEach((key) => {
|
|
865
|
+
// if the access token has claims in its key, remove the token key and the token
|
|
866
|
+
const credential = this.getAccessTokenCredential(key);
|
|
867
|
+
if (credential?.requestedClaimsHash &&
|
|
868
|
+
key.includes(credential.requestedClaimsHash.toLowerCase())) {
|
|
869
|
+
removedAccessTokens.push(this.removeAccessToken(key));
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
await Promise.all(removedAccessTokens);
|
|
873
|
+
// warn if any access tokens are removed
|
|
874
|
+
if (removedAccessTokens.length > 0) {
|
|
875
|
+
this.logger.warning(`${removedAccessTokens.length} access tokens with claims in the cache keys have been removed from the cache.`);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Add value to cookies
|
|
880
|
+
* @param cookieName
|
|
881
|
+
* @param cookieValue
|
|
882
|
+
* @param expires
|
|
883
|
+
* @deprecated
|
|
884
|
+
*/
|
|
885
|
+
setItemCookie(cookieName, cookieValue, expires) {
|
|
886
|
+
let cookieStr = `${encodeURIComponent(cookieName)}=${encodeURIComponent(cookieValue)};path=/;SameSite=Lax;`;
|
|
887
|
+
if (expires) {
|
|
888
|
+
const expireTime = this.getCookieExpirationTime(expires);
|
|
889
|
+
cookieStr += `expires=${expireTime};`;
|
|
890
|
+
}
|
|
891
|
+
if (this.cacheConfig.secureCookies) {
|
|
892
|
+
cookieStr += "Secure;";
|
|
893
|
+
}
|
|
894
|
+
document.cookie = cookieStr;
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Get one item by key from cookies
|
|
898
|
+
* @param cookieName
|
|
899
|
+
* @deprecated
|
|
900
|
+
*/
|
|
901
|
+
getItemCookie(cookieName) {
|
|
902
|
+
const name = `${encodeURIComponent(cookieName)}=`;
|
|
903
|
+
const cookieList = document.cookie.split(";");
|
|
904
|
+
for (let i = 0; i < cookieList.length; i++) {
|
|
905
|
+
let cookie = cookieList[i];
|
|
906
|
+
while (cookie.charAt(0) === " ") {
|
|
907
|
+
cookie = cookie.substring(1);
|
|
908
|
+
}
|
|
909
|
+
if (cookie.indexOf(name) === 0) {
|
|
910
|
+
return decodeURIComponent(cookie.substring(name.length, cookie.length));
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
return Constants.EMPTY_STRING;
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* Clear all msal-related cookies currently set in the browser. Should only be used to clear temporary cache items.
|
|
917
|
+
* @deprecated
|
|
918
|
+
*/
|
|
919
|
+
clearMsalCookies() {
|
|
920
|
+
const cookiePrefix = `${Constants.CACHE_PREFIX}.${this.clientId}`;
|
|
921
|
+
const cookieList = document.cookie.split(";");
|
|
922
|
+
cookieList.forEach((cookie) => {
|
|
923
|
+
while (cookie.charAt(0) === " ") {
|
|
924
|
+
// eslint-disable-next-line no-param-reassign
|
|
925
|
+
cookie = cookie.substring(1);
|
|
926
|
+
}
|
|
927
|
+
if (cookie.indexOf(cookiePrefix) === 0) {
|
|
928
|
+
const cookieKey = cookie.split("=")[0];
|
|
929
|
+
this.clearItemCookie(cookieKey);
|
|
930
|
+
}
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
934
|
+
* Clear an item in the cookies by key
|
|
935
|
+
* @param cookieName
|
|
936
|
+
* @deprecated
|
|
937
|
+
*/
|
|
938
|
+
clearItemCookie(cookieName) {
|
|
939
|
+
this.setItemCookie(cookieName, Constants.EMPTY_STRING, -1);
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* Get cookie expiration time
|
|
943
|
+
* @param cookieLifeDays
|
|
944
|
+
* @deprecated
|
|
945
|
+
*/
|
|
946
|
+
getCookieExpirationTime(cookieLifeDays) {
|
|
947
|
+
const today = new Date();
|
|
948
|
+
const expr = new Date(today.getTime() + cookieLifeDays * this.COOKIE_LIFE_MULTIPLIER);
|
|
949
|
+
return expr.toUTCString();
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* 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)
|
|
953
|
+
* @param key
|
|
954
|
+
* @param addInstanceId
|
|
955
|
+
*/
|
|
956
|
+
generateCacheKey(key) {
|
|
957
|
+
const generatedKey = this.validateAndParseJson(key);
|
|
958
|
+
if (!generatedKey) {
|
|
959
|
+
if (StringUtils.startsWith(key, Constants.CACHE_PREFIX) ||
|
|
960
|
+
StringUtils.startsWith(key, PersistentCacheKeys.ADAL_ID_TOKEN)) {
|
|
961
|
+
return key;
|
|
962
|
+
}
|
|
963
|
+
return `${Constants.CACHE_PREFIX}.${this.clientId}.${key}`;
|
|
964
|
+
}
|
|
965
|
+
return JSON.stringify(key);
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* Create authorityKey to cache authority
|
|
969
|
+
* @param state
|
|
970
|
+
*/
|
|
971
|
+
generateAuthorityKey(stateString) {
|
|
972
|
+
const { libraryState: { id: stateId }, } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
|
|
973
|
+
return this.generateCacheKey(`${TemporaryCacheKeys.AUTHORITY}.${stateId}`);
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Create Nonce key to cache nonce
|
|
977
|
+
* @param state
|
|
978
|
+
*/
|
|
979
|
+
generateNonceKey(stateString) {
|
|
980
|
+
const { libraryState: { id: stateId }, } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
|
|
981
|
+
return this.generateCacheKey(`${TemporaryCacheKeys.NONCE_IDTOKEN}.${stateId}`);
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* Creates full cache key for the request state
|
|
985
|
+
* @param stateString State string for the request
|
|
986
|
+
*/
|
|
987
|
+
generateStateKey(stateString) {
|
|
988
|
+
// Use the library state id to key temp storage for uniqueness for multiple concurrent requests
|
|
989
|
+
const { libraryState: { id: stateId }, } = ProtocolUtils.parseRequestState(this.cryptoImpl, stateString);
|
|
990
|
+
return this.generateCacheKey(`${TemporaryCacheKeys.REQUEST_STATE}.${stateId}`);
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* Gets the cached authority based on the cached state. Returns empty if no cached state found.
|
|
994
|
+
*/
|
|
995
|
+
getCachedAuthority(cachedState) {
|
|
996
|
+
const stateCacheKey = this.generateStateKey(cachedState);
|
|
997
|
+
const state = this.getTemporaryCache(stateCacheKey);
|
|
998
|
+
if (!state) {
|
|
999
|
+
return null;
|
|
1000
|
+
}
|
|
1001
|
+
const authorityCacheKey = this.generateAuthorityKey(state);
|
|
1002
|
+
return this.getTemporaryCache(authorityCacheKey);
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* Updates account, authority, and state in cache
|
|
1006
|
+
* @param serverAuthenticationRequest
|
|
1007
|
+
* @param account
|
|
1008
|
+
*/
|
|
1009
|
+
updateCacheEntries(state, nonce, authorityInstance, loginHint, account) {
|
|
1010
|
+
this.logger.trace("BrowserCacheManager.updateCacheEntries called");
|
|
1011
|
+
// Cache the request state
|
|
1012
|
+
const stateCacheKey = this.generateStateKey(state);
|
|
1013
|
+
this.setTemporaryCache(stateCacheKey, state, false);
|
|
1014
|
+
// Cache the nonce
|
|
1015
|
+
const nonceCacheKey = this.generateNonceKey(state);
|
|
1016
|
+
this.setTemporaryCache(nonceCacheKey, nonce, false);
|
|
1017
|
+
// Cache authorityKey
|
|
1018
|
+
const authorityCacheKey = this.generateAuthorityKey(state);
|
|
1019
|
+
this.setTemporaryCache(authorityCacheKey, authorityInstance, false);
|
|
1020
|
+
if (account) {
|
|
1021
|
+
const ccsCredential = {
|
|
1022
|
+
credential: account.homeAccountId,
|
|
1023
|
+
type: CcsCredentialType.HOME_ACCOUNT_ID,
|
|
1024
|
+
};
|
|
1025
|
+
this.setTemporaryCache(TemporaryCacheKeys.CCS_CREDENTIAL, JSON.stringify(ccsCredential), true);
|
|
1026
|
+
}
|
|
1027
|
+
else if (loginHint) {
|
|
1028
|
+
const ccsCredential = {
|
|
1029
|
+
credential: loginHint,
|
|
1030
|
+
type: CcsCredentialType.UPN,
|
|
1031
|
+
};
|
|
1032
|
+
this.setTemporaryCache(TemporaryCacheKeys.CCS_CREDENTIAL, JSON.stringify(ccsCredential), true);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
/**
|
|
1036
|
+
* Reset all temporary cache items
|
|
1037
|
+
* @param state
|
|
1038
|
+
*/
|
|
1039
|
+
resetRequestCache(state) {
|
|
1040
|
+
this.logger.trace("BrowserCacheManager.resetRequestCache called");
|
|
1041
|
+
// check state and remove associated cache items
|
|
1042
|
+
if (state) {
|
|
1043
|
+
this.temporaryCacheStorage.getKeys().forEach((key) => {
|
|
1044
|
+
if (key.indexOf(state) !== -1) {
|
|
1045
|
+
this.removeTemporaryItem(key);
|
|
1046
|
+
}
|
|
1047
|
+
});
|
|
1048
|
+
// delete generic interactive request parameters
|
|
1049
|
+
this.removeTemporaryItem(this.generateStateKey(state));
|
|
1050
|
+
this.removeTemporaryItem(this.generateNonceKey(state));
|
|
1051
|
+
this.removeTemporaryItem(this.generateAuthorityKey(state));
|
|
1052
|
+
}
|
|
1053
|
+
this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.REQUEST_PARAMS));
|
|
1054
|
+
this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.ORIGIN_URI));
|
|
1055
|
+
this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.URL_HASH));
|
|
1056
|
+
this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.CORRELATION_ID));
|
|
1057
|
+
this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.CCS_CREDENTIAL));
|
|
1058
|
+
this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.NATIVE_REQUEST));
|
|
1059
|
+
this.setInteractionInProgress(false);
|
|
1060
|
+
}
|
|
1061
|
+
/**
|
|
1062
|
+
* Removes temporary cache for the provided state
|
|
1063
|
+
* @param stateString
|
|
1064
|
+
*/
|
|
1065
|
+
cleanRequestByState(stateString) {
|
|
1066
|
+
this.logger.trace("BrowserCacheManager.cleanRequestByState called");
|
|
1067
|
+
// Interaction is completed - remove interaction status.
|
|
1068
|
+
if (stateString) {
|
|
1069
|
+
const stateKey = this.generateStateKey(stateString);
|
|
1070
|
+
const cachedState = this.temporaryCacheStorage.getItem(stateKey);
|
|
1071
|
+
this.logger.infoPii(`BrowserCacheManager.cleanRequestByState: Removing temporary cache items for state: ${cachedState}`);
|
|
1072
|
+
this.resetRequestCache(cachedState || Constants.EMPTY_STRING);
|
|
1073
|
+
}
|
|
1074
|
+
this.clearMsalCookies();
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Looks in temporary cache for any state values with the provided interactionType and removes all temporary cache items for that state
|
|
1078
|
+
* Used in scenarios where temp cache needs to be cleaned but state is not known, such as clicking browser back button.
|
|
1079
|
+
* @param interactionType
|
|
1080
|
+
*/
|
|
1081
|
+
cleanRequestByInteractionType(interactionType) {
|
|
1082
|
+
this.logger.trace("BrowserCacheManager.cleanRequestByInteractionType called");
|
|
1083
|
+
// Loop through all keys to find state key
|
|
1084
|
+
this.temporaryCacheStorage.getKeys().forEach((key) => {
|
|
1085
|
+
// If this key is not the state key, move on
|
|
1086
|
+
if (key.indexOf(TemporaryCacheKeys.REQUEST_STATE) === -1) {
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
// Retrieve state value, return if not a valid value
|
|
1090
|
+
const stateValue = this.temporaryCacheStorage.getItem(key);
|
|
1091
|
+
if (!stateValue) {
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
// Extract state and ensure it matches given InteractionType, then clean request cache
|
|
1095
|
+
const parsedState = extractBrowserRequestState(this.cryptoImpl, stateValue);
|
|
1096
|
+
if (parsedState &&
|
|
1097
|
+
parsedState.interactionType === interactionType) {
|
|
1098
|
+
this.logger.infoPii(`BrowserCacheManager.cleanRequestByInteractionType: Removing temporary cache items for state: ${stateValue}`);
|
|
1099
|
+
this.resetRequestCache(stateValue);
|
|
1100
|
+
}
|
|
1101
|
+
});
|
|
1102
|
+
this.clearMsalCookies();
|
|
1103
|
+
this.setInteractionInProgress(false);
|
|
1104
|
+
}
|
|
1105
|
+
cacheCodeRequest(authCodeRequest) {
|
|
1106
|
+
this.logger.trace("BrowserCacheManager.cacheCodeRequest called");
|
|
1107
|
+
const encodedValue = base64Encode(JSON.stringify(authCodeRequest));
|
|
1108
|
+
this.setTemporaryCache(TemporaryCacheKeys.REQUEST_PARAMS, encodedValue, true);
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
* Gets the token exchange parameters from the cache. Throws an error if nothing is found.
|
|
1112
|
+
*/
|
|
1113
|
+
getCachedRequest(state) {
|
|
1114
|
+
this.logger.trace("BrowserCacheManager.getCachedRequest called");
|
|
1115
|
+
// Get token request from cache and parse as TokenExchangeParameters.
|
|
1116
|
+
const encodedTokenRequest = this.getTemporaryCache(TemporaryCacheKeys.REQUEST_PARAMS, true);
|
|
1117
|
+
if (!encodedTokenRequest) {
|
|
1118
|
+
throw createBrowserAuthError(noTokenRequestCacheError);
|
|
1119
|
+
}
|
|
1120
|
+
let parsedRequest;
|
|
1121
|
+
try {
|
|
1122
|
+
parsedRequest = JSON.parse(base64Decode(encodedTokenRequest));
|
|
1123
|
+
}
|
|
1124
|
+
catch (e) {
|
|
1125
|
+
this.logger.errorPii(`Attempted to parse: ${encodedTokenRequest}`);
|
|
1126
|
+
this.logger.error(`Parsing cached token request threw with error: ${e}`);
|
|
1127
|
+
throw createBrowserAuthError(unableToParseTokenRequestCacheError);
|
|
1128
|
+
}
|
|
1129
|
+
this.removeTemporaryItem(this.generateCacheKey(TemporaryCacheKeys.REQUEST_PARAMS));
|
|
1130
|
+
// Get cached authority and use if no authority is cached with request.
|
|
1131
|
+
if (!parsedRequest.authority) {
|
|
1132
|
+
const authorityCacheKey = this.generateAuthorityKey(state);
|
|
1133
|
+
const cachedAuthority = this.getTemporaryCache(authorityCacheKey);
|
|
1134
|
+
if (!cachedAuthority) {
|
|
1135
|
+
throw createBrowserAuthError(noCachedAuthorityError);
|
|
1136
|
+
}
|
|
1137
|
+
parsedRequest.authority = cachedAuthority;
|
|
1138
|
+
}
|
|
1139
|
+
return parsedRequest;
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* Gets cached native request for redirect flows
|
|
1143
|
+
*/
|
|
1144
|
+
getCachedNativeRequest() {
|
|
1145
|
+
this.logger.trace("BrowserCacheManager.getCachedNativeRequest called");
|
|
1146
|
+
const cachedRequest = this.getTemporaryCache(TemporaryCacheKeys.NATIVE_REQUEST, true);
|
|
1147
|
+
if (!cachedRequest) {
|
|
1148
|
+
this.logger.trace("BrowserCacheManager.getCachedNativeRequest: No cached native request found");
|
|
1149
|
+
return null;
|
|
1150
|
+
}
|
|
1151
|
+
const parsedRequest = this.validateAndParseJson(cachedRequest);
|
|
1152
|
+
if (!parsedRequest) {
|
|
1153
|
+
this.logger.error("BrowserCacheManager.getCachedNativeRequest: Unable to parse native request");
|
|
1154
|
+
return null;
|
|
1155
|
+
}
|
|
1156
|
+
return parsedRequest;
|
|
1157
|
+
}
|
|
1158
|
+
isInteractionInProgress(matchClientId) {
|
|
1159
|
+
const clientId = this.getInteractionInProgress();
|
|
1160
|
+
if (matchClientId) {
|
|
1161
|
+
return clientId === this.clientId;
|
|
1162
|
+
}
|
|
1163
|
+
else {
|
|
1164
|
+
return !!clientId;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
getInteractionInProgress() {
|
|
1168
|
+
const key = `${Constants.CACHE_PREFIX}.${TemporaryCacheKeys.INTERACTION_STATUS_KEY}`;
|
|
1169
|
+
return this.getTemporaryCache(key, false);
|
|
1170
|
+
}
|
|
1171
|
+
setInteractionInProgress(inProgress) {
|
|
1172
|
+
// Ensure we don't overwrite interaction in progress for a different clientId
|
|
1173
|
+
const key = `${Constants.CACHE_PREFIX}.${TemporaryCacheKeys.INTERACTION_STATUS_KEY}`;
|
|
1174
|
+
if (inProgress) {
|
|
1175
|
+
if (this.getInteractionInProgress()) {
|
|
1176
|
+
throw createBrowserAuthError(interactionInProgress);
|
|
1177
|
+
}
|
|
1178
|
+
else {
|
|
1179
|
+
// No interaction is in progress
|
|
1180
|
+
this.setTemporaryCache(key, this.clientId, false);
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
else if (!inProgress &&
|
|
1184
|
+
this.getInteractionInProgress() === this.clientId) {
|
|
1185
|
+
this.removeTemporaryItem(key);
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Returns username retrieved from ADAL or MSAL v1 idToken
|
|
1190
|
+
* @deprecated
|
|
1191
|
+
*/
|
|
1192
|
+
getLegacyLoginHint() {
|
|
1193
|
+
// Only check for adal/msal token if no SSO params are being used
|
|
1194
|
+
const adalIdTokenString = this.getTemporaryCache(PersistentCacheKeys.ADAL_ID_TOKEN);
|
|
1195
|
+
if (adalIdTokenString) {
|
|
1196
|
+
this.browserStorage.removeItem(PersistentCacheKeys.ADAL_ID_TOKEN);
|
|
1197
|
+
this.logger.verbose("Cached ADAL id token retrieved.");
|
|
1198
|
+
}
|
|
1199
|
+
// Check for cached MSAL v1 id token
|
|
1200
|
+
const msalIdTokenString = this.getTemporaryCache(PersistentCacheKeys.ID_TOKEN, true);
|
|
1201
|
+
if (msalIdTokenString) {
|
|
1202
|
+
this.browserStorage.removeItem(this.generateCacheKey(PersistentCacheKeys.ID_TOKEN));
|
|
1203
|
+
this.logger.verbose("Cached MSAL.js v1 id token retrieved");
|
|
1204
|
+
}
|
|
1205
|
+
const cachedIdTokenString = msalIdTokenString || adalIdTokenString;
|
|
1206
|
+
if (cachedIdTokenString) {
|
|
1207
|
+
const idTokenClaims = AuthToken.extractTokenClaims(cachedIdTokenString, base64Decode);
|
|
1208
|
+
if (idTokenClaims.preferred_username) {
|
|
1209
|
+
this.logger.verbose("No SSO params used and ADAL/MSAL v1 token retrieved, setting ADAL/MSAL v1 preferred_username as loginHint");
|
|
1210
|
+
return idTokenClaims.preferred_username;
|
|
1211
|
+
}
|
|
1212
|
+
else if (idTokenClaims.upn) {
|
|
1213
|
+
this.logger.verbose("No SSO params used and ADAL/MSAL v1 token retrieved, setting ADAL/MSAL v1 upn as loginHint");
|
|
1214
|
+
return idTokenClaims.upn;
|
|
1215
|
+
}
|
|
1216
|
+
else {
|
|
1217
|
+
this.logger.verbose("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.");
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
return null;
|
|
1221
|
+
}
|
|
1222
|
+
/**
|
|
1223
|
+
* Updates a credential's cache key if the current cache key is outdated
|
|
1224
|
+
*/
|
|
1225
|
+
updateCredentialCacheKey(currentCacheKey, credential) {
|
|
1226
|
+
const updatedCacheKey = CacheHelpers.generateCredentialKey(credential);
|
|
1227
|
+
if (currentCacheKey !== updatedCacheKey) {
|
|
1228
|
+
const cacheItem = this.getItem(currentCacheKey);
|
|
1229
|
+
if (cacheItem) {
|
|
1230
|
+
this.browserStorage.removeItem(currentCacheKey);
|
|
1231
|
+
this.setItem(updatedCacheKey, cacheItem);
|
|
1232
|
+
this.logger.verbose(`Updated an outdated ${credential.credentialType} cache key`);
|
|
1233
|
+
return updatedCacheKey;
|
|
1234
|
+
}
|
|
1235
|
+
else {
|
|
1236
|
+
this.logger.error(`Attempted to update an outdated ${credential.credentialType} cache key but no item matching the outdated key was found in storage`);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
return currentCacheKey;
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Builds credential entities from AuthenticationResult object and saves the resulting credentials to the cache
|
|
1243
|
+
* @param result
|
|
1244
|
+
* @param request
|
|
1245
|
+
*/
|
|
1246
|
+
async hydrateCache(result, request) {
|
|
1247
|
+
const idTokenEntity = CacheHelpers.createIdTokenEntity(result.account?.homeAccountId, result.account?.environment, result.idToken, this.clientId, result.tenantId);
|
|
1248
|
+
let claimsHash;
|
|
1249
|
+
if (request.claims) {
|
|
1250
|
+
claimsHash = await this.cryptoImpl.hashString(request.claims);
|
|
1251
|
+
}
|
|
1252
|
+
/**
|
|
1253
|
+
* meta data for cache stores time in seconds from epoch
|
|
1254
|
+
* AuthenticationResult returns expiresOn and extExpiresOn in milliseconds (as a Date object which is in ms)
|
|
1255
|
+
* We need to map these for the cache when building tokens from AuthenticationResult
|
|
1256
|
+
*
|
|
1257
|
+
* The next MSAL VFuture should map these both to same value if possible
|
|
1258
|
+
*/
|
|
1259
|
+
const accessTokenEntity = CacheHelpers.createAccessTokenEntity(result.account?.homeAccountId, result.account.environment, result.accessToken, this.clientId, result.tenantId, result.scopes.join(" "), result.expiresOn ? result.expiresOn.getTime() / 1000 : 0, result.extExpiresOn ? result.extExpiresOn.getTime() / 1000 : 0, base64Decode, undefined, // refreshOn
|
|
1260
|
+
result.tokenType, undefined, // userAssertionHash
|
|
1261
|
+
request.sshKid, request.claims, claimsHash);
|
|
1262
|
+
const cacheRecord = {
|
|
1263
|
+
idToken: idTokenEntity,
|
|
1264
|
+
accessToken: accessTokenEntity,
|
|
1265
|
+
};
|
|
1266
|
+
return this.saveCacheRecord(cacheRecord);
|
|
1267
|
+
}
|
|
1268
|
+
/**
|
|
1269
|
+
* saves a cache record
|
|
1270
|
+
* @param cacheRecord {CacheRecord}
|
|
1271
|
+
* @param storeInCache {?StoreInCache}
|
|
1272
|
+
* @param correlationId {?string} correlation id
|
|
1273
|
+
*/
|
|
1274
|
+
async saveCacheRecord(cacheRecord, storeInCache, correlationId) {
|
|
1275
|
+
try {
|
|
1276
|
+
await super.saveCacheRecord(cacheRecord, storeInCache, correlationId);
|
|
1277
|
+
}
|
|
1278
|
+
catch (e) {
|
|
1279
|
+
if (e instanceof CacheError &&
|
|
1280
|
+
this.performanceClient &&
|
|
1281
|
+
correlationId) {
|
|
1282
|
+
try {
|
|
1283
|
+
const tokenKeys = this.getTokenKeys();
|
|
1284
|
+
this.performanceClient.addFields({
|
|
1285
|
+
cacheRtCount: tokenKeys.refreshToken.length,
|
|
1286
|
+
cacheIdCount: tokenKeys.idToken.length,
|
|
1287
|
+
cacheAtCount: tokenKeys.accessToken.length,
|
|
1288
|
+
}, correlationId);
|
|
1289
|
+
}
|
|
1290
|
+
catch (e) { }
|
|
1291
|
+
}
|
|
1292
|
+
throw e;
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
const DEFAULT_BROWSER_CACHE_MANAGER = (clientId, logger) => {
|
|
1297
|
+
const cacheOptions = {
|
|
1298
|
+
cacheLocation: BrowserCacheLocation.MemoryStorage,
|
|
1299
|
+
temporaryCacheLocation: BrowserCacheLocation.MemoryStorage,
|
|
1300
|
+
storeAuthStateInCookie: false,
|
|
1301
|
+
secureCookies: false,
|
|
1302
|
+
cacheMigrationEnabled: false,
|
|
1303
|
+
claimsBasedCachingEnabled: false,
|
|
1304
|
+
};
|
|
1305
|
+
return new BrowserCacheManager(clientId, cacheOptions, DEFAULT_CRYPTO_IMPLEMENTATION, logger);
|
|
1306
1306
|
};
|
|
1307
1307
|
|
|
1308
1308
|
export { BrowserCacheManager, DEFAULT_BROWSER_CACHE_MANAGER };
|